Merge "Move dimens from overlay to framework"
diff --git a/Android.bp b/Android.bp
index b64907a..0547481 100644
--- a/Android.bp
+++ b/Android.bp
@@ -414,6 +414,7 @@
         "libcore-platform-compat-config",
         "services-platform-compat-config",
         "media-provider-platform-compat-config",
+        "services-devicepolicy-platform-compat-config",
     ],
     sdk_version: "core_platform",
 }
@@ -1595,4 +1596,4 @@
         "core/java/com/android/internal/util/State.java",
         "core/java/com/android/internal/util/StateMachine.java",
     ],
-}
\ No newline at end of file
+}
diff --git a/apct-tests/perftests/autofill/src/android/view/autofill/AbstractAutofillPerfTestCase.java b/apct-tests/perftests/autofill/src/android/view/autofill/AbstractAutofillPerfTestCase.java
index 6979f0f..48ce8ab 100644
--- a/apct-tests/perftests/autofill/src/android/view/autofill/AbstractAutofillPerfTestCase.java
+++ b/apct-tests/perftests/autofill/src/android/view/autofill/AbstractAutofillPerfTestCase.java
@@ -20,9 +20,9 @@
 
 import android.os.Looper;
 import android.perftests.utils.PerfStatusReporter;
+import android.perftests.utils.PerfTestActivity;
 import android.perftests.utils.SettingsHelper;
 import android.perftests.utils.SettingsStateKeeperRule;
-import android.perftests.utils.StubActivity;
 import android.provider.Settings;
 
 import androidx.test.InstrumentationRegistry;
@@ -46,8 +46,8 @@
                     Settings.Secure.AUTOFILL_SERVICE);
 
     @Rule
-    public ActivityTestRule<StubActivity> mActivityRule =
-            new ActivityTestRule<StubActivity>(StubActivity.class);
+    public ActivityTestRule<PerfTestActivity> mActivityRule =
+            new ActivityTestRule<>(PerfTestActivity.class);
 
     @Rule
     public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
@@ -68,7 +68,7 @@
                     Looper.getMainLooper().getThread() == Thread.currentThread());
             assertTrue("We should be running on the main thread",
                     Looper.myLooper() == Looper.getMainLooper());
-            StubActivity activity = mActivityRule.getActivity();
+            PerfTestActivity activity = mActivityRule.getActivity();
             activity.setContentView(mLayoutId);
             onCreate(activity);
         });
@@ -89,9 +89,9 @@
     }
 
     /**
-     * Initializes the {@link StubActivity} after it was launched.
+     * Initializes the {@link PerfTestActivity} after it was launched.
      */
-    protected abstract void onCreate(StubActivity activity);
+    protected abstract void onCreate(PerfTestActivity activity);
 
     /**
      * Uses the {@code settings} binary to set the autofill service.
diff --git a/apct-tests/perftests/autofill/src/android/view/autofill/LoginTest.java b/apct-tests/perftests/autofill/src/android/view/autofill/LoginTest.java
index 8090826..fb5ea80 100644
--- a/apct-tests/perftests/autofill/src/android/view/autofill/LoginTest.java
+++ b/apct-tests/perftests/autofill/src/android/view/autofill/LoginTest.java
@@ -20,7 +20,7 @@
 import static android.view.autofill.AutofillManager.AutofillCallback.EVENT_INPUT_SHOWN;
 
 import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.StubActivity;
+import android.perftests.utils.PerfTestActivity;
 import android.view.View;
 import android.widget.EditText;
 
@@ -39,7 +39,7 @@
     }
 
     @Override
-    protected void onCreate(StubActivity activity) {
+    protected void onCreate(PerfTestActivity activity) {
         View root = activity.getWindow().getDecorView();
         mUsername = root.findViewById(R.id.username);
         mPassword = root.findViewById(R.id.password);
diff --git a/apct-tests/perftests/core/AndroidManifest.xml b/apct-tests/perftests/core/AndroidManifest.xml
index 525975d..290f178 100644
--- a/apct-tests/perftests/core/AndroidManifest.xml
+++ b/apct-tests/perftests/core/AndroidManifest.xml
@@ -13,7 +13,7 @@
 
     <application>
         <uses-library android:name="android.test.runner" />
-        <activity android:name="android.perftests.utils.StubActivity">
+        <activity android:name="android.perftests.utils.PerfTestActivity">
           <intent-filter>
             <action android:name="com.android.perftests.core.PERFTEST" />
           </intent-filter>
diff --git a/apct-tests/perftests/core/src/android/app/PendingIntentPerfTest.java b/apct-tests/perftests/core/src/android/app/PendingIntentPerfTest.java
index b3f8359..a320514 100644
--- a/apct-tests/perftests/core/src/android/app/PendingIntentPerfTest.java
+++ b/apct-tests/perftests/core/src/android/app/PendingIntentPerfTest.java
@@ -20,7 +20,7 @@
 import android.content.Intent;
 import android.perftests.utils.BenchmarkState;
 import android.perftests.utils.PerfStatusReporter;
-import android.perftests.utils.StubActivity;
+import android.perftests.utils.PerfTestActivity;
 
 import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.LargeTest;
@@ -48,7 +48,7 @@
     @Before
     public void setUp() {
         mContext = InstrumentationRegistry.getTargetContext();
-        mIntent = StubActivity.createLaunchIntent(mContext);
+        mIntent = PerfTestActivity.createLaunchIntent(mContext);
     }
 
     /**
diff --git a/apct-tests/perftests/core/src/android/graphics/perftests/PaintHasGlyphPerfTest.java b/apct-tests/perftests/core/src/android/graphics/perftests/PaintHasGlyphPerfTest.java
index 3a80020..b9c7af4 100644
--- a/apct-tests/perftests/core/src/android/graphics/perftests/PaintHasGlyphPerfTest.java
+++ b/apct-tests/perftests/core/src/android/graphics/perftests/PaintHasGlyphPerfTest.java
@@ -19,7 +19,7 @@
 import android.graphics.Paint;
 import android.perftests.utils.BenchmarkState;
 import android.perftests.utils.PerfStatusReporter;
-import android.perftests.utils.StubActivity;
+import android.perftests.utils.PerfTestActivity;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.rule.ActivityTestRule;
@@ -58,7 +58,8 @@
     }
 
     @Rule
-    public ActivityTestRule<StubActivity> mActivityRule = new ActivityTestRule(StubActivity.class);
+    public ActivityTestRule<PerfTestActivity> mActivityRule =
+            new ActivityTestRule<>(PerfTestActivity.class);
 
     @Rule
     public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
diff --git a/apct-tests/perftests/core/src/android/graphics/perftests/VectorDrawablePerfTest.java b/apct-tests/perftests/core/src/android/graphics/perftests/VectorDrawablePerfTest.java
index 3b2b8a9..d14e93e 100644
--- a/apct-tests/perftests/core/src/android/graphics/perftests/VectorDrawablePerfTest.java
+++ b/apct-tests/perftests/core/src/android/graphics/perftests/VectorDrawablePerfTest.java
@@ -26,7 +26,7 @@
 import android.perftests.utils.BenchmarkState;
 import android.perftests.utils.BitmapUtils;
 import android.perftests.utils.PerfStatusReporter;
-import android.perftests.utils.StubActivity;
+import android.perftests.utils.PerfTestActivity;
 import android.test.suitebuilder.annotation.LargeTest;
 
 import androidx.test.rule.ActivityTestRule;
@@ -48,8 +48,8 @@
     private int[] mTestHeights = {512, 1024};
 
     @Rule
-    public ActivityTestRule<StubActivity> mActivityRule =
-            new ActivityTestRule(StubActivity.class);
+    public ActivityTestRule<PerfTestActivity> mActivityRule =
+            new ActivityTestRule<>(PerfTestActivity.class);
 
     @Rule
     public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
diff --git a/apct-tests/perftests/core/src/android/text/DynamicLayoutPerfTest.java b/apct-tests/perftests/core/src/android/text/DynamicLayoutPerfTest.java
index 5be99d9..6b295e5 100644
--- a/apct-tests/perftests/core/src/android/text/DynamicLayoutPerfTest.java
+++ b/apct-tests/perftests/core/src/android/text/DynamicLayoutPerfTest.java
@@ -23,7 +23,7 @@
 import android.graphics.Paint.FontMetricsInt;
 import android.perftests.utils.BenchmarkState;
 import android.perftests.utils.PerfStatusReporter;
-import android.perftests.utils.StubActivity;
+import android.perftests.utils.PerfTestActivity;
 import android.text.style.ReplacementSpan;
 import android.util.ArraySet;
 
@@ -75,7 +75,8 @@
     }
 
     @Rule
-    public ActivityTestRule<StubActivity> mActivityRule = new ActivityTestRule(StubActivity.class);
+    public ActivityTestRule<PerfTestActivity> mActivityRule =
+            new ActivityTestRule<>(PerfTestActivity.class);
 
     @Rule
     public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
diff --git a/apct-tests/perftests/core/src/android/view/ViewShowHidePerfTest.java b/apct-tests/perftests/core/src/android/view/ViewShowHidePerfTest.java
index b34001d..b0edb11 100644
--- a/apct-tests/perftests/core/src/android/view/ViewShowHidePerfTest.java
+++ b/apct-tests/perftests/core/src/android/view/ViewShowHidePerfTest.java
@@ -23,7 +23,7 @@
 import android.graphics.drawable.ColorDrawable;
 import android.perftests.utils.BenchmarkState;
 import android.perftests.utils.PerfStatusReporter;
-import android.perftests.utils.StubActivity;
+import android.perftests.utils.PerfTestActivity;
 import android.view.View.MeasureSpec;
 import android.widget.FrameLayout;
 import android.widget.ImageView;
@@ -46,7 +46,8 @@
 public class ViewShowHidePerfTest {
 
     @Rule
-    public ActivityTestRule mActivityRule = new ActivityTestRule(StubActivity.class);
+    public ActivityTestRule<PerfTestActivity> mActivityRule =
+            new ActivityTestRule<>(PerfTestActivity.class);
 
     @Rule
     public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
diff --git a/apct-tests/perftests/core/src/android/widget/EditTextBackspacePerfTest.java b/apct-tests/perftests/core/src/android/widget/EditTextBackspacePerfTest.java
index b3ea62a..270b4e5 100644
--- a/apct-tests/perftests/core/src/android/widget/EditTextBackspacePerfTest.java
+++ b/apct-tests/perftests/core/src/android/widget/EditTextBackspacePerfTest.java
@@ -18,7 +18,7 @@
 
 import android.perftests.utils.BenchmarkState;
 import android.perftests.utils.PerfStatusReporter;
-import android.perftests.utils.StubActivity;
+import android.perftests.utils.PerfTestActivity;
 import android.text.Selection;
 import android.view.KeyEvent;
 import android.view.View.MeasureSpec;
@@ -80,7 +80,8 @@
     }
 
     @Rule
-    public ActivityTestRule<StubActivity> mActivityRule = new ActivityTestRule(StubActivity.class);
+    public ActivityTestRule<PerfTestActivity> mActivityRule =
+            new ActivityTestRule<>(PerfTestActivity.class);
 
     @Rule
     public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
diff --git a/apct-tests/perftests/core/src/android/widget/EditTextCursorMovementPerfTest.java b/apct-tests/perftests/core/src/android/widget/EditTextCursorMovementPerfTest.java
index aa47d5b..8028f11 100644
--- a/apct-tests/perftests/core/src/android/widget/EditTextCursorMovementPerfTest.java
+++ b/apct-tests/perftests/core/src/android/widget/EditTextCursorMovementPerfTest.java
@@ -18,7 +18,7 @@
 
 import android.perftests.utils.BenchmarkState;
 import android.perftests.utils.PerfStatusReporter;
-import android.perftests.utils.StubActivity;
+import android.perftests.utils.PerfTestActivity;
 import android.text.Selection;
 import android.view.KeyEvent;
 import android.view.View.MeasureSpec;
@@ -74,7 +74,8 @@
     }
 
     @Rule
-    public ActivityTestRule<StubActivity> mActivityRule = new ActivityTestRule(StubActivity.class);
+    public ActivityTestRule<PerfTestActivity> mActivityRule =
+            new ActivityTestRule<>(PerfTestActivity.class);
 
     @Rule
     public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
diff --git a/apct-tests/perftests/core/src/android/widget/EditTextLongTextPerfTest.java b/apct-tests/perftests/core/src/android/widget/EditTextLongTextPerfTest.java
index e50016c..f4ad5dd 100644
--- a/apct-tests/perftests/core/src/android/widget/EditTextLongTextPerfTest.java
+++ b/apct-tests/perftests/core/src/android/widget/EditTextLongTextPerfTest.java
@@ -19,7 +19,7 @@
 import android.app.Activity;
 import android.perftests.utils.BenchmarkState;
 import android.perftests.utils.PerfStatusReporter;
-import android.perftests.utils.StubActivity;
+import android.perftests.utils.PerfTestActivity;
 import android.view.KeyEvent;
 import android.view.View.MeasureSpec;
 import android.view.ViewGroup;
@@ -59,7 +59,8 @@
     }
 
     @Rule
-    public ActivityTestRule<StubActivity> mActivityRule = new ActivityTestRule(StubActivity.class);
+    public ActivityTestRule<PerfTestActivity> mActivityRule =
+            new ActivityTestRule<>(PerfTestActivity.class);
 
     @Rule
     public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
diff --git a/apct-tests/perftests/core/src/android/widget/LayoutPerfTest.java b/apct-tests/perftests/core/src/android/widget/LayoutPerfTest.java
index 644095b..223a316 100644
--- a/apct-tests/perftests/core/src/android/widget/LayoutPerfTest.java
+++ b/apct-tests/perftests/core/src/android/widget/LayoutPerfTest.java
@@ -28,7 +28,7 @@
 import android.os.Looper;
 import android.perftests.utils.BenchmarkState;
 import android.perftests.utils.PerfStatusReporter;
-import android.perftests.utils.StubActivity;
+import android.perftests.utils.PerfTestActivity;
 import android.view.View;
 import android.view.ViewGroup;
 
@@ -72,8 +72,8 @@
     }
 
     @Rule
-    public ActivityTestRule<StubActivity> mActivityRule =
-            new ActivityTestRule(StubActivity.class);
+    public ActivityTestRule<PerfTestActivity> mActivityRule =
+            new ActivityTestRule(PerfTestActivity.class);
 
     @Rule
     public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
diff --git a/apct-tests/perftests/core/src/android/widget/TextViewAutoSizeLayoutPerfTest.java b/apct-tests/perftests/core/src/android/widget/TextViewAutoSizeLayoutPerfTest.java
index bed173b..694e1f4 100644
--- a/apct-tests/perftests/core/src/android/widget/TextViewAutoSizeLayoutPerfTest.java
+++ b/apct-tests/perftests/core/src/android/widget/TextViewAutoSizeLayoutPerfTest.java
@@ -22,7 +22,7 @@
 import android.os.Looper;
 import android.perftests.utils.BenchmarkState;
 import android.perftests.utils.PerfStatusReporter;
-import android.perftests.utils.StubActivity;
+import android.perftests.utils.PerfTestActivity;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.rule.ActivityTestRule;
@@ -64,8 +64,8 @@
     }
 
     @Rule
-    public ActivityTestRule<StubActivity> mActivityRule =
-            new ActivityTestRule(StubActivity.class);
+    public ActivityTestRule<PerfTestActivity> mActivityRule =
+            new ActivityTestRule(PerfTestActivity.class);
 
     @Rule
     public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
diff --git a/apct-tests/perftests/core/src/android/widget/TextViewSetTextLocalePerfTest.java b/apct-tests/perftests/core/src/android/widget/TextViewSetTextLocalePerfTest.java
index 00bd8db..a546667 100644
--- a/apct-tests/perftests/core/src/android/widget/TextViewSetTextLocalePerfTest.java
+++ b/apct-tests/perftests/core/src/android/widget/TextViewSetTextLocalePerfTest.java
@@ -18,7 +18,7 @@
 
 import android.perftests.utils.BenchmarkState;
 import android.perftests.utils.PerfStatusReporter;
-import android.perftests.utils.StubActivity;
+import android.perftests.utils.PerfTestActivity;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.rule.ActivityTestRule;
@@ -56,7 +56,8 @@
     }
 
     @Rule
-    public ActivityTestRule<StubActivity> mActivityRule = new ActivityTestRule(StubActivity.class);
+    public ActivityTestRule<PerfTestActivity> mActivityRule =
+            new ActivityTestRule<>(PerfTestActivity.class);
 
     @Rule
     public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
diff --git a/apct-tests/perftests/core/src/android/wm/InternalWindowOperationPerfTest.java b/apct-tests/perftests/core/src/android/wm/InternalWindowOperationPerfTest.java
new file mode 100644
index 0000000..c096cd2
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/wm/InternalWindowOperationPerfTest.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.wm;
+
+import static android.perftests.utils.ManualBenchmarkState.StatsReport;
+
+import android.os.ParcelFileDescriptor;
+import android.os.SystemClock;
+import android.perftests.utils.ManualBenchmarkState;
+import android.perftests.utils.ManualBenchmarkState.ManualBenchmarkTest;
+import android.perftests.utils.PerfManualStatusReporter;
+import android.perftests.utils.TraceMarkParser;
+import android.perftests.utils.TraceMarkParser.TraceMarkSlice;
+import android.util.Log;
+
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.lifecycle.Stage;
+
+import org.junit.Rule;
+import org.junit.Test;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.concurrent.TimeUnit;
+
+/** Measure the performance of internal methods in window manager service by trace tag. */
+@LargeTest
+public class InternalWindowOperationPerfTest extends WindowManagerPerfTestBase {
+    private static final String TAG = InternalWindowOperationPerfTest.class.getSimpleName();
+
+    @Rule
+    public final PerfManualStatusReporter mPerfStatusReporter = new PerfManualStatusReporter();
+
+    @Rule
+    public final PerfTestActivityRule mActivityRule = new PerfTestActivityRule();
+
+    private final TraceMarkParser mTraceMarkParser = new TraceMarkParser(
+            "applyPostLayoutPolicy",
+            "applySurfaceChanges",
+            "AppTransitionReady",
+            "closeSurfaceTransactiom",
+            "openSurfaceTransaction",
+            "performLayout",
+            "performSurfacePlacement",
+            "prepareSurfaces",
+            "updateInputWindows",
+            "WSA#startAnimation",
+            "activityIdle",
+            "activityPaused",
+            "activityStopped",
+            "activityDestroyed",
+            "finishActivity",
+            "startActivityInner");
+
+    @Test
+    @ManualBenchmarkTest(
+            targetTestDurationNs = 20 * TIME_1_S_IN_NS,
+            statsReport = @StatsReport(
+                    flags = StatsReport.FLAG_ITERATION | StatsReport.FLAG_MEAN
+                            | StatsReport.FLAG_MAX | StatsReport.FLAG_COEFFICIENT_VAR))
+    public void testLaunchAndFinishActivity() throws Throwable {
+        final ManualBenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        long measuredTimeNs = 0;
+        boolean isTraceStarted = false;
+
+        while (state.keepRunning(measuredTimeNs)) {
+            if (!isTraceStarted && !state.isWarmingUp()) {
+                startAsyncAtrace();
+                isTraceStarted = true;
+            }
+            final long startTime = SystemClock.elapsedRealtimeNanos();
+            mActivityRule.launchActivity();
+            mActivityRule.finishActivity();
+            mActivityRule.waitForIdleSync(Stage.DESTROYED);
+            measuredTimeNs = SystemClock.elapsedRealtimeNanos() - startTime;
+        }
+
+        stopAsyncAtrace();
+
+        mTraceMarkParser.forAllSlices((key, slices) -> {
+            for (TraceMarkSlice slice : slices) {
+                state.addExtraResult(key, (long) (slice.getDurarionInSeconds() * NANOS_PER_S));
+            }
+        });
+
+        Log.i(TAG, String.valueOf(mTraceMarkParser));
+    }
+
+    private void startAsyncAtrace() throws IOException {
+        sUiAutomation.executeShellCommand("atrace -b 32768 --async_start wm");
+        // Avoid atrace isn't ready immediately.
+        SystemClock.sleep(TimeUnit.NANOSECONDS.toMillis(TIME_1_S_IN_NS));
+    }
+
+    private void stopAsyncAtrace() throws IOException {
+        final ParcelFileDescriptor pfd = sUiAutomation.executeShellCommand("atrace --async_stop");
+        final InputStream inputStream = new ParcelFileDescriptor.AutoCloseInputStream(pfd);
+        try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) {
+            String line;
+            while ((line = reader.readLine()) != null) {
+                mTraceMarkParser.visit(line);
+            }
+        }
+    }
+}
diff --git a/apct-tests/perftests/core/src/android/wm/RecentsAnimationPerfTest.java b/apct-tests/perftests/core/src/android/wm/RecentsAnimationPerfTest.java
index 9cfc3d2..73b4a19 100644
--- a/apct-tests/perftests/core/src/android/wm/RecentsAnimationPerfTest.java
+++ b/apct-tests/perftests/core/src/android/wm/RecentsAnimationPerfTest.java
@@ -16,16 +16,13 @@
 
 package android.wm;
 
-import static android.perftests.utils.ManualBenchmarkState.STATS_REPORT_COEFFICIENT_VAR;
-import static android.perftests.utils.ManualBenchmarkState.STATS_REPORT_ITERATION;
-import static android.perftests.utils.ManualBenchmarkState.STATS_REPORT_MEAN;
+import static android.perftests.utils.ManualBenchmarkState.StatsReport;
 
 import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
 
 import static org.hamcrest.core.AnyOf.anyOf;
 import static org.hamcrest.core.Is.is;
 
-import android.app.Activity;
 import android.app.ActivityManager.TaskSnapshot;
 import android.app.ActivityTaskManager;
 import android.app.IActivityTaskManager;
@@ -39,23 +36,16 @@
 import android.perftests.utils.ManualBenchmarkState;
 import android.perftests.utils.ManualBenchmarkState.ManualBenchmarkTest;
 import android.perftests.utils.PerfManualStatusReporter;
-import android.perftests.utils.StubActivity;
 import android.util.Pair;
 import android.view.IRecentsAnimationController;
 import android.view.IRecentsAnimationRunner;
 import android.view.RemoteAnimationTarget;
-import android.view.WindowManager;
 
 import androidx.test.filters.LargeTest;
-import androidx.test.rule.ActivityTestRule;
-import androidx.test.runner.lifecycle.ActivityLifecycleCallback;
-import androidx.test.runner.lifecycle.ActivityLifecycleMonitorRegistry;
 import androidx.test.runner.lifecycle.Stage;
 
-import org.junit.After;
 import org.junit.AfterClass;
 import org.junit.Assume;
-import org.junit.Before;
 import org.junit.BeforeClass;
 import org.junit.Rule;
 import org.junit.Test;
@@ -77,11 +67,10 @@
     public final PerfManualStatusReporter mPerfStatusReporter = new PerfManualStatusReporter();
 
     @Rule
-    public final ActivityTestRule<StubActivity> mActivityRule = new ActivityTestRule<>(
-            StubActivity.class, false /* initialTouchMode */, false /* launchActivity */);
+    public final PerfTestActivityRule mActivityRule =
+            new PerfTestActivityRule(true /* launchActivity */);
 
     private long mMeasuredTimeNs;
-    private LifecycleListener mLifecycleListener;
 
     @Parameterized.Parameter(0)
     public int intervalBetweenOperations;
@@ -127,24 +116,6 @@
         sUiAutomation.dropShellPermissionIdentity();
     }
 
-    @Before
-    @Override
-    public void setUp() {
-        super.setUp();
-        final Activity testActivity = mActivityRule.launchActivity(null /* intent */);
-        try {
-            mActivityRule.runOnUiThread(() -> testActivity.getWindow()
-                    .addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON));
-        } catch (Throwable ignored) { }
-        mLifecycleListener = new LifecycleListener(testActivity);
-        ActivityLifecycleMonitorRegistry.getInstance().addLifecycleCallback(mLifecycleListener);
-    }
-
-    @After
-    public void tearDown() {
-        ActivityLifecycleMonitorRegistry.getInstance().removeLifecycleCallback(mLifecycleListener);
-    }
-
     /** Simulate the timing of touch. */
     private void makeInterval() {
         SystemClock.sleep(intervalBetweenOperations);
@@ -167,8 +138,8 @@
     @ManualBenchmarkTest(
             warmupDurationNs = TIME_1_S_IN_NS,
             targetTestDurationNs = TIME_5_S_IN_NS,
-            statsReportFlags =
-                    STATS_REPORT_ITERATION | STATS_REPORT_MEAN | STATS_REPORT_COEFFICIENT_VAR)
+            statsReport = @StatsReport(flags = StatsReport.FLAG_ITERATION | StatsReport.FLAG_MEAN
+                    | StatsReport.FLAG_COEFFICIENT_VAR))
     public void testRecentsAnimation() throws Throwable {
         final ManualBenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         final IActivityTaskManager atm = ActivityTaskManager.getService();
@@ -201,7 +172,7 @@
                 state.addExtraResult(finishCase.first, elapsedTimeNsOfFinish);
 
                 if (moveRecentsToTop) {
-                    mLifecycleListener.waitForIdleSync(Stage.STOPPED);
+                    mActivityRule.waitForIdleSync(Stage.STOPPED);
 
                     startTime = SystemClock.elapsedRealtimeNanos();
                     atm.startActivityFromRecents(testActivityTaskId, null /* options */);
@@ -209,7 +180,7 @@
                     mMeasuredTimeNs += elapsedTimeNs;
                     state.addExtraResult("startFromRecents", elapsedTimeNs);
 
-                    mLifecycleListener.waitForIdleSync(Stage.RESUMED);
+                    mActivityRule.waitForIdleSync(Stage.RESUMED);
                 }
 
                 makeInterval();
@@ -223,55 +194,18 @@
             }
         };
 
+        recentsSemaphore.tryAcquire();
         while (state.keepRunning(mMeasuredTimeNs)) {
-            Assume.assumeTrue(recentsSemaphore.tryAcquire(TIME_5_S_IN_NS, TimeUnit.NANOSECONDS));
+            mMeasuredTimeNs = 0;
 
             final long startTime = SystemClock.elapsedRealtimeNanos();
             atm.startRecentsActivity(sRecentsIntent, null /* unused */, anim);
             final long elapsedTimeNsOfStart = SystemClock.elapsedRealtimeNanos() - startTime;
             mMeasuredTimeNs += elapsedTimeNsOfStart;
             state.addExtraResult("start", elapsedTimeNsOfStart);
-        }
 
-        // Ensure the last round of animation callback is done.
-        recentsSemaphore.tryAcquire(TIME_5_S_IN_NS, TimeUnit.NANOSECONDS);
-        recentsSemaphore.release();
-    }
-
-    private static class LifecycleListener implements ActivityLifecycleCallback {
-        private final Activity mTargetActivity;
-        private Stage mWaitingStage;
-        private Stage mReceivedStage;
-
-        LifecycleListener(Activity activity) {
-            mTargetActivity = activity;
-        }
-
-        void waitForIdleSync(Stage state) {
-            synchronized (this) {
-                if (state != mReceivedStage) {
-                    mWaitingStage = state;
-                    try {
-                        wait(TimeUnit.NANOSECONDS.toMillis(TIME_5_S_IN_NS));
-                    } catch (InterruptedException impossible) { }
-                }
-                mWaitingStage = mReceivedStage = null;
-            }
-            getInstrumentation().waitForIdleSync();
-        }
-
-        @Override
-        public void onActivityLifecycleChanged(Activity activity, Stage stage) {
-            if (mTargetActivity != activity) {
-                return;
-            }
-
-            synchronized (this) {
-                mReceivedStage = stage;
-                if (mWaitingStage == mReceivedStage) {
-                    notifyAll();
-                }
-            }
+            // Ensure the animation callback is done.
+            Assume.assumeTrue(recentsSemaphore.tryAcquire(TIME_5_S_IN_NS, TimeUnit.NANOSECONDS));
         }
     }
 }
diff --git a/apct-tests/perftests/core/src/android/wm/RelayoutPerfTest.java b/apct-tests/perftests/core/src/android/wm/RelayoutPerfTest.java
index f0c474b..f43bdf8 100644
--- a/apct-tests/perftests/core/src/android/wm/RelayoutPerfTest.java
+++ b/apct-tests/perftests/core/src/android/wm/RelayoutPerfTest.java
@@ -24,7 +24,7 @@
 import android.os.RemoteException;
 import android.perftests.utils.BenchmarkState;
 import android.perftests.utils.PerfStatusReporter;
-import android.perftests.utils.StubActivity;
+import android.perftests.utils.PerfTestActivity;
 import android.util.MergedConfiguration;
 import android.view.DisplayCutout;
 import android.view.IWindow;
@@ -57,8 +57,8 @@
     public final PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
 
     @Rule
-    public final ActivityTestRule<StubActivity> mActivityRule =
-            new ActivityTestRule<>(StubActivity.class);
+    public final ActivityTestRule<PerfTestActivity> mActivityRule =
+            new ActivityTestRule<>(PerfTestActivity.class);
 
     /** This is only a placement to match the input parameters from {@link #getParameters}. */
     @Parameterized.Parameter(0)
diff --git a/apct-tests/perftests/core/src/android/wm/WindowManagerPerfTestBase.java b/apct-tests/perftests/core/src/android/wm/WindowManagerPerfTestBase.java
index 4864da4..4d278c3 100644
--- a/apct-tests/perftests/core/src/android/wm/WindowManagerPerfTestBase.java
+++ b/apct-tests/perftests/core/src/android/wm/WindowManagerPerfTestBase.java
@@ -18,9 +18,21 @@
 
 import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
 
+import android.app.Activity;
 import android.app.UiAutomation;
+import android.content.Intent;
+import android.perftests.utils.PerfTestActivity;
 
-import org.junit.Before;
+import androidx.test.rule.ActivityTestRule;
+import androidx.test.runner.lifecycle.ActivityLifecycleCallback;
+import androidx.test.runner.lifecycle.ActivityLifecycleMonitorRegistry;
+import androidx.test.runner.lifecycle.Stage;
+
+import org.junit.BeforeClass;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+
+import java.util.concurrent.TimeUnit;
 
 public class WindowManagerPerfTestBase {
     static final UiAutomation sUiAutomation = getInstrumentation().getUiAutomation();
@@ -28,10 +40,102 @@
     static final long TIME_1_S_IN_NS = 1 * NANOS_PER_S;
     static final long TIME_5_S_IN_NS = 5 * NANOS_PER_S;
 
-    @Before
-    public void setUp() {
+    @BeforeClass
+    public static void setUpOnce() {
         // In order to be closer to the real use case.
         sUiAutomation.executeShellCommand("input keyevent KEYCODE_WAKEUP");
         sUiAutomation.executeShellCommand("wm dismiss-keyguard");
+        getInstrumentation().getContext().startActivity(new Intent(Intent.ACTION_MAIN)
+                .addCategory(Intent.CATEGORY_HOME).setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
+    }
+
+    /**
+     * Provides an activity that keeps screen on and is able to wait for a stable lifecycle stage.
+     */
+    static class PerfTestActivityRule extends ActivityTestRule<PerfTestActivity> {
+        private final Intent mStartIntent =
+                new Intent().putExtra(PerfTestActivity.INTENT_EXTRA_KEEP_SCREEN_ON, true);
+        private final LifecycleListener mLifecycleListener = new LifecycleListener();
+
+        PerfTestActivityRule() {
+            this(false /* launchActivity */);
+        }
+
+        PerfTestActivityRule(boolean launchActivity) {
+            super(PerfTestActivity.class, false /* initialTouchMode */, launchActivity);
+        }
+
+        @Override
+        public Statement apply(Statement base, Description description) {
+            final Statement wrappedStatement = new Statement() {
+                @Override
+                public void evaluate() throws Throwable {
+                    ActivityLifecycleMonitorRegistry.getInstance()
+                            .addLifecycleCallback(mLifecycleListener);
+                    base.evaluate();
+                    ActivityLifecycleMonitorRegistry.getInstance()
+                            .removeLifecycleCallback(mLifecycleListener);
+                }
+            };
+            return super.apply(wrappedStatement, description);
+        }
+
+        @Override
+        protected Intent getActivityIntent() {
+            return mStartIntent;
+        }
+
+        @Override
+        public PerfTestActivity launchActivity(Intent intent) {
+            final PerfTestActivity activity = super.launchActivity(intent);
+            mLifecycleListener.setTargetActivity(activity);
+            return activity;
+        }
+
+        PerfTestActivity launchActivity() {
+            return launchActivity(mStartIntent);
+        }
+
+        void waitForIdleSync(Stage state) {
+            mLifecycleListener.waitForIdleSync(state);
+        }
+    }
+
+    static class LifecycleListener implements ActivityLifecycleCallback {
+        private Activity mTargetActivity;
+        private Stage mWaitingStage;
+        private Stage mReceivedStage;
+
+        void setTargetActivity(Activity activity) {
+            mTargetActivity = activity;
+            mReceivedStage = mWaitingStage = null;
+        }
+
+        void waitForIdleSync(Stage stage) {
+            synchronized (this) {
+                if (stage != mReceivedStage) {
+                    mWaitingStage = stage;
+                    try {
+                        wait(TimeUnit.NANOSECONDS.toMillis(TIME_5_S_IN_NS));
+                    } catch (InterruptedException impossible) { }
+                }
+                mWaitingStage = mReceivedStage = null;
+            }
+            getInstrumentation().waitForIdleSync();
+        }
+
+        @Override
+        public void onActivityLifecycleChanged(Activity activity, Stage stage) {
+            if (mTargetActivity != activity) {
+                return;
+            }
+
+            synchronized (this) {
+                mReceivedStage = stage;
+                if (mWaitingStage == mReceivedStage) {
+                    notifyAll();
+                }
+            }
+        }
     }
 }
diff --git a/apct-tests/perftests/utils/src/android/perftests/utils/ManualBenchmarkState.java b/apct-tests/perftests/utils/src/android/perftests/utils/ManualBenchmarkState.java
index ffe39e8..a83254b 100644
--- a/apct-tests/perftests/utils/src/android/perftests/utils/ManualBenchmarkState.java
+++ b/apct-tests/perftests/utils/src/android/perftests/utils/ManualBenchmarkState.java
@@ -59,27 +59,37 @@
 public final class ManualBenchmarkState {
     private static final String TAG = ManualBenchmarkState.class.getSimpleName();
 
-    @IntDef(prefix = {"STATS_REPORT"}, value = {
-            STATS_REPORT_MEDIAN,
-            STATS_REPORT_MEAN,
-            STATS_REPORT_MIN,
-            STATS_REPORT_MAX,
-            STATS_REPORT_PERCENTILE90,
-            STATS_REPORT_PERCENTILE95,
-            STATS_REPORT_STDDEV,
-            STATS_REPORT_ITERATION,
-    })
-    public @interface StatsReport {}
+    @Target(ElementType.ANNOTATION_TYPE)
+    @Retention(RetentionPolicy.RUNTIME)
+    public @interface StatsReport {
+        int FLAG_MEDIAN = 0x00000001;
+        int FLAG_MEAN = 0x00000002;
+        int FLAG_MIN = 0x00000004;
+        int FLAG_MAX = 0x00000008;
+        int FLAG_STDDEV = 0x00000010;
+        int FLAG_COEFFICIENT_VAR = 0x00000020;
+        int FLAG_ITERATION = 0x00000040;
 
-    public static final int STATS_REPORT_MEDIAN = 0x00000001;
-    public static final int STATS_REPORT_MEAN = 0x00000002;
-    public static final int STATS_REPORT_MIN = 0x00000004;
-    public static final int STATS_REPORT_MAX = 0x00000008;
-    public static final int STATS_REPORT_PERCENTILE90 = 0x00000010;
-    public static final int STATS_REPORT_PERCENTILE95 = 0x00000020;
-    public static final int STATS_REPORT_STDDEV = 0x00000040;
-    public static final int STATS_REPORT_COEFFICIENT_VAR = 0x00000080;
-    public static final int STATS_REPORT_ITERATION = 0x00000100;
+        @Retention(RetentionPolicy.RUNTIME)
+        @IntDef(value = {
+                FLAG_MEDIAN,
+                FLAG_MEAN,
+                FLAG_MIN,
+                FLAG_MAX,
+                FLAG_STDDEV,
+                FLAG_COEFFICIENT_VAR,
+                FLAG_ITERATION,
+        })
+        @interface Flag {}
+
+        /** Defines which type of statistics should output. */
+        @Flag int flags() default -1;
+        /** An array with value 0~100 to provide the percentiles. */
+        int[] percentiles() default {};
+    }
+
+    /** It means the entire {@link StatsReport} is not given. */
+    private static final int DEFAULT_STATS_REPORT = -2;
 
     // TODO: Tune these values.
     // warm-up for duration
@@ -116,8 +126,9 @@
     // The computation needs double precision, but long int is fine for final reporting.
     private Stats mStats;
 
-    private int mStatsReportFlags = STATS_REPORT_MEDIAN | STATS_REPORT_MEAN
-            | STATS_REPORT_PERCENTILE90 | STATS_REPORT_PERCENTILE95 | STATS_REPORT_STDDEV;
+    private int mStatsReportFlags =
+            StatsReport.FLAG_MEDIAN | StatsReport.FLAG_MEAN | StatsReport.FLAG_STDDEV;
+    private int[] mStatsReportPercentiles = {90 , 95};
 
     private boolean shouldReport(int statsReportFlag) {
         return (mStatsReportFlags & statsReportFlag) != 0;
@@ -136,9 +147,10 @@
         if (targetTestDurationNs >= 0) {
             mTargetTestDurationNs = targetTestDurationNs;
         }
-        final int statsReportFlags = testAnnotation.statsReportFlags();
-        if (statsReportFlags >= 0) {
-            mStatsReportFlags = statsReportFlags;
+        final StatsReport statsReport = testAnnotation.statsReport();
+        if (statsReport != null && statsReport.flags() != DEFAULT_STATS_REPORT) {
+            mStatsReportFlags = statsReport.flags();
+            mStatsReportPercentiles = statsReport.percentiles();
         }
     }
 
@@ -189,11 +201,20 @@
     }
 
     /**
-     * Adds additional result while this benchmark is running. It is used when a sequence of
+     * @return {@code true} if the benchmark is in warmup state. It can be used to skip the
+     *         operations or measurements that are unnecessary while the test isn't running the
+     *         actual benchmark.
+     */
+    public boolean isWarmingUp() {
+        return mState == WARMUP;
+    }
+
+    /**
+     * Adds additional result while this benchmark isn't warming up. It is used when a sequence of
      * operations is executed consecutively, the duration of each operation can also be recorded.
      */
     public void addExtraResult(String key, long duration) {
-        if (mState != RUNNING) {
+        if (isWarmingUp()) {
             return;
         }
         if (mExtraResults == null) {
@@ -221,31 +242,30 @@
     }
 
     private void fillStatus(Bundle status, String key, Stats stats) {
-        if (shouldReport(STATS_REPORT_ITERATION)) {
+        if (shouldReport(StatsReport.FLAG_ITERATION)) {
             status.putLong(key + "_iteration", stats.getSize());
         }
-        if (shouldReport(STATS_REPORT_MEDIAN)) {
+        if (shouldReport(StatsReport.FLAG_MEDIAN)) {
             status.putLong(key + "_median", stats.getMedian());
         }
-        if (shouldReport(STATS_REPORT_MEAN)) {
+        if (shouldReport(StatsReport.FLAG_MEAN)) {
             status.putLong(key + "_mean", Math.round(stats.getMean()));
         }
-        if (shouldReport(STATS_REPORT_MIN)) {
+        if (shouldReport(StatsReport.FLAG_MIN)) {
             status.putLong(key + "_min", stats.getMin());
         }
-        if (shouldReport(STATS_REPORT_MAX)) {
+        if (shouldReport(StatsReport.FLAG_MAX)) {
             status.putLong(key + "_max", stats.getMax());
         }
-        if (shouldReport(STATS_REPORT_PERCENTILE90)) {
-            status.putLong(key + "_percentile90", stats.getPercentile90());
+        if (mStatsReportPercentiles != null) {
+            for (int percentile : mStatsReportPercentiles) {
+                status.putLong(key + "_percentile" + percentile, stats.getPercentile(percentile));
+            }
         }
-        if (shouldReport(STATS_REPORT_PERCENTILE95)) {
-            status.putLong(key + "_percentile95", stats.getPercentile95());
-        }
-        if (shouldReport(STATS_REPORT_STDDEV)) {
+        if (shouldReport(StatsReport.FLAG_STDDEV)) {
             status.putLong(key + "_stddev", Math.round(stats.getStandardDeviation()));
         }
-        if (shouldReport(STATS_REPORT_COEFFICIENT_VAR)) {
+        if (shouldReport(StatsReport.FLAG_COEFFICIENT_VAR)) {
             status.putLong(key + "_cv",
                     Math.round((100 * stats.getStandardDeviation() / stats.getMean())));
         }
@@ -276,6 +296,6 @@
     public @interface ManualBenchmarkTest {
         long warmupDurationNs() default -1;
         long targetTestDurationNs() default -1;
-        @StatsReport int statsReportFlags() default -1;
+        StatsReport statsReport() default @StatsReport(flags = DEFAULT_STATS_REPORT);
     }
 }
diff --git a/apct-tests/perftests/utils/src/android/perftests/utils/PerfTestActivity.java b/apct-tests/perftests/utils/src/android/perftests/utils/PerfTestActivity.java
new file mode 100644
index 0000000..e934feb
--- /dev/null
+++ b/apct-tests/perftests/utils/src/android/perftests/utils/PerfTestActivity.java
@@ -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.
+ */
+
+package android.perftests.utils;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.view.WindowManager;
+
+/**
+ * A simple activity used for testing, e.g. performance of activity switching, or as a base
+ * container of testing view.
+ */
+public class PerfTestActivity extends Activity {
+    public static final String INTENT_EXTRA_KEEP_SCREEN_ON = "keep_screen_on";
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        if (getIntent().getBooleanExtra(INTENT_EXTRA_KEEP_SCREEN_ON, false)) {
+            getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
+        }
+    }
+
+    public static Intent createLaunchIntent(Context context) {
+        final Intent intent = new Intent();
+        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        intent.setClass(context, PerfTestActivity.class);
+        return intent;
+    }
+}
diff --git a/apct-tests/perftests/utils/src/android/perftests/utils/Stats.java b/apct-tests/perftests/utils/src/android/perftests/utils/Stats.java
index f650e81..fb516a8 100644
--- a/apct-tests/perftests/utils/src/android/perftests/utils/Stats.java
+++ b/apct-tests/perftests/utils/src/android/perftests/utils/Stats.java
@@ -16,34 +16,34 @@
 
 package android.perftests.utils;
 
+import android.annotation.IntRange;
+
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
 
 public class Stats {
-    private long mMedian, mMin, mMax, mPercentile90, mPercentile95;
+    private long mMedian, mMin, mMax;
     private double mMean, mStandardDeviation;
-    private final int mSize;
+    private final List<Long> mValues;
 
     /* Calculate stats in constructor. */
     public Stats(List<Long> values) {
-        // make a copy since we're modifying it
-        values = new ArrayList<>(values);
         final int size = values.size();
         if (size < 2) {
             throw new IllegalArgumentException("At least two results are necessary.");
         }
 
+        // Make a copy since we're modifying it.
+        mValues = values = new ArrayList<>(values);
+
         Collections.sort(values);
 
-        mSize = size;
         mMin = values.get(0);
         mMax = values.get(values.size() - 1);
 
         mMedian = size % 2 == 0 ? (values.get(size / 2) + values.get(size / 2 - 1)) / 2 :
                 values.get(size / 2);
-        mPercentile90 = getPercentile(values, 90);
-        mPercentile95 = getPercentile(values, 95);
 
         for (int i = 0; i < size; ++i) {
             long result = values.get(i);
@@ -59,7 +59,7 @@
     }
 
     public int getSize() {
-        return mSize;
+        return mValues.size();
     }
 
     public double getMean() {
@@ -82,12 +82,8 @@
         return mStandardDeviation;
     }
 
-    public long getPercentile90() {
-        return mPercentile90;
-    }
-
-    public long getPercentile95() {
-        return mPercentile95;
+    public long getPercentile(@IntRange(from = 0, to = 100) int percentile) {
+        return getPercentile(mValues, percentile);
     }
 
     private static long getPercentile(List<Long> values, int percentile) {
diff --git a/apct-tests/perftests/utils/src/android/perftests/utils/StubActivity.java b/apct-tests/perftests/utils/src/android/perftests/utils/StubActivity.java
deleted file mode 100644
index 8f03f7e..0000000
--- a/apct-tests/perftests/utils/src/android/perftests/utils/StubActivity.java
+++ /dev/null
@@ -1,30 +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 android.perftests.utils;
-
-import android.app.Activity;
-import android.content.Context;
-import android.content.Intent;
-
-public class StubActivity extends Activity {
-    public static Intent createLaunchIntent(Context context) {
-        final Intent intent = new Intent();
-        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-        intent.setClass(context, StubActivity.class);
-        return intent;
-    }
-}
diff --git a/apct-tests/perftests/utils/src/android/perftests/utils/TraceMarkParser.java b/apct-tests/perftests/utils/src/android/perftests/utils/TraceMarkParser.java
new file mode 100644
index 0000000..1afed3a
--- /dev/null
+++ b/apct-tests/perftests/utils/src/android/perftests/utils/TraceMarkParser.java
@@ -0,0 +1,232 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.perftests.utils;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.function.BiConsumer;
+import java.util.function.Predicate;
+
+/**
+ * Utility to get the slice of tracing_mark_write S,F,B,E (Async: start, finish, Sync: begin, end).
+ * Use {@link #visit(String)} to process the trace in text form. The filtered results can be
+ * obtained by {@link #forAllSlices(BiConsumer)}.
+ *
+ * @see android.os.Trace
+ */
+public class TraceMarkParser {
+    /** All slices by the name of {@link TraceMarkLine}. */
+    private final Map<String, List<TraceMarkSlice>> mSlicesMap = new HashMap<>();
+    /** The nested depth of each task-pid. */
+    private final Map<String, Integer> mDepthMap = new HashMap<>();
+    /** The start trace lines that haven't matched the corresponding end. */
+    private final Map<String, TraceMarkLine> mPendingStarts = new HashMap<>();
+
+    private final Predicate<TraceMarkLine> mTraceLineFilter;
+
+    public TraceMarkParser(Predicate<TraceMarkLine> traceLineFilter) {
+        mTraceLineFilter = traceLineFilter;
+    }
+
+    /** Only accept the trace event with the given names. */
+    public TraceMarkParser(String... traceNames) {
+        this(line -> {
+            for (String name : traceNames) {
+                if (name.equals(line.name)) {
+                    return true;
+                }
+            }
+            return false;
+        });
+    }
+
+    /** Computes {@link TraceMarkSlice} by the given trace line. */
+    public void visit(String textTraceLine) {
+        final TraceMarkLine line = TraceMarkLine.parse(textTraceLine);
+        if (line == null) {
+            return;
+        }
+
+        if (line.isAsync) {
+            // Async-trace contains name in the start and finish event.
+            if (mTraceLineFilter.test(line)) {
+                if (line.isBegin) {
+                    mPendingStarts.put(line.name, line);
+                } else {
+                    final TraceMarkLine start = mPendingStarts.remove(line.name);
+                    if (start != null) {
+                        addSlice(start, line);
+                    }
+                }
+            }
+            return;
+        }
+
+        int depth = 1;
+        if (line.isBegin) {
+            final Integer existingDepth = mDepthMap.putIfAbsent(line.taskPid, 1);
+            if (existingDepth != null) {
+                mDepthMap.put(line.taskPid, depth = existingDepth + 1);
+            }
+            // Sync-trace only contains name in the begin event.
+            if (mTraceLineFilter.test(line)) {
+                mPendingStarts.put(getSyncPendingStartKey(line, depth), line);
+            }
+        } else {
+            final Integer existingDepth = mDepthMap.get(line.taskPid);
+            if (existingDepth != null) {
+                depth = existingDepth;
+                mDepthMap.put(line.taskPid, existingDepth - 1);
+            }
+            final TraceMarkLine begin = mPendingStarts.remove(getSyncPendingStartKey(line, depth));
+            if (begin != null) {
+                addSlice(begin, line);
+            }
+        }
+    }
+
+    private static String getSyncPendingStartKey(TraceMarkLine line, int depth) {
+        return line.taskPid + "@" + depth;
+    }
+
+    private void addSlice(TraceMarkLine begin, TraceMarkLine end) {
+        mSlicesMap.computeIfAbsent(
+                begin.name, k -> new ArrayList<>()).add(new TraceMarkSlice(begin, end));
+    }
+
+    public void forAllSlices(BiConsumer<String, List<TraceMarkSlice>> consumer) {
+        for (Map.Entry<String, List<TraceMarkSlice>> entry : mSlicesMap.entrySet()) {
+            consumer.accept(entry.getKey(), entry.getValue());
+        }
+    }
+
+    @Override
+    public String toString() {
+        final StringBuilder sb = new StringBuilder();
+        forAllSlices((key, slices) -> {
+            double totalMs = 0;
+            for (TraceMarkSlice s : slices) {
+                totalMs += s.getDurarionInSeconds() * 1000;
+            }
+            sb.append(key).append(" count=").append(slices.size()).append(" avg=")
+                    .append(totalMs / slices.size()).append("ms\n");
+        });
+
+        if (!mPendingStarts.isEmpty()) {
+            sb.append("[Warning] Unresolved events:").append(mPendingStarts).append("\n");
+        }
+        return sb.toString();
+    }
+
+    public static class TraceMarkSlice {
+        public final TraceMarkLine begin;
+        public final TraceMarkLine end;
+
+        TraceMarkSlice(TraceMarkLine begin, TraceMarkLine end) {
+            this.begin = begin;
+            this.end = end;
+        }
+
+        public double getDurarionInSeconds() {
+            return end.timestamp - begin.timestamp;
+        }
+    }
+
+    // taskPid                               timestamp                           name
+    // # Async:
+    // Binder:129_F-349  ( 1296) [003] ...1  12.2776: tracing_mark_write: S|1296|launching: a.test|0
+    // android.anim-135  ( 1296) [005] ...1  12.3361: tracing_mark_write: F|1296|launching: a.test|0
+    // # Normal:
+    // Binder:129_6-315  ( 1296) [007] ...1  97.4576: tracing_mark_write: B|1296|relayoutWindow: xxx
+    // ... there may have other nested begin/end
+    // Binder:129_6-315  ( 1296) [007] ...1  97.4580: tracing_mark_write: E|1296
+    public static class TraceMarkLine {
+        static final String EVENT_KEYWORD = ": tracing_mark_write: ";
+        static final char ASYNC_START = 'S';
+        static final char ASYNC_FINISH = 'F';
+        static final char SYNC_BEGIN = 'B';
+        static final char SYNC_END = 'E';
+
+        public final String taskPid;
+        public final double timestamp;
+        public final String name;
+        public final boolean isAsync;
+        public final boolean isBegin;
+
+        TraceMarkLine(String rawLine, int typePos, int type) throws IllegalArgumentException {
+            taskPid = rawLine.substring(0, rawLine.indexOf('(')).trim();
+            final int timeEnd = rawLine.indexOf(':', taskPid.length());
+            if (timeEnd < 0) {
+                throw new IllegalArgumentException("Timestamp end not found");
+            }
+            final int timeBegin = rawLine.lastIndexOf(' ', timeEnd);
+            if (timeBegin < 0) {
+                throw new IllegalArgumentException("Timestamp start not found");
+            }
+            timestamp = Double.parseDouble(rawLine.substring(timeBegin, timeEnd));
+            isAsync = type == ASYNC_START || type == ASYNC_FINISH;
+            isBegin = type == ASYNC_START || type == SYNC_BEGIN;
+
+            if (!isAsync && !isBegin) {
+                name = "";
+            } else {
+                // Get the position of the second '|' from "S|1234|name".
+                final int nameBegin = rawLine.indexOf('|', typePos + 2) + 1;
+                if (nameBegin == 0) {
+                    throw new IllegalArgumentException("Name begin not found");
+                }
+                if (isAsync) {
+                    // Get the name from "S|1234|name|0".
+                    name = rawLine.substring(nameBegin, rawLine.lastIndexOf('|'));
+                } else {
+                    name = rawLine.substring(nameBegin);
+                }
+            }
+        }
+
+        static TraceMarkLine parse(String rawLine) {
+            final int eventPos = rawLine.indexOf(EVENT_KEYWORD);
+            if (eventPos < 0) {
+                return null;
+            }
+            final int typePos = eventPos + EVENT_KEYWORD.length();
+            if (typePos >= rawLine.length()) {
+                return null;
+            }
+            final int type = rawLine.charAt(typePos);
+            if (type != ASYNC_START && type != ASYNC_FINISH
+                    && type != SYNC_BEGIN  && type != SYNC_END) {
+                return null;
+            }
+
+            try {
+                return new TraceMarkLine(rawLine, typePos, type);
+            } catch (IllegalArgumentException e) {
+                e.printStackTrace();
+            }
+            return null;
+        }
+
+        @Override
+        public String toString() {
+            return "TraceMarkLine{pid=" + taskPid + " time=" + timestamp + " name=" + name
+                    + " async=" + isAsync + " begin=" + isBegin + "}";
+        }
+    }
+}
diff --git a/apex/jobscheduler/service/java/com/android/server/job/restrictions/ThermalStatusRestriction.java b/apex/jobscheduler/service/java/com/android/server/job/restrictions/ThermalStatusRestriction.java
index b97da59..aa7696d 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/restrictions/ThermalStatusRestriction.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/restrictions/ThermalStatusRestriction.java
@@ -17,13 +17,8 @@
 package com.android.server.job.restrictions;
 
 import android.app.job.JobParameters;
-import android.content.Context;
-import android.os.IThermalService;
-import android.os.IThermalStatusListener;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.os.Temperature;
-import android.util.Slog;
+import android.os.PowerManager;
+import android.os.PowerManager.OnThermalStatusChangedListener;
 import android.util.proto.ProtoOutputStream;
 
 import com.android.internal.util.IndentingPrintWriter;
@@ -36,31 +31,29 @@
 
     private volatile boolean mIsThermalRestricted = false;
 
+    private PowerManager mPowerManager;
+
     public ThermalStatusRestriction(JobSchedulerService service) {
         super(service, JobParameters.REASON_DEVICE_THERMAL);
     }
 
     @Override
     public void onSystemServicesReady() {
-        final IThermalService thermalService = IThermalService.Stub.asInterface(
-                ServiceManager.getService(Context.THERMAL_SERVICE));
-        if (thermalService != null) {
-            try {
-                thermalService.registerThermalStatusListener(new IThermalStatusListener.Stub() {
-                    @Override
-                    public void onStatusChange(int status) {
-                        final boolean shouldBeActive = status >= Temperature.THROTTLING_SEVERE;
-                        if (mIsThermalRestricted == shouldBeActive) {
-                            return;
-                        }
-                        mIsThermalRestricted = shouldBeActive;
-                        mService.onControllerStateChanged();
-                    }
-                });
-            } catch (RemoteException e) {
-                Slog.e(TAG, "Failed to register thermal callback.", e);
+        mPowerManager = mService.getContext().getSystemService(PowerManager.class);
+        // Use MainExecutor
+        mPowerManager.addThermalStatusListener(new OnThermalStatusChangedListener() {
+            @Override
+            public void onThermalStatusChanged(int status) {
+                // This is called on the main thread. Do not do any slow operations in it.
+                // mService.onControllerStateChanged() will just post a message, which is okay.
+                final boolean shouldBeActive = status >= PowerManager.THERMAL_STATUS_SEVERE;
+                if (mIsThermalRestricted == shouldBeActive) {
+                    return;
+                }
+                mIsThermalRestricted = shouldBeActive;
+                mService.onControllerStateChanged();
             }
-        }
+        });
     }
 
     @Override
diff --git a/api/current.txt b/api/current.txt
index 603dec2..40106b3 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -5872,6 +5872,7 @@
     method public android.service.notification.StatusBarNotification[] getActiveNotifications();
     method public android.app.AutomaticZenRule getAutomaticZenRule(String);
     method public java.util.Map<java.lang.String,android.app.AutomaticZenRule> getAutomaticZenRules();
+    method @Nullable public android.app.NotificationManager.Policy getConsolidatedNotificationPolicy();
     method public final int getCurrentInterruptionFilter();
     method public int getImportance();
     method public android.app.NotificationChannel getNotificationChannel(String);
@@ -24651,6 +24652,7 @@
     field public static final int DolbyVisionLevelUhd30 = 64; // 0x40
     field public static final int DolbyVisionLevelUhd48 = 128; // 0x80
     field public static final int DolbyVisionLevelUhd60 = 256; // 0x100
+    field public static final int DolbyVisionProfileDvav110 = 1024; // 0x400
     field public static final int DolbyVisionProfileDvavPen = 2; // 0x2
     field public static final int DolbyVisionProfileDvavPer = 1; // 0x1
     field public static final int DolbyVisionProfileDvavSe = 512; // 0x200
@@ -38280,6 +38282,7 @@
     field public static final String COLUMN_MIME_TYPE = "mime_type";
     field public static final String COLUMN_SIZE = "_size";
     field public static final String COLUMN_SUMMARY = "summary";
+    field public static final int FLAG_DIR_BLOCKS_TREE = 32768; // 0x8000
     field public static final int FLAG_DIR_PREFERS_GRID = 16; // 0x10
     field public static final int FLAG_DIR_PREFERS_LAST_MODIFIED = 32; // 0x20
     field public static final int FLAG_DIR_SUPPORTS_CREATE = 8; // 0x8
@@ -38498,16 +38501,17 @@
 
   public static final class MediaStore.Audio {
     ctor public MediaStore.Audio();
-    method public static String keyFor(String);
+    method @Deprecated @Nullable public static String keyFor(@Nullable String);
   }
 
   public static interface MediaStore.Audio.AlbumColumns {
     field public static final String ALBUM = "album";
     field @Deprecated public static final String ALBUM_ART = "album_art";
     field public static final String ALBUM_ID = "album_id";
-    field public static final String ALBUM_KEY = "album_key";
+    field @Deprecated public static final String ALBUM_KEY = "album_key";
     field public static final String ARTIST = "artist";
     field public static final String ARTIST_ID = "artist_id";
+    field @Deprecated public static final String ARTIST_KEY = "artist_key";
     field public static final String FIRST_YEAR = "minyear";
     field public static final String LAST_YEAR = "maxyear";
     field public static final String NUMBER_OF_SONGS = "numsongs";
@@ -38526,7 +38530,7 @@
 
   public static interface MediaStore.Audio.ArtistColumns {
     field public static final String ARTIST = "artist";
-    field public static final String ARTIST_KEY = "artist_key";
+    field @Deprecated public static final String ARTIST_KEY = "artist_key";
     field public static final String NUMBER_OF_ALBUMS = "number_of_albums";
     field public static final String NUMBER_OF_TRACKS = "number_of_tracks";
   }
@@ -38549,19 +38553,23 @@
   public static interface MediaStore.Audio.AudioColumns extends android.provider.MediaStore.MediaColumns {
     field public static final String ALBUM = "album";
     field public static final String ALBUM_ID = "album_id";
-    field public static final String ALBUM_KEY = "album_key";
+    field @Deprecated public static final String ALBUM_KEY = "album_key";
     field public static final String ARTIST = "artist";
     field public static final String ARTIST_ID = "artist_id";
-    field public static final String ARTIST_KEY = "artist_key";
+    field @Deprecated public static final String ARTIST_KEY = "artist_key";
     field public static final String BOOKMARK = "bookmark";
     field public static final String COMPOSER = "composer";
+    field public static final String GENRE = "genre";
+    field public static final String GENRE_ID = "genre_id";
+    field @Deprecated public static final String GENRE_KEY = "genre_key";
     field public static final String IS_ALARM = "is_alarm";
     field public static final String IS_AUDIOBOOK = "is_audiobook";
     field public static final String IS_MUSIC = "is_music";
     field public static final String IS_NOTIFICATION = "is_notification";
     field public static final String IS_PODCAST = "is_podcast";
     field public static final String IS_RINGTONE = "is_ringtone";
-    field public static final String TITLE_KEY = "title_key";
+    field @Deprecated public static final String TITLE_KEY = "title_key";
+    field public static final String TITLE_RESOURCE_URI = "title_resource_uri";
     field public static final String TRACK = "track";
     field public static final String YEAR = "year";
   }
@@ -44250,6 +44258,7 @@
     field public static final String KEY_DEFAULT_VM_NUMBER_STRING = "default_vm_number_string";
     field public static final String KEY_DIAL_STRING_REPLACE_STRING_ARRAY = "dial_string_replace_string_array";
     field public static final String KEY_DISABLE_CDMA_ACTIVATION_CODE_BOOL = "disable_cdma_activation_code_bool";
+    field public static final String KEY_DISCONNECT_CAUSE_PLAY_BUSYTONE_INT_ARRAY = "disconnect_cause_play_busytone_int_array";
     field public static final String KEY_DISPLAY_HD_AUDIO_PROPERTY_BOOL = "display_hd_audio_property_bool";
     field public static final String KEY_DROP_VIDEO_CALL_WHEN_ANSWERING_AUDIO_CALL_BOOL = "drop_video_call_when_answering_audio_call_bool";
     field public static final String KEY_DTMF_TYPE_ENABLED_BOOL = "dtmf_type_enabled_bool";
@@ -44595,6 +44604,7 @@
     method public int describeContents();
     method public int getAsuLevel();
     method public int getDbm();
+    method public int getEcNo();
     method @IntRange(from=android.telephony.CellSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN, to=android.telephony.CellSignalStrength.SIGNAL_STRENGTH_GREAT) public int getLevel();
     method public void writeToParcel(android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.telephony.CellSignalStrengthWcdma> CREATOR;
@@ -45100,6 +45110,7 @@
     method public long getDataLimitBytes();
     method public long getDataUsageBytes();
     method public long getDataUsageTime();
+    method @Nullable public int[] getNetworkTypes();
     method @Nullable public CharSequence getSummary();
     method @Nullable public CharSequence getTitle();
     method public void writeToParcel(android.os.Parcel, int);
@@ -45119,6 +45130,7 @@
     method public static android.telephony.SubscriptionPlan.Builder createRecurring(java.time.ZonedDateTime, java.time.Period);
     method public android.telephony.SubscriptionPlan.Builder setDataLimit(long, int);
     method public android.telephony.SubscriptionPlan.Builder setDataUsage(long, long);
+    method @NonNull public android.telephony.SubscriptionPlan.Builder setNetworkTypes(@Nullable int[]);
     method public android.telephony.SubscriptionPlan.Builder setSummary(@Nullable CharSequence);
     method public android.telephony.SubscriptionPlan.Builder setTitle(@Nullable CharSequence);
   }
diff --git a/api/lint-baseline.txt b/api/lint-baseline.txt
index e19c1fd..2ca8cf4 100644
--- a/api/lint-baseline.txt
+++ b/api/lint-baseline.txt
@@ -8,7 +8,7 @@
 
 
 AllUpper: android.media.MediaCodecInfo.CodecCapabilities#FEATURE_LowLatency:
-    Constant field names must be named with only upper case characters: `android.media.MediaCodecInfo.CodecCapabilities#FEATURE_LowLatency`, should be `FEATURE__LOW_LATENCY`?
+    
 
 
 ArrayReturn: android.app.Notification.MessagingStyle.Message#getMessagesFromBundleArray(android.os.Parcelable[]) parameter #0:
@@ -453,6 +453,10 @@
     
 
 
+GenericException: android.content.res.loader.ResourcesProvider#finalize():
+    Methods must not throw generic exceptions (`java.lang.Throwable`)
+
+
 HiddenSuperclass: android.content.res.ColorStateList:
     
 HiddenSuperclass: android.graphics.Canvas:
@@ -528,9 +532,9 @@
 MissingNullability: android.icu.util.VersionInfo#UNICODE_12_1:
     
 MissingNullability: android.media.MediaMetadataRetriever#getFrameAtTime(long, int, android.media.MediaMetadataRetriever.BitmapParams):
-    Missing nullability on method `getFrameAtTime` return
+    
 MissingNullability: android.media.MediaMetadataRetriever#getScaledFrameAtTime(long, int, int, int, android.media.MediaMetadataRetriever.BitmapParams):
-    Missing nullability on method `getScaledFrameAtTime` return
+    
 
 
 RequiresPermission: android.accounts.AccountManager#getAccountsByTypeAndFeatures(String, String[], android.accounts.AccountManagerCallback<android.accounts.Account[]>, android.os.Handler):
@@ -1159,6 +1163,10 @@
     
 
 
+StreamFiles: android.content.res.loader.DirectoryResourceLoader#DirectoryResourceLoader(java.io.File):
+    Methods accepting `File` should also accept `FileDescriptor` or streams: constructor android.content.res.loader.DirectoryResourceLoader(java.io.File)
+
+
 Todo: android.hardware.camera2.params.StreamConfigurationMap:
     
 Todo: android.hardware.camera2.params.StreamConfigurationMap#getOutputMinFrameDuration(Class<T>, android.util.Size):
diff --git a/api/system-current.txt b/api/system-current.txt
index ed379a4..46413d3 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -1359,7 +1359,7 @@
 
   public abstract class Context {
     method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public boolean bindServiceAsUser(@RequiresPermission android.content.Intent, android.content.ServiceConnection, int, android.os.UserHandle);
-    method @NonNull public android.content.Context createContextAsUser(@NonNull android.os.UserHandle);
+    method @NonNull public android.content.Context createContextAsUser(@NonNull android.os.UserHandle, int);
     method public abstract android.content.Context createCredentialProtectedStorageContext();
     method @NonNull public android.content.Context createPackageContextAsUser(@NonNull String, int, @NonNull android.os.UserHandle) throws android.content.pm.PackageManager.NameNotFoundException;
     method @Nullable public abstract java.io.File getPreloadsFileCache();
@@ -1754,7 +1754,9 @@
     field public static final int PROTECTION_FLAG_INCIDENT_REPORT_APPROVER = 1048576; // 0x100000
     field public static final int PROTECTION_FLAG_OEM = 16384; // 0x4000
     field public static final int PROTECTION_FLAG_SYSTEM_TEXT_CLASSIFIER = 65536; // 0x10000
+    field public static final int PROTECTION_FLAG_TELEPHONY = 4194304; // 0x400000
     field public static final int PROTECTION_FLAG_WELLBEING = 131072; // 0x20000
+    field public static final int PROTECTION_FLAG_WIFI = 8388608; // 0x800000
     field @Nullable public final String backgroundPermission;
     field @StringRes public int requestRes;
   }
@@ -3420,7 +3422,9 @@
   public class Location implements android.os.Parcelable {
     method public boolean isComplete();
     method public void makeComplete();
+    method public void setExtraLocation(@Nullable String, @Nullable android.location.Location);
     method public void setIsFromMockProvider(boolean);
+    field public static final String EXTRA_NO_GPS_LOCATION = "noGPSLocation";
   }
 
   public class LocationManager {
@@ -7312,7 +7316,7 @@
   public abstract class CellBroadcastService extends android.app.Service {
     ctor public CellBroadcastService();
     method @CallSuper public android.os.IBinder onBind(android.content.Intent);
-    method public abstract void onCdmaCellBroadcastSms(int, byte[]);
+    method public abstract void onCdmaCellBroadcastSms(int, byte[], int);
     method public abstract void onGsmCellBroadcastSms(int, byte[]);
     field public static final String CELL_BROADCAST_SERVICE_INTERFACE = "android.telephony.CellBroadcastService";
   }
@@ -9758,9 +9762,10 @@
     method public final long getUserActivityTimeout();
     method public final void setUserActivityTimeout(long);
     field @RequiresPermission(android.Manifest.permission.HIDE_NON_SYSTEM_OVERLAY_WINDOWS) public static final int SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS = 524288; // 0x80000
+    field @RequiresPermission(android.Manifest.permission.INTERNAL_SYSTEM_WINDOW) public static final int SYSTEM_FLAG_SHOW_FOR_ALL_USERS = 16; // 0x10
   }
 
-  @IntDef(flag=true, prefix={"SYSTEM_FLAG_"}, value={android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface WindowManager.LayoutParams.SystemFlags {
+  @IntDef(flag=true, prefix={"SYSTEM_FLAG_"}, value={android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS, android.view.WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface WindowManager.LayoutParams.SystemFlags {
   }
 
 }
diff --git a/api/system-lint-baseline.txt b/api/system-lint-baseline.txt
index d4a5152..c4af17e 100644
--- a/api/system-lint-baseline.txt
+++ b/api/system-lint-baseline.txt
@@ -702,7 +702,7 @@
 AllUpper: android.media.MediaCodecInfo.CodecCapabilities#FEATURE_IntraRefresh:
     
 AllUpper: android.media.MediaCodecInfo.CodecCapabilities#FEATURE_LowLatency:
-    Constant field names must be named with only upper case characters: `android.media.MediaCodecInfo.CodecCapabilities#FEATURE_LowLatency`, should be `FEATURE__LOW_LATENCY`?
+    
 AllUpper: android.media.MediaCodecInfo.CodecCapabilities#FEATURE_MultipleFrames:
     
 AllUpper: android.media.MediaCodecInfo.CodecCapabilities#FEATURE_PartialFrame:
@@ -1648,7 +1648,7 @@
 ArrayReturn: android.text.util.Rfc822Tokenizer#tokenize(CharSequence):
     
 ArrayReturn: android.util.ArraySet#ArraySet(E[]) parameter #0:
-    Method parameter should be Collection<E> (or subclass) instead of raw array; was `E[]`
+    
 ArrayReturn: android.util.ArraySet#toArray():
     
 ArrayReturn: android.util.ArraySet#toArray(T[]):
@@ -2430,39 +2430,39 @@
 BuilderSetStyle: android.animation.AnimatorSet.Builder:
     
 BuilderSetStyle: android.animation.AnimatorSet.Builder#with(android.animation.Animator):
-    Builder methods names should use setFoo() style: method android.animation.AnimatorSet.Builder.with(android.animation.Animator)
+    
 BuilderSetStyle: android.content.ContentProviderOperation.Builder:
     
 BuilderSetStyle: android.content.ContentProviderOperation.Builder#withExceptionAllowed(boolean):
-    Builder methods names should use setFoo() style: method android.content.ContentProviderOperation.Builder.withExceptionAllowed(boolean)
+    
 BuilderSetStyle: android.content.ContentProviderOperation.Builder#withExpectedCount(int):
-    Builder methods names should use setFoo() style: method android.content.ContentProviderOperation.Builder.withExpectedCount(int)
+    
 BuilderSetStyle: android.content.ContentProviderOperation.Builder#withExtra(String, Object):
-    Builder methods names should use setFoo() style: method android.content.ContentProviderOperation.Builder.withExtra(String,Object)
+    
 BuilderSetStyle: android.content.ContentProviderOperation.Builder#withExtraBackReference(String, int):
-    Builder methods names should use setFoo() style: method android.content.ContentProviderOperation.Builder.withExtraBackReference(String,int)
+    
 BuilderSetStyle: android.content.ContentProviderOperation.Builder#withExtraBackReference(String, int, String):
-    Builder methods names should use setFoo() style: method android.content.ContentProviderOperation.Builder.withExtraBackReference(String,int,String)
+    
 BuilderSetStyle: android.content.ContentProviderOperation.Builder#withExtras(android.os.Bundle):
-    Builder methods names should use setFoo() style: method android.content.ContentProviderOperation.Builder.withExtras(android.os.Bundle)
+    
 BuilderSetStyle: android.content.ContentProviderOperation.Builder#withSelection(String, String[]):
-    Builder methods names should use setFoo() style: method android.content.ContentProviderOperation.Builder.withSelection(String,String[])
+    
 BuilderSetStyle: android.content.ContentProviderOperation.Builder#withSelectionBackReference(int, int):
-    Builder methods names should use setFoo() style: method android.content.ContentProviderOperation.Builder.withSelectionBackReference(int,int)
+    
 BuilderSetStyle: android.content.ContentProviderOperation.Builder#withSelectionBackReference(int, int, String):
-    Builder methods names should use setFoo() style: method android.content.ContentProviderOperation.Builder.withSelectionBackReference(int,int,String)
+    
 BuilderSetStyle: android.content.ContentProviderOperation.Builder#withValue(String, Object):
-    Builder methods names should use setFoo() style: method android.content.ContentProviderOperation.Builder.withValue(String,Object)
+    
 BuilderSetStyle: android.content.ContentProviderOperation.Builder#withValueBackReference(String, int):
-    Builder methods names should use setFoo() style: method android.content.ContentProviderOperation.Builder.withValueBackReference(String,int)
+    
 BuilderSetStyle: android.content.ContentProviderOperation.Builder#withValueBackReference(String, int, String):
-    Builder methods names should use setFoo() style: method android.content.ContentProviderOperation.Builder.withValueBackReference(String,int,String)
+    
 BuilderSetStyle: android.content.ContentProviderOperation.Builder#withValueBackReferences(android.content.ContentValues):
-    Builder methods names should use setFoo() style: method android.content.ContentProviderOperation.Builder.withValueBackReferences(android.content.ContentValues)
+    
 BuilderSetStyle: android.content.ContentProviderOperation.Builder#withValues(android.content.ContentValues):
-    Builder methods names should use setFoo() style: method android.content.ContentProviderOperation.Builder.withValues(android.content.ContentValues)
+    
 BuilderSetStyle: android.content.ContentProviderOperation.Builder#withYieldAllowed(boolean):
-    Builder methods names should use setFoo() style: method android.content.ContentProviderOperation.Builder.withYieldAllowed(boolean)
+    
 
 
 CallbackInterface: android.accounts.AccountManagerCallback:
@@ -2680,7 +2680,7 @@
 ConcreteCollection: android.util.ArraySet#ArraySet():
     
 ConcreteCollection: android.util.ArraySet#ArraySet(E[]):
-    Return type is concrete collection (`android.util.ArraySet`); must be higher-level interface
+    
 ConcreteCollection: android.util.ArraySet#ArraySet(android.util.ArraySet<E>):
     
 ConcreteCollection: android.util.ArraySet#ArraySet(android.util.ArraySet<E>) parameter #0:
@@ -3439,6 +3439,8 @@
     
 GenericException: android.content.res.AssetManager.AssetInputStream#finalize():
     
+GenericException: android.content.res.loader.ResourcesProvider#finalize():
+    Methods must not throw generic exceptions (`java.lang.Throwable`)
 GenericException: android.database.CursorWindow#finalize():
     
 GenericException: android.database.sqlite.SQLiteDatabase#finalize():
@@ -18114,7 +18116,7 @@
 MissingNullability: android.media.MediaMetadataRetriever#getFrameAtTime(long, int):
     
 MissingNullability: android.media.MediaMetadataRetriever#getFrameAtTime(long, int, android.media.MediaMetadataRetriever.BitmapParams):
-    Missing nullability on method `getFrameAtTime` return
+    
 MissingNullability: android.media.MediaMetadataRetriever#getImageAtIndex(int):
     
 MissingNullability: android.media.MediaMetadataRetriever#getImageAtIndex(int, android.media.MediaMetadataRetriever.BitmapParams):
@@ -18126,7 +18128,7 @@
 MissingNullability: android.media.MediaMetadataRetriever#getScaledFrameAtTime(long, int, int, int):
     
 MissingNullability: android.media.MediaMetadataRetriever#getScaledFrameAtTime(long, int, int, int, android.media.MediaMetadataRetriever.BitmapParams):
-    Missing nullability on method `getScaledFrameAtTime` return
+    
 MissingNullability: android.media.MediaMetadataRetriever#setDataSource(String) parameter #0:
     
 MissingNullability: android.media.MediaMetadataRetriever#setDataSource(String, java.util.Map<java.lang.String,java.lang.String>) parameter #0:
@@ -35886,7 +35888,7 @@
 MissingNullability: android.view.SurfaceView#getSurfaceControl():
     
 MissingNullability: android.view.SurfaceView#setClipBounds(android.graphics.Rect) parameter #0:
-    Missing nullability on parameter `clipBounds` in method `setClipBounds`
+    
 MissingNullability: android.view.TextureView#TextureView(android.content.Context) parameter #0:
     
 MissingNullability: android.view.TextureView#TextureView(android.content.Context, android.util.AttributeSet) parameter #0:
@@ -47988,79 +47990,79 @@
 SetterReturnsThis: android.database.sqlite.SQLiteQueryBuilder:
     
 SetterReturnsThis: android.database.sqlite.SQLiteQueryBuilder#setCursorFactory(android.database.sqlite.SQLiteDatabase.CursorFactory):
-    Methods must return the builder object (return type android.database.sqlite.SQLiteQueryBuilder instead of void): method android.database.sqlite.SQLiteQueryBuilder.setCursorFactory(android.database.sqlite.SQLiteDatabase.CursorFactory)
+    
 SetterReturnsThis: android.database.sqlite.SQLiteQueryBuilder#setDistinct(boolean):
-    Methods must return the builder object (return type android.database.sqlite.SQLiteQueryBuilder instead of void): method android.database.sqlite.SQLiteQueryBuilder.setDistinct(boolean)
+    
 SetterReturnsThis: android.database.sqlite.SQLiteQueryBuilder#setProjectionMap(java.util.Map<java.lang.String,java.lang.String>):
-    Methods must return the builder object (return type android.database.sqlite.SQLiteQueryBuilder instead of void): method android.database.sqlite.SQLiteQueryBuilder.setProjectionMap(java.util.Map<java.lang.String,java.lang.String>)
+    
 SetterReturnsThis: android.database.sqlite.SQLiteQueryBuilder#setStrict(boolean):
-    Methods must return the builder object (return type android.database.sqlite.SQLiteQueryBuilder instead of void): method android.database.sqlite.SQLiteQueryBuilder.setStrict(boolean)
+    
 SetterReturnsThis: android.database.sqlite.SQLiteQueryBuilder#setStrictColumns(boolean):
-    Methods must return the builder object (return type android.database.sqlite.SQLiteQueryBuilder instead of void): method android.database.sqlite.SQLiteQueryBuilder.setStrictColumns(boolean)
+    
 SetterReturnsThis: android.database.sqlite.SQLiteQueryBuilder#setStrictGrammar(boolean):
-    Methods must return the builder object (return type android.database.sqlite.SQLiteQueryBuilder instead of void): method android.database.sqlite.SQLiteQueryBuilder.setStrictGrammar(boolean)
+    
 SetterReturnsThis: android.database.sqlite.SQLiteQueryBuilder#setTables(String):
-    Methods must return the builder object (return type android.database.sqlite.SQLiteQueryBuilder instead of void): method android.database.sqlite.SQLiteQueryBuilder.setTables(String)
+    
 SetterReturnsThis: android.hardware.camera2.CaptureRequest.Builder:
     
 SetterReturnsThis: android.hardware.camera2.CaptureRequest.Builder#set(android.hardware.camera2.CaptureRequest.Key<T>, T):
-    Methods must return the builder object (return type android.hardware.camera2.CaptureRequest.Builder instead of void): method android.hardware.camera2.CaptureRequest.Builder.set(android.hardware.camera2.CaptureRequest.Key<T>,T)
+    
 SetterReturnsThis: android.hardware.camera2.CaptureRequest.Builder#setTag(Object):
-    Methods must return the builder object (return type android.hardware.camera2.CaptureRequest.Builder instead of void): method android.hardware.camera2.CaptureRequest.Builder.setTag(Object)
+    
 SetterReturnsThis: android.print.PrintJobInfo.Builder:
     
 SetterReturnsThis: android.print.PrintJobInfo.Builder#setAttributes(android.print.PrintAttributes):
-    Methods must return the builder object (return type android.print.PrintJobInfo.Builder instead of void): method android.print.PrintJobInfo.Builder.setAttributes(android.print.PrintAttributes)
+    
 SetterReturnsThis: android.print.PrintJobInfo.Builder#setCopies(int):
-    Methods must return the builder object (return type android.print.PrintJobInfo.Builder instead of void): method android.print.PrintJobInfo.Builder.setCopies(int)
+    
 SetterReturnsThis: android.print.PrintJobInfo.Builder#setPages(android.print.PageRange[]):
-    Methods must return the builder object (return type android.print.PrintJobInfo.Builder instead of void): method android.print.PrintJobInfo.Builder.setPages(android.print.PageRange[])
+    
 SetterReturnsThis: android.renderscript.Sampler.Builder:
     
 SetterReturnsThis: android.renderscript.Sampler.Builder#setAnisotropy(float):
-    Methods must return the builder object (return type android.renderscript.Sampler.Builder instead of void): method android.renderscript.Sampler.Builder.setAnisotropy(float)
+    
 SetterReturnsThis: android.renderscript.Sampler.Builder#setMagnification(android.renderscript.Sampler.Value):
-    Methods must return the builder object (return type android.renderscript.Sampler.Builder instead of void): method android.renderscript.Sampler.Builder.setMagnification(android.renderscript.Sampler.Value)
+    
 SetterReturnsThis: android.renderscript.Sampler.Builder#setMinification(android.renderscript.Sampler.Value):
-    Methods must return the builder object (return type android.renderscript.Sampler.Builder instead of void): method android.renderscript.Sampler.Builder.setMinification(android.renderscript.Sampler.Value)
+    
 SetterReturnsThis: android.renderscript.Sampler.Builder#setWrapS(android.renderscript.Sampler.Value):
-    Methods must return the builder object (return type android.renderscript.Sampler.Builder instead of void): method android.renderscript.Sampler.Builder.setWrapS(android.renderscript.Sampler.Value)
+    
 SetterReturnsThis: android.renderscript.Sampler.Builder#setWrapT(android.renderscript.Sampler.Value):
-    Methods must return the builder object (return type android.renderscript.Sampler.Builder instead of void): method android.renderscript.Sampler.Builder.setWrapT(android.renderscript.Sampler.Value)
+    
 SetterReturnsThis: android.text.SpannableStringBuilder:
     
 SetterReturnsThis: android.text.SpannableStringBuilder#setFilters(android.text.InputFilter[]):
-    Methods must return the builder object (return type android.text.SpannableStringBuilder instead of void): method android.text.SpannableStringBuilder.setFilters(android.text.InputFilter[])
+    
 SetterReturnsThis: android.text.SpannableStringBuilder#setSpan(Object, int, int, int):
-    Methods must return the builder object (return type android.text.SpannableStringBuilder instead of void): method android.text.SpannableStringBuilder.setSpan(Object,int,int,int)
+    
 SetterReturnsThis: android.text.style.TtsSpan.Builder:
     
 SetterReturnsThis: android.text.style.TtsSpan.Builder#setIntArgument(String, int):
-    Methods must return the builder object (return type android.text.style.TtsSpan.Builder<C> instead of C): method android.text.style.TtsSpan.Builder.setIntArgument(String,int)
+    
 SetterReturnsThis: android.text.style.TtsSpan.Builder#setLongArgument(String, long):
-    Methods must return the builder object (return type android.text.style.TtsSpan.Builder<C> instead of C): method android.text.style.TtsSpan.Builder.setLongArgument(String,long)
+    
 SetterReturnsThis: android.text.style.TtsSpan.Builder#setStringArgument(String, String):
-    Methods must return the builder object (return type android.text.style.TtsSpan.Builder<C> instead of C): method android.text.style.TtsSpan.Builder.setStringArgument(String,String)
+    
 SetterReturnsThis: android.view.textclassifier.TextClassifierEvent.Builder:
     
 SetterReturnsThis: android.view.textclassifier.TextClassifierEvent.Builder#setActionIndices(int...):
-    Methods must return the builder object (return type android.view.textclassifier.TextClassifierEvent.Builder<T> instead of T): method android.view.textclassifier.TextClassifierEvent.Builder.setActionIndices(int...)
+    
 SetterReturnsThis: android.view.textclassifier.TextClassifierEvent.Builder#setEntityTypes(java.lang.String...):
-    Methods must return the builder object (return type android.view.textclassifier.TextClassifierEvent.Builder<T> instead of T): method android.view.textclassifier.TextClassifierEvent.Builder.setEntityTypes(java.lang.String...)
+    
 SetterReturnsThis: android.view.textclassifier.TextClassifierEvent.Builder#setEventContext(android.view.textclassifier.TextClassificationContext):
-    Methods must return the builder object (return type android.view.textclassifier.TextClassifierEvent.Builder<T> instead of T): method android.view.textclassifier.TextClassifierEvent.Builder.setEventContext(android.view.textclassifier.TextClassificationContext)
+    
 SetterReturnsThis: android.view.textclassifier.TextClassifierEvent.Builder#setEventIndex(int):
-    Methods must return the builder object (return type android.view.textclassifier.TextClassifierEvent.Builder<T> instead of T): method android.view.textclassifier.TextClassifierEvent.Builder.setEventIndex(int)
+    
 SetterReturnsThis: android.view.textclassifier.TextClassifierEvent.Builder#setExtras(android.os.Bundle):
-    Methods must return the builder object (return type android.view.textclassifier.TextClassifierEvent.Builder<T> instead of T): method android.view.textclassifier.TextClassifierEvent.Builder.setExtras(android.os.Bundle)
+    
 SetterReturnsThis: android.view.textclassifier.TextClassifierEvent.Builder#setLocale(android.icu.util.ULocale):
-    Methods must return the builder object (return type android.view.textclassifier.TextClassifierEvent.Builder<T> instead of T): method android.view.textclassifier.TextClassifierEvent.Builder.setLocale(android.icu.util.ULocale)
+    
 SetterReturnsThis: android.view.textclassifier.TextClassifierEvent.Builder#setModelName(String):
-    Methods must return the builder object (return type android.view.textclassifier.TextClassifierEvent.Builder<T> instead of T): method android.view.textclassifier.TextClassifierEvent.Builder.setModelName(String)
+    
 SetterReturnsThis: android.view.textclassifier.TextClassifierEvent.Builder#setResultId(String):
-    Methods must return the builder object (return type android.view.textclassifier.TextClassifierEvent.Builder<T> instead of T): method android.view.textclassifier.TextClassifierEvent.Builder.setResultId(String)
+    
 SetterReturnsThis: android.view.textclassifier.TextClassifierEvent.Builder#setScores(float...):
-    Methods must return the builder object (return type android.view.textclassifier.TextClassifierEvent.Builder<T> instead of T): method android.view.textclassifier.TextClassifierEvent.Builder.setScores(float...)
+    
 
 
 SingletonConstructor: android.text.Editable.Factory#Factory():
@@ -48265,6 +48267,8 @@
 
 StreamFiles: android.app.backup.BackupAgent#fullBackupFile(java.io.File, android.app.backup.FullBackupDataOutput):
     
+StreamFiles: android.content.res.loader.DirectoryResourceLoader#DirectoryResourceLoader(java.io.File):
+    Methods accepting `File` should also accept `FileDescriptor` or streams: constructor android.content.res.loader.DirectoryResourceLoader(java.io.File)
 StreamFiles: android.database.sqlite.SQLiteDatabase#deleteDatabase(java.io.File):
     
 StreamFiles: android.database.sqlite.SQLiteDatabase#openDatabase(java.io.File, android.database.sqlite.SQLiteDatabase.OpenParams):
diff --git a/api/test-current.txt b/api/test-current.txt
index 6715f4a..9b85b96 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -659,7 +659,7 @@
   }
 
   public abstract class Context {
-    method @NonNull public android.content.Context createContextAsUser(@NonNull android.os.UserHandle);
+    method @NonNull public android.content.Context createContextAsUser(@NonNull android.os.UserHandle, int);
     method @NonNull public android.content.Context createPackageContextAsUser(@NonNull String, int, @NonNull android.os.UserHandle) throws android.content.pm.PackageManager.NameNotFoundException;
     method public abstract android.view.Display getDisplay();
     method public abstract int getDisplayId();
@@ -731,6 +731,7 @@
     method @RequiresPermission(anyOf={"android.permission.GRANT_RUNTIME_PERMISSIONS", "android.permission.REVOKE_RUNTIME_PERMISSIONS", "android.permission.GET_RUNTIME_PERMISSIONS"}) public abstract int getPermissionFlags(@NonNull String, @NonNull String, @NonNull android.os.UserHandle);
     method @NonNull public abstract String getServicesSystemSharedLibraryPackageName();
     method @NonNull public abstract String getSharedSystemSharedLibraryPackageName();
+    method @Nullable public String[] getTelephonyPackageNames();
     method @Nullable public String getWellbeingPackageName();
     method @RequiresPermission("android.permission.GRANT_RUNTIME_PERMISSIONS") public abstract void grantRuntimePermission(@NonNull String, @NonNull String, @NonNull android.os.UserHandle);
     method @RequiresPermission("android.permission.OBSERVE_GRANT_REVOKE_PERMISSIONS") public abstract void removeOnPermissionsChangeListener(@NonNull android.content.pm.PackageManager.OnPermissionsChangedListener);
@@ -769,8 +770,10 @@
     field public static final int PROTECTION_FLAG_INCIDENT_REPORT_APPROVER = 1048576; // 0x100000
     field public static final int PROTECTION_FLAG_OEM = 16384; // 0x4000
     field public static final int PROTECTION_FLAG_SYSTEM_TEXT_CLASSIFIER = 65536; // 0x10000
+    field public static final int PROTECTION_FLAG_TELEPHONY = 4194304; // 0x400000
     field public static final int PROTECTION_FLAG_VENDOR_PRIVILEGED = 32768; // 0x8000
     field public static final int PROTECTION_FLAG_WELLBEING = 131072; // 0x20000
+    field public static final int PROTECTION_FLAG_WIFI = 8388608; // 0x800000
     field @Nullable public final String backgroundPermission;
   }
 
@@ -1091,11 +1094,14 @@
 
   public class Location implements android.os.Parcelable {
     method public void makeComplete();
+    method public void setExtraLocation(@Nullable String, @Nullable android.location.Location);
+    field public static final String EXTRA_NO_GPS_LOCATION = "noGPSLocation";
   }
 
   public class LocationManager {
     method @NonNull public String[] getBackgroundThrottlingWhitelist();
     method @NonNull public String[] getIgnoreSettingsWhitelist();
+    method @Nullable @RequiresPermission("android.permission.READ_DEVICE_CONFIG") public java.util.List<java.lang.String> getProviderPackages(@NonNull String);
     method @NonNull public java.util.List<android.location.LocationRequest> getTestProviderCurrentRequests(String);
     method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestLocationUpdates(@Nullable android.location.LocationRequest, @NonNull android.location.LocationListener, @Nullable android.os.Looper);
     method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestLocationUpdates(@Nullable android.location.LocationRequest, @NonNull java.util.concurrent.Executor, @NonNull android.location.LocationListener);
diff --git a/cmds/statsd/Android.bp b/cmds/statsd/Android.bp
index cb27325..8af925a 100644
--- a/cmds/statsd/Android.bp
+++ b/cmds/statsd/Android.bp
@@ -230,6 +230,7 @@
         "tests/e2e/Anomaly_duration_sum_e2e_test.cpp",
         "tests/e2e/Attribution_e2e_test.cpp",
         "tests/e2e/ConfigTtl_e2e_test.cpp",
+        "tests/e2e/CountMetric_e2e_test.cpp",
         "tests/e2e/DurationMetric_e2e_test.cpp",
         "tests/e2e/GaugeMetric_e2e_pull_test.cpp",
         "tests/e2e/GaugeMetric_e2e_push_test.cpp",
diff --git a/cmds/statsd/src/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp
index ff7416c..6c3dff2 100644
--- a/cmds/statsd/src/StatsLogProcessor.cpp
+++ b/cmds/statsd/src/StatsLogProcessor.cpp
@@ -16,24 +16,26 @@
 
 #define DEBUG false  // STOPSHIP if true
 #include "Log.h"
-#include "statslog.h"
+
+#include "StatsLogProcessor.h"
 
 #include <android-base/file.h>
 #include <dirent.h>
 #include <frameworks/base/cmds/statsd/src/active_config_list.pb.h>
-#include "StatsLogProcessor.h"
+#include <log/log_event_list.h>
+#include <utils/Errors.h>
+#include <utils/SystemClock.h>
+
 #include "android-base/stringprintf.h"
 #include "external/StatsPullerManager.h"
 #include "guardrail/StatsdStats.h"
 #include "metrics/CountMetricProducer.h"
+#include "state/StateManager.h"
 #include "stats_log_util.h"
 #include "stats_util.h"
+#include "statslog.h"
 #include "storage/StorageManager.h"
 
-#include <log/log_event_list.h>
-#include <utils/Errors.h>
-#include <utils/SystemClock.h>
-
 using namespace android;
 using android::base::StringPrintf;
 using android::util::FIELD_COUNT_REPEATED;
@@ -218,6 +220,8 @@
         onIsolatedUidChangedEventLocked(*event);
     }
 
+    StateManager::getInstance().onLogEvent(*event);
+
     if (mMetricsManagers.empty()) {
         return;
     }
diff --git a/cmds/statsd/src/StatsLogProcessor.h b/cmds/statsd/src/StatsLogProcessor.h
index 3d002d2..8292a3a 100644
--- a/cmds/statsd/src/StatsLogProcessor.h
+++ b/cmds/statsd/src/StatsLogProcessor.h
@@ -262,6 +262,10 @@
     FRIEND_TEST(MetricActivationE2eTest, TestCountMetricWithSameDeactivation);
     FRIEND_TEST(MetricActivationE2eTest, TestCountMetricWithTwoMetricsTwoDeactivations);
 
+    FRIEND_TEST(CountMetricE2eTest, TestWithSimpleState);
+    FRIEND_TEST(CountMetricE2eTest, TestWithMappedState);
+    FRIEND_TEST(CountMetricE2eTest, TestWithMultipleStates);
+
     FRIEND_TEST(DurationMetricE2eTest, TestOneBucket);
     FRIEND_TEST(DurationMetricE2eTest, TestTwoBuckets);
     FRIEND_TEST(DurationMetricE2eTest, TestWithActivation);
diff --git a/cmds/statsd/src/anomaly/subscriber_util.cpp b/cmds/statsd/src/anomaly/subscriber_util.cpp
index e09d575..4c30c4c 100644
--- a/cmds/statsd/src/anomaly/subscriber_util.cpp
+++ b/cmds/statsd/src/anomaly/subscriber_util.cpp
@@ -40,7 +40,7 @@
 
     for (const Subscription& subscription : subscriptions) {
         if (subscription.probability_of_informing() < 1
-                && ((float)rand() / RAND_MAX) >= subscription.probability_of_informing()) {
+                && ((float)rand() / (float)RAND_MAX) >= subscription.probability_of_informing()) {
             // Note that due to float imprecision, 0.0 and 1.0 might not truly mean never/always.
             // The config writer was advised to use -0.1 and 1.1 for never/always.
             ALOGI("Fate decided that a subscriber would not be informed.");
diff --git a/cmds/statsd/src/metrics/CountMetricProducer.cpp b/cmds/statsd/src/metrics/CountMetricProducer.cpp
index b5c8e35..4a06387 100644
--- a/cmds/statsd/src/metrics/CountMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/CountMetricProducer.cpp
@@ -18,13 +18,15 @@
 #include "Log.h"
 
 #include "CountMetricProducer.h"
-#include "guardrail/StatsdStats.h"
-#include "stats_util.h"
-#include "stats_log_util.h"
 
+#include <inttypes.h>
 #include <limits.h>
 #include <stdlib.h>
 
+#include "guardrail/StatsdStats.h"
+#include "stats_log_util.h"
+#include "stats_util.h"
+
 using android::util::FIELD_COUNT_REPEATED;
 using android::util::FIELD_TYPE_BOOL;
 using android::util::FIELD_TYPE_FLOAT;
@@ -65,16 +67,16 @@
 const int FIELD_ID_START_BUCKET_ELAPSED_MILLIS = 5;
 const int FIELD_ID_END_BUCKET_ELAPSED_MILLIS = 6;
 
-CountMetricProducer::CountMetricProducer(const ConfigKey& key, const CountMetric& metric,
-                                         const int conditionIndex,
-                                         const sp<ConditionWizard>& wizard,
-                                         const int64_t timeBaseNs, const int64_t startTimeNs,
-                                         const unordered_map<int, shared_ptr<Activation>>&
-                                                 eventActivationMap,
-                                         const unordered_map<int, vector<shared_ptr<Activation>>>&
-                                                 eventDeactivationMap)
+CountMetricProducer::CountMetricProducer(
+        const ConfigKey& key, const CountMetric& metric, const int conditionIndex,
+        const sp<ConditionWizard>& wizard, const int64_t timeBaseNs, const int64_t startTimeNs,
+
+        const unordered_map<int, shared_ptr<Activation>>& eventActivationMap,
+        const unordered_map<int, vector<shared_ptr<Activation>>>& eventDeactivationMap,
+        const vector<int>& slicedStateAtoms,
+        const unordered_map<int, unordered_map<int, int64_t>>& stateGroupMap)
     : MetricProducer(metric.id(), key, timeBaseNs, conditionIndex, wizard, eventActivationMap,
-                     eventDeactivationMap) {
+                     eventDeactivationMap, slicedStateAtoms, stateGroupMap) {
     if (metric.has_bucket()) {
         mBucketSizeNs =
                 TimeUnitToBucketSizeInMillisGuardrailed(key.GetUid(), metric.bucket()) * 1000000;
@@ -100,6 +102,8 @@
         mConditionSliced = true;
     }
 
+    // TODO(tsaichristine): b/142124705 handle metric state links
+
     flushIfNeededLocked(startTimeNs);
     // Adjust start for partial bucket
     mCurrentBucketStartTimeNs = startTimeNs;
@@ -112,6 +116,12 @@
     VLOG("~CountMetricProducer() called");
 }
 
+void CountMetricProducer::onStateChanged(int atomId, const HashableDimensionKey& primaryKey,
+                                         int oldState, int newState) {
+    VLOG("CountMetric %lld onStateChanged State%d, key %s, %d -> %d", (long long)mMetricId, atomId,
+         primaryKey.toString().c_str(), oldState, newState);
+}
+
 void CountMetricProducer::dumpStatesLocked(FILE* out, bool verbose) const {
     if (mCurrentSlicedCounter == nullptr ||
         mCurrentSlicedCounter->size() == 0) {
diff --git a/cmds/statsd/src/metrics/CountMetricProducer.h b/cmds/statsd/src/metrics/CountMetricProducer.h
index 61913c7..61e0892 100644
--- a/cmds/statsd/src/metrics/CountMetricProducer.h
+++ b/cmds/statsd/src/metrics/CountMetricProducer.h
@@ -17,15 +17,16 @@
 #ifndef COUNT_METRIC_PRODUCER_H
 #define COUNT_METRIC_PRODUCER_H
 
-#include <unordered_map>
-
 #include <android/util/ProtoOutputStream.h>
 #include <gtest/gtest_prod.h>
-#include "../anomaly/AnomalyTracker.h"
-#include "../condition/ConditionTracker.h"
-#include "../matchers/matcher_util.h"
+
+#include <unordered_map>
+
 #include "MetricProducer.h"
+#include "anomaly/AnomalyTracker.h"
+#include "condition/ConditionTracker.h"
 #include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
+#include "matchers/matcher_util.h"
 #include "stats_util.h"
 
 namespace android {
@@ -40,16 +41,20 @@
 
 class CountMetricProducer : public MetricProducer {
 public:
-    CountMetricProducer(const ConfigKey& key, const CountMetric& countMetric,
-                        const int conditionIndex, const sp<ConditionWizard>& wizard,
-                        const int64_t timeBaseNs, const int64_t startTimeNs,
-                        const std::unordered_map<int, std::shared_ptr<Activation>>&
-                                eventActivationMap = {},
-                        const std::unordered_map<int, std::vector<std::shared_ptr<Activation>>>&
-                                eventDeactivationMap = {});
+    CountMetricProducer(
+            const ConfigKey& key, const CountMetric& countMetric, const int conditionIndex,
+            const sp<ConditionWizard>& wizard, const int64_t timeBaseNs, const int64_t startTimeNs,
+            const std::unordered_map<int, std::shared_ptr<Activation>>& eventActivationMap = {},
+            const std::unordered_map<int, std::vector<std::shared_ptr<Activation>>>&
+                    eventDeactivationMap = {},
+            const vector<int>& slicedStateAtoms = {},
+            const unordered_map<int, unordered_map<int, int64_t>>& stateGroupMap = {});
 
     virtual ~CountMetricProducer();
 
+    void onStateChanged(int atomId, const HashableDimensionKey& primaryKey, int oldState,
+                        int newState) override;
+
 protected:
     void onMatchedLogEventInternalLocked(
             const size_t matcherIndex, const MetricDimensionKey& eventKey,
@@ -106,6 +111,7 @@
     FRIEND_TEST(CountMetricProducerTest, TestEventWithAppUpgrade);
     FRIEND_TEST(CountMetricProducerTest, TestEventWithAppUpgradeInNextBucket);
     FRIEND_TEST(CountMetricProducerTest, TestFirstBucket);
+    FRIEND_TEST(CountMetricProducerTest, TestOneWeekTimeUnit);
 };
 
 }  // namespace statsd
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.cpp b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
index 31b90f3..ab2a1c3 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
@@ -69,9 +69,11 @@
         const bool nesting, const sp<ConditionWizard>& wizard,
         const FieldMatcher& internalDimensions, const int64_t timeBaseNs, const int64_t startTimeNs,
         const unordered_map<int, shared_ptr<Activation>>& eventActivationMap,
-        const unordered_map<int, vector<shared_ptr<Activation>>>& eventDeactivationMap)
+        const unordered_map<int, vector<shared_ptr<Activation>>>& eventDeactivationMap,
+        const vector<int>& slicedStateAtoms,
+        const unordered_map<int, unordered_map<int, int64_t>>& stateGroupMap)
     : MetricProducer(metric.id(), key, timeBaseNs, conditionIndex, wizard, eventActivationMap,
-                     eventDeactivationMap),
+                     eventDeactivationMap, slicedStateAtoms, stateGroupMap),
       mAggregationType(metric.aggregation_type()),
       mStartIndex(startIndex),
       mStopIndex(stopIndex),
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.h b/cmds/statsd/src/metrics/DurationMetricProducer.h
index 0592b18..7457d7f 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.h
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.h
@@ -38,16 +38,16 @@
 
 class DurationMetricProducer : public MetricProducer {
 public:
-    DurationMetricProducer(const ConfigKey& key, const DurationMetric& durationMetric,
-                           const int conditionIndex, const size_t startIndex,
-                           const size_t stopIndex, const size_t stopAllIndex, const bool nesting,
-                           const sp<ConditionWizard>& wizard,
-                           const FieldMatcher& internalDimensions, const int64_t timeBaseNs,
-                           const int64_t startTimeNs,
-                           const unordered_map<int, shared_ptr<Activation>>&
-                                   eventActivationMap = {},
-                           const unordered_map<int, vector<shared_ptr<Activation>>>&
-                                   eventDeactivationMap = {});
+    DurationMetricProducer(
+            const ConfigKey& key, const DurationMetric& durationMetric, const int conditionIndex,
+            const size_t startIndex, const size_t stopIndex, const size_t stopAllIndex,
+            const bool nesting, const sp<ConditionWizard>& wizard,
+            const FieldMatcher& internalDimensions, const int64_t timeBaseNs,
+            const int64_t startTimeNs,
+            const unordered_map<int, shared_ptr<Activation>>& eventActivationMap = {},
+            const unordered_map<int, vector<shared_ptr<Activation>>>& eventDeactivationMap = {},
+            const vector<int>& slicedStateAtoms = {},
+            const unordered_map<int, unordered_map<int, int64_t>>& stateGroupMap = {});
 
     virtual ~DurationMetricProducer();
 
diff --git a/cmds/statsd/src/metrics/EventMetricProducer.cpp b/cmds/statsd/src/metrics/EventMetricProducer.cpp
index a60a916..32eb077 100644
--- a/cmds/statsd/src/metrics/EventMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/EventMetricProducer.cpp
@@ -52,16 +52,15 @@
 const int FIELD_ID_ELAPSED_TIMESTAMP_NANOS = 1;
 const int FIELD_ID_ATOMS = 2;
 
-EventMetricProducer::EventMetricProducer(const ConfigKey& key, const EventMetric& metric,
-                                         const int conditionIndex,
-                                         const sp<ConditionWizard>& wizard,
-                                         const int64_t startTimeNs,
-                                         const unordered_map<int, shared_ptr<Activation>>&
-                                                 eventActivationMap,
-                                         const unordered_map<int, vector<shared_ptr<Activation>>>&
-                                                 eventDeactivationMap)
+EventMetricProducer::EventMetricProducer(
+        const ConfigKey& key, const EventMetric& metric, const int conditionIndex,
+        const sp<ConditionWizard>& wizard, const int64_t startTimeNs,
+        const unordered_map<int, shared_ptr<Activation>>& eventActivationMap,
+        const unordered_map<int, vector<shared_ptr<Activation>>>& eventDeactivationMap,
+        const vector<int>& slicedStateAtoms,
+        const unordered_map<int, unordered_map<int, int64_t>>& stateGroupMap)
     : MetricProducer(metric.id(), key, startTimeNs, conditionIndex, wizard, eventActivationMap,
-                     eventDeactivationMap) {
+                     eventDeactivationMap, slicedStateAtoms, stateGroupMap) {
     if (metric.links().size() > 0) {
         for (const auto& link : metric.links()) {
             Metric2Condition mc;
diff --git a/cmds/statsd/src/metrics/EventMetricProducer.h b/cmds/statsd/src/metrics/EventMetricProducer.h
index aab53c8..dca37e8 100644
--- a/cmds/statsd/src/metrics/EventMetricProducer.h
+++ b/cmds/statsd/src/metrics/EventMetricProducer.h
@@ -33,13 +33,14 @@
 
 class EventMetricProducer : public MetricProducer {
 public:
-    EventMetricProducer(const ConfigKey& key, const EventMetric& eventMetric,
-                        const int conditionIndex, const sp<ConditionWizard>& wizard,
-                        const int64_t startTimeNs,
-                        const std::unordered_map<int, std::shared_ptr<Activation>>&
-                                eventActivationMap = {},
-                        const std::unordered_map<int, std::vector<std::shared_ptr<Activation>>>&
-                                eventDeactivationMap = {});
+    EventMetricProducer(
+            const ConfigKey& key, const EventMetric& eventMetric, const int conditionIndex,
+            const sp<ConditionWizard>& wizard, const int64_t startTimeNs,
+            const std::unordered_map<int, std::shared_ptr<Activation>>& eventActivationMap = {},
+            const std::unordered_map<int, std::vector<std::shared_ptr<Activation>>>&
+                    eventDeactivationMap = {},
+            const vector<int>& slicedStateAtoms = {},
+            const unordered_map<int, unordered_map<int, int64_t>>& stateGroupMap = {});
 
     virtual ~EventMetricProducer();
 
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
index e409b6fb..d0f88a8 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
@@ -74,9 +74,11 @@
         const int atomId, const int64_t timeBaseNs, const int64_t startTimeNs,
         const sp<StatsPullerManager>& pullerManager,
         const unordered_map<int, shared_ptr<Activation>>& eventActivationMap,
-        const unordered_map<int, vector<shared_ptr<Activation>>>& eventDeactivationMap)
+        const unordered_map<int, vector<shared_ptr<Activation>>>& eventDeactivationMap,
+        const vector<int>& slicedStateAtoms,
+        const unordered_map<int, unordered_map<int, int64_t>>& stateGroupMap)
     : MetricProducer(metric.id(), key, timeBaseNs, conditionIndex, wizard, eventActivationMap,
-            eventDeactivationMap),
+                     eventDeactivationMap, slicedStateAtoms, stateGroupMap),
       mWhatMatcherIndex(whatMatcherIndex),
       mEventMatcherWizard(matcherWizard),
       mPullerManager(pullerManager),
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.h b/cmds/statsd/src/metrics/GaugeMetricProducer.h
index dfe1d56..640a02a 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.h
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.h
@@ -56,16 +56,17 @@
 // producer always reports the guage at the earliest time of the bucket when the condition is met.
 class GaugeMetricProducer : public virtual MetricProducer, public virtual PullDataReceiver {
 public:
-    GaugeMetricProducer(const ConfigKey& key, const GaugeMetric& gaugeMetric,
-                        const int conditionIndex, const sp<ConditionWizard>& conditionWizard,
-                        const int whatMatcherIndex,const sp<EventMatcherWizard>& matcherWizard,
-                        const int pullTagId, const int triggerAtomId, const int atomId,
-                        const int64_t timeBaseNs, const int64_t startTimeNs,
-                        const sp<StatsPullerManager>& pullerManager,
-                        const std::unordered_map<int, std::shared_ptr<Activation>>&
-                                eventActivationMap = {},
-                        const std::unordered_map<int, std::vector<std::shared_ptr<Activation>>>&
-                                eventDeactivationMap = {});
+    GaugeMetricProducer(
+            const ConfigKey& key, const GaugeMetric& gaugeMetric, const int conditionIndex,
+            const sp<ConditionWizard>& conditionWizard, const int whatMatcherIndex,
+            const sp<EventMatcherWizard>& matcherWizard, const int pullTagId,
+            const int triggerAtomId, const int atomId, const int64_t timeBaseNs,
+            const int64_t startTimeNs, const sp<StatsPullerManager>& pullerManager,
+            const std::unordered_map<int, std::shared_ptr<Activation>>& eventActivationMap = {},
+            const std::unordered_map<int, std::vector<std::shared_ptr<Activation>>>&
+                    eventDeactivationMap = {},
+            const vector<int>& slicedStateAtoms = {},
+            const unordered_map<int, unordered_map<int, int64_t>>& stateGroupMap = {});
 
     virtual ~GaugeMetricProducer();
 
diff --git a/cmds/statsd/src/metrics/MetricProducer.cpp b/cmds/statsd/src/metrics/MetricProducer.cpp
index 3426a19..2a700ef 100644
--- a/cmds/statsd/src/metrics/MetricProducer.cpp
+++ b/cmds/statsd/src/metrics/MetricProducer.cpp
@@ -45,23 +45,27 @@
         const int conditionIndex, const sp<ConditionWizard>& wizard,
         const std::unordered_map<int, std::shared_ptr<Activation>>& eventActivationMap,
         const std::unordered_map<int, std::vector<std::shared_ptr<Activation>>>&
-                eventDeactivationMap)
-        : mMetricId(metricId),
-          mConfigKey(key),
-          mTimeBaseNs(timeBaseNs),
-          mCurrentBucketStartTimeNs(timeBaseNs),
-          mCurrentBucketNum(0),
-          mCondition(initialCondition(conditionIndex)),
-          mConditionTrackerIndex(conditionIndex),
-          mConditionSliced(false),
-          mWizard(wizard),
-          mContainANYPositionInDimensionsInWhat(false),
-          mSliceByPositionALL(false),
-          mHasLinksToAllConditionDimensionsInTracker(false),
-          mEventActivationMap(eventActivationMap),
-          mEventDeactivationMap(eventDeactivationMap),
-          mIsActive(mEventActivationMap.empty()) {
-    }
+                eventDeactivationMap,
+        const vector<int>& slicedStateAtoms,
+        const unordered_map<int, unordered_map<int, int64_t>>& stateGroupMap)
+    : mMetricId(metricId),
+      mConfigKey(key),
+      mTimeBaseNs(timeBaseNs),
+      mCurrentBucketStartTimeNs(timeBaseNs),
+      mCurrentBucketNum(0),
+      mCondition(initialCondition(conditionIndex)),
+      mConditionTrackerIndex(conditionIndex),
+      mConditionSliced(false),
+      mWizard(wizard),
+      mContainANYPositionInDimensionsInWhat(false),
+      mSliceByPositionALL(false),
+      mHasLinksToAllConditionDimensionsInTracker(false),
+      mEventActivationMap(eventActivationMap),
+      mEventDeactivationMap(eventDeactivationMap),
+      mIsActive(mEventActivationMap.empty()),
+      mSlicedStateAtoms(slicedStateAtoms),
+      mStateGroupMap(stateGroupMap) {
+}
 
 void MetricProducer::onMatchedLogEventLocked(const size_t matcherIndex, const LogEvent& event) {
     if (!mIsActive) {
diff --git a/cmds/statsd/src/metrics/MetricProducer.h b/cmds/statsd/src/metrics/MetricProducer.h
index 1e1eb69..a72de22 100644
--- a/cmds/statsd/src/metrics/MetricProducer.h
+++ b/cmds/statsd/src/metrics/MetricProducer.h
@@ -17,19 +17,19 @@
 #ifndef METRIC_PRODUCER_H
 #define METRIC_PRODUCER_H
 
-#include <shared_mutex>
-
 #include <frameworks/base/cmds/statsd/src/active_config_list.pb.h>
+#include <log/logprint.h>
+#include <utils/RefBase.h>
+
+#include <unordered_map>
+
 #include "HashableDimensionKey.h"
 #include "anomaly/AnomalyTracker.h"
 #include "condition/ConditionWizard.h"
 #include "config/ConfigKey.h"
 #include "matchers/matcher_util.h"
 #include "packages/PackageInfoListener.h"
-
-#include <log/logprint.h>
-#include <utils/RefBase.h>
-#include <unordered_map>
+#include "state/StateListener.h"
 
 namespace android {
 namespace os {
@@ -86,13 +86,15 @@
 // writing the report to dropbox. MetricProducers should respond to package changes as required in
 // PackageInfoListener, but if none of the metrics are slicing by package name, then the update can
 // be a no-op.
-class MetricProducer : public virtual PackageInfoListener {
+class MetricProducer : public virtual PackageInfoListener, public virtual StateListener {
 public:
     MetricProducer(const int64_t& metricId, const ConfigKey& key, const int64_t timeBaseNs,
                    const int conditionIndex, const sp<ConditionWizard>& wizard,
                    const std::unordered_map<int, std::shared_ptr<Activation>>& eventActivationMap,
                    const std::unordered_map<int, std::vector<std::shared_ptr<Activation>>>&
-                           eventDeactivationMap);
+                           eventDeactivationMap,
+                   const vector<int>& slicedStateAtoms,
+                   const unordered_map<int, unordered_map<int, int64_t>>& stateGroupMap);
 
     virtual ~MetricProducer(){};
 
@@ -151,6 +153,9 @@
         return mConditionSliced;
     };
 
+    void onStateChanged(int atomId, const HashableDimensionKey& primaryKey, int oldState,
+                        int newState){};
+
     // Output the metrics data to [protoOutput]. All metrics reports end with the same timestamp.
     // This method clears all the past buckets.
     void onDumpReport(const int64_t dumpTimeNs,
@@ -230,6 +235,11 @@
         return mBucketSizeNs;
     }
 
+    inline const std::vector<int> getSlicedStateAtoms() {
+        std::lock_guard<std::mutex> lock(mMutex);
+        return mSlicedStateAtoms;
+    }
+
     /* If alert is valid, adds an AnomalyTracker and returns it. If invalid, returns nullptr. */
     virtual sp<AnomalyTracker> addAnomalyTracker(const Alert &alert,
                                                  const sp<AlarmMonitor>& anomalyAlarmMonitor) {
@@ -381,6 +391,16 @@
 
     bool mIsActive;
 
+    // The slice_by_state atom ids defined in statsd_config.
+    std::vector<int> mSlicedStateAtoms;
+
+    // Maps atom ids and state values to group_ids (<atom_id, <value, group_id>>).
+    std::unordered_map<int, std::unordered_map<int, int64_t>> mStateGroupMap;
+
+    FRIEND_TEST(CountMetricE2eTest, TestWithSimpleState);
+    FRIEND_TEST(CountMetricE2eTest, TestWithMappedState);
+    FRIEND_TEST(CountMetricE2eTest, TestWithMultipleStates);
+
     FRIEND_TEST(DurationMetricE2eTest, TestOneBucket);
     FRIEND_TEST(DurationMetricE2eTest, TestTwoBuckets);
     FRIEND_TEST(DurationMetricE2eTest, TestWithActivation);
diff --git a/cmds/statsd/src/metrics/MetricsManager.cpp b/cmds/statsd/src/metrics/MetricsManager.cpp
index 963205e..7bae4b9 100644
--- a/cmds/statsd/src/metrics/MetricsManager.cpp
+++ b/cmds/statsd/src/metrics/MetricsManager.cpp
@@ -15,8 +15,12 @@
  */
 #define DEBUG false  // STOPSHIP if true
 #include "Log.h"
+
 #include "MetricsManager.h"
-#include "statslog.h"
+
+#include <log/logprint.h>
+#include <private/android_filesystem_config.h>
+#include <utils/SystemClock.h>
 
 #include "CountMetricProducer.h"
 #include "condition/CombinationConditionTracker.h"
@@ -25,12 +29,10 @@
 #include "matchers/CombinationLogMatchingTracker.h"
 #include "matchers/SimpleLogMatchingTracker.h"
 #include "metrics_manager_util.h"
-#include "stats_util.h"
+#include "state/StateManager.h"
 #include "stats_log_util.h"
-
-#include <log/logprint.h>
-#include <private/android_filesystem_config.h>
-#include <utils/SystemClock.h>
+#include "stats_util.h"
+#include "statslog.h"
 
 using android::util::FIELD_COUNT_REPEATED;
 using android::util::FIELD_TYPE_INT32;
@@ -149,6 +151,12 @@
 }
 
 MetricsManager::~MetricsManager() {
+    for (auto it : mAllMetricProducers) {
+        for (int atomId : it->getSlicedStateAtoms()) {
+            StateManager::getInstance().unregisterListener(atomId, it);
+        }
+    }
+
     VLOG("~MetricsManager()");
 }
 
diff --git a/cmds/statsd/src/metrics/MetricsManager.h b/cmds/statsd/src/metrics/MetricsManager.h
index 3dad614..d184121 100644
--- a/cmds/statsd/src/metrics/MetricsManager.h
+++ b/cmds/statsd/src/metrics/MetricsManager.h
@@ -282,6 +282,10 @@
             TestActivationOnBootMultipleActivationsDifferentActivationTypes);
     FRIEND_TEST(StatsLogProcessorTest, TestActivationsPersistAcrossSystemServerRestart);
 
+    FRIEND_TEST(CountMetricE2eTest, TestWithSimpleState);
+    FRIEND_TEST(CountMetricE2eTest, TestWithMappedState);
+    FRIEND_TEST(CountMetricE2eTest, TestWithMultipleStates);
+
     FRIEND_TEST(DurationMetricE2eTest, TestOneBucket);
     FRIEND_TEST(DurationMetricE2eTest, TestTwoBuckets);
     FRIEND_TEST(DurationMetricE2eTest, TestWithActivation);
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.cpp b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
index 7fe5a83..6fd0327 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
@@ -83,9 +83,11 @@
         const sp<EventMatcherWizard>& matcherWizard, const int pullTagId, const int64_t timeBaseNs,
         const int64_t startTimeNs, const sp<StatsPullerManager>& pullerManager,
         const unordered_map<int, shared_ptr<Activation>>& eventActivationMap,
-        const unordered_map<int, vector<shared_ptr<Activation>>>& eventDeactivationMap)
+        const unordered_map<int, vector<shared_ptr<Activation>>>& eventDeactivationMap,
+        const vector<int>& slicedStateAtoms,
+        const unordered_map<int, unordered_map<int, int64_t>>& stateGroupMap)
     : MetricProducer(metric.id(), key, timeBaseNs, conditionIndex, conditionWizard,
-                     eventActivationMap, eventDeactivationMap),
+                     eventActivationMap, eventDeactivationMap, slicedStateAtoms, stateGroupMap),
       mWhatMatcherIndex(whatMatcherIndex),
       mEventMatcherWizard(matcherWizard),
       mPullerManager(pullerManager),
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.h b/cmds/statsd/src/metrics/ValueMetricProducer.h
index d7cd397..206e602 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.h
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.h
@@ -52,15 +52,17 @@
 // - an alarm set to the end of the bucket
 class ValueMetricProducer : public virtual MetricProducer, public virtual PullDataReceiver {
 public:
-    ValueMetricProducer(const ConfigKey& key, const ValueMetric& valueMetric,
-                        const int conditionIndex, const sp<ConditionWizard>& conditionWizard,
-                        const int whatMatcherIndex, const sp<EventMatcherWizard>& matcherWizard,
-                        const int pullTagId, const int64_t timeBaseNs, const int64_t startTimeNs,
-                        const sp<StatsPullerManager>& pullerManager,
-                        const std::unordered_map<int, std::shared_ptr<Activation>>&
-                                eventActivationMap = {},
-                        const std::unordered_map<int, std::vector<std::shared_ptr<Activation>>>&
-                                eventDeactivationMap = {});
+    ValueMetricProducer(
+            const ConfigKey& key, const ValueMetric& valueMetric, const int conditionIndex,
+            const sp<ConditionWizard>& conditionWizard, const int whatMatcherIndex,
+            const sp<EventMatcherWizard>& matcherWizard, const int pullTagId,
+            const int64_t timeBaseNs, const int64_t startTimeNs,
+            const sp<StatsPullerManager>& pullerManager,
+            const std::unordered_map<int, std::shared_ptr<Activation>>& eventActivationMap = {},
+            const std::unordered_map<int, std::vector<std::shared_ptr<Activation>>>&
+                    eventDeactivationMap = {},
+            const vector<int>& slicedStateAtoms = {},
+            const unordered_map<int, unordered_map<int, int64_t>>& stateGroupMap = {});
 
     virtual ~ValueMetricProducer();
 
diff --git a/cmds/statsd/src/metrics/metrics_manager_util.cpp b/cmds/statsd/src/metrics/metrics_manager_util.cpp
index 0fee71e..33e162e 100644
--- a/cmds/statsd/src/metrics/metrics_manager_util.cpp
+++ b/cmds/statsd/src/metrics/metrics_manager_util.cpp
@@ -20,24 +20,24 @@
 #include "metrics_manager_util.h"
 #include "MetricProducer.h"
 
-#include "../condition/CombinationConditionTracker.h"
-#include "../condition/SimpleConditionTracker.h"
-#include "../condition/StateConditionTracker.h"
-#include "../external/StatsPullerManager.h"
-#include "../matchers/CombinationLogMatchingTracker.h"
-#include "../matchers/SimpleLogMatchingTracker.h"
-#include "../matchers/EventMatcherWizard.h"
-#include "../metrics/CountMetricProducer.h"
-#include "../metrics/DurationMetricProducer.h"
-#include "../metrics/EventMetricProducer.h"
-#include "../metrics/GaugeMetricProducer.h"
-#include "../metrics/ValueMetricProducer.h"
+#include <inttypes.h>
 
+#include "condition/CombinationConditionTracker.h"
+#include "condition/SimpleConditionTracker.h"
+#include "condition/StateConditionTracker.h"
+#include "external/StatsPullerManager.h"
+#include "matchers/CombinationLogMatchingTracker.h"
+#include "matchers/EventMatcherWizard.h"
+#include "matchers/SimpleLogMatchingTracker.h"
+#include "metrics/CountMetricProducer.h"
+#include "metrics/DurationMetricProducer.h"
+#include "metrics/EventMetricProducer.h"
+#include "metrics/GaugeMetricProducer.h"
+#include "metrics/ValueMetricProducer.h"
+#include "state/StateManager.h"
 #include "stats_util.h"
 #include "statslog.h"
 
-#include <inttypes.h>
-
 using std::set;
 using std::string;
 using std::unordered_map;
@@ -138,6 +138,41 @@
     return true;
 }
 
+// Initializes state data structures for a metric.
+// input:
+// [config]: the input config
+// [stateIds]: the slice_by_state ids for this metric
+// [stateAtomIdMap]: this map contains the mapping from all state ids to atom ids
+// [allStateGroupMaps]: this map contains the mapping from state ids and state
+//                      values to state group ids for all states
+// output:
+// [slicedStateAtoms]: a vector of atom ids of all the slice_by_states
+// [stateGroupMap]: this map should contain the mapping from states ids and state
+//                      values to state group ids for all states that this metric
+//                      is interested in
+bool handleMetricWithStates(
+        const StatsdConfig& config, const ::google::protobuf::RepeatedField<int64_t>& stateIds,
+        const unordered_map<int64_t, int>& stateAtomIdMap,
+        const unordered_map<int64_t, unordered_map<int, int64_t>>& allStateGroupMaps,
+        vector<int>& slicedStateAtoms,
+        unordered_map<int, unordered_map<int, int64_t>>& stateGroupMap) {
+    for (const auto& stateId : stateIds) {
+        auto it = stateAtomIdMap.find(stateId);
+        if (it == stateAtomIdMap.end()) {
+            ALOGW("cannot find State %" PRId64 " in the config", stateId);
+            return false;
+        }
+        int atomId = it->second;
+        slicedStateAtoms.push_back(atomId);
+
+        auto stateIt = allStateGroupMaps.find(stateId);
+        if (stateIt != allStateGroupMaps.end()) {
+            stateGroupMap[atomId] = stateIt->second;
+        }
+    }
+    return true;
+}
+
 // Validates a metricActivation and populates state.
 // EventActivationMap and EventDeactivationMap are supplied to a MetricProducer
 //      to provide the producer with state about its activators and deactivators.
@@ -342,12 +377,32 @@
     return true;
 }
 
+bool initStates(const StatsdConfig& config, unordered_map<int64_t, int>& stateAtomIdMap,
+                unordered_map<int64_t, unordered_map<int, int64_t>>& allStateGroupMaps) {
+    for (int i = 0; i < config.state_size(); i++) {
+        const State& state = config.state(i);
+        const int64_t stateId = state.id();
+        stateAtomIdMap[stateId] = state.atom_id();
+
+        const StateMap& stateMap = state.map();
+        for (auto group : stateMap.group()) {
+            for (auto value : group.value()) {
+                allStateGroupMaps[stateId][value] = group.group_id();
+            }
+        }
+    }
+
+    return true;
+}
+
 bool initMetrics(const ConfigKey& key, const StatsdConfig& config, const int64_t timeBaseTimeNs,
                  const int64_t currentTimeNs, UidMap& uidMap,
                  const sp<StatsPullerManager>& pullerManager,
                  const unordered_map<int64_t, int>& logTrackerMap,
                  const unordered_map<int64_t, int>& conditionTrackerMap,
                  const vector<sp<LogMatchingTracker>>& allAtomMatchers,
+                 const unordered_map<int64_t, int>& stateAtomIdMap,
+                 const unordered_map<int64_t, unordered_map<int, int64_t>>& allStateGroupMaps,
                  vector<sp<ConditionTracker>>& allConditionTrackers,
                  vector<sp<MetricProducer>>& allMetricProducers,
                  unordered_map<int, vector<int>>& conditionToMetricMap,
@@ -398,10 +453,9 @@
 
         int conditionIndex = -1;
         if (metric.has_condition()) {
-            bool good = handleMetricWithConditions(
-                    metric.condition(), metricIndex, conditionTrackerMap, metric.links(),
-                    allConditionTrackers, conditionIndex, conditionToMetricMap);
-            if (!good) {
+            if (!handleMetricWithConditions(metric.condition(), metricIndex, conditionTrackerMap,
+                                            metric.links(), allConditionTrackers, conditionIndex,
+                                            conditionToMetricMap)) {
                 return false;
             }
         } else {
@@ -411,6 +465,18 @@
             }
         }
 
+        std::vector<int> slicedStateAtoms;
+        unordered_map<int, unordered_map<int, int64_t>> stateGroupMap;
+        if (metric.slice_by_state_size() > 0) {
+            if (!handleMetricWithStates(config, metric.slice_by_state(), stateAtomIdMap,
+                                        allStateGroupMaps, slicedStateAtoms, stateGroupMap)) {
+                return false;
+            }
+        }
+
+        // TODO(tsaichristine): add check for unequal number of MetricStateLinks
+        // and slice_by_states
+
         unordered_map<int, shared_ptr<Activation>> eventActivationMap;
         unordered_map<int, vector<shared_ptr<Activation>>> eventDeactivationMap;
         bool success = handleMetricActivation(config, metric.id(), metricIndex,
@@ -421,7 +487,7 @@
 
         sp<MetricProducer> countProducer = new CountMetricProducer(
                 key, metric, conditionIndex, wizard, timeBaseTimeNs, currentTimeNs,
-                eventActivationMap, eventDeactivationMap);
+                eventActivationMap, eventDeactivationMap, slicedStateAtoms, stateGroupMap);
         allMetricProducers.push_back(countProducer);
     }
 
@@ -721,6 +787,13 @@
     }
     for (const auto& it : allMetricProducers) {
         uidMap.addListener(it);
+
+        // Register metrics to StateTrackers
+        for (int atomId : it->getSlicedStateAtoms()) {
+            if (!StateManager::getInstance().registerListener(atomId, it)) {
+                return false;
+            }
+        }
     }
     return true;
 }
@@ -846,6 +919,8 @@
     unordered_map<int64_t, int> logTrackerMap;
     unordered_map<int64_t, int> conditionTrackerMap;
     unordered_map<int64_t, int> metricProducerMap;
+    unordered_map<int64_t, int> stateAtomIdMap;
+    unordered_map<int64_t, unordered_map<int, int64_t>> allStateGroupMaps;
 
     if (!initLogTrackers(config, uidMap, logTrackerMap, allAtomMatchers, allTagIds)) {
         ALOGE("initLogMatchingTrackers failed");
@@ -858,9 +933,13 @@
         ALOGE("initConditionTrackers failed");
         return false;
     }
-
+    if (!initStates(config, stateAtomIdMap, allStateGroupMaps)) {
+        ALOGE("initStates failed");
+        return false;
+    }
     if (!initMetrics(key, config, timeBaseNs, currentTimeNs, uidMap, pullerManager, logTrackerMap,
-                     conditionTrackerMap, allAtomMatchers, allConditionTrackers, allMetricProducers,
+                     conditionTrackerMap, allAtomMatchers, stateAtomIdMap, allStateGroupMaps,
+                     allConditionTrackers, allMetricProducers,
                      conditionToMetricMap, trackerToMetricMap, metricProducerMap,
                      noReportMetricIds, activationAtomTrackerToMetricMap,
                      deactivationAtomTrackerToMetricMap, metricsWithActivation)) {
diff --git a/cmds/statsd/src/metrics/metrics_manager_util.h b/cmds/statsd/src/metrics/metrics_manager_util.h
index 3802948..95b2ab8 100644
--- a/cmds/statsd/src/metrics/metrics_manager_util.h
+++ b/cmds/statsd/src/metrics/metrics_manager_util.h
@@ -68,6 +68,17 @@
                     std::unordered_map<int, std::vector<int>>& trackerToConditionMap,
                     std::unordered_map<int, std::vector<MetricConditionLink>>& eventConditionLinks);
 
+// Initialize State maps using State protos in the config. These maps will
+// eventually be passed to MetricProducers to initialize their state info.
+// input:
+// [config]: the input config
+// output:
+// [stateAtomIdMap]: this map should contain the mapping from state ids to atom ids
+// [allStateGroupMaps]: this map should contain the mapping from states ids and state
+//                      values to state group ids for all states
+bool initStates(const StatsdConfig& config, unordered_map<int64_t, int>& stateAtomIdMap,
+                unordered_map<int64_t, unordered_map<int, int64_t>>& allStateGroupMaps);
+
 // Initialize MetricProducers.
 // input:
 // [key]: the config key that this config belongs to
@@ -75,6 +86,9 @@
 // [timeBaseSec]: start time base for all metrics
 // [logTrackerMap]: LogMatchingTracker name to index mapping from previous step.
 // [conditionTrackerMap]: condition name to index mapping
+// [stateAtomIdMap]: contains the mapping from state ids to atom ids
+// [allStateGroupMaps]: contains the mapping from atom ids and state values to
+//                      state group ids for all states
 // output:
 // [allMetricProducers]: contains the list of sp to the MetricProducers created.
 // [conditionToMetricMap]: contains the mapping from condition tracker index to
@@ -87,6 +101,8 @@
         const std::unordered_map<int64_t, int>& conditionTrackerMap,
         const std::unordered_map<int, std::vector<MetricConditionLink>>& eventConditionLinks,
         const vector<sp<LogMatchingTracker>>& allAtomMatchers,
+        const unordered_map<int64_t, int>& stateAtomIdMap,
+        const unordered_map<int64_t, unordered_map<int, int64_t>>& allStateGroupMaps,
         vector<sp<ConditionTracker>>& allConditionTrackers,
         std::vector<sp<MetricProducer>>& allMetricProducers,
         std::unordered_map<int, std::vector<int>>& conditionToMetricMap,
diff --git a/cmds/statsd/src/state/StateManager.cpp b/cmds/statsd/src/state/StateManager.cpp
index a3059c5..95b2c76 100644
--- a/cmds/statsd/src/state/StateManager.cpp
+++ b/cmds/statsd/src/state/StateManager.cpp
@@ -35,26 +35,25 @@
     }
 }
 
-bool StateManager::registerListener(int stateAtomId, wp<StateListener> listener) {
+bool StateManager::registerListener(int atomId, wp<StateListener> listener) {
     std::lock_guard<std::mutex> lock(mMutex);
 
     // Check if state tracker already exists
-    if (mStateTrackers.find(stateAtomId) == mStateTrackers.end()) {
+    if (mStateTrackers.find(atomId) == mStateTrackers.end()) {
         // Create a new state tracker iff atom is a state atom
-        auto it = android::util::AtomsInfo::kStateAtomsFieldOptions.find(stateAtomId);
+        auto it = android::util::AtomsInfo::kStateAtomsFieldOptions.find(atomId);
         if (it != android::util::AtomsInfo::kStateAtomsFieldOptions.end()) {
-            mStateTrackers[stateAtomId] = new StateTracker(stateAtomId, it->second);
+            mStateTrackers[atomId] = new StateTracker(atomId, it->second);
         } else {
-            ALOGE("StateManager cannot register listener, Atom %d is not a state atom",
-                  stateAtomId);
+            ALOGE("StateManager cannot register listener, Atom %d is not a state atom", atomId);
             return false;
         }
     }
-    mStateTrackers[stateAtomId]->registerListener(listener);
+    mStateTrackers[atomId]->registerListener(listener);
     return true;
 }
 
-void StateManager::unregisterListener(int stateAtomId, wp<StateListener> listener) {
+void StateManager::unregisterListener(int atomId, wp<StateListener> listener) {
     std::unique_lock<std::mutex> lock(mMutex);
 
     // Hold the sp<> until the lock is released so that ~StateTracker() is
@@ -62,7 +61,7 @@
     sp<StateTracker> toRemove;
 
     // Unregister listener from correct StateTracker
-    auto it = mStateTrackers.find(stateAtomId);
+    auto it = mStateTrackers.find(atomId);
     if (it != mStateTrackers.end()) {
         it->second->unregisterListener(listener);
 
@@ -73,15 +72,15 @@
         }
     } else {
         ALOGE("StateManager cannot unregister listener, StateTracker for atom %d does not exist",
-              stateAtomId);
+              atomId);
     }
     lock.unlock();
 }
 
-int StateManager::getState(int stateAtomId, const HashableDimensionKey& key) {
+int StateManager::getStateValue(int atomId, const HashableDimensionKey& key) {
     std::lock_guard<std::mutex> lock(mMutex);
-    if (mStateTrackers.find(stateAtomId) != mStateTrackers.end()) {
-        return mStateTrackers[stateAtomId]->getState(key);
+    if (mStateTrackers.find(atomId) != mStateTrackers.end()) {
+        return mStateTrackers[atomId]->getStateValue(key);
     }
 
     return StateTracker::kStateUnknown;
diff --git a/cmds/statsd/src/state/StateManager.h b/cmds/statsd/src/state/StateManager.h
index ce60f14..89ee6c0 100644
--- a/cmds/statsd/src/state/StateManager.h
+++ b/cmds/statsd/src/state/StateManager.h
@@ -15,10 +15,11 @@
  */
 #pragma once
 
-//#include <utils/Log.h>
+#include <gtest/gtest_prod.h>
+#include <inttypes.h>
 #include <utils/RefBase.h>
-#include "HashableDimensionKey.h"
 
+#include "HashableDimensionKey.h"
 #include "state/StateListener.h"
 #include "state/StateTracker.h"
 
@@ -38,29 +39,29 @@
     // Notifies the correct StateTracker of an event.
     void onLogEvent(const LogEvent& event);
 
-    // Returns true if stateAtomId is the id of a state atom and notifies the
-    // correct StateTracker to register the listener. If the correct
-    // StateTracker does not exist, a new StateTracker is created.
-    bool registerListener(int stateAtomId, wp<StateListener> listener);
+    // Returns true if atomId is being tracked and is associated with a state
+    // atom. StateManager notifies the correct StateTracker to register listener.
+    // If the correct StateTracker does not exist, a new StateTracker is created.
+    bool registerListener(int atomId, wp<StateListener> listener);
 
     // Notifies the correct StateTracker to unregister a listener
     // and removes the tracker if it no longer has any listeners.
-    void unregisterListener(int stateAtomId, wp<StateListener> listener);
+    void unregisterListener(int atomId, wp<StateListener> listener);
 
-    // Queries the correct StateTracker for the state that is mapped to the given
-    // query key.
+    // Queries the correct StateTracker for the original/un-mapped state value
+    // that is mapped to the given query key.
     // If the StateTracker doesn't exist, returns StateTracker::kStateUnknown.
-    int getState(int stateAtomId, const HashableDimensionKey& queryKey);
+    int getStateValue(int atomId, const HashableDimensionKey& queryKey);
 
     inline int getStateTrackersCount() {
         std::lock_guard<std::mutex> lock(mMutex);
         return mStateTrackers.size();
     }
 
-    inline int getListenersCount(int stateAtomId) {
+    inline int getListenersCount(int atomId) {
         std::lock_guard<std::mutex> lock(mMutex);
-        if (mStateTrackers.find(stateAtomId) != mStateTrackers.end()) {
-            return mStateTrackers[stateAtomId]->getListenersCount();
+        if (mStateTrackers.find(atomId) != mStateTrackers.end()) {
+            return mStateTrackers[atomId]->getListenersCount();
         }
         return -1;
     }
diff --git a/cmds/statsd/src/state/StateTracker.cpp b/cmds/statsd/src/state/StateTracker.cpp
index 5a91950..323fc0e 100644
--- a/cmds/statsd/src/state/StateTracker.cpp
+++ b/cmds/statsd/src/state/StateTracker.cpp
@@ -30,13 +30,13 @@
   : mAtomId(atomId),
     mStateField(getSimpleMatcher(atomId, stateAtomInfo.exclusiveField)) {
     // create matcher for each primary field
-    // TODO(tsaichristine): handle when primary field is first uid in chain
+    // TODO(tsaichristine): b/142108433 handle when primary field is first uid in chain
     for (const auto& primary : stateAtomInfo.primaryFields) {
         Matcher matcher = getSimpleMatcher(atomId, primary);
         mPrimaryFields.push_back(matcher);
     }
 
-    // TODO(tsaichristine): set default state, reset state, and nesting
+    // TODO(tsaichristine): b/142108433 set default state, reset state, and nesting
 }
 
 void StateTracker::onLogEvent(const LogEvent& event) {
@@ -96,7 +96,7 @@
     mListeners.erase(listener);
 }
 
-int StateTracker::getState(const HashableDimensionKey& queryKey) const {
+int StateTracker::getStateValue(const HashableDimensionKey& queryKey) const {
     if (queryKey.getValues().size() == mPrimaryFields.size()) {
         auto it = mStateMap.find(queryKey);
         if (it != mStateMap.end()) {
diff --git a/cmds/statsd/src/state/StateTracker.h b/cmds/statsd/src/state/StateTracker.h
index f22706c..cfa9fd8 100644
--- a/cmds/statsd/src/state/StateTracker.h
+++ b/cmds/statsd/src/state/StateTracker.h
@@ -48,7 +48,7 @@
     // Returns the state value mapped to the given query key.
     // If the key isn't mapped to a state or the key size doesn't match the
     // primary key size, the default state is returned.
-    int getState(const HashableDimensionKey& queryKey) const;
+    int getStateValue(const HashableDimensionKey& queryKey) const;
 
     inline int getListenersCount() const {
         return mListeners.size();
diff --git a/cmds/statsd/src/stats_log_util.cpp b/cmds/statsd/src/stats_log_util.cpp
index 67625eb..c22e3cc 100644
--- a/cmds/statsd/src/stats_log_util.cpp
+++ b/cmds/statsd/src/stats_log_util.cpp
@@ -445,6 +445,8 @@
             return 12 * 60 * 60 * 1000LL;
         case ONE_DAY:
             return 24 * 60 * 60 * 1000LL;
+        case ONE_WEEK:
+            return 7 * 24 * 60 * 60 * 1000LL;
         case CTS:
             return 1000;
         case TIME_UNIT_UNSPECIFIED:
diff --git a/cmds/statsd/src/statsd_config.proto b/cmds/statsd/src/statsd_config.proto
index c107397..c98b2cf 100644
--- a/cmds/statsd/src/statsd_config.proto
+++ b/cmds/statsd/src/statsd_config.proto
@@ -44,6 +44,7 @@
   SIX_HOURS = 7;
   TWELVE_HOURS = 8;
   ONE_DAY = 9;
+  ONE_WEEK = 10;
   CTS = 1000;
 }
 
diff --git a/cmds/statsd/tests/e2e/CountMetric_e2e_test.cpp b/cmds/statsd/tests/e2e/CountMetric_e2e_test.cpp
new file mode 100644
index 0000000..6591d69
--- /dev/null
+++ b/cmds/statsd/tests/e2e/CountMetric_e2e_test.cpp
@@ -0,0 +1,197 @@
+/*
+ * Copyright (C) 2019, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 <gtest/gtest.h>
+
+#include "src/StatsLogProcessor.h"
+#include "src/state/StateManager.h"
+#include "tests/statsd_test_util.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+#ifdef __ANDROID__
+
+TEST(CountMetricE2eTest, TestWithSimpleState) {
+    // Initialize config
+    StatsdConfig config;
+    config.add_allowed_log_source("AID_ROOT");  // LogEvent defaults to UID of root.
+
+    auto syncStartMatcher = CreateSyncStartAtomMatcher();
+    *config.add_atom_matcher() = syncStartMatcher;
+
+    auto state = CreateScreenState();
+    *config.add_state() = state;
+
+    // Create count metric that slices by screen state
+    int64_t metricId = 123456;
+    auto countMetric = config.add_count_metric();
+    countMetric->set_id(metricId);
+    countMetric->set_what(syncStartMatcher.id());
+    countMetric->set_bucket(TimeUnit::ONE_MINUTE);
+    countMetric->add_slice_by_state(state.id());
+
+    // Initialize StatsLogProcessor
+    const int64_t baseTimeNs = 0;                                   // 0:00
+    const int64_t configAddedTimeNs = baseTimeNs + 1 * NS_PER_SEC;  // 0:01
+    const int64_t bucketSizeNs =
+            TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000LL * 1000LL;
+
+    int uid = 12345;
+    int64_t cfgId = 98765;
+    ConfigKey cfgKey(uid, cfgId);
+
+    auto processor = CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, cfgKey);
+
+    // Check that StateTrackers were properly initialized.
+    EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount());
+    EXPECT_EQ(1,
+              StateManager::getInstance().getListenersCount(android::util::SCREEN_STATE_CHANGED));
+
+    // Check that CountMetricProducer was initialized correctly.
+    EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
+    sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second;
+    EXPECT_TRUE(metricsManager->isConfigValid());
+    EXPECT_EQ(metricsManager->mAllMetricProducers.size(), 1);
+    sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0];
+    EXPECT_EQ(metricProducer->mSlicedStateAtoms.size(), 1);
+    EXPECT_EQ(metricProducer->mSlicedStateAtoms.at(0), android::util::SCREEN_STATE_CHANGED);
+    EXPECT_EQ(metricProducer->mStateGroupMap.size(), 0);
+}
+
+TEST(CountMetricE2eTest, TestWithMappedState) {
+    // Initialize config
+    StatsdConfig config;
+    config.add_allowed_log_source("AID_ROOT");  // LogEvent defaults to UID of root.
+
+    auto syncStartMatcher = CreateSyncStartAtomMatcher();
+    *config.add_atom_matcher() = syncStartMatcher;
+
+    auto state = CreateScreenStateWithOnOffMap();
+    *config.add_state() = state;
+
+    // Create count metric that slices by screen state with on/off map
+    int64_t metricId = 123456;
+    auto countMetric = config.add_count_metric();
+    countMetric->set_id(metricId);
+    countMetric->set_what(syncStartMatcher.id());
+    countMetric->set_bucket(TimeUnit::ONE_MINUTE);
+    countMetric->add_slice_by_state(state.id());
+
+    // Initialize StatsLogProcessor
+    const int64_t baseTimeNs = 0;                                   // 0:00
+    const int64_t configAddedTimeNs = baseTimeNs + 1 * NS_PER_SEC;  // 0:01
+    const int64_t bucketSizeNs =
+            TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000LL * 1000LL;
+
+    int uid = 12345;
+    int64_t cfgId = 98765;
+    ConfigKey cfgKey(uid, cfgId);
+
+    auto processor = CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, cfgKey);
+
+    // Check that StateTrackers were properly initialized.
+    EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount());
+    EXPECT_EQ(1,
+              StateManager::getInstance().getListenersCount(android::util::SCREEN_STATE_CHANGED));
+
+    // Check that CountMetricProducer was initialized correctly.
+    EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
+    sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second;
+    EXPECT_TRUE(metricsManager->isConfigValid());
+    EXPECT_EQ(metricsManager->mAllMetricProducers.size(), 1);
+    sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0];
+    EXPECT_EQ(metricProducer->mSlicedStateAtoms.size(), 1);
+    EXPECT_EQ(metricProducer->mSlicedStateAtoms.at(0), android::util::SCREEN_STATE_CHANGED);
+    EXPECT_EQ(metricProducer->mStateGroupMap.size(), 1);
+
+    StateMap map = state.map();
+    for (auto group : map.group()) {
+        for (auto value : group.value()) {
+            EXPECT_EQ(metricProducer->mStateGroupMap[android::util::SCREEN_STATE_CHANGED][value],
+                      group.group_id());
+        }
+    }
+}
+
+TEST(CountMetricE2eTest, TestWithMultipleStates) {
+    // Initialize config
+    StatsdConfig config;
+    config.add_allowed_log_source("AID_ROOT");  // LogEvent defaults to UID of root.
+
+    auto syncStartMatcher = CreateSyncStartAtomMatcher();
+    *config.add_atom_matcher() = syncStartMatcher;
+
+    auto state1 = CreateScreenStateWithOnOffMap();
+    *config.add_state() = state1;
+    auto state2 = CreateUidProcessState();
+    *config.add_state() = state2;
+
+    // Create count metric that slices by screen state with on/off map
+    int64_t metricId = 123456;
+    auto countMetric = config.add_count_metric();
+    countMetric->set_id(metricId);
+    countMetric->set_what(syncStartMatcher.id());
+    countMetric->set_bucket(TimeUnit::ONE_MINUTE);
+    countMetric->add_slice_by_state(state1.id());
+    countMetric->add_slice_by_state(state2.id());
+
+    // Initialize StatsLogProcessor
+    const int64_t baseTimeNs = 0;                                   // 0:00
+    const int64_t configAddedTimeNs = baseTimeNs + 1 * NS_PER_SEC;  // 0:01
+    const int64_t bucketSizeNs =
+            TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000LL * 1000LL;
+
+    int uid = 12345;
+    int64_t cfgId = 98765;
+    ConfigKey cfgKey(uid, cfgId);
+
+    auto processor = CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, cfgKey);
+
+    // Check that StateTrackers were properly initialized.
+    EXPECT_EQ(2, StateManager::getInstance().getStateTrackersCount());
+    EXPECT_EQ(1,
+              StateManager::getInstance().getListenersCount(android::util::SCREEN_STATE_CHANGED));
+    EXPECT_EQ(1, StateManager::getInstance().getListenersCount(
+                         android::util::UID_PROCESS_STATE_CHANGED));
+
+    // Check that CountMetricProducer was initialized correctly.
+    EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
+    sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second;
+    EXPECT_TRUE(metricsManager->isConfigValid());
+    EXPECT_EQ(metricsManager->mAllMetricProducers.size(), 1);
+    sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0];
+    EXPECT_EQ(metricProducer->mSlicedStateAtoms.size(), 2);
+    EXPECT_EQ(metricProducer->mSlicedStateAtoms.at(0), android::util::SCREEN_STATE_CHANGED);
+    EXPECT_EQ(metricProducer->mSlicedStateAtoms.at(1), android::util::UID_PROCESS_STATE_CHANGED);
+    EXPECT_EQ(metricProducer->mStateGroupMap.size(), 1);
+
+    StateMap map = state1.map();
+    for (auto group : map.group()) {
+        for (auto value : group.value()) {
+            EXPECT_EQ(metricProducer->mStateGroupMap[android::util::SCREEN_STATE_CHANGED][value],
+                      group.group_id());
+        }
+    }
+}
+
+}  // namespace statsd
+}  // namespace os
+}  // namespace android
+#else
+GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
diff --git a/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp b/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp
index 839daa4..8915c73 100644
--- a/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp
@@ -43,8 +43,8 @@
     metric.set_bucket(ONE_MINUTE);
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
-    CountMetricProducer countProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
-                                      5, 600 * NS_PER_SEC + NS_PER_SEC/2);
+    CountMetricProducer countProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, 5,
+                                      600 * NS_PER_SEC + NS_PER_SEC / 2);
     EXPECT_EQ(600500000000, countProducer.mCurrentBucketStartTimeNs);
     EXPECT_EQ(10, countProducer.mCurrentBucketNum);
     EXPECT_EQ(660000000005, countProducer.getCurrentBucketEndTimeNs());
@@ -131,7 +131,8 @@
 
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
-    CountMetricProducer countProducer(kConfigKey, metric, 1, wizard, bucketStartTimeNs, bucketStartTimeNs);
+    CountMetricProducer countProducer(kConfigKey, metric, 1, wizard, bucketStartTimeNs,
+                                      bucketStartTimeNs);
 
     countProducer.onConditionChanged(true, bucketStartTimeNs);
     countProducer.onMatchedLogEvent(1 /*matcher index*/, event1);
@@ -396,6 +397,26 @@
             std::ceil(1.0 * event7.GetElapsedTimestampNs() / NS_PER_SEC + refPeriodSec));
 }
 
+TEST(CountMetricProducerTest, TestOneWeekTimeUnit) {
+    CountMetric metric;
+    metric.set_id(1);
+    metric.set_bucket(ONE_WEEK);
+
+    sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+
+    int64_t oneDayNs = 24 * 60 * 60 * 1e9;
+    int64_t fiveWeeksNs = 5 * 7 * oneDayNs;
+
+    CountMetricProducer countProducer(
+            kConfigKey, metric, -1 /* meaning no condition */, wizard, oneDayNs, fiveWeeksNs);
+
+    int64_t fiveWeeksOneDayNs = fiveWeeksNs + oneDayNs;
+
+    EXPECT_EQ(fiveWeeksNs, countProducer.mCurrentBucketStartTimeNs);
+    EXPECT_EQ(4, countProducer.mCurrentBucketNum);
+    EXPECT_EQ(fiveWeeksOneDayNs, countProducer.getCurrentBucketEndTimeNs());
+}
+
 }  // namespace statsd
 }  // namespace os
 }  // namespace android
diff --git a/cmds/statsd/tests/state/StateTracker_test.cpp b/cmds/statsd/tests/state/StateTracker_test.cpp
index c89ffea..8d38000 100644
--- a/cmds/statsd/tests/state/StateTracker_test.cpp
+++ b/cmds/statsd/tests/state/StateTracker_test.cpp
@@ -44,7 +44,7 @@
 
     std::vector<Update> updates;
 
-    void onStateChanged(int stateAtomId, const HashableDimensionKey& primaryKey, int oldState,
+    void onStateChanged(int atomId, const HashableDimensionKey& primaryKey, int oldState,
                         int newState) {
         updates.emplace_back(primaryKey, newState);
     }
@@ -82,6 +82,7 @@
     return event;
 }
 
+// Incorrect event - missing fields
 std::shared_ptr<LogEvent> buildIncorrectOverlayEvent(int uid, const std::string& packageName, int state) {
     std::shared_ptr<LogEvent> event =
             std::make_shared<LogEvent>(android::util::OVERLAY_STATE_CHANGED, 1000 /*timestamp*/);
@@ -91,6 +92,18 @@
     event->init();
     return event;
 }
+
+// Incorrect event - exclusive state has wrong type
+std::shared_ptr<LogEvent> buildOverlayEventBadStateType(int uid, const std::string& packageName) {
+    std::shared_ptr<LogEvent> event =
+            std::make_shared<LogEvent>(android::util::OVERLAY_STATE_CHANGED, 1000 /*timestamp*/);
+    event->write((int32_t)uid);
+    event->write(packageName);
+    event->write(true);
+    event->write("string");  // exclusive state: string instead of int
+    event->init();
+    return event;
+}
 // END: build event functions.
 
 // START: get primary key functions
@@ -148,22 +161,22 @@
 
     // Register listener to non-existing StateTracker
     EXPECT_EQ(0, mgr.getStateTrackersCount());
-    mgr.registerListener(android::util::SCREEN_STATE_CHANGED, listener1);
+    EXPECT_TRUE(mgr.registerListener(android::util::SCREEN_STATE_CHANGED, listener1));
     EXPECT_EQ(1, mgr.getStateTrackersCount());
     EXPECT_EQ(1, mgr.getListenersCount(android::util::SCREEN_STATE_CHANGED));
 
     // Register listener to existing StateTracker
-    mgr.registerListener(android::util::SCREEN_STATE_CHANGED, listener2);
+    EXPECT_TRUE(mgr.registerListener(android::util::SCREEN_STATE_CHANGED, listener2));
     EXPECT_EQ(1, mgr.getStateTrackersCount());
     EXPECT_EQ(2, mgr.getListenersCount(android::util::SCREEN_STATE_CHANGED));
 
     // Register already registered listener to existing StateTracker
-    mgr.registerListener(android::util::SCREEN_STATE_CHANGED, listener2);
+    EXPECT_TRUE(mgr.registerListener(android::util::SCREEN_STATE_CHANGED, listener2));
     EXPECT_EQ(1, mgr.getStateTrackersCount());
     EXPECT_EQ(2, mgr.getListenersCount(android::util::SCREEN_STATE_CHANGED));
 
     // Register listener to non-state atom
-    mgr.registerListener(android::util::BATTERY_LEVEL_CHANGED, listener2);
+    EXPECT_FALSE(mgr.registerListener(android::util::BATTERY_LEVEL_CHANGED, listener2));
     EXPECT_EQ(1, mgr.getStateTrackersCount());
 }
 
@@ -227,12 +240,12 @@
 
     // check StateTracker was updated by querying for state
     HashableDimensionKey queryKey = DEFAULT_DIMENSION_KEY;
-    EXPECT_EQ(2, mgr.getState(android::util::SCREEN_STATE_CHANGED, queryKey));
+    EXPECT_EQ(2, mgr.getStateValue(android::util::SCREEN_STATE_CHANGED, queryKey));
 }
 
 /**
  * Test StateManager's onLogEvent and StateListener's onStateChanged correctly
- * updates listener for states with primary keys.
+ * updates listener for states with one primary key.
  */
 TEST(StateTrackerTest, TestStateChangeOnePrimaryField) {
     sp<TestStateListener> listener1 = new TestStateListener();
@@ -240,9 +253,8 @@
     mgr.registerListener(android::util::UID_PROCESS_STATE_CHANGED, listener1);
 
     // log event
-    std::shared_ptr<LogEvent> event = buildUidProcessEvent(
-            1000,
-            android::app::ProcessStateEnum::PROCESS_STATE_TOP);  //  state value: 1002
+    std::shared_ptr<LogEvent> event =
+            buildUidProcessEvent(1000 /* uid */, android::app::ProcessStateEnum::PROCESS_STATE_TOP);
     mgr.onLogEvent(*event);
 
     // check listener was updated
@@ -252,23 +264,33 @@
 
     // check StateTracker was updated by querying for state
     HashableDimensionKey queryKey;
-    getUidProcessKey(1000, &queryKey);
-    EXPECT_EQ(1002, mgr.getState(android::util::UID_PROCESS_STATE_CHANGED, queryKey));
+    getUidProcessKey(1000 /* uid */, &queryKey);
+    EXPECT_EQ(1002, mgr.getStateValue(android::util::UID_PROCESS_STATE_CHANGED, queryKey));
 }
 
+/**
+ * Test StateManager's onLogEvent and StateListener's onStateChanged correctly
+ * updates listener for states with multiple primary keys.
+ */
 TEST(StateTrackerTest, TestStateChangeMultiplePrimaryFields) {
     sp<TestStateListener> listener1 = new TestStateListener();
     StateManager mgr;
     mgr.registerListener(android::util::OVERLAY_STATE_CHANGED, listener1);
 
     // log event
-    std::shared_ptr<LogEvent> event = buildOverlayEvent(1000, "package1", 1);  // state: ENTERED
+    std::shared_ptr<LogEvent> event =
+            buildOverlayEvent(1000 /* uid */, "package1", 1);  // state: ENTERED
     mgr.onLogEvent(*event);
 
-    // check listener update
+    // check listener was updated
     EXPECT_EQ(1, listener1->updates.size());
     EXPECT_EQ(1000, listener1->updates[0].mKey.getValues()[0].mValue.int_value);
     EXPECT_EQ(1, listener1->updates[0].mState);
+
+    // check StateTracker was updated by querying for state
+    HashableDimensionKey queryKey;
+    getOverlayKey(1000 /* uid */, "package1", &queryKey);
+    EXPECT_EQ(1, mgr.getStateValue(android::util::OVERLAY_STATE_CHANGED, queryKey));
 }
 
 /**
@@ -282,11 +304,14 @@
     mgr.registerListener(android::util::OVERLAY_STATE_CHANGED, listener1);
 
     // log event
-    std::shared_ptr<LogEvent> event =
-            buildIncorrectOverlayEvent(1000, "package1", 1);  // state: ENTERED
-    mgr.onLogEvent(*event);
+    std::shared_ptr<LogEvent> event1 =
+            buildIncorrectOverlayEvent(1000 /* uid */, "package1", 1 /* state */);
+    std::shared_ptr<LogEvent> event2 = buildOverlayEventBadStateType(1001 /* uid */, "package2");
 
-    // check listener update
+    // check listener was updated
+    mgr.onLogEvent(*event1);
+    EXPECT_EQ(0, listener1->updates.size());
+    mgr.onLogEvent(*event2);
     EXPECT_EQ(0, listener1->updates.size());
 }
 
@@ -301,18 +326,19 @@
 
     std::shared_ptr<LogEvent> event1 = buildUidProcessEvent(
             1000,
-            android::app::ProcessStateEnum::PROCESS_STATE_TOP);  // state value: 1002
+            android::app::ProcessStateEnum::PROCESS_STATE_TOP);  //  state value: 1002
     std::shared_ptr<LogEvent> event2 = buildUidProcessEvent(
             1001,
-            android::app::ProcessStateEnum::PROCESS_STATE_FOREGROUND_SERVICE);  // state value: 1003
+            android::app::ProcessStateEnum::PROCESS_STATE_FOREGROUND_SERVICE);  //  state value:
+                                                                                //  1003
     std::shared_ptr<LogEvent> event3 = buildUidProcessEvent(
             1002,
-            android::app::ProcessStateEnum::PROCESS_STATE_PERSISTENT);  // state value: 1000
+            android::app::ProcessStateEnum::PROCESS_STATE_PERSISTENT);  //  state value: 1000
     std::shared_ptr<LogEvent> event4 = buildUidProcessEvent(
             1001,
-            android::app::ProcessStateEnum::PROCESS_STATE_TOP);  // state value: 1002
+            android::app::ProcessStateEnum::PROCESS_STATE_TOP);  //  state value: 1002
     std::shared_ptr<LogEvent> event5 =
-            buildScreenEvent(android::view::DisplayStateEnum::DISPLAY_STATE_ON);  // state value:
+            buildScreenEvent(android::view::DisplayStateEnum::DISPLAY_STATE_ON);
     std::shared_ptr<LogEvent> event6 = buildOverlayEvent(1000, "package1", 1);
     std::shared_ptr<LogEvent> event7 = buildOverlayEvent(1000, "package2", 2);
 
@@ -327,25 +353,25 @@
     // Query for UidProcessState of uid 1001
     HashableDimensionKey queryKey1;
     getUidProcessKey(1001, &queryKey1);
-    EXPECT_EQ(1003, mgr.getState(android::util::UID_PROCESS_STATE_CHANGED, queryKey1));
+    EXPECT_EQ(1003, mgr.getStateValue(android::util::UID_PROCESS_STATE_CHANGED, queryKey1));
 
     // Query for UidProcessState of uid 1004 - not in state map
     HashableDimensionKey queryKey2;
     getUidProcessKey(1004, &queryKey2);
-    EXPECT_EQ(-1,
-              mgr.getState(android::util::UID_PROCESS_STATE_CHANGED, queryKey2));  // default state
+    EXPECT_EQ(-1, mgr.getStateValue(android::util::UID_PROCESS_STATE_CHANGED,
+                                    queryKey2));  // default state
 
     // Query for UidProcessState of uid 1001 - after change in state
     mgr.onLogEvent(*event4);
-    EXPECT_EQ(1002, mgr.getState(android::util::UID_PROCESS_STATE_CHANGED, queryKey1));
+    EXPECT_EQ(1002, mgr.getStateValue(android::util::UID_PROCESS_STATE_CHANGED, queryKey1));
 
     // Query for ScreenState
-    EXPECT_EQ(2, mgr.getState(android::util::SCREEN_STATE_CHANGED, DEFAULT_DIMENSION_KEY));
+    EXPECT_EQ(2, mgr.getStateValue(android::util::SCREEN_STATE_CHANGED, DEFAULT_DIMENSION_KEY));
 
     // Query for OverlayState of uid 1000, package name "package2"
     HashableDimensionKey queryKey3;
     getOverlayKey(1000, "package2", &queryKey3);
-    EXPECT_EQ(2, mgr.getState(android::util::OVERLAY_STATE_CHANGED, queryKey3));
+    EXPECT_EQ(2, mgr.getStateValue(android::util::OVERLAY_STATE_CHANGED, queryKey3));
 }
 
 }  // namespace statsd
diff --git a/cmds/statsd/tests/statsd_test_util.cpp b/cmds/statsd/tests/statsd_test_util.cpp
index 2c4f3c7..38c22ab 100644
--- a/cmds/statsd/tests/statsd_test_util.cpp
+++ b/cmds/statsd/tests/statsd_test_util.cpp
@@ -251,6 +251,101 @@
     return predicate;
 }
 
+State CreateScreenState() {
+    State state;
+    state.set_id(StringToId("ScreenState"));
+    state.set_atom_id(29);
+    return state;
+}
+
+State CreateUidProcessState() {
+    State state;
+    state.set_id(StringToId("UidProcessState"));
+    state.set_atom_id(27);
+    return state;
+}
+
+State CreateOverlayState() {
+    State state;
+    state.set_id(StringToId("OverlayState"));
+    state.set_atom_id(59);
+    return state;
+}
+
+State CreateScreenStateWithOnOffMap() {
+    State state;
+    state.set_id(StringToId("ScreenStateOnOff"));
+    state.set_atom_id(29);
+
+    auto map = CreateScreenStateOnOffMap();
+    *state.mutable_map() = map;
+
+    return state;
+}
+
+State CreateScreenStateWithInDozeMap() {
+    State state;
+    state.set_id(StringToId("ScreenStateInDoze"));
+    state.set_atom_id(29);
+
+    auto map = CreateScreenStateInDozeMap();
+    *state.mutable_map() = map;
+
+    return state;
+}
+
+StateMap_StateGroup CreateScreenStateOnGroup() {
+    StateMap_StateGroup group;
+    group.set_group_id(StringToId("SCREEN_ON"));
+    group.add_value(2);
+    group.add_value(5);
+    group.add_value(6);
+    return group;
+}
+
+StateMap_StateGroup CreateScreenStateOffGroup() {
+    StateMap_StateGroup group;
+    group.set_group_id(StringToId("SCREEN_OFF"));
+    group.add_value(0);
+    group.add_value(1);
+    group.add_value(3);
+    group.add_value(4);
+    return group;
+}
+
+StateMap CreateScreenStateOnOffMap() {
+    StateMap map;
+    *map.add_group() = CreateScreenStateOnGroup();
+    *map.add_group() = CreateScreenStateOffGroup();
+    return map;
+}
+
+StateMap_StateGroup CreateScreenStateInDozeGroup() {
+    StateMap_StateGroup group;
+    group.set_group_id(StringToId("SCREEN_DOZE"));
+    group.add_value(3);
+    group.add_value(4);
+    return group;
+}
+
+StateMap_StateGroup CreateScreenStateNotDozeGroup() {
+    StateMap_StateGroup group;
+    group.set_group_id(StringToId("SCREEN_NOT_DOZE"));
+    group.add_value(0);
+    group.add_value(1);
+    group.add_value(2);
+    group.add_value(5);
+    group.add_value(6);
+    return group;
+}
+
+StateMap CreateScreenStateInDozeMap() {
+    StateMap map;
+    *map.add_group() = CreateScreenStateInDozeGroup();
+    *map.add_group() = CreateScreenStateNotDozeGroup();
+    return map;
+}
+
 void addPredicateToPredicateCombination(const Predicate& predicate,
                                         Predicate* combinationPredicate) {
     combinationPredicate->mutable_combination()->add_predicate(predicate.id());
diff --git a/cmds/statsd/tests/statsd_test_util.h b/cmds/statsd/tests/statsd_test_util.h
index 635c583..c026105 100644
--- a/cmds/statsd/tests/statsd_test_util.h
+++ b/cmds/statsd/tests/statsd_test_util.h
@@ -104,6 +104,37 @@
 // Create a Predicate proto for app is in background.
 Predicate CreateIsInBackgroundPredicate();
 
+// Create State proto for screen state atom.
+State CreateScreenState();
+
+// Create State proto for uid process state atom.
+State CreateUidProcessState();
+
+// Create State proto for overlay state atom.
+State CreateOverlayState();
+
+State CreateScreenStateWithOnOffMap();
+
+State CreateScreenStateWithInDozeMap();
+
+// Create StateGroup proto for ScreenState ON group
+StateMap_StateGroup CreateScreenStateOnGroup();
+
+// Create StateGroup proto for ScreenState OFF group
+StateMap_StateGroup CreateScreenStateOffGroup();
+
+// Create StateMap proto for ScreenState ON/OFF map
+StateMap CreateScreenStateOnOffMap();
+
+// Create StateGroup proto for ScreenState IN DOZE group
+StateMap_StateGroup CreateScreenStateInDozeGroup();
+
+// Create StateGroup proto for ScreenState NOT IN DOZE group
+StateMap_StateGroup CreateScreenStateNotDozeGroup();
+
+// Create StateMap proto for ScreenState IN DOZE map
+StateMap CreateScreenStateInDozeMap();
+
 // Add a predicate to the predicate combination.
 void addPredicateToPredicateCombination(const Predicate& predicate, Predicate* combination);
 
@@ -319,4 +350,4 @@
 }
 }  // namespace statsd
 }  // namespace os
-}  // namespace android
\ No newline at end of file
+}  // namespace android
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 86bf20a..03ef286 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -3160,6 +3160,15 @@
     }
 
     @Override
+    public String[] getTelephonyPackageNames() {
+        try {
+            return mPM.getTelephonyPackageNames();
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
+    @Override
     public String getSystemCaptionsServicePackageName() {
         try {
             return mPM.getSystemCaptionsServicePackageName();
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 39fab63..d5bc9b0 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -2212,9 +2212,9 @@
     }
 
     @Override
-    public Context createContextAsUser(UserHandle user) {
+    public Context createContextAsUser(UserHandle user, @CreatePackageOptions int flags) {
         try {
-            return createPackageContextAsUser(getPackageName(), mFlags, user);
+            return createPackageContextAsUser(getPackageName(), flags, user);
         } catch (NameNotFoundException e) {
             throw new IllegalStateException("Own package not found: package=" + getPackageName());
         }
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index 316cab8..58f6741 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -997,8 +997,13 @@
     }
 
     /**
-     * @hide
+     * <p>
+     *  Gets the currently applied notification policy. If {@link #getCurrentInterruptionFilter}
+     * is equal to {@link #INTERRUPTION_FILTER_ALL}, then the consolidated notification policy
+     * will match the default notification policy returned by {@link #getNotificationPolicy}.
+     * </p>
      */
+    @Nullable
     public NotificationManager.Policy getConsolidatedNotificationPolicy() {
         INotificationManager service = getService();
         try {
diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java
index 9e6054c..68ab89c 100644
--- a/core/java/android/app/ResourcesManager.java
+++ b/core/java/android/app/ResourcesManager.java
@@ -884,21 +884,24 @@
      * @param activityToken optional token to clean up Activity resources
      */
     private void cleanupReferences(IBinder activityToken) {
-        if (activityToken != null) {
-            ActivityResources activityResources = mActivityResourceReferences.get(activityToken);
-            if (activityResources != null) {
-                ArrayUtils.unstableRemoveIf(activityResources.activityResources,
-                        sEmptyReferencePredicate);
+        synchronized (this) {
+            if (activityToken != null) {
+                ActivityResources activityResources = mActivityResourceReferences.get(
+                        activityToken);
+                if (activityResources != null) {
+                    ArrayUtils.unstableRemoveIf(activityResources.activityResources,
+                            sEmptyReferencePredicate);
+                }
+            } else {
+                ArrayUtils.unstableRemoveIf(mResourceReferences, sEmptyReferencePredicate);
             }
-        } else {
-            ArrayUtils.unstableRemoveIf(mResourceReferences, sEmptyReferencePredicate);
-        }
 
-        for (int index = mResourcesWithLoaders.size() - 1; index >= 0; index--) {
-            ResourcesWithLoaders resourcesWithLoaders = mResourcesWithLoaders.get(index);
-            Resources resources = resourcesWithLoaders.resources();
-            if (resources == null) {
-                mResourcesWithLoaders.remove(index);
+            for (int index = mResourcesWithLoaders.size() - 1; index >= 0; index--) {
+                ResourcesWithLoaders resourcesWithLoaders = mResourcesWithLoaders.get(index);
+                Resources resources = resourcesWithLoaders.resources();
+                if (resources == null) {
+                    mResourcesWithLoaders.remove(index);
+                }
             }
         }
     }
diff --git a/core/java/android/app/admin/PasswordMetrics.java b/core/java/android/app/admin/PasswordMetrics.java
index 9929855..464f75c 100644
--- a/core/java/android/app/admin/PasswordMetrics.java
+++ b/core/java/android/app/admin/PasswordMetrics.java
@@ -27,9 +27,6 @@
 import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_SOMETHING;
 import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
 
-import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PASSWORD;
-import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PATTERN;
-
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.app.admin.DevicePolicyManager.PasswordComplexity;
@@ -37,8 +34,7 @@
 import android.os.Parcelable;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.Preconditions;
-import com.android.internal.widget.LockPatternUtils.CredentialType;
+import com.android.internal.widget.LockscreenCredential;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -226,21 +222,21 @@
     };
 
     /**
-     * Returnsthe {@code PasswordMetrics} for a given credential.
+     * Returns the {@code PasswordMetrics} for a given credential.
      *
      * If the credential is a pin or a password, equivalent to {@link #computeForPassword(byte[])}.
      * {@code credential} cannot be null when {@code type} is
      * {@link com.android.internal.widget.LockPatternUtils#CREDENTIAL_TYPE_PASSWORD}.
      */
-    public static PasswordMetrics computeForCredential(
-            @CredentialType int type, byte[] credential) {
-        if (type == CREDENTIAL_TYPE_PASSWORD) {
-            Preconditions.checkNotNull(credential, "credential cannot be null");
-            return PasswordMetrics.computeForPassword(credential);
-        } else if (type == CREDENTIAL_TYPE_PATTERN)  {
+    public static PasswordMetrics computeForCredential(LockscreenCredential credential) {
+        if (credential.isPassword()) {
+            return PasswordMetrics.computeForPassword(credential.getCredential());
+        } else if (credential.isPattern())  {
             return new PasswordMetrics(PASSWORD_QUALITY_SOMETHING);
-        } else /* if (type == CREDENTIAL_TYPE_NONE) */ {
+        } else if (credential.isNone()) {
             return new PasswordMetrics(PASSWORD_QUALITY_UNSPECIFIED);
+        } else {
+            throw new IllegalArgumentException("Unknown credential type " + credential.getType());
         }
     }
 
diff --git a/core/java/android/app/slice/SliceManager.java b/core/java/android/app/slice/SliceManager.java
index 955093d..90ecce2 100644
--- a/core/java/android/app/slice/SliceManager.java
+++ b/core/java/android/app/slice/SliceManager.java
@@ -390,6 +390,8 @@
             }
             Bundle extras = new Bundle();
             extras.putParcelable(SliceProvider.EXTRA_INTENT, intent);
+            extras.putParcelableArrayList(SliceProvider.EXTRA_SUPPORTED_SPECS,
+                    new ArrayList<>(supportedSpecs));
             final Bundle res = provider.call(SliceProvider.METHOD_MAP_INTENT, null, extras);
             if (res == null) {
                 return null;
diff --git a/core/java/android/app/timedetector/TimeSignal.java b/core/java/android/app/timedetector/TimeSignal.java
index da21794..b494260 100644
--- a/core/java/android/app/timedetector/TimeSignal.java
+++ b/core/java/android/app/timedetector/TimeSignal.java
@@ -56,8 +56,7 @@
 
     private static TimeSignal createFromParcel(Parcel in) {
         String sourceId = in.readString();
-        TimestampedValue<Long> utcTime =
-                TimestampedValue.readFromParcel(in, null /* classLoader */, Long.class);
+        TimestampedValue<Long> utcTime = in.readParcelable(null /* classLoader */);
         return new TimeSignal(sourceId, utcTime);
     }
 
@@ -69,7 +68,7 @@
     @Override
     public void writeToParcel(@NonNull Parcel dest, int flags) {
         dest.writeString(mSourceId);
-        TimestampedValue.writeToParcel(dest, mUtcTime);
+        dest.writeParcelable(mUtcTime, 0);
     }
 
     @NonNull
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 227684b..e7e278f 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -5244,7 +5244,7 @@
     @SystemApi
     @TestApi
     @NonNull
-    public Context createContextAsUser(@NonNull UserHandle user) {
+    public Context createContextAsUser(@NonNull UserHandle user, @CreatePackageOptions int flags) {
         if (Build.IS_ENG) {
             throw new IllegalStateException("createContextAsUser not overridden!");
         }
diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java
index f7cd51e..7993ea1 100644
--- a/core/java/android/content/ContextWrapper.java
+++ b/core/java/android/content/ContextWrapper.java
@@ -885,8 +885,8 @@
 
     /** @hide */
     @Override
-    public Context createContextAsUser(UserHandle user) {
-        return mBase.createContextAsUser(user);
+    public Context createContextAsUser(UserHandle user, @CreatePackageOptions int flags) {
+        return mBase.createContextAsUser(user, flags);
     }
 
     /** @hide */
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index 19d8edf..1d78e2c 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -682,6 +682,8 @@
 
     String getWellbeingPackageName();
 
+    String[] getTelephonyPackageNames();
+
     String getAppPredictionServicePackageName();
 
     String getSystemCaptionsServicePackageName();
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 9513ce8..7509065 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -2858,6 +2858,14 @@
 
     /**
      * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}:
+     * The device does not have slices implementation.
+     * @hide
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_SLICES_DISABLED = "android.software.slices_disabled";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}:
      * The device supports device-unique Keystore attestations.  Only available on devices that
      * also support {@link #FEATURE_STRONGBOX_KEYSTORE}, and can only be used by device owner
      * apps (see {@link android.app.admin.DevicePolicyManager#generateKeyPair}).
@@ -7416,6 +7424,18 @@
     }
 
     /**
+     * @return the system defined telephony package names, or null if there's none.
+     *
+     * @hide
+     */
+    @Nullable
+    @TestApi
+    public String[] getTelephonyPackageNames() {
+        throw new UnsupportedOperationException(
+                "getTelephonyPackageNames not implemented in subclass");
+    }
+
+    /**
      * @return the system defined content capture service package name, or null if there's none.
      *
      * @hide
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 0b157fa..cf21e96 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -577,8 +577,6 @@
      */
     public interface Callback {
         boolean hasFeature(String feature);
-        String[] getOverlayPaths(String targetPackageName, String targetPath);
-        String[] getOverlayApks(String targetPackageName);
     }
 
     /**
@@ -595,14 +593,6 @@
         @Override public boolean hasFeature(String feature) {
             return mPm.hasSystemFeature(feature);
         }
-
-        @Override public String[] getOverlayPaths(String targetPackageName, String targetPath) {
-            return null;
-        }
-
-        @Override public String[] getOverlayApks(String targetPackageName) {
-            return null;
-        }
     }
 
     /**
@@ -1195,19 +1185,7 @@
             }
 
             final byte[] bytes = IoUtils.readFileAsByteArray(cacheFile.getAbsolutePath());
-            Package p = fromCacheEntry(bytes);
-            if (mCallback != null) {
-                String[] overlayApks = mCallback.getOverlayApks(p.packageName);
-                if (overlayApks != null && overlayApks.length > 0) {
-                    for (String overlayApk : overlayApks) {
-                        // If a static RRO is updated, return null.
-                        if (!isCacheUpToDate(new File(overlayApk), cacheFile)) {
-                            return null;
-                        }
-                    }
-                }
-            }
-            return p;
+            return fromCacheEntry(bytes);
         } catch (Throwable e) {
             Slog.w(TAG, "Error reading package cache: ", e);
 
@@ -1381,7 +1359,7 @@
             final Resources res = new Resources(assets, mMetrics, null);
 
             final String[] outError = new String[1];
-            final Package pkg = parseBaseApk(apkPath, res, parser, flags, outError);
+            final Package pkg = parseBaseApk(res, parser, flags, outError);
             if (pkg == null) {
                 throw new PackageParserException(mParseError,
                         apkPath + " (at " + parser.getPositionDescription() + "): " + outError[0]);
@@ -1944,7 +1922,6 @@
      * need to consider whether they should be supported by split APKs and child
      * packages.
      *
-     * @param apkPath The package apk file path
      * @param res The resources from which to resolve values
      * @param parser The manifest parser
      * @param flags Flags how to parse
@@ -1954,8 +1931,7 @@
      * @throws XmlPullParserException
      * @throws IOException
      */
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
-    private Package parseBaseApk(String apkPath, Resources res, XmlResourceParser parser, int flags,
+    private Package parseBaseApk(Resources res, XmlResourceParser parser, int flags,
             String[] outError) throws XmlPullParserException, IOException {
         final String splitName;
         final String pkgName;
@@ -1975,15 +1951,6 @@
             return null;
         }
 
-        if (mCallback != null) {
-            String[] overlayPaths = mCallback.getOverlayPaths(pkgName, apkPath);
-            if (overlayPaths != null && overlayPaths.length > 0) {
-                for (String overlayPath : overlayPaths) {
-                    res.getAssets().addOverlayPath(overlayPath);
-                }
-            }
-        }
-
         final Package pkg = new Package(pkgName);
 
         TypedArray sa = res.obtainAttributes(parser,
diff --git a/core/java/android/content/pm/PermissionInfo.java b/core/java/android/content/pm/PermissionInfo.java
index dd5c6a5..aa6f58e 100644
--- a/core/java/android/content/pm/PermissionInfo.java
+++ b/core/java/android/content/pm/PermissionInfo.java
@@ -237,6 +237,28 @@
     @TestApi
     public static final int PROTECTION_FLAG_APP_PREDICTOR = 0x200000;
 
+    /**
+     * Additional flag for {@link #protectionLevel}, corresponding
+     * to the <code>telephony</code> value of
+     * {@link android.R.attr#protectionLevel}.
+     *
+     * @hide
+     */
+    @SystemApi
+    @TestApi
+    public static final int PROTECTION_FLAG_TELEPHONY = 0x400000;
+
+    /**
+     * Additional flag for {@link #protectionLevel}, corresponding
+     * to the <code>wifi</code> value of
+     * {@link android.R.attr#protectionLevel}.
+     *
+     * @hide
+     */
+    @SystemApi
+    @TestApi
+    public static final int PROTECTION_FLAG_WIFI = 0x800000;
+
     /** @hide */
     @IntDef(flag = true, prefix = { "PROTECTION_FLAG_" }, value = {
             PROTECTION_FLAG_PRIVILEGED,
@@ -258,6 +280,8 @@
             PROTECTION_FLAG_CONFIGURATOR,
             PROTECTION_FLAG_INCIDENT_REPORT_APPROVER,
             PROTECTION_FLAG_APP_PREDICTOR,
+            PROTECTION_FLAG_TELEPHONY,
+            PROTECTION_FLAG_WIFI,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface ProtectionFlags {}
@@ -501,6 +525,12 @@
         if ((level & PermissionInfo.PROTECTION_FLAG_APP_PREDICTOR) != 0) {
             protLevel += "|appPredictor";
         }
+        if ((level & PermissionInfo.PROTECTION_FLAG_TELEPHONY) != 0) {
+            protLevel += "|telephony";
+        }
+        if ((level & PermissionInfo.PROTECTION_FLAG_WIFI) != 0) {
+            protLevel += "|wifi";
+        }
         return protLevel;
     }
 
diff --git a/core/java/android/net/INetworkPolicyListener.aidl b/core/java/android/net/INetworkPolicyListener.aidl
index 106b7be..fe9141c 100644
--- a/core/java/android/net/INetworkPolicyListener.aidl
+++ b/core/java/android/net/INetworkPolicyListener.aidl
@@ -15,6 +15,7 @@
  */
 
 package android.net;
+import android.telephony.SubscriptionPlan;
 
 /** {@hide} */
 oneway interface INetworkPolicyListener {
@@ -22,5 +23,6 @@
     void onMeteredIfacesChanged(in String[] meteredIfaces);
     void onRestrictBackgroundChanged(boolean restrictBackground);
     void onUidPoliciesChanged(int uid, int uidPolicies);
-    void onSubscriptionOverride(int subId, int overrideMask, int overrideValue, long networkTypeMask);
+    void onSubscriptionOverride(int subId, int overrideMask, int overrideValue);
+    void onSubscriptionPlansChanged(int subId, in SubscriptionPlan[] plans);
 }
diff --git a/core/java/android/net/INetworkPolicyManager.aidl b/core/java/android/net/INetworkPolicyManager.aidl
index 90327663..385cb1d 100644
--- a/core/java/android/net/INetworkPolicyManager.aidl
+++ b/core/java/android/net/INetworkPolicyManager.aidl
@@ -76,7 +76,7 @@
     SubscriptionPlan[] getSubscriptionPlans(int subId, String callingPackage);
     void setSubscriptionPlans(int subId, in SubscriptionPlan[] plans, String callingPackage);
     String getSubscriptionPlansOwner(int subId);
-    void setSubscriptionOverride(int subId, int overrideMask, int overrideValue, long networkTypeMask, long timeoutMillis, String callingPackage);
+    void setSubscriptionOverride(int subId, int overrideMask, int overrideValue, long timeoutMillis, String callingPackage);
 
     void factoryReset(String subscriber);
 
diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java
index 628dcd2..9150aae 100644
--- a/core/java/android/net/NetworkPolicyManager.java
+++ b/core/java/android/net/NetworkPolicyManager.java
@@ -31,6 +31,7 @@
 import android.os.Build;
 import android.os.RemoteException;
 import android.os.UserHandle;
+import android.telephony.SubscriptionPlan;
 import android.util.DebugUtils;
 import android.util.Pair;
 import android.util.Range;
@@ -380,7 +381,8 @@
         @Override public void onMeteredIfacesChanged(String[] meteredIfaces) { }
         @Override public void onRestrictBackgroundChanged(boolean restrictBackground) { }
         @Override public void onUidPoliciesChanged(int uid, int uidPolicies) { }
-        @Override public void onSubscriptionOverride(int subId, int overrideMask, int overrideValue,
-                long networkTypeMask) { }
+        @Override public void onSubscriptionOverride(int subId, int overrideMask,
+                int overrideValue) { }
+        @Override public void onSubscriptionPlansChanged(int subId, SubscriptionPlan[] plans) { }
     }
 }
diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java
index 2c9333b..ef3afab 100644
--- a/core/java/android/os/Binder.java
+++ b/core/java/android/os/Binder.java
@@ -1085,4 +1085,13 @@
         StrictMode.clearGatheredViolations();
         return res;
     }
+
+    /**
+     * Returns the specified service from servicemanager. If the service is not running,
+     * servicemanager will attempt to start it, and this function will wait for it to be ready.
+     * Returns nullptr only if there are permission problems or fatal errors.
+     * @hide
+     */
+    public static final native @Nullable IBinder waitForService(@NonNull String serviceName)
+            throws RemoteException;
 }
diff --git a/core/java/android/print/TEST_MAPPING b/core/java/android/print/TEST_MAPPING
new file mode 100644
index 0000000..4fa8822
--- /dev/null
+++ b/core/java/android/print/TEST_MAPPING
@@ -0,0 +1,12 @@
+{
+  "presubmit": [
+    {
+      "name": "CtsPrintTestCases",
+      "options": [
+        {
+          "include-annotation": "android.platform.test.annotations.Presubmit"
+        }
+      ]
+    }
+  ]
+}
diff --git a/core/java/android/print/pdf/TEST_MAPPING b/core/java/android/print/pdf/TEST_MAPPING
new file mode 100644
index 0000000..d763598
--- /dev/null
+++ b/core/java/android/print/pdf/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+  "presubmit": [
+    {
+      "name": "CtsPdfTestCases"
+    }
+  ]
+}
diff --git a/core/java/android/provider/DocumentsContract.java b/core/java/android/provider/DocumentsContract.java
index fd81178..eb09930 100644
--- a/core/java/android/provider/DocumentsContract.java
+++ b/core/java/android/provider/DocumentsContract.java
@@ -363,15 +363,22 @@
          * <p>
          * Type: INTEGER (int)
          *
-         * @see #FLAG_SUPPORTS_WRITE
-         * @see #FLAG_SUPPORTS_DELETE
-         * @see #FLAG_SUPPORTS_THUMBNAIL
+         * @see #FLAG_DIR_BLOCKS_TREE
          * @see #FLAG_DIR_PREFERS_GRID
          * @see #FLAG_DIR_PREFERS_LAST_MODIFIED
-         * @see #FLAG_VIRTUAL_DOCUMENT
+         * @see #FLAG_DIR_SUPPORTS_CREATE
+         * @see #FLAG_PARTIAL
          * @see #FLAG_SUPPORTS_COPY
+         * @see #FLAG_SUPPORTS_DELETE
+         * @see #FLAG_SUPPORTS_METADATA
          * @see #FLAG_SUPPORTS_MOVE
          * @see #FLAG_SUPPORTS_REMOVE
+         * @see #FLAG_SUPPORTS_RENAME
+         * @see #FLAG_SUPPORTS_SETTINGS
+         * @see #FLAG_SUPPORTS_THUMBNAIL
+         * @see #FLAG_SUPPORTS_WRITE
+         * @see #FLAG_VIRTUAL_DOCUMENT
+         * @see #FLAG_WEB_LINKABLE
          */
         public static final String COLUMN_FLAGS = "flags";
 
@@ -542,6 +549,23 @@
          * @see DocumentsContract#getDocumentMetadata(ContentInterface, Uri)
          */
         public static final int FLAG_SUPPORTS_METADATA = 1 << 14;
+
+        /**
+         * Flag indicating that a document is a directory that wants to block itself
+         * from being selected when the user launches an {@link Intent#ACTION_OPEN_DOCUMENT_TREE}
+         * intent. Only valid when {@link #COLUMN_MIME_TYPE} is {@link #MIME_TYPE_DIR}.
+         * <p>
+         * Note that this flag <em>only</em> applies to the single directory to which it is
+         * applied. It does <em>not</em> block the user from selecting either a parent or
+         * child directory during an {@link Intent#ACTION_OPEN_DOCUMENT_TREE} request.
+         * In particular, the only way to guarantee that a specific directory can never
+         * be granted via an {@link Intent#ACTION_OPEN_DOCUMENT_TREE} request is to ensure
+         * that both it and <em>all of its parent directories</em> have set this flag.
+         *
+         * @see Intent#ACTION_OPEN_DOCUMENT_TREE
+         * @see #COLUMN_FLAGS
+         */
+        public static final int FLAG_DIR_BLOCKS_TREE = 1 << 15;
     }
 
     /**
diff --git a/core/java/android/provider/MediaStore.java b/core/java/android/provider/MediaStore.java
index 493f9a2..a1333df 100644
--- a/core/java/android/provider/MediaStore.java
+++ b/core/java/android/provider/MediaStore.java
@@ -39,7 +39,6 @@
 import android.content.Intent;
 import android.content.UriPermission;
 import android.database.Cursor;
-import android.database.DatabaseUtils;
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
 import android.graphics.ImageDecoder;
@@ -70,15 +69,19 @@
 
 import com.android.internal.annotations.GuardedBy;
 
+import libcore.util.HexEncoding;
+
 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.text.Collator;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
+import java.util.Locale;
 import java.util.Objects;
 import java.util.Set;
 import java.util.regex.Pattern;
@@ -2059,7 +2062,17 @@
             /**
              * A non human readable key calculated from the TITLE, used for
              * searching, sorting and grouping
+             *
+             * @see Audio#keyFor(String)
+             * @deprecated These keys are generated using
+             *             {@link java.util.Locale#ROOT}, which means they don't
+             *             reflect locale-specific sorting preferences. To apply
+             *             locale-specific sorting preferences, use
+             *             {@link ContentResolver#QUERY_ARG_SQL_SORT_ORDER} with
+             *             {@code COLLATE LOCALIZED}, or
+             *             {@link ContentResolver#QUERY_ARG_SORT_LOCALE}.
              */
+            @Deprecated
             @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
             public static final String TITLE_KEY = "title_key";
 
@@ -2104,7 +2117,17 @@
             /**
              * A non human readable key calculated from the ARTIST, used for
              * searching, sorting and grouping
+             *
+             * @see Audio#keyFor(String)
+             * @deprecated These keys are generated using
+             *             {@link java.util.Locale#ROOT}, which means they don't
+             *             reflect locale-specific sorting preferences. To apply
+             *             locale-specific sorting preferences, use
+             *             {@link ContentResolver#QUERY_ARG_SQL_SORT_ORDER} with
+             *             {@code COLLATE LOCALIZED}, or
+             *             {@link ContentResolver#QUERY_ARG_SORT_LOCALE}.
              */
+            @Deprecated
             @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
             public static final String ARTIST_KEY = "artist_key";
 
@@ -2129,7 +2152,17 @@
             /**
              * A non human readable key calculated from the ALBUM, used for
              * searching, sorting and grouping
+             *
+             * @see Audio#keyFor(String)
+             * @deprecated These keys are generated using
+             *             {@link java.util.Locale#ROOT}, which means they don't
+             *             reflect locale-specific sorting preferences. To apply
+             *             locale-specific sorting preferences, use
+             *             {@link ContentResolver#QUERY_ARG_SQL_SORT_ORDER} with
+             *             {@code COLLATE LOCALIZED}, or
+             *             {@link ContentResolver#QUERY_ARG_SORT_LOCALE}.
              */
+            @Deprecated
             @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
             public static final String ALBUM_KEY = "album_key";
 
@@ -2186,91 +2219,89 @@
             public static final String IS_AUDIOBOOK = "is_audiobook";
 
             /**
-             * The genre of the audio file, if any
-             * Does not exist in the database - only used by the media scanner for inserts.
-             * @hide
+             * The id of the genre the audio file is from, if any
              */
-            @Deprecated
-            // @Column(Cursor.FIELD_TYPE_STRING)
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
+            public static final String GENRE_ID = "genre_id";
+
+            /**
+             * The genre of the audio file, if any.
+             */
+            @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
             public static final String GENRE = "genre";
 
             /**
-             * The resource URI of a localized title, if any
+             * A non human readable key calculated from the GENRE, used for
+             * searching, sorting and grouping
+             *
+             * @see Audio#keyFor(String)
+             * @deprecated These keys are generated using
+             *             {@link java.util.Locale#ROOT}, which means they don't
+             *             reflect locale-specific sorting preferences. To apply
+             *             locale-specific sorting preferences, use
+             *             {@link ContentResolver#QUERY_ARG_SQL_SORT_ORDER} with
+             *             {@code COLLATE LOCALIZED}, or
+             *             {@link ContentResolver#QUERY_ARG_SORT_LOCALE}.
+             */
+            @Deprecated
+            @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
+            public static final String GENRE_KEY = "genre_key";
+
+            /**
+             * The resource URI of a localized title, if any.
+             * <p>
              * Conforms to this pattern:
-             *   Scheme: {@link ContentResolver.SCHEME_ANDROID_RESOURCE}
-             *   Authority: Package Name of ringtone title provider
-             *   First Path Segment: Type of resource (must be "string")
-             *   Second Path Segment: Resource ID of title
-             * @hide
+             * <ul>
+             * <li>Scheme: {@link ContentResolver#SCHEME_ANDROID_RESOURCE}
+             * <li>Authority: Package Name of ringtone title provider
+             * <li>First Path Segment: Type of resource (must be "string")
+             * <li>Second Path Segment: Resource ID of title
+             * </ul>
              */
             @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
             public static final String TITLE_RESOURCE_URI = "title_resource_uri";
         }
 
+        private static final Pattern PATTERN_TRIM_BEFORE = Pattern.compile(
+                "(?i)(^(the|an|a) |,\\s*(the|an|a)$|[^\\w\\s]|^\\s+|\\s+$)");
+        private static final Pattern PATTERN_TRIM_AFTER = Pattern.compile(
+                "(^(00)+|(00)+$)");
+
         /**
-         * Converts a name to a "key" that can be used for grouping, sorting
-         * and searching.
-         * The rules that govern this conversion are:
-         * - remove 'special' characters like ()[]'!?.,
-         * - remove leading/trailing spaces
-         * - convert everything to lowercase
-         * - remove leading "the ", "an " and "a "
-         * - remove trailing ", the|an|a"
-         * - remove accents. This step leaves us with CollationKey data,
-         *   which is not human readable
+         * Converts a user-visible string into a "key" that can be used for
+         * grouping, sorting, and searching.
          *
-         * @param name The artist or album name to convert
-         * @return The "key" for the given name.
+         * @return Opaque token that should not be parsed or displayed to users.
+         * @deprecated These keys are generated using
+         *             {@link java.util.Locale#ROOT}, which means they don't
+         *             reflect locale-specific sorting preferences. To apply
+         *             locale-specific sorting preferences, use
+         *             {@link ContentResolver#QUERY_ARG_SQL_SORT_ORDER} with
+         *             {@code COLLATE LOCALIZED}, or
+         *             {@link ContentResolver#QUERY_ARG_SORT_LOCALE}.
          */
-        public static String keyFor(String name) {
-            if (name != null)  {
-                boolean sortfirst = false;
-                if (name.equals(UNKNOWN_STRING)) {
-                    return "\001";
-                }
-                // Check if the first character is \001. We use this to
-                // force sorting of certain special files, like the silent ringtone.
-                if (name.startsWith("\001")) {
-                    sortfirst = true;
-                }
-                name = name.trim().toLowerCase();
-                if (name.startsWith("the ")) {
-                    name = name.substring(4);
-                }
-                if (name.startsWith("an ")) {
-                    name = name.substring(3);
-                }
-                if (name.startsWith("a ")) {
-                    name = name.substring(2);
-                }
-                if (name.endsWith(", the") || name.endsWith(",the") ||
-                    name.endsWith(", an") || name.endsWith(",an") ||
-                    name.endsWith(", a") || name.endsWith(",a")) {
-                    name = name.substring(0, name.lastIndexOf(','));
-                }
-                name = name.replaceAll("[\\[\\]\\(\\)\"'.,?!]", "").trim();
-                if (name.length() > 0) {
-                    // Insert a separator between the characters to avoid
-                    // matches on a partial character. If we ever change
-                    // to start-of-word-only matches, this can be removed.
-                    StringBuilder b = new StringBuilder();
-                    b.append('.');
-                    int nl = name.length();
-                    for (int i = 0; i < nl; i++) {
-                        b.append(name.charAt(i));
-                        b.append('.');
-                    }
-                    name = b.toString();
-                    String key = DatabaseUtils.getCollationKey(name);
-                    if (sortfirst) {
-                        key = "\001" + key;
-                    }
-                    return key;
-               } else {
-                    return "";
-                }
+        @Deprecated
+        public static @Nullable String keyFor(@Nullable String name) {
+            if (TextUtils.isEmpty(name)) return null;
+
+            if (UNKNOWN_STRING.equals(name)) {
+                return "01";
             }
-            return null;
+
+            final boolean sortFirst = name.startsWith("\001");
+
+            name = PATTERN_TRIM_BEFORE.matcher(name).replaceAll("");
+            if (TextUtils.isEmpty(name)) return null;
+
+            final Collator c = Collator.getInstance(Locale.ROOT);
+            c.setStrength(Collator.PRIMARY);
+            name = HexEncoding.encodeToString(c.getCollationKey(name).toByteArray(), false);
+
+            name = PATTERN_TRIM_AFTER.matcher(name).replaceAll("");
+            if (sortFirst) {
+                name = "01" + name;
+            }
+            return name;
         }
 
         public static final class Media implements AudioColumns {
@@ -2632,7 +2663,17 @@
             /**
              * A non human readable key calculated from the ARTIST, used for
              * searching, sorting and grouping
+             *
+             * @see Audio#keyFor(String)
+             * @deprecated These keys are generated using
+             *             {@link java.util.Locale#ROOT}, which means they don't
+             *             reflect locale-specific sorting preferences. To apply
+             *             locale-specific sorting preferences, use
+             *             {@link ContentResolver#QUERY_ARG_SQL_SORT_ORDER} with
+             *             {@code COLLATE LOCALIZED}, or
+             *             {@link ContentResolver#QUERY_ARG_SORT_LOCALE}.
              */
+            @Deprecated
             @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
             public static final String ARTIST_KEY = "artist_key";
 
@@ -2736,6 +2777,23 @@
             public static final String ARTIST = "artist";
 
             /**
+             * A non human readable key calculated from the ARTIST, used for
+             * searching, sorting and grouping
+             *
+             * @see Audio#keyFor(String)
+             * @deprecated These keys are generated using
+             *             {@link java.util.Locale#ROOT}, which means they don't
+             *             reflect locale-specific sorting preferences. To apply
+             *             locale-specific sorting preferences, use
+             *             {@link ContentResolver#QUERY_ARG_SQL_SORT_ORDER} with
+             *             {@code COLLATE LOCALIZED}, or
+             *             {@link ContentResolver#QUERY_ARG_SORT_LOCALE}.
+             */
+            @Deprecated
+            @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
+            public static final String ARTIST_KEY = "artist_key";
+
+            /**
              * The number of songs on this album
              */
             @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
@@ -2770,7 +2828,17 @@
             /**
              * A non human readable key calculated from the ALBUM, used for
              * searching, sorting and grouping
+             *
+             * @see Audio#keyFor(String)
+             * @deprecated These keys are generated using
+             *             {@link java.util.Locale#ROOT}, which means they don't
+             *             reflect locale-specific sorting preferences. To apply
+             *             locale-specific sorting preferences, use
+             *             {@link ContentResolver#QUERY_ARG_SQL_SORT_ORDER} with
+             *             {@code COLLATE LOCALIZED}, or
+             *             {@link ContentResolver#QUERY_ARG_SORT_LOCALE}.
              */
+            @Deprecated
             @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
             public static final String ALBUM_KEY = "album_key";
 
diff --git a/core/java/android/util/DebugUtils.java b/core/java/android/util/DebugUtils.java
index 20e0d14..bc5edf8 100644
--- a/core/java/android/util/DebugUtils.java
+++ b/core/java/android/util/DebugUtils.java
@@ -255,7 +255,7 @@
                     if (value == 0 && flagsWasZero) {
                         return constNameWithoutPrefix(prefix, field);
                     }
-                    if ((flags & value) == value) {
+                    if (value != 0 && (flags & value) == value) {
                         flags &= ~value;
                         res.append(constNameWithoutPrefix(prefix, field)).append('|');
                     }
diff --git a/core/java/android/util/TimestampedValue.java b/core/java/android/util/TimestampedValue.java
index 1289e4d..4505673 100644
--- a/core/java/android/util/TimestampedValue.java
+++ b/core/java/android/util/TimestampedValue.java
@@ -19,6 +19,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.os.Parcel;
+import android.os.Parcelable;
 import android.os.SystemClock;
 
 import java.util.Objects;
@@ -30,14 +31,14 @@
  * If a suitable clock is used the reference time can be used to identify the age of a value or
  * ordering between values.
  *
- * <p>To read and write a timestamped value from / to a Parcel see
- * {@link #readFromParcel(Parcel, ClassLoader, Class)} and
- * {@link #writeToParcel(Parcel, TimestampedValue)}.
+ * <p>This class implements {@link Parcelable} for convenience but instances will only actually be
+ * parcelable if the value type held is {@code null}, {@link Parcelable}, or one of the other types
+ * supported by {@link Parcel#writeValue(Object)} / {@link Parcel#readValue(ClassLoader)}.
  *
  * @param <T> the type of the value with an associated timestamp
  * @hide
  */
-public final class TimestampedValue<T> {
+public final class TimestampedValue<T> implements Parcelable {
     private final long mReferenceTimeMillis;
     private final T mValue;
 
@@ -81,57 +82,43 @@
     }
 
     /**
-     * Read a {@link TimestampedValue} from a parcel that was stored using
-     * {@link #writeToParcel(Parcel, TimestampedValue)}.
-     *
-     * <p>The marshalling/unmarshalling of the value relies upon {@link Parcel#writeValue(Object)}
-     * and {@link Parcel#readValue(ClassLoader)} and so this method can only be used with types
-     * supported by those methods.
-     *
-     * @param in the Parcel to read from
-     * @param classLoader the ClassLoader to pass to {@link Parcel#readValue(ClassLoader)}
-     * @param valueClass the expected type of the value, typically the same as {@code <T>} but can
-     *     also be a subclass
-     * @throws RuntimeException if the value read is not compatible with {@code valueClass} or the
-     *     object could not be read
-     */
-    @SuppressWarnings("unchecked")
-    @NonNull
-    public static <T> TimestampedValue<T> readFromParcel(
-            @NonNull Parcel in, @Nullable ClassLoader classLoader, Class<? extends T> valueClass) {
-        long referenceTimeMillis = in.readLong();
-        T value = (T) in.readValue(classLoader);
-        // Equivalent to static code: if (!(value.getClass() instanceof {valueClass})) {
-        if (value != null && !valueClass.isAssignableFrom(value.getClass())) {
-            throw new RuntimeException("Value was of type " + value.getClass()
-                    + " is not assignable to " + valueClass);
-        }
-        return new TimestampedValue<>(referenceTimeMillis, value);
-    }
-
-    /**
-     * Write a {@link TimestampedValue} to a parcel so that it can be read using
-     * {@link #readFromParcel(Parcel, ClassLoader, Class)}.
-     *
-     * <p>The marshalling/unmarshalling of the value relies upon {@link Parcel#writeValue(Object)}
-     * and {@link Parcel#readValue(ClassLoader)} and so this method can only be used with types
-     * supported by those methods.
-     *
-     * @param dest the Parcel
-     * @param timestampedValue the value
-     * @throws RuntimeException if the value could not be written to the Parcel
-     */
-    public static void writeToParcel(
-            @NonNull Parcel dest, @NonNull TimestampedValue<?> timestampedValue) {
-        dest.writeLong(timestampedValue.mReferenceTimeMillis);
-        dest.writeValue(timestampedValue.mValue);
-    }
-
-    /**
      * Returns the difference in milliseconds between two instance's reference times.
      */
     public static long referenceTimeDifference(
             @NonNull TimestampedValue<?> one, @NonNull TimestampedValue<?> two) {
         return one.mReferenceTimeMillis - two.mReferenceTimeMillis;
     }
+
+    public static final @NonNull Parcelable.Creator<TimestampedValue<?>> CREATOR =
+            new Parcelable.ClassLoaderCreator<TimestampedValue<?>>() {
+
+                @Override
+                public TimestampedValue<?> createFromParcel(@NonNull Parcel source) {
+                    return createFromParcel(source, null);
+                }
+
+                @Override
+                public TimestampedValue<?> createFromParcel(
+                        @NonNull Parcel source, @Nullable ClassLoader classLoader) {
+                    long referenceTimeMillis = source.readLong();
+                    Object value = source.readValue(classLoader);
+                    return new TimestampedValue<>(referenceTimeMillis, value);
+                }
+
+                @Override
+                public TimestampedValue[] newArray(int size) {
+                    return new TimestampedValue[size];
+                }
+            };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeLong(mReferenceTimeMillis);
+        dest.writeValue(mValue);
+    }
 }
diff --git a/core/java/android/view/IRecentsAnimationController.aidl b/core/java/android/view/IRecentsAnimationController.aidl
index 955be8d..762366e 100644
--- a/core/java/android/view/IRecentsAnimationController.aidl
+++ b/core/java/android/view/IRecentsAnimationController.aidl
@@ -120,4 +120,10 @@
      * @see IRecentsAnimationRunner#onCancelled
      */
     void setDeferCancelUntilNextTransition(boolean defer, boolean screenshot);
+
+    /**
+     * Sets a state for controller to decide which surface is the destination when the recents
+     * animation is cancelled through fail safe mechanism.
+     */
+    void setWillFinishToHome(boolean willFinishToHome);
 }
diff --git a/core/java/android/view/IWindow.aidl b/core/java/android/view/IWindow.aidl
index 4b872d3..8bf99ec 100644
--- a/core/java/android/view/IWindow.aidl
+++ b/core/java/android/view/IWindow.aidl
@@ -81,6 +81,14 @@
      */
     void showInsets(int types, boolean fromIme);
 
+    /**
+     * Called when a set of insets source window should be hidden by policy.
+     *
+     * @param types internal inset types (WindowInsets.Type.InsetType) to hide
+     * @param fromIme true if this request originated from IME (InputMethodService).
+     */
+    void hideInsets(int types, boolean fromIme);
+
     void moved(int newX, int newY);
     void dispatchAppVisibility(boolean visible);
     void dispatchGetNewSurface();
diff --git a/core/java/android/view/InputMonitor.java b/core/java/android/view/InputMonitor.java
index 1a1d7e6..ad1f201 100644
--- a/core/java/android/view/InputMonitor.java
+++ b/core/java/android/view/InputMonitor.java
@@ -40,8 +40,6 @@
     private static final boolean DEBUG = false;
 
     @NonNull
-    private final String mName;
-    @NonNull
     private final InputChannel mInputChannel;
     @NonNull
     private final IInputMonitorHost mHost;
@@ -81,23 +79,19 @@
 
 
 
-    // Code below generated by codegen v1.0.1.
+    // Code below generated by codegen v1.0.7.
     //
     // DO NOT MODIFY!
+    // CHECKSTYLE:OFF Generated code
     //
     // To regenerate run:
     // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/view/InputMonitor.java
-    //
-    // CHECKSTYLE:OFF Generated code
+
 
     @DataClass.Generated.Member
     public InputMonitor(
-            @NonNull String name,
             @NonNull InputChannel inputChannel,
             @NonNull IInputMonitorHost host) {
-        this.mName = name;
-        com.android.internal.util.AnnotationValidations.validate(
-                NonNull.class, null, mName);
         this.mInputChannel = inputChannel;
         com.android.internal.util.AnnotationValidations.validate(
                 NonNull.class, null, mInputChannel);
@@ -109,11 +103,6 @@
     }
 
     @DataClass.Generated.Member
-    public @NonNull String getName() {
-        return mName;
-    }
-
-    @DataClass.Generated.Member
     public @NonNull InputChannel getInputChannel() {
         return mInputChannel;
     }
@@ -130,7 +119,6 @@
         // String fieldNameToString() { ... }
 
         return "InputMonitor { " +
-                "name = " + mName + ", " +
                 "inputChannel = " + mInputChannel + ", " +
                 "host = " + mHost +
         " }";
@@ -142,7 +130,6 @@
         // You can override field parcelling by defining methods like:
         // void parcelFieldName(Parcel dest, int flags) { ... }
 
-        dest.writeString(mName);
         dest.writeTypedObject(mInputChannel, flags);
         dest.writeStrongInterface(mHost);
     }
@@ -151,6 +138,26 @@
     @DataClass.Generated.Member
     public int describeContents() { return 0; }
 
+    /** @hide */
+    @SuppressWarnings({"unchecked", "RedundantCast"})
+    @DataClass.Generated.Member
+    /* package-private */ InputMonitor(Parcel in) {
+        // You can override field unparcelling by defining methods like:
+        // static FieldType unparcelFieldName(Parcel in) { ... }
+
+        InputChannel inputChannel = (InputChannel) in.readTypedObject(InputChannel.CREATOR);
+        IInputMonitorHost host = IInputMonitorHost.Stub.asInterface(in.readStrongBinder());
+
+        this.mInputChannel = inputChannel;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, mInputChannel);
+        this.mHost = host;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, mHost);
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
     @DataClass.Generated.Member
     public static final @NonNull Parcelable.Creator<InputMonitor> CREATOR
             = new Parcelable.Creator<InputMonitor>() {
@@ -160,26 +167,16 @@
         }
 
         @Override
-        @SuppressWarnings({"unchecked", "RedundantCast"})
         public InputMonitor createFromParcel(Parcel in) {
-            // You can override field unparcelling by defining methods like:
-            // static FieldType unparcelFieldName(Parcel in) { ... }
-
-            String name = in.readString();
-            InputChannel inputChannel = (InputChannel) in.readTypedObject(InputChannel.CREATOR);
-            IInputMonitorHost host = IInputMonitorHost.Stub.asInterface(in.readStrongBinder());
-            return new InputMonitor(
-                    name,
-                    inputChannel,
-                    host);
+            return new InputMonitor(in);
         }
     };
 
     @DataClass.Generated(
-            time = 1569871940995L,
-            codegenVersion = "1.0.1",
+            time = 1571177265149L,
+            codegenVersion = "1.0.7",
             sourceFile = "frameworks/base/core/java/android/view/InputMonitor.java",
-            inputSignatures = "private static final  java.lang.String TAG\nprivate static final  boolean DEBUG\nprivate final @android.annotation.NonNull java.lang.String mName\nprivate final @android.annotation.NonNull android.view.InputChannel mInputChannel\nprivate final @android.annotation.NonNull android.view.IInputMonitorHost mHost\npublic  void pilferPointers()\npublic  void dispose()\nclass InputMonitor extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true)")
+            inputSignatures = "private static final  java.lang.String TAG\nprivate static final  boolean DEBUG\nprivate final @android.annotation.NonNull android.view.InputChannel mInputChannel\nprivate final @android.annotation.NonNull android.view.IInputMonitorHost mHost\npublic  void pilferPointers()\npublic  void dispose()\nclass InputMonitor extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true)")
     @Deprecated
     private void __metadata() {}
 
diff --git a/core/java/android/view/InsetsAnimationControlImpl.java b/core/java/android/view/InsetsAnimationControlImpl.java
index 341c214..e4deffa 100644
--- a/core/java/android/view/InsetsAnimationControlImpl.java
+++ b/core/java/android/view/InsetsAnimationControlImpl.java
@@ -229,6 +229,10 @@
             final InsetsSourceConsumer consumer = items.valueAt(i);
             final InsetsSource source = mInitialInsetsState.getSource(consumer.getType());
             final InsetsSourceControl control = consumer.getControl();
+            if (control == null) {
+                // Control may not be available for consumer yet or revoked.
+                continue;
+            }
             final SurfaceControl leash = consumer.getControl().getLeash();
 
             mTmpMatrix.setTranslate(control.getSurfacePosition().x, control.getSurfacePosition().y);
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index 5a8636d..5bb4f63 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -251,6 +251,10 @@
 
     @Override
     public void hide(@InsetType int types) {
+        hide(types, false /* fromIme */);
+    }
+
+    void hide(@InsetType int types, boolean fromIme) {
         int typesReady = 0;
         final ArraySet<Integer> internalTypes = InsetsState.toInternalType(types);
         for (int i = internalTypes.size() - 1; i >= 0; i--) {
@@ -265,7 +269,7 @@
             }
             typesReady |= InsetsState.toPublicType(consumer.getType());
         }
-        applyAnimation(typesReady, false /* show */, false /* fromIme */);
+        applyAnimation(typesReady, false /* show */, fromIme /* fromIme */);
     }
 
     @Override
@@ -331,42 +335,35 @@
         boolean isReady = true;
         for (int i = internalTypes.size() - 1; i >= 0; i--) {
             InsetsSourceConsumer consumer = getSourceConsumer(internalTypes.valueAt(i));
-            // Double check for IME that IME target window has focus.
-            if (consumer.getType() != TYPE_IME || consumer.hasWindowFocus()) {
-                boolean setVisible = !consumer.isVisible();
-                if (setVisible) {
-                    // Show request
-                    switch(consumer.requestShow(fromIme)) {
-                        case ShowResult.SHOW_IMMEDIATELY:
-                            typesReady |= InsetsState.toPublicType(consumer.getType());
-                            break;
-                        case ShowResult.SHOW_DELAYED:
-                            isReady = false;
-                            break;
-                        case ShowResult.SHOW_FAILED:
-                            // IME cannot be shown (since it didn't have focus), proceed
-                            // with animation of other types.
-                            if (mPendingTypesToShow != 0) {
-                                // remove IME from pending because view no longer has focus.
-                                mPendingTypesToShow &= ~InsetsState.toPublicType(TYPE_IME);
-                            }
-                            break;
-                    }
-                } else {
-                    // Hide request
-                    // TODO: Move notifyHidden() to beginning of the hide animation
-                    // (when visibility actually changes using hideDirectly()).
-                    consumer.notifyHidden();
-                    typesReady |= InsetsState.toPublicType(consumer.getType());
+            boolean setVisible = !consumer.isVisible();
+            if (setVisible) {
+                // Show request
+                switch(consumer.requestShow(fromIme)) {
+                    case ShowResult.SHOW_IMMEDIATELY:
+                        typesReady |= InsetsState.toPublicType(consumer.getType());
+                        break;
+                    case ShowResult.SHOW_DELAYED:
+                        isReady = false;
+                        break;
+                    case ShowResult.SHOW_FAILED:
+                        // IME cannot be shown (since it didn't have focus), proceed
+                        // with animation of other types.
+                        if (mPendingTypesToShow != 0) {
+                            // remove IME from pending because view no longer has focus.
+                            mPendingTypesToShow &= ~InsetsState.toPublicType(TYPE_IME);
+                        }
+                        break;
                 }
-                consumers.put(consumer.getType(), consumer);
             } else {
-                // window doesnt have focus, no-op.
-                isReady = false;
-                // TODO: Let the calling app know that window has lost focus and
-                //       show()/hide()/controlWindowInsetsAnimation requests will be ignored.
-                typesReady &= ~InsetsState.toPublicType(consumer.getType());
+                // Hide request
+                // TODO: Move notifyHidden() to beginning of the hide animation
+                // (when visibility actually changes using hideDirectly()).
+                if (!fromIme) {
+                    consumer.notifyHidden();
+                }
+                typesReady |= InsetsState.toPublicType(consumer.getType());
             }
+            consumers.put(consumer.getType(), consumer);
         }
         return new Pair<>(typesReady, isReady);
     }
diff --git a/core/java/android/view/ViewDebug.java b/core/java/android/view/ViewDebug.java
index 8a1fd62..ad59ae5 100644
--- a/core/java/android/view/ViewDebug.java
+++ b/core/java/android/view/ViewDebug.java
@@ -32,6 +32,7 @@
 import android.os.Debug;
 import android.os.Handler;
 import android.os.Looper;
+import android.os.Message;
 import android.os.RemoteException;
 import android.util.DisplayMetrics;
 import android.util.Log;
@@ -46,6 +47,7 @@
 import java.io.IOException;
 import java.io.OutputStream;
 import java.io.OutputStreamWriter;
+import java.lang.annotation.Annotation;
 import java.lang.annotation.ElementType;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -53,12 +55,12 @@
 import java.lang.reflect.AccessibleObject;
 import java.lang.reflect.Field;
 import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Member;
 import java.lang.reflect.Method;
 import java.util.ArrayDeque;
-import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.HashMap;
 import java.util.concurrent.Callable;
-import java.util.concurrent.CancellationException;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.Executor;
@@ -68,6 +70,7 @@
 import java.util.concurrent.atomic.AtomicReference;
 import java.util.concurrent.locks.ReentrantLock;
 import java.util.function.Function;
+import java.util.stream.Stream;
 
 /**
  * Various debugging/tracing tools related to {@link View} and the view hierarchy.
@@ -331,11 +334,83 @@
         public View findHierarchyView(String className, int hashCode);
     }
 
-    private static HashMap<Class<?>, Method[]> mCapturedViewMethodsForClasses = null;
-    private static HashMap<Class<?>, Field[]> mCapturedViewFieldsForClasses = null;
+    private abstract static class PropertyInfo<T extends Annotation,
+            R extends AccessibleObject & Member> {
+
+        public final R member;
+        public final T property;
+        public final String name;
+        public final Class<?> returnType;
+
+        public String entrySuffix = "";
+        public String valueSuffix = "";
+
+        PropertyInfo(Class<T> property, R member, Class<?> returnType) {
+            this.member = member;
+            this.name = member.getName();
+            this.property = member.getAnnotation(property);
+            this.returnType = returnType;
+        }
+
+        public abstract Object invoke(Object target) throws Exception;
+
+        static <T extends Annotation> PropertyInfo<T, ?> forMethod(Method method,
+                Class<T> property) {
+            // Ensure the method return and parameter types can be resolved.
+            try {
+                if ((method.getReturnType() == Void.class)
+                        || (method.getParameterTypes().length != 0)) {
+                    return null;
+                }
+            } catch (NoClassDefFoundError e) {
+                return null;
+            }
+            if (!method.isAnnotationPresent(property)) {
+                return null;
+            }
+            method.setAccessible(true);
+
+            PropertyInfo info = new MethodPI(method, property);
+            info.entrySuffix = "()";
+            info.valueSuffix = ";";
+            return info;
+        }
+
+        static <T extends Annotation> PropertyInfo<T, ?> forField(Field field, Class<T> property) {
+            if (!field.isAnnotationPresent(property)) {
+                return null;
+            }
+            field.setAccessible(true);
+            return new FieldPI<>(field, property);
+        }
+    }
+
+    private static class MethodPI<T extends Annotation> extends PropertyInfo<T, Method> {
+
+        MethodPI(Method method, Class<T> property) {
+            super(property, method, method.getReturnType());
+        }
+
+        @Override
+        public Object invoke(Object target) throws Exception {
+            return member.invoke(target);
+        }
+    }
+
+    private static class FieldPI<T extends Annotation> extends PropertyInfo<T, Field> {
+
+        FieldPI(Field field, Class<T> property) {
+            super(property, field, field.getType());
+        }
+
+        @Override
+        public Object invoke(Object target) throws Exception {
+            return member.get(target);
+        }
+    }
 
     // Maximum delay in ms after which we stop trying to capture a View's drawing
-    private static final int CAPTURE_TIMEOUT = 4000;
+    private static final int CAPTURE_TIMEOUT = 6000;
 
     private static final String REMOTE_COMMAND_CAPTURE = "CAPTURE";
     private static final String REMOTE_COMMAND_DUMP = "DUMP";
@@ -346,9 +421,9 @@
     private static final String REMOTE_COMMAND_CAPTURE_LAYERS = "CAPTURE_LAYERS";
     private static final String REMOTE_COMMAND_OUTPUT_DISPLAYLIST = "OUTPUT_DISPLAYLIST";
 
-    private static HashMap<Class<?>, Field[]> sFieldsForClasses;
-    private static HashMap<Class<?>, Method[]> sMethodsForClasses;
-    private static HashMap<AccessibleObject, ExportedProperty> sAnnotations;
+    private static HashMap<Class<?>, PropertyInfo<ExportedProperty, ?>[]> sExportProperties;
+    private static HashMap<Class<?>, PropertyInfo<CapturedViewProperty, ?>[]>
+            sCapturedViewProperties;
 
     /**
      * @deprecated This enum is now unused
@@ -1157,6 +1232,69 @@
 
     private static void dumpViewHierarchy(Context context, ViewGroup group,
             BufferedWriter out, int level, boolean skipChildren, boolean includeProperties) {
+        cacheExportedProperties(group.getClass());
+        if (!skipChildren) {
+            cacheExportedPropertiesForChildren(group);
+        }
+        // Try to use the handler provided by the view
+        Handler handler = group.getHandler();
+        // Fall back on using the main thread
+        if (handler == null) {
+            handler = new Handler(Looper.getMainLooper());
+        }
+
+        if (handler.getLooper() == Looper.myLooper()) {
+            dumpViewHierarchyOnUIThread(context, group, out, level, skipChildren,
+                    includeProperties);
+        } else {
+            FutureTask task = new FutureTask(() ->
+                    dumpViewHierarchyOnUIThread(context, group, out, level, skipChildren,
+                            includeProperties), null);
+            Message msg = Message.obtain(handler, task);
+            msg.setAsynchronous(true);
+            handler.sendMessage(msg);
+            while (true) {
+                try {
+                    task.get(CAPTURE_TIMEOUT, java.util.concurrent.TimeUnit.MILLISECONDS);
+                    return;
+                } catch (InterruptedException e) {
+                    // try again
+                } catch (ExecutionException | TimeoutException e) {
+                    // Something unexpected happened.
+                    throw new RuntimeException(e);
+                }
+            }
+        }
+    }
+
+    private static void cacheExportedPropertiesForChildren(ViewGroup group) {
+        final int count = group.getChildCount();
+        for (int i = 0; i < count; i++) {
+            final View view = group.getChildAt(i);
+            cacheExportedProperties(view.getClass());
+            if (view instanceof ViewGroup) {
+                cacheExportedPropertiesForChildren((ViewGroup) view);
+            }
+        }
+    }
+
+    private static void cacheExportedProperties(Class<?> klass) {
+        if (sExportProperties != null && sExportProperties.containsKey(klass)) {
+            return;
+        }
+        do {
+            for (PropertyInfo<ExportedProperty, ?> info : getExportedProperties(klass)) {
+                if (!info.returnType.isPrimitive() && info.property.deepExport()) {
+                    cacheExportedProperties(info.returnType);
+                }
+            }
+            klass = klass.getSuperclass();
+        } while (klass != Object.class);
+    }
+
+
+    private static void dumpViewHierarchyOnUIThread(Context context, ViewGroup group,
+            BufferedWriter out, int level, boolean skipChildren, boolean includeProperties) {
         if (!dumpView(context, group, out, level, includeProperties)) {
             return;
         }
@@ -1169,16 +1307,16 @@
         for (int i = 0; i < count; i++) {
             final View view = group.getChildAt(i);
             if (view instanceof ViewGroup) {
-                dumpViewHierarchy(context, (ViewGroup) view, out, level + 1, skipChildren,
-                        includeProperties);
+                dumpViewHierarchyOnUIThread(context, (ViewGroup) view, out, level + 1,
+                        skipChildren, includeProperties);
             } else {
                 dumpView(context, view, out, level + 1, includeProperties);
             }
             if (view.mOverlay != null) {
                 ViewOverlay overlay = view.getOverlay();
                 ViewGroup overlayContainer = overlay.mOverlayViewGroup;
-                dumpViewHierarchy(context, overlayContainer, out, level + 2, skipChildren,
-                        includeProperties);
+                dumpViewHierarchyOnUIThread(context, overlayContainer, out, level + 2,
+                        skipChildren, includeProperties);
             }
         }
         if (group instanceof HierarchyHandler) {
@@ -1212,81 +1350,28 @@
         return true;
     }
 
-    private static Field[] getExportedPropertyFields(Class<?> klass) {
-        if (sFieldsForClasses == null) {
-            sFieldsForClasses = new HashMap<Class<?>, Field[]>();
-        }
-        if (sAnnotations == null) {
-            sAnnotations = new HashMap<AccessibleObject, ExportedProperty>(512);
-        }
-
-        final HashMap<Class<?>, Field[]> map = sFieldsForClasses;
-
-        Field[] fields = map.get(klass);
-        if (fields != null) {
-            return fields;
-        }
-
-        try {
-            final Field[] declaredFields = klass.getDeclaredFieldsUnchecked(false);
-            final ArrayList<Field> foundFields = new ArrayList<Field>();
-            for (final Field field : declaredFields) {
-              // Fields which can't be resolved have a null type.
-              if (field.getType() != null && field.isAnnotationPresent(ExportedProperty.class)) {
-                  field.setAccessible(true);
-                  foundFields.add(field);
-                  sAnnotations.put(field, field.getAnnotation(ExportedProperty.class));
-              }
-            }
-            fields = foundFields.toArray(new Field[foundFields.size()]);
-            map.put(klass, fields);
-        } catch (NoClassDefFoundError e) {
-            throw new AssertionError(e);
-        }
-
-        return fields;
+    private static <T extends Annotation> PropertyInfo<T, ?>[] convertToPropertyInfos(
+            Method[] methods, Field[] fields, Class<T> property) {
+        return Stream.of(Arrays.stream(methods).map(m -> PropertyInfo.forMethod(m, property)),
+                Arrays.stream(fields).map(f -> PropertyInfo.forField(f, property)))
+                .flatMap(Function.identity())
+                .filter(i -> i != null)
+                .toArray(PropertyInfo[]::new);
     }
 
-    private static Method[] getExportedPropertyMethods(Class<?> klass) {
-        if (sMethodsForClasses == null) {
-            sMethodsForClasses = new HashMap<Class<?>, Method[]>(100);
+    private static PropertyInfo<ExportedProperty, ?>[] getExportedProperties(Class<?> klass) {
+        if (sExportProperties == null) {
+            sExportProperties = new HashMap<>();
         }
-        if (sAnnotations == null) {
-            sAnnotations = new HashMap<AccessibleObject, ExportedProperty>(512);
+        final HashMap<Class<?>, PropertyInfo<ExportedProperty, ?>[]> map = sExportProperties;
+        PropertyInfo<ExportedProperty, ?>[] properties = sExportProperties.get(klass);
+
+        if (properties == null) {
+            properties = convertToPropertyInfos(klass.getDeclaredMethodsUnchecked(false),
+                    klass.getDeclaredFieldsUnchecked(false), ExportedProperty.class);
+            map.put(klass, properties);
         }
-
-        final HashMap<Class<?>, Method[]> map = sMethodsForClasses;
-
-        Method[] methods = map.get(klass);
-        if (methods != null) {
-            return methods;
-        }
-
-        methods = klass.getDeclaredMethodsUnchecked(false);
-
-        final ArrayList<Method> foundMethods = new ArrayList<Method>();
-        for (final Method method : methods) {
-            // Ensure the method return and parameter types can be resolved.
-            try {
-                method.getReturnType();
-                method.getParameterTypes();
-            } catch (NoClassDefFoundError e) {
-                continue;
-            }
-
-            if (method.getParameterTypes().length == 0 &&
-                    method.isAnnotationPresent(ExportedProperty.class) &&
-                    method.getReturnType() != Void.class) {
-                method.setAccessible(true);
-                foundMethods.add(method);
-                sAnnotations.put(method, method.getAnnotation(ExportedProperty.class));
-            }
-        }
-
-        methods = foundMethods.toArray(new Method[foundMethods.size()]);
-        map.put(klass, methods);
-
-        return methods;
+        return properties;
     }
 
     private static void dumpViewProperties(Context context, Object view,
@@ -1305,233 +1390,97 @@
 
         Class<?> klass = view.getClass();
         do {
-            exportFields(context, view, out, klass, prefix);
-            exportMethods(context, view, out, klass, prefix);
+            writeExportedProperties(context, view, out, klass, prefix);
             klass = klass.getSuperclass();
         } while (klass != Object.class);
     }
 
-    private static Object callMethodOnAppropriateTheadBlocking(final Method method,
-            final Object object) throws IllegalAccessException, InvocationTargetException,
-            TimeoutException {
-        if (!(object instanceof View)) {
-            return method.invoke(object, (Object[]) null);
-        }
-
-        final View view = (View) object;
-        Callable<Object> callable = new Callable<Object>() {
-            @Override
-            public Object call() throws IllegalAccessException, InvocationTargetException {
-                return method.invoke(view, (Object[]) null);
-            }
-        };
-        FutureTask<Object> future = new FutureTask<Object>(callable);
-        // Try to use the handler provided by the view
-        Handler handler = view.getHandler();
-        // Fall back on using the main thread
-        if (handler == null) {
-            handler = new Handler(android.os.Looper.getMainLooper());
-        }
-        handler.post(future);
-        while (true) {
-            try {
-                return future.get(CAPTURE_TIMEOUT, java.util.concurrent.TimeUnit.MILLISECONDS);
-            } catch (ExecutionException e) {
-                Throwable t = e.getCause();
-                if (t instanceof IllegalAccessException) {
-                    throw (IllegalAccessException)t;
-                }
-                if (t instanceof InvocationTargetException) {
-                    throw (InvocationTargetException)t;
-                }
-                throw new RuntimeException("Unexpected exception", t);
-            } catch (InterruptedException e) {
-                // Call get again
-            } catch (CancellationException e) {
-                throw new RuntimeException("Unexpected cancellation exception", e);
-            }
-        }
-    }
-
     private static String formatIntToHexString(int value) {
         return "0x" + Integer.toHexString(value).toUpperCase();
     }
 
-    private static void exportMethods(Context context, Object view, BufferedWriter out,
+    private static void writeExportedProperties(Context context, Object view, BufferedWriter out,
             Class<?> klass, String prefix) throws IOException {
-
-        final Method[] methods = getExportedPropertyMethods(klass);
-        int count = methods.length;
-        for (int i = 0; i < count; i++) {
-            final Method method = methods[i];
+        for (PropertyInfo<ExportedProperty, ?> info : getExportedProperties(klass)) {
             //noinspection EmptyCatchBlock
+            Object value;
             try {
-                Object methodValue = callMethodOnAppropriateTheadBlocking(method, view);
-                final Class<?> returnType = method.getReturnType();
-                final ExportedProperty property = sAnnotations.get(method);
-                String categoryPrefix =
-                        property.category().length() != 0 ? property.category() + ":" : "";
-
-                if (returnType == int.class) {
-                    if (property.resolveId() && context != null) {
-                        final int id = (Integer) methodValue;
-                        methodValue = resolveId(context, id);
-                    } else {
-                        final FlagToString[] flagsMapping = property.flagMapping();
-                        if (flagsMapping.length > 0) {
-                            final int intValue = (Integer) methodValue;
-                            final String valuePrefix =
-                                    categoryPrefix + prefix + method.getName() + '_';
-                            exportUnrolledFlags(out, flagsMapping, intValue, valuePrefix);
-                        }
-
-                        final IntToString[] mapping = property.mapping();
-                        if (mapping.length > 0) {
-                            final int intValue = (Integer) methodValue;
-                            boolean mapped = false;
-                            int mappingCount = mapping.length;
-                            for (int j = 0; j < mappingCount; j++) {
-                                final IntToString mapper = mapping[j];
-                                if (mapper.from() == intValue) {
-                                    methodValue = mapper.to();
-                                    mapped = true;
-                                    break;
-                                }
-                            }
-
-                            if (!mapped) {
-                                methodValue = intValue;
-                            }
-                        }
-                    }
-                } else if (returnType == int[].class) {
-                    final int[] array = (int[]) methodValue;
-                    final String valuePrefix = categoryPrefix + prefix + method.getName() + '_';
-                    final String suffix = "()";
-
-                    exportUnrolledArray(context, out, property, array, valuePrefix, suffix);
-
-                    continue;
-                } else if (returnType == String[].class) {
-                    final String[] array = (String[]) methodValue;
-                    if (property.hasAdjacentMapping() && array != null) {
-                        for (int j = 0; j < array.length; j += 2) {
-                            if (array[j] != null) {
-                                writeEntry(out, categoryPrefix + prefix, array[j], "()",
-                                        array[j + 1] == null ? "null" : array[j + 1]);
-                            }
-
-                        }
-                    }
-
-                    continue;
-                } else if (!returnType.isPrimitive()) {
-                    if (property.deepExport()) {
-                        dumpViewProperties(context, methodValue, out, prefix + property.prefix());
-                        continue;
-                    }
-                }
-
-                writeEntry(out, categoryPrefix + prefix, method.getName(), "()", methodValue);
-            } catch (IllegalAccessException e) {
-            } catch (InvocationTargetException e) {
-            } catch (TimeoutException e) {
+                value = info.invoke(view);
+            } catch (Exception e) {
+                // ignore
+                continue;
             }
-        }
-    }
 
-    private static void exportFields(Context context, Object view, BufferedWriter out,
-            Class<?> klass, String prefix) throws IOException {
+            String categoryPrefix =
+                    info.property.category().length() != 0 ? info.property.category() + ":" : "";
 
-        final Field[] fields = getExportedPropertyFields(klass);
+            if (info.returnType == int.class || info.returnType == byte.class) {
+                if (info.property.resolveId() && context != null) {
+                    final int id = (Integer) value;
+                    value = resolveId(context, id);
 
-        int count = fields.length;
-        for (int i = 0; i < count; i++) {
-            final Field field = fields[i];
-
-            //noinspection EmptyCatchBlock
-            try {
-                Object fieldValue = null;
-                final Class<?> type = field.getType();
-                final ExportedProperty property = sAnnotations.get(field);
-                String categoryPrefix =
-                        property.category().length() != 0 ? property.category() + ":" : "";
-
-                if (type == int.class || type == byte.class) {
-                    if (property.resolveId() && context != null) {
-                        final int id = field.getInt(view);
-                        fieldValue = resolveId(context, id);
-                    } else {
-                        final FlagToString[] flagsMapping = property.flagMapping();
-                        if (flagsMapping.length > 0) {
-                            final int intValue = field.getInt(view);
-                            final String valuePrefix =
-                                    categoryPrefix + prefix + field.getName() + '_';
-                            exportUnrolledFlags(out, flagsMapping, intValue, valuePrefix);
-                        }
-
-                        final IntToString[] mapping = property.mapping();
-                        if (mapping.length > 0) {
-                            final int intValue = field.getInt(view);
-                            int mappingCount = mapping.length;
-                            for (int j = 0; j < mappingCount; j++) {
-                                final IntToString mapped = mapping[j];
-                                if (mapped.from() == intValue) {
-                                    fieldValue = mapped.to();
-                                    break;
-                                }
-                            }
-
-                            if (fieldValue == null) {
-                                fieldValue = intValue;
-                            }
-                        }
-
-                        if (property.formatToHexString()) {
-                            fieldValue = field.get(view);
-                            if (type == int.class) {
-                                fieldValue = formatIntToHexString((Integer) fieldValue);
-                            } else if (type == byte.class) {
-                                fieldValue = "0x"
-                                        + HexEncoding.encodeToString((Byte) fieldValue, true);
-                            }
-                        }
+                } else if (info.property.formatToHexString()) {
+                    if (info.returnType == int.class) {
+                        value = formatIntToHexString((Integer) value);
+                    } else if (info.returnType == byte.class) {
+                        value = "0x"
+                                + HexEncoding.encodeToString((Byte) value, true);
                     }
-                } else if (type == int[].class) {
-                    final int[] array = (int[]) field.get(view);
-                    final String valuePrefix = categoryPrefix + prefix + field.getName() + '_';
-                    final String suffix = "";
-
-                    exportUnrolledArray(context, out, property, array, valuePrefix, suffix);
-
-                    continue;
-                } else if (type == String[].class) {
-                    final String[] array = (String[]) field.get(view);
-                    if (property.hasAdjacentMapping() && array != null) {
-                        for (int j = 0; j < array.length; j += 2) {
-                            if (array[j] != null) {
-                                writeEntry(out, categoryPrefix + prefix, array[j], "",
-                                        array[j + 1] == null ? "null" : array[j + 1]);
-                            }
-                        }
+                } else {
+                    final ViewDebug.FlagToString[] flagsMapping = info.property.flagMapping();
+                    if (flagsMapping.length > 0) {
+                        final int intValue = (Integer) value;
+                        final String valuePrefix =
+                                categoryPrefix + prefix + info.name + '_';
+                        exportUnrolledFlags(out, flagsMapping, intValue, valuePrefix);
                     }
 
-                    continue;
-                } else if (!type.isPrimitive()) {
-                    if (property.deepExport()) {
-                        dumpViewProperties(context, field.get(view), out, prefix +
-                                property.prefix());
-                        continue;
+                    final ViewDebug.IntToString[] mapping = info.property.mapping();
+                    if (mapping.length > 0) {
+                        final int intValue = (Integer) value;
+                        boolean mapped = false;
+                        int mappingCount = mapping.length;
+                        for (int j = 0; j < mappingCount; j++) {
+                            final ViewDebug.IntToString mapper = mapping[j];
+                            if (mapper.from() == intValue) {
+                                value = mapper.to();
+                                mapped = true;
+                                break;
+                            }
+                        }
+
+                        if (!mapped) {
+                            value = intValue;
+                        }
+                    }
+                }
+            } else if (info.returnType == int[].class) {
+                final int[] array = (int[]) value;
+                final String valuePrefix = categoryPrefix + prefix + info.name + '_';
+                exportUnrolledArray(context, out, info.property, array, valuePrefix,
+                        info.entrySuffix);
+
+                continue;
+            } else if (info.returnType == String[].class) {
+                final String[] array = (String[]) value;
+                if (info.property.hasAdjacentMapping() && array != null) {
+                    for (int j = 0; j < array.length; j += 2) {
+                        if (array[j] != null) {
+                            writeEntry(out, categoryPrefix + prefix, array[j],
+                                    info.entrySuffix, array[j + 1] == null ? "null" : array[j + 1]);
+                        }
                     }
                 }
 
-                if (fieldValue == null) {
-                    fieldValue = field.get(view);
+                continue;
+            } else if (!info.returnType.isPrimitive()) {
+                if (info.property.deepExport()) {
+                    dumpViewProperties(context, value, out, prefix + info.property.prefix());
+                    continue;
                 }
-
-                writeEntry(out, categoryPrefix + prefix, field.getName(), "", fieldValue);
-            } catch (IllegalAccessException e) {
             }
+
+            writeEntry(out, categoryPrefix + prefix, info.name, info.entrySuffix, value);
         }
     }
 
@@ -1721,91 +1670,40 @@
         }
     }
 
-    private static Field[] capturedViewGetPropertyFields(Class<?> klass) {
-        if (mCapturedViewFieldsForClasses == null) {
-            mCapturedViewFieldsForClasses = new HashMap<Class<?>, Field[]>();
+    private static PropertyInfo<CapturedViewProperty, ?>[] getCapturedViewProperties(
+            Class<?> klass) {
+        if (sCapturedViewProperties == null) {
+            sCapturedViewProperties = new HashMap<>();
         }
-        final HashMap<Class<?>, Field[]> map = mCapturedViewFieldsForClasses;
+        final HashMap<Class<?>, PropertyInfo<CapturedViewProperty, ?>[]> map =
+                sCapturedViewProperties;
 
-        Field[] fields = map.get(klass);
-        if (fields != null) {
-            return fields;
+        PropertyInfo<CapturedViewProperty, ?>[] infos = map.get(klass);
+        if (infos == null) {
+            infos = convertToPropertyInfos(klass.getMethods(), klass.getFields(),
+                    CapturedViewProperty.class);
+            map.put(klass, infos);
         }
-
-        final ArrayList<Field> foundFields = new ArrayList<Field>();
-        fields = klass.getFields();
-
-        int count = fields.length;
-        for (int i = 0; i < count; i++) {
-            final Field field = fields[i];
-            if (field.isAnnotationPresent(CapturedViewProperty.class)) {
-                field.setAccessible(true);
-                foundFields.add(field);
-            }
-        }
-
-        fields = foundFields.toArray(new Field[foundFields.size()]);
-        map.put(klass, fields);
-
-        return fields;
+        return infos;
     }
 
-    private static Method[] capturedViewGetPropertyMethods(Class<?> klass) {
-        if (mCapturedViewMethodsForClasses == null) {
-            mCapturedViewMethodsForClasses = new HashMap<Class<?>, Method[]>();
-        }
-        final HashMap<Class<?>, Method[]> map = mCapturedViewMethodsForClasses;
-
-        Method[] methods = map.get(klass);
-        if (methods != null) {
-            return methods;
-        }
-
-        final ArrayList<Method> foundMethods = new ArrayList<Method>();
-        methods = klass.getMethods();
-
-        int count = methods.length;
-        for (int i = 0; i < count; i++) {
-            final Method method = methods[i];
-            if (method.getParameterTypes().length == 0 &&
-                    method.isAnnotationPresent(CapturedViewProperty.class) &&
-                    method.getReturnType() != Void.class) {
-                method.setAccessible(true);
-                foundMethods.add(method);
-            }
-        }
-
-        methods = foundMethods.toArray(new Method[foundMethods.size()]);
-        map.put(klass, methods);
-
-        return methods;
-    }
-
-    private static String capturedViewExportMethods(Object obj, Class<?> klass,
-            String prefix) {
-
+    private static String exportCapturedViewProperties(Object obj, Class<?> klass, String prefix) {
         if (obj == null) {
             return "null";
         }
 
         StringBuilder sb = new StringBuilder();
-        final Method[] methods = capturedViewGetPropertyMethods(klass);
 
-        int count = methods.length;
-        for (int i = 0; i < count; i++) {
-            final Method method = methods[i];
+        for (PropertyInfo<CapturedViewProperty, ?> pi : getCapturedViewProperties(klass)) {
             try {
-                Object methodValue = method.invoke(obj, (Object[]) null);
-                final Class<?> returnType = method.getReturnType();
+                Object methodValue = pi.invoke(obj);
 
-                CapturedViewProperty property = method.getAnnotation(CapturedViewProperty.class);
-                if (property.retrieveReturn()) {
+                if (pi.property.retrieveReturn()) {
                     //we are interested in the second level data only
-                    sb.append(capturedViewExportMethods(methodValue, returnType, method.getName() + "#"));
+                    sb.append(exportCapturedViewProperties(methodValue, pi.returnType,
+                            pi.name + "#"));
                 } else {
-                    sb.append(prefix);
-                    sb.append(method.getName());
-                    sb.append("()=");
+                    sb.append(prefix).append(pi.name).append(pi.entrySuffix).append("=");
 
                     if (methodValue != null) {
                         final String value = methodValue.toString().replace("\n", "\\n");
@@ -1813,47 +1711,10 @@
                     } else {
                         sb.append("null");
                     }
-                    sb.append("; ");
+                    sb.append(pi.valueSuffix).append(" ");
                 }
-            } catch (IllegalAccessException e) {
-                //Exception IllegalAccess, it is OK here
-                //we simply ignore this method
-            } catch (InvocationTargetException e) {
-                //Exception InvocationTarget, it is OK here
-                //we simply ignore this method
-            }
-        }
-        return sb.toString();
-    }
-
-    private static String capturedViewExportFields(Object obj, Class<?> klass, String prefix) {
-        if (obj == null) {
-            return "null";
-        }
-
-        StringBuilder sb = new StringBuilder();
-        final Field[] fields = capturedViewGetPropertyFields(klass);
-
-        int count = fields.length;
-        for (int i = 0; i < count; i++) {
-            final Field field = fields[i];
-            try {
-                Object fieldValue = field.get(obj);
-
-                sb.append(prefix);
-                sb.append(field.getName());
-                sb.append("=");
-
-                if (fieldValue != null) {
-                    final String value = fieldValue.toString().replace("\n", "\\n");
-                    sb.append(value);
-                } else {
-                    sb.append("null");
-                }
-                sb.append(' ');
-            } catch (IllegalAccessException e) {
-                //Exception IllegalAccess, it is OK here
-                //we simply ignore this field
+            } catch (Exception e) {
+                //It is OK here, we simply ignore this property
             }
         }
         return sb.toString();
@@ -1869,8 +1730,7 @@
     public static void dumpCapturedView(String tag, Object view) {
         Class<?> klass = view.getClass();
         StringBuilder sb = new StringBuilder(klass.getName() + ": ");
-        sb.append(capturedViewExportFields(view, klass, ""));
-        sb.append(capturedViewExportMethods(view, klass, ""));
+        sb.append(exportCapturedViewProperties(view, klass, ""));
         Log.d(tag, sb.toString());
     }
 
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 9ddd84f..20dc234 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -4570,6 +4570,7 @@
     private static final int MSG_SYSTEM_GESTURE_EXCLUSION_CHANGED = 32;
     private static final int MSG_LOCATION_IN_PARENT_DISPLAY_CHANGED = 33;
     private static final int MSG_SHOW_INSETS = 34;
+    private static final int MSG_HIDE_INSETS = 35;
 
 
     final class ViewRootHandler extends Handler {
@@ -4636,6 +4637,8 @@
                     return "MSG_LOCATION_IN_PARENT_DISPLAY_CHANGED";
                 case MSG_SHOW_INSETS:
                     return "MSG_SHOW_INSETS";
+                case MSG_HIDE_INSETS:
+                    return "MSG_HIDE_INSETS";
             }
             return super.getMessageName(message);
         }
@@ -4754,6 +4757,10 @@
                     mInsetsController.show(msg.arg1, msg.arg2 == 1);
                     break;
                 }
+                case MSG_HIDE_INSETS: {
+                    mInsetsController.hide(msg.arg1, msg.arg2 == 1);
+                    break;
+                }
                 case MSG_WINDOW_MOVED:
                     if (mAdded) {
                         final int w = mWinFrame.width();
@@ -7559,6 +7566,10 @@
         mHandler.obtainMessage(MSG_SHOW_INSETS, types, fromIme ? 1 : 0).sendToTarget();
     }
 
+    private void hideInsets(@InsetType int types, boolean fromIme) {
+        mHandler.obtainMessage(MSG_HIDE_INSETS, types, fromIme ? 1 : 0).sendToTarget();
+    }
+
     public void dispatchMoved(int newX, int newY) {
         if (DEBUG_LAYOUT) Log.v(mTag, "Window moved " + this + ": newX=" + newX + " newY=" + newY);
         if (mTranslator != null) {
@@ -8682,6 +8693,14 @@
         }
 
         @Override
+        public void hideInsets(@InsetType int types, boolean fromIme) {
+            final ViewRootImpl viewAncestor = mViewAncestor.get();
+            if (viewAncestor != null) {
+                viewAncestor.hideInsets(types, fromIme);
+            }
+        }
+
+        @Override
         public void moved(int newX, int newY) {
             final ViewRootImpl viewAncestor = mViewAncestor.get();
             if (viewAncestor != null) {
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 0b42cd9..db76bb6 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -1680,8 +1680,9 @@
          * to determine its default behavior.
          *
          * {@hide} */
-        @UnsupportedAppUsage
-        public static final int PRIVATE_FLAG_SHOW_FOR_ALL_USERS = 0x00000010;
+        @SystemApi
+        @RequiresPermission(permission.INTERNAL_SYSTEM_WINDOW)
+        public static final int SYSTEM_FLAG_SHOW_FOR_ALL_USERS = 0x00000010;
 
         /**
          * Never animate position changes of the window.
@@ -1842,6 +1843,7 @@
         @Retention(RetentionPolicy.SOURCE)
         @IntDef(flag = true, prefix = { "SYSTEM_FLAG_" }, value = {
                 SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS,
+                SYSTEM_FLAG_SHOW_FOR_ALL_USERS,
         })
         public @interface SystemFlags {}
 
@@ -1863,8 +1865,8 @@
                         equals = PRIVATE_FLAG_WANTS_OFFSET_NOTIFICATIONS,
                         name = "WANTS_OFFSET_NOTIFICATIONS"),
                 @ViewDebug.FlagToString(
-                        mask = PRIVATE_FLAG_SHOW_FOR_ALL_USERS,
-                        equals = PRIVATE_FLAG_SHOW_FOR_ALL_USERS,
+                        mask = SYSTEM_FLAG_SHOW_FOR_ALL_USERS,
+                        equals = SYSTEM_FLAG_SHOW_FOR_ALL_USERS,
                         name = "SHOW_FOR_ALL_USERS"),
                 @ViewDebug.FlagToString(
                         mask = PRIVATE_FLAG_NO_MOVE_ANIMATION,
diff --git a/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java b/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java
index 925a589..0b15cd0 100644
--- a/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java
+++ b/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java
@@ -246,7 +246,7 @@
                 Toast warningToast = mFrameworkObjectProvider.makeToastFromText(
                         mContext, toastMessage, Toast.LENGTH_LONG);
                 warningToast.getWindowParams().privateFlags |=
-                        WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
+                        WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
                 warningToast.show();
             }
 
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index 407a85f..068056f 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -388,21 +388,24 @@
         mResolverDrawerLayout.setPadding(mSystemWindowInsets.left, mSystemWindowInsets.top,
                 mSystemWindowInsets.right, 0);
 
-        View emptyView = findViewById(R.id.empty);
-        if (emptyView != null) {
-            emptyView.setPadding(0, 0, 0, mSystemWindowInsets.bottom
-                    + getResources().getDimensionPixelSize(
-                            R.dimen.chooser_edge_margin_normal) * 2);
-        }
-
-        if (mFooterSpacer == null) {
-            mFooterSpacer = new Space(getApplicationContext());
+        // Need extra padding so the list can fully scroll up
+        if (useLayoutWithDefault()) {
+            if (mFooterSpacer == null) {
+                mFooterSpacer = new Space(getApplicationContext());
+            } else {
+                ((ListView) mAdapterView).removeFooterView(mFooterSpacer);
+            }
+            mFooterSpacer.setLayoutParams(new AbsListView.LayoutParams(LayoutParams.MATCH_PARENT,
+                                                                       mSystemWindowInsets.bottom));
+            ((ListView) mAdapterView).addFooterView(mFooterSpacer);
         } else {
-            ((ListView) mAdapterView).removeFooterView(mFooterSpacer);
+            View emptyView = findViewById(R.id.empty);
+            if (emptyView != null) {
+                emptyView.setPadding(0, 0, 0, mSystemWindowInsets.bottom
+                                     + getResources().getDimensionPixelSize(
+                                             R.dimen.chooser_edge_margin_normal) * 2);
+            }
         }
-        mFooterSpacer.setLayoutParams(new AbsListView.LayoutParams(LayoutParams.MATCH_PARENT,
-                mSystemWindowInsets.bottom));
-        ((ListView) mAdapterView).addFooterView(mFooterSpacer);
 
         resetButtonBar();
 
@@ -561,7 +564,7 @@
                         intent.getData().getHost(),
                         mAdapter.getFilteredItem().getDisplayLabel());
             } else if (mAdapter.areAllTargetsBrowsers()) {
-                dialogTitle =  getString(ActionTitle.BROWSABLE_TITLE_RES);
+                dialogTitle = getString(ActionTitle.BROWSABLE_TITLE_RES);
             } else {
                 dialogTitle = getString(ActionTitle.BROWSABLE_HOST_TITLE_RES,
                         intent.getData().getHost());
@@ -1304,6 +1307,7 @@
         // In case this method is called again (due to activity recreation), avoid adding a new
         // header if one is already present.
         if (useHeader && listView != null && listView.getHeaderViewsCount() == 0) {
+            listView.setHeaderDividersEnabled(true);
             listView.addHeaderView(LayoutInflater.from(this).inflate(
                     R.layout.resolver_different_item_header, listView, false));
         }
@@ -1346,11 +1350,13 @@
         final ViewGroup buttonLayout = findViewById(R.id.button_bar);
         if (buttonLayout != null) {
             buttonLayout.setVisibility(View.VISIBLE);
-            int inset = mSystemWindowInsets != null ? mSystemWindowInsets.bottom : 0;
-            buttonLayout.setPadding(buttonLayout.getPaddingLeft(), buttonLayout.getPaddingTop(),
-                    buttonLayout.getPaddingRight(), getResources().getDimensionPixelSize(
-                        R.dimen.resolver_button_bar_spacing) + inset);
 
+            if (!useLayoutWithDefault()) {
+                int inset = mSystemWindowInsets != null ? mSystemWindowInsets.bottom : 0;
+                buttonLayout.setPadding(buttonLayout.getPaddingLeft(), buttonLayout.getPaddingTop(),
+                        buttonLayout.getPaddingRight(), getResources().getDimensionPixelSize(
+                                R.dimen.resolver_button_bar_spacing) + inset);
+            }
             mOnceButton = (Button) buttonLayout.findViewById(R.id.button_once);
             mAlwaysButton = (Button) buttonLayout.findViewById(R.id.button_always);
 
@@ -2057,7 +2063,9 @@
             CharSequence subLabel = info.getExtendedInfo();
             if (TextUtils.equals(label, subLabel)) subLabel = null;
 
-            if (!TextUtils.equals(holder.text2.getText(), subLabel)) {
+            if (!TextUtils.equals(holder.text2.getText(), subLabel)
+                    && !TextUtils.isEmpty(subLabel)) {
+                holder.text2.setVisibility(View.VISIBLE);
                 holder.text2.setText(subLabel);
             }
 
diff --git a/core/java/com/android/internal/app/procstats/AssociationState.java b/core/java/com/android/internal/app/procstats/AssociationState.java
index 0ed2524..1d4239f 100644
--- a/core/java/com/android/internal/app/procstats/AssociationState.java
+++ b/core/java/com/android/internal/app/procstats/AssociationState.java
@@ -950,6 +950,14 @@
 
         proto.write(PackageAssociationProcessStatsProto.COMPONENT_NAME, mName);
 
+        proto.write(PackageAssociationProcessStatsProto.TOTAL_COUNT, mTotalCount);
+        proto.write(PackageAssociationProcessStatsProto.TOTAL_DURATION_MS, getTotalDuration(now));
+        if (mTotalActiveCount != 0) {
+            proto.write(PackageAssociationProcessStatsProto.ACTIVE_COUNT, mTotalActiveCount);
+            proto.write(PackageAssociationProcessStatsProto.ACTIVE_DURATION_MS,
+                    getActiveDuration(now));
+        }
+
         final int NSRC = mSources.size();
         for (int isrc = 0; isrc < NSRC; isrc++) {
             final SourceKey key = mSources.keyAt(isrc);
diff --git a/core/java/com/android/internal/content/FileSystemProvider.java b/core/java/com/android/internal/content/FileSystemProvider.java
index cdb79ab..f5708a5c 100644
--- a/core/java/com/android/internal/content/FileSystemProvider.java
+++ b/core/java/com/android/internal/content/FileSystemProvider.java
@@ -17,6 +17,7 @@
 package com.android.internal.content;
 
 import android.annotation.CallSuper;
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.ContentResolver;
 import android.content.Intent;
@@ -552,6 +553,11 @@
                     flags |= Document.FLAG_SUPPORTS_DELETE;
                     flags |= Document.FLAG_SUPPORTS_RENAME;
                     flags |= Document.FLAG_SUPPORTS_MOVE;
+
+                    if (shouldBlockFromTree(docId)) {
+                        flags |= Document.FLAG_DIR_BLOCKS_TREE;
+                    }
+
                 } else {
                     flags |= Document.FLAG_SUPPORTS_WRITE;
                     flags |= Document.FLAG_SUPPORTS_DELETE;
@@ -592,6 +598,10 @@
         return row;
     }
 
+    protected boolean shouldBlockFromTree(@NonNull String docId) {
+        return false;
+    }
+
     protected boolean typeSupportsMetadata(String mimeType) {
         return MetadataReader.isSupportedMimeType(mimeType)
                 || Document.MIME_TYPE_DIR.equals(mimeType);
diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
index a845b58..659134a 100644
--- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
@@ -17,6 +17,7 @@
 package com.android.internal.statusbar;
 
 import android.app.Notification;
+import android.net.Uri;
 import android.content.ComponentName;
 import android.graphics.Rect;
 import android.os.Bundle;
@@ -77,6 +78,7 @@
     void onNotificationSettingsViewed(String key);
     void setSystemUiVisibility(int displayId, int vis, int mask, String cause);
     void onNotificationBubbleChanged(String key, boolean isBubble);
+    void grantInlineReplyUriPermission(String key, in Uri uri);
 
     void onGlobalActionsShown();
     void onGlobalActionsHidden();
diff --git a/core/java/com/android/internal/view/BaseIWindow.java b/core/java/com/android/internal/view/BaseIWindow.java
index 0e078dd..7e1f13a 100644
--- a/core/java/com/android/internal/view/BaseIWindow.java
+++ b/core/java/com/android/internal/view/BaseIWindow.java
@@ -80,6 +80,10 @@
     }
 
     @Override
+    public void hideInsets(@InsetType int types, boolean fromIme)  throws RemoteException {
+    }
+
+    @Override
     public void moved(int newX, int newY) {
     }
 
diff --git a/core/java/com/android/internal/widget/LockPatternChecker.java b/core/java/com/android/internal/widget/LockPatternChecker.java
index 09bc28c..85a45fd 100644
--- a/core/java/com/android/internal/widget/LockPatternChecker.java
+++ b/core/java/com/android/internal/widget/LockPatternChecker.java
@@ -1,13 +1,9 @@
 package com.android.internal.widget;
 
-import android.annotation.UnsupportedAppUsage;
 import android.os.AsyncTask;
 
 import com.android.internal.widget.LockPatternUtils.RequestThrottledException;
 
-import java.util.ArrayList;
-import java.util.List;
-
 /**
  * Helper class to check/verify PIN/Password/Pattern asynchronously.
  */
@@ -53,34 +49,28 @@
     }
 
     /**
-     * Verify a pattern asynchronously.
+     * Verify a lockscreen credential asynchronously.
      *
      * @param utils The LockPatternUtils instance to use.
-     * @param pattern The pattern to check.
-     * @param challenge The challenge to verify against the pattern.
-     * @param userId The user to check against the pattern.
+     * @param credential The credential to check.
+     * @param challenge The challenge to verify against the credential.
+     * @param userId The user to check against the credential.
      * @param callback The callback to be invoked with the verification result.
      */
-    public static AsyncTask<?, ?, ?> verifyPattern(final LockPatternUtils utils,
-            final List<LockPatternView.Cell> pattern,
+    public static AsyncTask<?, ?, ?> verifyCredential(final LockPatternUtils utils,
+            final LockscreenCredential credential,
             final long challenge,
             final int userId,
             final OnVerifyCallback callback) {
+        // Create a copy of the credential since checking credential is asynchrounous.
+        final LockscreenCredential credentialCopy = credential.duplicate();
         AsyncTask<Void, Void, byte[]> task = new AsyncTask<Void, Void, byte[]>() {
             private int mThrottleTimeout;
-            private List<LockPatternView.Cell> patternCopy;
-
-            @Override
-            protected void onPreExecute() {
-                // Make a copy of the pattern to prevent race conditions.
-                // No need to clone the individual cells because they are immutable.
-                patternCopy = new ArrayList(pattern);
-            }
 
             @Override
             protected byte[] doInBackground(Void... args) {
                 try {
-                    return utils.verifyPattern(patternCopy, challenge, userId);
+                    return utils.verifyCredential(credentialCopy, challenge, userId);
                 } catch (RequestThrottledException ex) {
                     mThrottleTimeout = ex.getTimeoutMs();
                     return null;
@@ -90,6 +80,12 @@
             @Override
             protected void onPostExecute(byte[] result) {
                 callback.onVerified(result, mThrottleTimeout);
+                credentialCopy.zeroize();
+            }
+
+            @Override
+            protected void onCancelled() {
+                credentialCopy.zeroize();
             }
         };
         task.execute();
@@ -97,32 +93,26 @@
     }
 
     /**
-     * Checks a pattern asynchronously.
+     * Checks a lockscreen credential asynchronously.
      *
      * @param utils The LockPatternUtils instance to use.
-     * @param pattern The pattern to check.
-     * @param userId The user to check against the pattern.
+     * @param credential The credential to check.
+     * @param userId The user to check against the credential.
      * @param callback The callback to be invoked with the check result.
      */
-    public static AsyncTask<?, ?, ?> checkPattern(final LockPatternUtils utils,
-            final List<LockPatternView.Cell> pattern,
+    public static AsyncTask<?, ?, ?> checkCredential(final LockPatternUtils utils,
+            final LockscreenCredential credential,
             final int userId,
             final OnCheckCallback callback) {
+        // Create a copy of the credential since checking credential is asynchrounous.
+        final LockscreenCredential credentialCopy = credential.duplicate();
         AsyncTask<Void, Void, Boolean> task = new AsyncTask<Void, Void, Boolean>() {
             private int mThrottleTimeout;
-            private List<LockPatternView.Cell> patternCopy;
-
-            @Override
-            protected void onPreExecute() {
-                // Make a copy of the pattern to prevent race conditions.
-                // No need to clone the individual cells because they are immutable.
-                patternCopy = new ArrayList(pattern);
-            }
 
             @Override
             protected Boolean doInBackground(Void... args) {
                 try {
-                    return utils.checkPattern(patternCopy, userId, callback::onEarlyMatched);
+                    return utils.checkCredential(credentialCopy, userId, callback::onEarlyMatched);
                 } catch (RequestThrottledException ex) {
                     mThrottleTimeout = ex.getTimeoutMs();
                     return false;
@@ -132,11 +122,13 @@
             @Override
             protected void onPostExecute(Boolean result) {
                 callback.onChecked(result, mThrottleTimeout);
+                credentialCopy.zeroize();
             }
 
             @Override
             protected void onCancelled() {
                 callback.onCancelled();
+                credentialCopy.zeroize();
             }
         };
         task.execute();
@@ -144,84 +136,29 @@
     }
 
     /**
-     * Verify a password asynchronously.
+     * Perform a lockscreen credential verification explicitly on a managed profile with unified
+     * challenge, using the parent user's credential.
      *
      * @param utils The LockPatternUtils instance to use.
-     * @param password The password to check.
-     * @param challenge The challenge to verify against the pattern.
-     * @param userId The user to check against the pattern.
-     * @param callback The callback to be invoked with the verification result.
-     *
-     * @deprecated Pass the password as a byte array.
-     */
-    @Deprecated
-    public static AsyncTask<?, ?, ?> verifyPassword(final LockPatternUtils utils,
-            final String password,
-            final long challenge,
-            final int userId,
-            final OnVerifyCallback callback) {
-        byte[] passwordBytes = password != null ? password.getBytes() : null;
-        return verifyPassword(utils, passwordBytes, challenge, userId, callback);
-    }
-
-    /**
-     * Verify a password asynchronously.
-     *
-     * @param utils The LockPatternUtils instance to use.
-     * @param password The password to check.
-     * @param challenge The challenge to verify against the pattern.
-     * @param userId The user to check against the pattern.
-     * @param callback The callback to be invoked with the verification result.
-     */
-    public static AsyncTask<?, ?, ?> verifyPassword(final LockPatternUtils utils,
-            final byte[] password,
-            final long challenge,
-            final int userId,
-            final OnVerifyCallback callback) {
-        AsyncTask<Void, Void, byte[]> task = new AsyncTask<Void, Void, byte[]>() {
-            private int mThrottleTimeout;
-
-            @Override
-            protected byte[] doInBackground(Void... args) {
-                try {
-                    return utils.verifyPassword(password, challenge, userId);
-                } catch (RequestThrottledException ex) {
-                    mThrottleTimeout = ex.getTimeoutMs();
-                    return null;
-                }
-            }
-
-            @Override
-            protected void onPostExecute(byte[] result) {
-                callback.onVerified(result, mThrottleTimeout);
-            }
-        };
-        task.execute();
-        return task;
-    }
-
-    /**
-     * Verify a password asynchronously.
-     *
-     * @param utils The LockPatternUtils instance to use.
-     * @param password The password to check.
-     * @param challenge The challenge to verify against the pattern.
-     * @param userId The user to check against the pattern.
+     * @param credential The credential to check.
+     * @param challenge The challenge to verify against the credential.
+     * @param userId The user to check against the credential.
      * @param callback The callback to be invoked with the verification result.
      */
     public static AsyncTask<?, ?, ?> verifyTiedProfileChallenge(final LockPatternUtils utils,
-            final byte[] password,
-            final boolean isPattern,
+            final LockscreenCredential credential,
             final long challenge,
             final int userId,
             final OnVerifyCallback callback) {
+        // Create a copy of the credential since checking credential is asynchrounous.
+        final LockscreenCredential credentialCopy = credential.duplicate();
         AsyncTask<Void, Void, byte[]> task = new AsyncTask<Void, Void, byte[]>() {
             private int mThrottleTimeout;
 
             @Override
             protected byte[] doInBackground(Void... args) {
                 try {
-                    return utils.verifyTiedProfileChallenge(password, isPattern, challenge, userId);
+                    return utils.verifyTiedProfileChallenge(credentialCopy, challenge, userId);
                 } catch (RequestThrottledException ex) {
                     mThrottleTimeout = ex.getTimeoutMs();
                     return null;
@@ -231,64 +168,12 @@
             @Override
             protected void onPostExecute(byte[] result) {
                 callback.onVerified(result, mThrottleTimeout);
-            }
-        };
-        task.execute();
-        return task;
-    }
-
-    /**
-     * Checks a password asynchronously.
-     *
-     * @param utils The LockPatternUtils instance to use.
-     * @param password The password to check.
-     * @param userId The user to check against the pattern.
-     * @param callback The callback to be invoked with the check result.
-     * @deprecated Pass passwords as byte[]
-     */
-    @UnsupportedAppUsage
-    @Deprecated
-    public static AsyncTask<?, ?, ?> checkPassword(final LockPatternUtils utils,
-            final String password,
-            final int userId,
-            final OnCheckCallback callback) {
-        byte[] passwordBytes = password != null ? password.getBytes() : null;
-        return checkPassword(utils, passwordBytes, userId, callback);
-    }
-
-    /**
-     * Checks a password asynchronously.
-     *
-     * @param utils The LockPatternUtils instance to use.
-     * @param passwordBytes The password to check.
-     * @param userId The user to check against the pattern.
-     * @param callback The callback to be invoked with the check result.
-     */
-    public static AsyncTask<?, ?, ?> checkPassword(final LockPatternUtils utils,
-            final byte[] passwordBytes,
-            final int userId,
-            final OnCheckCallback callback) {
-        AsyncTask<Void, Void, Boolean> task = new AsyncTask<Void, Void, Boolean>() {
-            private int mThrottleTimeout;
-
-            @Override
-            protected Boolean doInBackground(Void... args) {
-                try {
-                    return utils.checkPassword(passwordBytes, userId, callback::onEarlyMatched);
-                } catch (RequestThrottledException ex) {
-                    mThrottleTimeout = ex.getTimeoutMs();
-                    return false;
-                }
-            }
-
-            @Override
-            protected void onPostExecute(Boolean result) {
-                callback.onChecked(result, mThrottleTimeout);
+                credentialCopy.zeroize();
             }
 
             @Override
             protected void onCancelled() {
-                callback.onCancelled();
+                credentialCopy.zeroize();
             }
         };
         task.execute();
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index 070121c..1daa25a 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -26,10 +26,10 @@
 import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
 
 import android.annotation.IntDef;
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.UnsupportedAppUsage;
 import android.app.admin.DevicePolicyManager;
-import android.app.admin.PasswordMetrics;
 import android.app.trust.IStrongAuthTracker;
 import android.app.trust.TrustManager;
 import android.content.ComponentName;
@@ -365,11 +365,24 @@
                 null /* componentName */, userId);
     }
 
-    private byte[] verifyCredential(byte[] credential, int type, long challenge, int userId)
-            throws RequestThrottledException {
+    /**
+     * Check to see if a credential matches the saved one.
+     * If credential matches, return an opaque attestation that the challenge was verified.
+     *
+     * @param credential The credential to check.
+     * @param challenge The challenge to verify against the credential
+     * @return the attestation that the challenge was verified, or null
+     * @param userId The user whose credential is being verified
+     * @throws RequestThrottledException if credential verification is being throttled due to
+     *         to many incorrect attempts.
+     * @throws IllegalStateException if called on the main thread.
+     */
+    public byte[] verifyCredential(@NonNull LockscreenCredential credential, long challenge,
+            int userId) throws RequestThrottledException {
+        throwIfCalledOnMainThread();
         try {
-            VerifyCredentialResponse response = getLockSettings().verifyCredential(credential,
-                    type, challenge, userId);
+            VerifyCredentialResponse response = getLockSettings().verifyCredential(
+                    credential.getCredential(), credential.getType(), challenge, userId);
             if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK) {
                 return response.getPayload();
             } else if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_RETRY) {
@@ -382,11 +395,24 @@
         }
     }
 
-    private boolean checkCredential(byte[] credential, int type, int userId,
+    /**
+     * Check to see if a credential matches the saved one.
+     *
+     * @param credential The credential to check.
+     * @param userId The user whose credential is being checked
+     * @param progressCallback callback to deliver early signal that the credential matches
+     * @return {@code true} if credential matches, {@code false} otherwise
+     * @throws RequestThrottledException if credential verification is being throttled due to
+     *         to many incorrect attempts.
+     * @throws IllegalStateException if called on the main thread.
+     */
+    public boolean checkCredential(@NonNull LockscreenCredential credential, int userId,
             @Nullable CheckCredentialProgressCallback progressCallback)
             throws RequestThrottledException {
+        throwIfCalledOnMainThread();
         try {
-            VerifyCredentialResponse response = getLockSettings().checkCredential(credential, type,
+            VerifyCredentialResponse response = getLockSettings().checkCredential(
+                    credential.getCredential(), credential.getType(),
                     userId, wrapCallback(progressCallback));
 
             if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK) {
@@ -402,79 +428,26 @@
     }
 
     /**
-     * Check to see if a pattern matches the saved pattern.
-     * If pattern matches, return an opaque attestation that the challenge
-     * was verified.
+     * Check if the credential of a managed profile with unified challenge matches. In this context,
+     * The credential should be the parent user's lockscreen password. If credential matches,
+     * return an opaque attestation associated with the managed profile that the challenge was
+     * verified.
      *
-     * @param pattern The pattern to check.
-     * @param challenge The challenge to verify against the pattern
-     * @return the attestation that the challenge was verified, or null.
+     * @param credential The parent user's credential to check.
+     * @param challenge The challenge to verify against the credential
+     * @return the attestation that the challenge was verified, or null
+     * @param userId The managed profile user id
+     * @throws RequestThrottledException if credential verification is being throttled due to
+     *         to many incorrect attempts.
+     * @throws IllegalStateException if called on the main thread.
      */
-    public byte[] verifyPattern(List<LockPatternView.Cell> pattern, long challenge, int userId)
-            throws RequestThrottledException {
-        throwIfCalledOnMainThread();
-        return verifyCredential(patternToByteArray(pattern), CREDENTIAL_TYPE_PATTERN, challenge,
-                userId);
-    }
-
-    /**
-     * Check to see if a pattern matches the saved pattern.  If no pattern exists,
-     * always returns true.
-     * @param pattern The pattern to check.
-     * @return Whether the pattern matches the stored one.
-     */
-    public boolean checkPattern(List<LockPatternView.Cell> pattern, int userId)
-            throws RequestThrottledException {
-        return checkPattern(pattern, userId, null /* progressCallback */);
-    }
-
-    /**
-     * Check to see if a pattern matches the saved pattern.  If no pattern exists,
-     * always returns true.
-     * @param pattern The pattern to check.
-     * @return Whether the pattern matches the stored one.
-     */
-    public boolean checkPattern(List<LockPatternView.Cell> pattern, int userId,
-            @Nullable CheckCredentialProgressCallback progressCallback)
-            throws RequestThrottledException {
-        throwIfCalledOnMainThread();
-        return checkCredential(patternToByteArray(pattern), CREDENTIAL_TYPE_PATTERN, userId,
-                progressCallback);
-    }
-
-    /**
-     * Check to see if a password matches the saved password.
-     * If password matches, return an opaque attestation that the challenge
-     * was verified.
-     *
-     * @param password The password to check.
-     * @param challenge The challenge to verify against the password
-     * @return the attestation that the challenge was verified, or null.
-     */
-    public byte[] verifyPassword(byte[] password, long challenge, int userId)
-            throws RequestThrottledException {
-        throwIfCalledOnMainThread();
-        return verifyCredential(password, CREDENTIAL_TYPE_PASSWORD, challenge, userId);
-    }
-
-
-    /**
-     * Check to see if a password matches the saved password.
-     * If password matches, return an opaque attestation that the challenge
-     * was verified.
-     *
-     * @param password The password to check.
-     * @param challenge The challenge to verify against the password
-     * @return the attestation that the challenge was verified, or null.
-     */
-    public byte[] verifyTiedProfileChallenge(byte[] password, boolean isPattern, long challenge,
-            int userId) throws RequestThrottledException {
+    public byte[] verifyTiedProfileChallenge(@NonNull LockscreenCredential credential,
+            long challenge, int userId) throws RequestThrottledException {
         throwIfCalledOnMainThread();
         try {
             VerifyCredentialResponse response =
-                    getLockSettings().verifyTiedProfileChallenge(password,
-                            isPattern ? CREDENTIAL_TYPE_PATTERN : CREDENTIAL_TYPE_PASSWORD, challenge,
-                            userId);
+                    getLockSettings().verifyTiedProfileChallenge(
+                            credential.getCredential(), credential.getType(), challenge, userId);
 
             if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK) {
                 return response.getPayload();
@@ -489,61 +462,6 @@
     }
 
     /**
-     *
-     * Check to see if a password matches the saved password.  If no password exists,
-     * always returns true.
-     * @param password The password to check.
-     * @return Whether the password matches the stored one.
-     */
-    @UnsupportedAppUsage
-    public boolean checkPassword(String password, int userId) throws RequestThrottledException {
-        byte[] passwordBytes = password != null ? password.getBytes() : null;
-        return checkPassword(passwordBytes, userId, null /* progressCallback */);
-    }
-
-
-    /**
-     *
-     * Check to see if a password matches the saved password.  If no password exists,
-     * always returns true.
-     * @param password The password to check.
-     * @return Whether the password matches the stored one.
-     */
-    public boolean checkPassword(byte[] password, int userId) throws RequestThrottledException {
-        return checkPassword(password, userId, null /* progressCallback */);
-    }
-
-    // TODO(b/120484642): This method is necessary for vendor/qcom code and is a hidden api
-    /* *
-     * Check to see if a password matches the saved password.  If no password exists,
-     * always returns true.
-     * @param password The password to check.
-     * @return Whether the password matches the stored one.
-     */
-    public boolean checkPassword(String password, int userId,
-            @Nullable CheckCredentialProgressCallback progressCallback)
-            throws RequestThrottledException {
-        byte[] passwordBytes = password != null ? password.getBytes() : null;
-        throwIfCalledOnMainThread();
-        return checkCredential(passwordBytes, CREDENTIAL_TYPE_PASSWORD, userId, progressCallback);
-
-    }
-
-    /**
-     * Check to see if a password matches the saved password.  If no password exists,
-     * always returns true.
-     * @param password The password to check.
-     * @return Whether the password matches the stored one.
-     */
-
-    public boolean checkPassword(byte[] password, int userId,
-            @Nullable CheckCredentialProgressCallback progressCallback)
-            throws RequestThrottledException {
-        throwIfCalledOnMainThread();
-        return checkCredential(password, CREDENTIAL_TYPE_PASSWORD, userId, progressCallback);
-    }
-
-    /**
      * Check to see if vold already has the password.
      * Note that this also clears vold's copy of the password.
      * @return Whether the vold password matches or not.
@@ -560,9 +478,10 @@
      * Returns the password history hash factor, needed to check new password against password
      * history with {@link #checkPasswordHistory(byte[], byte[], int)}
      */
-    public byte[] getPasswordHistoryHashFactor(byte[] currentPassword, int userId) {
+    public byte[] getPasswordHistoryHashFactor(@NonNull LockscreenCredential currentPassword,
+            int userId) {
         try {
-            return getLockSettings().getHashFactor(currentPassword, userId);
+            return getLockSettings().getHashFactor(currentPassword.getCredential(), userId);
         } catch (RemoteException e) {
             Log.e(TAG, "failed to get hash factor", e);
             return null;
@@ -679,57 +598,6 @@
     }
 
     /**
-     * Clear any lock pattern or password.
-
-     * <p> This method will fail (returning {@code false}) if the previously
-     * saved password provided is incorrect, or if the lockscreen verification
-     * is still being throttled.
-     *
-     * @param savedCredential The previously saved credential
-     * @param userHandle the user whose pattern is to be saved.
-     * @return whether this was successful or not.
-     * @throws RuntimeException if password change encountered an unrecoverable error.
-     */
-    public boolean clearLock(byte[] savedCredential, int userHandle) {
-        return clearLock(savedCredential, userHandle, false);
-    }
-
-    /**
-     * Clear any lock pattern or password, with the option to ignore incorrect existing credential.
-     * <p> This method will fail (returning {@code false}) if the previously
-     * saved password provided is incorrect, or if the lockscreen verification
-     * is still being throttled.
-     *
-     * @param savedCredential The previously saved credential
-     * @param userHandle the user whose pattern is to be saved.
-     * @return whether this was successful or not.
-     * @throws RuntimeException if password change encountered an unrecoverable error.
-     */
-    public boolean clearLock(byte[] savedCredential, int userHandle, boolean allowUntrustedChange) {
-        final int currentQuality = getKeyguardStoredPasswordQuality(userHandle);
-        setKeyguardStoredPasswordQuality(PASSWORD_QUALITY_UNSPECIFIED, userHandle);
-
-        try {
-            if (!getLockSettings().setLockCredential(null, CREDENTIAL_TYPE_NONE, savedCredential,
-                    PASSWORD_QUALITY_UNSPECIFIED, userHandle, allowUntrustedChange)) {
-                return false;
-            }
-        } catch (RemoteException | RuntimeException e) {
-            setKeyguardStoredPasswordQuality(currentQuality, userHandle);
-            throw new RuntimeException("Failed to clear lock", e);
-        }
-
-        if (userHandle == UserHandle.USER_SYSTEM) {
-            // Set the encryption password to default.
-            updateEncryptionPassword(StorageManager.CRYPT_TYPE_DEFAULT, null);
-            setCredentialRequiredToDecrypt(false);
-        }
-
-        onAfterChangingPassword(userHandle);
-        return true;
-    }
-
-    /**
      * Disable showing lock screen at all for a given user.
      * This is only meaningful if pattern, pin or password are not set.
      *
@@ -762,75 +630,88 @@
                 || isDemoUser;
     }
 
-    /**
-     * Save a lock pattern.
-     *
-     * <p> This method will fail (returning {@code false}) if the previously saved pattern provided
-     * is incorrect, or if the lockscreen verification is still being throttled.
-     *
-     * @param pattern The new pattern to save.
-     * @param savedPattern The previously saved pattern, converted to byte[] format
-     * @param userId the user whose pattern is to be saved.
-     * @return whether this was successful or not.
-     * @throws RuntimeException if password change encountered an unrecoverable error.
-     */
-    public boolean saveLockPattern(List<LockPatternView.Cell> pattern, byte[] savedPattern,
-            int userId) {
-        return saveLockPattern(pattern, savedPattern, userId, false);
+    /** Returns if the given quality maps to an alphabetic password */
+    public static boolean isQualityAlphabeticPassword(int quality) {
+        return quality >= PASSWORD_QUALITY_ALPHABETIC;
+    }
+
+    /** Returns if the given quality maps to an numeric pin */
+    public static boolean isQualityNumericPin(int quality) {
+        return quality == PASSWORD_QUALITY_NUMERIC || quality == PASSWORD_QUALITY_NUMERIC_COMPLEX;
     }
 
     /**
-     * Save a lock pattern.
+     * Save a new lockscreen credential.
      *
-     * <p> This method will fail (returning {@code false}) if the previously saved pattern provided
-     * is incorrect, or if the lockscreen verification is still being throttled.
+     * <p> This method will fail (returning {@code false}) if the previously saved credential
+     * provided is incorrect, or if the lockscreen verification is still being throttled.
      *
-     * @param pattern The new pattern to save.
-     * @param savedPattern The previously saved pattern, converted to byte[] format
-     * @param userId the user whose pattern is to be saved.
-     * @param allowUntrustedChange whether we want to allow saving a new password if the existing
-     * password being provided is incorrect.
-     * @return whether this was successful or not.
+     * @param newCredential The new credential to save
+     * @param savedCredential The current credential
+     * @param userId the user whose lockscreen credential is to be changed
+     *
+     * @return whether this method saved the new password successfully or not. This flow will fail
+     * and return false if the given credential is wrong.
      * @throws RuntimeException if password change encountered an unrecoverable error.
      */
-    public boolean saveLockPattern(List<LockPatternView.Cell> pattern, byte[] savedPattern,
-            int userId, boolean allowUntrustedChange) {
+    public boolean setLockCredential(@NonNull LockscreenCredential newCredential,
+            @NonNull LockscreenCredential savedCredential, int userId) {
+        return setLockCredential(newCredential, savedCredential, userId, false);
+    }
+
+    /**
+     * Save a new lockscreen credential.
+     * <p> This method will fail (returning {@code false}) if the previously saved pattern provided
+     * is incorrect and allowUntrustedChange is false, or if the lockscreen verification is still
+     * being throttled.
+     * @param newCredential The new credential to save
+     * @param savedCredential The current credential
+     * @param userHandle the user whose lockscreen credential is to be changed
+     * @param allowUntrustedChange whether we want to allow saving a new pattern if the existing
+     * credentialt being provided is incorrect.
+     *
+     * @return whether this method saved the new password successfully or not. This flow will fail
+     * and return false if the given credential is wrong and allowUntrustedChange is false.
+     * @throws RuntimeException if password change encountered an unrecoverable error.
+     */
+    public boolean setLockCredential(@NonNull LockscreenCredential newCredential,
+            @NonNull LockscreenCredential savedCredential, int userHandle,
+            boolean allowUntrustedChange) {
         if (!hasSecureLockScreen()) {
             throw new UnsupportedOperationException(
                     "This operation requires the lock screen feature.");
         }
-        if (pattern == null || pattern.size() < MIN_LOCK_PATTERN_SIZE) {
-            throw new IllegalArgumentException("pattern must not be null and at least "
-                    + MIN_LOCK_PATTERN_SIZE + " dots long.");
-        }
+        newCredential.checkLength();
 
-        final byte[] bytePattern = patternToByteArray(pattern);
-        final int currentQuality = getKeyguardStoredPasswordQuality(userId);
-        setKeyguardStoredPasswordQuality(PASSWORD_QUALITY_SOMETHING, userId);
+        final int currentQuality = getKeyguardStoredPasswordQuality(userHandle);
+        setKeyguardStoredPasswordQuality(newCredential.getQuality(), userHandle);
+
         try {
-            if (!getLockSettings().setLockCredential(bytePattern, CREDENTIAL_TYPE_PATTERN,
-                    savedPattern, PASSWORD_QUALITY_SOMETHING, userId, allowUntrustedChange)) {
+            if (!getLockSettings().setLockCredential(
+                    newCredential.getCredential(), newCredential.getType(),
+                    savedCredential.getCredential(),
+                    newCredential.getQuality(), userHandle, allowUntrustedChange)) {
+                setKeyguardStoredPasswordQuality(currentQuality, userHandle);
                 return false;
             }
         } catch (RemoteException | RuntimeException e) {
-            setKeyguardStoredPasswordQuality(currentQuality, userId);
-            throw new RuntimeException("Couldn't save lock pattern", e);
-        }
-        // Update the device encryption password.
-        if (userId == UserHandle.USER_SYSTEM
-                && LockPatternUtils.isDeviceEncryptionEnabled()) {
-            if (!shouldEncryptWithCredentials(true)) {
-                clearEncryptionPassword();
-            } else {
-                updateEncryptionPassword(StorageManager.CRYPT_TYPE_PATTERN, bytePattern);
-            }
+            setKeyguardStoredPasswordQuality(currentQuality, userHandle);
+            throw new RuntimeException("Unable to save lock password", e);
         }
 
-        reportPatternWasChosen(userId);
-        onAfterChangingPassword(userId);
+        onPostPasswordChanged(newCredential, userHandle);
         return true;
     }
 
+    private void onPostPasswordChanged(LockscreenCredential newCredential, int userHandle) {
+        updateEncryptionPasswordIfNeeded(newCredential, userHandle);
+        if (newCredential.isPattern()) {
+            reportPatternWasChosen(userHandle);
+        }
+        updatePasswordHistory(newCredential, userHandle);
+        reportEnabledTrustAgentsChanged(userHandle);
+    }
+
     private void updateCryptoUserInfo(int userId) {
         if (userId != UserHandle.USER_SYSTEM) {
             return;
@@ -929,149 +810,35 @@
     }
 
     /**
-     * Save a lock password.  Does not ensure that the password is as good
-     * as the requested mode, but will adjust the mode to be as good as the
-     * password.
-     *
-     * <p> This method will fail (returning {@code false}) if the previously
-     * saved password provided is incorrect, or if the lockscreen verification
-     * is still being throttled.
-     *
-     * @param password The password to save
-     * @param savedPassword The previously saved lock password, or null if none
-     * @param requestedQuality {@see DevicePolicyManager#getPasswordQuality(
-     * android.content.ComponentName)}
-     * @param userHandle The userId of the user to change the password for
-     * @return whether this was successful or not.
-     * @throws RuntimeException if password change encountered an unrecoverable error.
-     * @deprecated Pass password as a byte array
-     */
-    @Deprecated
-    public boolean saveLockPassword(String password, String savedPassword, int requestedQuality,
-            int userHandle) {
-        byte[] passwordBytes = password != null ? password.getBytes() : null;
-        byte[] savedPasswordBytes = savedPassword != null ? savedPassword.getBytes() : null;
-        return saveLockPassword(passwordBytes, savedPasswordBytes, requestedQuality, userHandle);
-    }
-
-    /**
-     * Save a lock password.  Does not ensure that the password is as good
-     * as the requested mode, but will adjust the mode to be as good as the
-     * password.
-     *
-     * <p> This method will fail (returning {@code false}) if the previously
-     * saved password provided is incorrect, or if the lockscreen verification
-     * is still being throttled.
-     *
-     * @param password The password to save
-     * @param savedPassword The previously saved lock password, or null if none
-     * @param requestedQuality {@see DevicePolicyManager#getPasswordQuality(
-     * android.content.ComponentName)}
-     * @param userHandle The userId of the user to change the password for
-     * @return whether this was successful or not.
-     * @throws RuntimeException if password change encountered an unrecoverable error.
-     */
-    public boolean saveLockPassword(byte[] password, byte[] savedPassword, int requestedQuality,
-            int userHandle) {
-        return saveLockPassword(password, savedPassword, requestedQuality,
-                userHandle, false);
-    }
-
-    /**
-     * Save a lock password.  Does not ensure that the password is as good
-     * as the requested mode, but will adjust the mode to be as good as the
-     * password.
-     *
-     * <p> This method will fail (returning {@code false}) if the previously
-     * saved password provided is incorrect, or if the lockscreen verification
-     * is still being throttled.
-     *
-     * @param password The password to save
-     * @param savedPassword The previously saved lock password, or null if none
-     * @param requestedQuality {@see DevicePolicyManager#getPasswordQuality(
-     * android.content.ComponentName)}
-     * @param userHandle The userId of the user to change the password for
-     * @param allowUntrustedChange whether we want to allow saving a new password if the existing
-     * password being provided is incorrect.
-     * @return whether this method saved the new password successfully or not. This flow will fail
-     * and return false if the given credential is wrong and allowUntrustedChange is false.
-     * @throws RuntimeException if password change encountered an unrecoverable error.
-     */
-    public boolean saveLockPassword(byte[] password, byte[] savedPassword,
-            int requestedQuality, int userHandle, boolean allowUntrustedChange) {
-        if (!hasSecureLockScreen()) {
-            throw new UnsupportedOperationException(
-                    "This operation requires the lock screen feature.");
-        }
-        if (password == null || password.length < MIN_LOCK_PASSWORD_SIZE) {
-            throw new IllegalArgumentException("password must not be null and at least "
-                    + "of length " + MIN_LOCK_PASSWORD_SIZE);
-        }
-
-        if (requestedQuality < PASSWORD_QUALITY_NUMERIC) {
-            throw new IllegalArgumentException("quality must be at least NUMERIC, but was "
-                    + requestedQuality);
-        }
-
-        final int currentQuality = getKeyguardStoredPasswordQuality(userHandle);
-        final int passwordQuality = PasswordMetrics.computeForPassword(password).quality;
-        final int newKeyguardQuality =
-                computeKeyguardQuality(CREDENTIAL_TYPE_PASSWORD, requestedQuality, passwordQuality);
-        setKeyguardStoredPasswordQuality(newKeyguardQuality, userHandle);
-        try {
-            getLockSettings().setLockCredential(password, CREDENTIAL_TYPE_PASSWORD, savedPassword,
-                    requestedQuality, userHandle, allowUntrustedChange);
-        } catch (RemoteException | RuntimeException e) {
-            setKeyguardStoredPasswordQuality(currentQuality, userHandle);
-            throw new RuntimeException("Unable to save lock password", e);
-        }
-
-        updateEncryptionPasswordIfNeeded(password, passwordQuality, userHandle);
-        updatePasswordHistory(password, userHandle);
-        onAfterChangingPassword(userHandle);
-        return true;
-    }
-
-    /**
-     * Compute keyguard credential quality to store in PASSWORD_TYPE_KEY by computing max between
-     * them so that digit-only password is distinguished from PIN.
-     *
-     * TODO: remove this method and make CREDENTIAL_TYPE distinguish between PIN and password, so
-     * that this quality is no longer needs to be persisted.
-     */
-    private int computeKeyguardQuality(
-            @CredentialType int credentialType, int requestedQuality, int passwordQuality) {
-        return credentialType == CREDENTIAL_TYPE_PASSWORD
-                ? Math.max(passwordQuality, requestedQuality) : passwordQuality;
-    }
-
-    /**
      * Update device encryption password if calling user is USER_SYSTEM and device supports
      * encryption.
      */
-    private void updateEncryptionPasswordIfNeeded(byte[] password, int quality, int userHandle) {
+    private void updateEncryptionPasswordIfNeeded(LockscreenCredential credential, int userHandle) {
         // Update the device encryption password.
-        if (userHandle == UserHandle.USER_SYSTEM
-                && LockPatternUtils.isDeviceEncryptionEnabled()) {
-            if (!shouldEncryptWithCredentials(true)) {
-                clearEncryptionPassword();
-            } else {
-                boolean numeric = quality == PASSWORD_QUALITY_NUMERIC;
-                boolean numericComplex = quality == PASSWORD_QUALITY_NUMERIC_COMPLEX;
-                int type = numeric || numericComplex ? StorageManager.CRYPT_TYPE_PIN
-                        : StorageManager.CRYPT_TYPE_PASSWORD;
-                updateEncryptionPassword(type, password);
-            }
+        if (userHandle != UserHandle.USER_SYSTEM || !isDeviceEncryptionEnabled()) {
+            return;
         }
+        if (!shouldEncryptWithCredentials(true)) {
+            updateEncryptionPassword(StorageManager.CRYPT_TYPE_DEFAULT, null);
+            return;
+        }
+        if (credential.isNone()) {
+            // Set the encryption password to default.
+            setCredentialRequiredToDecrypt(false);
+        }
+        updateEncryptionPassword(credential.getStorageCryptType(), credential.getCredential());
     }
 
     /**
      * Store the hash of the *current* password in the password history list, if device policy
      * enforces password history requirement.
      */
-    private void updatePasswordHistory(byte[] password, int userHandle) {
-        if (password == null || password.length == 0) {
-            Log.e(TAG, "checkPasswordHistory: empty password");
+    private void updatePasswordHistory(LockscreenCredential password, int userHandle) {
+        if (password.isNone()) {
+            return;
+        }
+        if (password.isPattern()) {
+            // Do not keep track of historical patterns
             return;
         }
         // Add the password to the password history. We assume all
@@ -1085,10 +852,10 @@
             passwordHistory = "";
         } else {
             final byte[] hashFactor = getPasswordHistoryHashFactor(password, userHandle);
-            String hash = passwordToHistoryHash(password, hashFactor, userHandle);
+            String hash = passwordToHistoryHash(password.getCredential(), hashFactor, userHandle);
             if (hash == null) {
                 Log.e(TAG, "Compute new style password hash failed, fallback to legacy style");
-                hash = legacyPasswordToHash(password, userHandle);
+                hash = legacyPasswordToHash(password.getCredential(), userHandle);
             }
             if (TextUtils.isEmpty(passwordHistory)) {
                 passwordHistory = hash;
@@ -1132,8 +899,8 @@
     }
 
     /**
-     * Retrieves the quality mode for {@param userHandle}.
-     * {@see DevicePolicyManager#getPasswordQuality(android.content.ComponentName)}
+     * Retrieves the quality mode for {@code userHandle}.
+     * @see DevicePolicyManager#getPasswordQuality(android.content.ComponentName)
      *
      * @return stored password quality
      */
@@ -1147,37 +914,37 @@
     }
 
     /**
-     * Enables/disables the Separate Profile Challenge for this {@param userHandle}. This is a no-op
+     * Enables/disables the Separate Profile Challenge for this {@code userHandle}. This is a no-op
      * for user handles that do not belong to a managed profile.
      *
      * @param userHandle Managed profile user id
      * @param enabled True if separate challenge is enabled
-     * @param managedUserPassword Managed profile previous password. Null when {@param enabled} is
+     * @param managedUserPassword Managed profile previous password. Null when {@code enabled} is
      *            true
      */
     public void setSeparateProfileChallengeEnabled(int userHandle, boolean enabled,
-            byte[] managedUserPassword) {
+            LockscreenCredential managedUserPassword) {
         if (!isManagedProfile(userHandle)) {
             return;
         }
         try {
             getLockSettings().setSeparateProfileChallengeEnabled(userHandle, enabled,
-                    managedUserPassword);
-            onAfterChangingPassword(userHandle);
+                    managedUserPassword.getCredential());
+            reportEnabledTrustAgentsChanged(userHandle);
         } catch (RemoteException e) {
             Log.e(TAG, "Couldn't update work profile challenge enabled");
         }
     }
 
     /**
-     * Returns true if {@param userHandle} is a managed profile with separate challenge.
+     * Returns true if {@code userHandle} is a managed profile with separate challenge.
      */
     public boolean isSeparateProfileChallengeEnabled(int userHandle) {
         return isManagedProfile(userHandle) && hasSeparateChallenge(userHandle);
     }
 
     /**
-     * Returns true if {@param userHandle} is a managed profile with unified challenge.
+     * Returns true if {@code userHandle} is a managed profile with unified challenge.
      */
     public boolean isManagedProfileWithUnifiedChallenge(int userHandle) {
         return isManagedProfile(userHandle) && !hasSeparateChallenge(userHandle);
@@ -1218,20 +985,6 @@
 
     /**
      * Deserialize a pattern.
-     * @param string The pattern serialized with {@link #patternToString}
-     * @return The pattern.
-     * @deprecated Pass patterns as byte[] and use byteArrayToPattern
-     */
-    @Deprecated
-    public static List<LockPatternView.Cell> stringToPattern(String string) {
-        if (string == null) {
-            return null;
-        }
-        return byteArrayToPattern(string.getBytes());
-    }
-
-    /**
-     * Deserialize a pattern.
      * @param  bytes The pattern serialized with {@link #patternToByteArray}
      * @return The pattern.
      */
@@ -1252,19 +1005,6 @@
     /**
      * Serialize a pattern.
      * @param pattern The pattern.
-     * @return The pattern in string form.
-     * @deprecated Use patternToByteArray instead.
-     */
-    @UnsupportedAppUsage
-    @Deprecated
-    public static String patternToString(List<LockPatternView.Cell> pattern) {
-        return new String(patternToByteArray(pattern));
-    }
-
-
-    /**
-     * Serialize a pattern.
-     * @param pattern The pattern.
      * @return The pattern in byte array form.
      */
     public static byte[] patternToByteArray(List<LockPatternView.Cell> pattern) {
@@ -1281,34 +1021,6 @@
         return res;
     }
 
-    /*
-     * Generate an SHA-1 hash for the pattern. Not the most secure, but it is
-     * at least a second level of protection. First level is that the file
-     * is in a location only readable by the system process.
-     * @param pattern the gesture pattern.
-     * @return the hash of the pattern in a byte array.
-     */
-    @UnsupportedAppUsage
-    public static byte[] patternToHash(List<LockPatternView.Cell> pattern) {
-        if (pattern == null) {
-            return null;
-        }
-
-        final int patternSize = pattern.size();
-        byte[] res = new byte[patternSize];
-        for (int i = 0; i < patternSize; i++) {
-            LockPatternView.Cell cell = pattern.get(i);
-            res[i] = (byte) (cell.getRow() * 3 + cell.getColumn());
-        }
-        try {
-            MessageDigest md = MessageDigest.getInstance("SHA-1");
-            byte[] hash = md.digest(res);
-            return hash;
-        } catch (NoSuchAlgorithmException nsa) {
-            return res;
-        }
-    }
-
     private String getSalt(int userId) {
         long salt = getLong(LOCK_PASSWORD_SALT_KEY, 0, userId);
         if (salt == 0) {
@@ -1332,6 +1044,7 @@
      * @param password the gesture pattern.
      *
      * @return the hash of the pattern in a byte array.
+     * TODO: move to LockscreenCredential class
      */
     public String legacyPasswordToHash(byte[] password, int userId) {
         if (password == null || password.length == 0) {
@@ -1362,6 +1075,7 @@
 
     /**
      * Hash the password for password history check purpose.
+     * TODO: move to LockscreenCredential class
      */
     private String passwordToHistoryHash(byte[] passwordToHash, byte[] hashFactor, int userId) {
         if (passwordToHash == null || passwordToHash.length == 0 || hashFactor == null) {
@@ -1629,7 +1343,7 @@
     }
 
     /**
-     * Disable trust until credentials have been entered for user {@param userId}.
+     * Disable trust until credentials have been entered for user {@code userId}.
      *
      * Requires the {@link android.Manifest.permission#ACCESS_KEYGUARD_SECURE_STORAGE} permission.
      *
@@ -1640,7 +1354,7 @@
     }
 
     /**
-     * Requests strong authentication for user {@param userId}.
+     * Requests strong authentication for user {@code userId}.
      *
      * Requires the {@link android.Manifest.permission#ACCESS_KEYGUARD_SECURE_STORAGE} permission.
      *
@@ -1657,7 +1371,7 @@
         }
     }
 
-    private void onAfterChangingPassword(int userHandle) {
+    private void reportEnabledTrustAgentsChanged(int userHandle) {
         getTrustManager().reportEnabledTrustAgentsChanged(userHandle);
     }
 
@@ -1823,54 +1537,36 @@
      * <p>This method is only available to code running in the system server process itself.
      *
      * @param credential The new credential to be set
-     * @param type Credential type: password / pattern / none.
-     * @param requestedQuality the requested password quality by DevicePolicyManager.
-     *        See {@link DevicePolicyManager#getPasswordQuality(android.content.ComponentName)}
      * @param tokenHandle Handle of the escrow token
      * @param token Escrow token
-     * @param userId The user who's lock credential to be changed
+     * @param userHandle The user who's lock credential to be changed
      * @return {@code true} if the operation is successful.
      */
-    public boolean setLockCredentialWithToken(byte[] credential, int type, int requestedQuality,
-            long tokenHandle, byte[] token, int userId) {
+    public boolean setLockCredentialWithToken(LockscreenCredential credential, long tokenHandle,
+            byte[] token, int userHandle) {
         if (!hasSecureLockScreen()) {
             throw new UnsupportedOperationException(
                     "This operation requires the lock screen feature.");
         }
+        credential.checkLength();
         LockSettingsInternal localService = getLockSettingsInternal();
-        if (type != CREDENTIAL_TYPE_NONE) {
-            if (credential == null || credential.length < MIN_LOCK_PASSWORD_SIZE) {
-                throw new IllegalArgumentException("password must not be null and at least "
-                        + "of length " + MIN_LOCK_PASSWORD_SIZE);
-            }
-            final int quality = PasswordMetrics.computeForCredential(type, credential).quality;
-            final int keyguardQuality = computeKeyguardQuality(type, quality, requestedQuality);
-            if (!localService.setLockCredentialWithToken(credential, type, tokenHandle, token,
-                    keyguardQuality, userId)) {
+
+        final int currentQuality = getKeyguardStoredPasswordQuality(userHandle);
+        setKeyguardStoredPasswordQuality(credential.getQuality(), userHandle);
+
+        try {
+            if (!localService.setLockCredentialWithToken(credential.getCredential(),
+                    credential.getType(),
+                    tokenHandle, token, credential.getType(), userHandle)) {
+                setKeyguardStoredPasswordQuality(currentQuality, userHandle);
                 return false;
             }
-            setKeyguardStoredPasswordQuality(quality, userId);
-
-            updateEncryptionPasswordIfNeeded(credential, quality, userId);
-            updatePasswordHistory(credential, userId);
-            onAfterChangingPassword(userId);
-        } else {
-            if (!(credential == null || credential.length == 0)) {
-                throw new IllegalArgumentException("password must be emtpy for NONE type");
-            }
-            if (!localService.setLockCredentialWithToken(null, CREDENTIAL_TYPE_NONE, tokenHandle,
-                    token, PASSWORD_QUALITY_UNSPECIFIED, userId)) {
-                return false;
-            }
-            setKeyguardStoredPasswordQuality(PASSWORD_QUALITY_UNSPECIFIED, userId);
-
-            if (userId == UserHandle.USER_SYSTEM) {
-                // Set the encryption password to default.
-                updateEncryptionPassword(StorageManager.CRYPT_TYPE_DEFAULT, null);
-                setCredentialRequiredToDecrypt(false);
-            }
+        } catch (RuntimeException e) {
+            setKeyguardStoredPasswordQuality(currentQuality, userHandle);
+            throw new RuntimeException("Unable to save lock credential", e);
         }
-        onAfterChangingPassword(userId);
+
+        onPostPasswordChanged(credential, userHandle);
         return true;
     }
 
@@ -1997,7 +1693,7 @@
         }
 
         /**
-         * @return true if unlocking with trust alone is allowed for {@param userId} by the current
+         * @return true if unlocking with trust alone is allowed for {@code userId} by the current
          * strong authentication requirements.
          */
         public boolean isTrustAllowedForUser(int userId) {
@@ -2005,7 +1701,7 @@
         }
 
         /**
-         * @return true if unlocking with a biometric method alone is allowed for {@param userId}
+         * @return true if unlocking with a biometric method alone is allowed for {@code userId}
          * by the current strong authentication requirements.
          */
         public boolean isBiometricAllowedForUser(int userId) {
@@ -2013,7 +1709,7 @@
         }
 
         /**
-         * Called when the strong authentication requirements for {@param userId} changed.
+         * Called when the strong authentication requirements for {@code userId} changed.
          */
         public void onStrongAuthRequiredChanged(int userId) {
         }
@@ -2102,22 +1798,4 @@
         return FRP_CREDENTIAL_ENABLED && context.getResources().getBoolean(
                 com.android.internal.R.bool.config_enableCredentialFactoryResetProtection);
     }
-
-    /**
-     * Converts a CharSequence to a byte array without requiring a toString(), which creates an
-     * additional copy.
-     *
-     * @param chars The CharSequence to convert
-     * @return A byte array representing the input
-     */
-    public static byte[] charSequenceToByteArray(CharSequence chars) {
-        if (chars == null) {
-            return null;
-        }
-        byte[] bytes = new byte[chars.length()];
-        for (int i = 0; i < chars.length(); i++) {
-            bytes[i] = (byte) chars.charAt(i);
-        }
-        return bytes;
-    }
 }
diff --git a/core/java/com/android/internal/widget/LockPatternView.java b/core/java/com/android/internal/widget/LockPatternView.java
index 3f6c4d4..74a0aa3 100644
--- a/core/java/com/android/internal/widget/LockPatternView.java
+++ b/core/java/com/android/internal/widget/LockPatternView.java
@@ -1318,7 +1318,7 @@
         super.onRestoreInstanceState(ss.getSuperState());
         setPattern(
                 DisplayMode.Correct,
-                LockPatternUtils.stringToPattern(ss.getSerializedPattern()));
+                LockPatternUtils.byteArrayToPattern(ss.getSerializedPattern().getBytes()));
         mPatternDisplayMode = DisplayMode.values()[ss.getDisplayMode()];
         mInputEnabled = ss.isInputEnabled();
         mInStealthMode = ss.isInStealthMode();
diff --git a/core/java/com/android/internal/widget/LockscreenCredential.java b/core/java/com/android/internal/widget/LockscreenCredential.java
new file mode 100644
index 0000000..19e6d97
--- /dev/null
+++ b/core/java/com/android/internal/widget/LockscreenCredential.java
@@ -0,0 +1,339 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.widget;
+
+import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC;
+import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_NUMERIC;
+import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_SOMETHING;
+import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
+
+import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_NONE;
+import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PASSWORD;
+import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PATTERN;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.storage.StorageManager;
+import android.text.TextUtils;
+
+import com.android.internal.util.Preconditions;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * A class representing a lockscreen credential. It can be either an empty password, a pattern
+ * or a password (or PIN).
+ *
+ * <p> As required by some security certification, the framework tries its best to
+ * remove copies of the lockscreen credential bytes from memory. In this regard, this class
+ * abuses the {@link AutoCloseable} interface for sanitizing memory. This
+ * presents a nice syntax to auto-zeroize memory with the try-with-resource statement:
+ * <pre>
+ * try {LockscreenCredential credential = LockscreenCredential.createPassword(...) {
+ *     // Process the credential in some way
+ * }
+ * </pre>
+ * With this construct, we can garantee that there will be no copies of the password left in
+ * memory when the credential goes out of scope. This should help mitigate certain class of
+ * attacks where the attcker gains read-only access to full device memory (cold boot attack,
+ * unsecured software/hardware memory dumping interfaces such as JTAG).
+ */
+public class LockscreenCredential implements Parcelable, AutoCloseable {
+
+    private final int mType;
+    // Stores raw credential bytes, or null if credential has been zeroized. An empty password
+    // is represented as a byte array of length 0.
+    private byte[] mCredential;
+    // Store the quality of the password, this is used to distinguish between pin
+    // (PASSWORD_QUALITY_NUMERIC) and password (PASSWORD_QUALITY_ALPHABETIC).
+    private final int mQuality;
+
+    /**
+     * Private constructor, use static builder methods instead.
+     *
+     * <p> Builder methods should create a private copy of the credential bytes and pass in here.
+     * LockscreenCredential will only store the reference internally without copying. This is to
+     * minimize the number of extra copies introduced.
+     */
+    private LockscreenCredential(int type, int quality, byte[] credential) {
+        Preconditions.checkNotNull(credential);
+        if (type == CREDENTIAL_TYPE_NONE) {
+            Preconditions.checkArgument(credential.length == 0);
+        } else {
+            Preconditions.checkArgument(credential.length > 0);
+        }
+        mType = type;
+        mQuality = quality;
+        mCredential = credential;
+    }
+
+    /**
+     * Creates a LockscreenCredential object representing empty password.
+     */
+    public static LockscreenCredential createNone() {
+        return new LockscreenCredential(CREDENTIAL_TYPE_NONE, PASSWORD_QUALITY_UNSPECIFIED,
+                new byte[0]);
+    }
+
+    /**
+     * Creates a LockscreenCredential object representing the given pattern.
+     */
+    public static LockscreenCredential createPattern(@NonNull List<LockPatternView.Cell> pattern) {
+        return new LockscreenCredential(CREDENTIAL_TYPE_PATTERN,
+                PASSWORD_QUALITY_SOMETHING,
+                LockPatternUtils.patternToByteArray(pattern));
+    }
+
+    /**
+     * Creates a LockscreenCredential object representing the given alphabetic password.
+     */
+    public static LockscreenCredential createPassword(@NonNull CharSequence password) {
+        return new LockscreenCredential(CREDENTIAL_TYPE_PASSWORD,
+                PASSWORD_QUALITY_ALPHABETIC,
+                charSequenceToByteArray(password));
+    }
+
+    /**
+     * Creates a LockscreenCredential object representing the given numeric PIN.
+     */
+    public static LockscreenCredential createPin(@NonNull CharSequence pin) {
+        return new LockscreenCredential(CREDENTIAL_TYPE_PASSWORD,
+                PASSWORD_QUALITY_NUMERIC,
+                charSequenceToByteArray(pin));
+    }
+
+    /**
+     * Creates a LockscreenCredential object representing the given alphabetic password.
+     * If the supplied password is empty, create an empty credential object.
+     */
+    public static LockscreenCredential createPasswordOrNone(@Nullable CharSequence password) {
+        if (TextUtils.isEmpty(password)) {
+            return createNone();
+        } else {
+            return createPassword(password);
+        }
+    }
+
+    /**
+     * Creates a LockscreenCredential object representing the given numeric PIN.
+     * If the supplied password is empty, create an empty credential object.
+     */
+    public static LockscreenCredential createPinOrNone(@Nullable CharSequence pin) {
+        if (TextUtils.isEmpty(pin)) {
+            return createNone();
+        } else {
+            return createPin(pin);
+        }
+    }
+
+    /**
+     * Create a LockscreenCredential object based on raw credential and type
+     * TODO: Remove once LSS.setUserPasswordMetrics accepts a LockscreenCredential
+     */
+    public static LockscreenCredential createRaw(int type, byte[] credential) {
+        if (type == CREDENTIAL_TYPE_NONE) {
+            return createNone();
+        } else {
+            return new LockscreenCredential(type, PASSWORD_QUALITY_UNSPECIFIED, credential);
+        }
+    }
+
+    private void ensureNotZeroized() {
+        Preconditions.checkState(mCredential != null, "Credential is already zeroized");
+    }
+    /**
+     * Returns the type of this credential. Can be one of {@link #CREDENTIAL_TYPE_NONE},
+     * {@link #CREDENTIAL_TYPE_PATTERN} or {@link #CREDENTIAL_TYPE_PASSWORD}.
+     *
+     * TODO: Remove once credential type is internal. Callers should use {@link #isNone},
+     * {@link #isPattern} and {@link #isPassword} instead.
+     */
+    public int getType() {
+        ensureNotZeroized();
+        return mType;
+    }
+
+    /**
+     * Returns the quality type of the credential
+     */
+    public int getQuality() {
+        ensureNotZeroized();
+        return mQuality;
+    }
+
+    /**
+     * Returns the credential bytes. This is a direct reference of the internal field so
+     * callers should not modify it.
+     *
+     */
+    public byte[] getCredential() {
+        ensureNotZeroized();
+        return mCredential;
+    }
+
+    /**
+     *  Returns the credential type recognized by {@link StorageManager}. Can be one of
+     *  {@link StorageManager#CRYPT_TYPE_DEFAULT}, {@link StorageManager#CRYPT_TYPE_PATTERN},
+     *  {@link StorageManager#CRYPT_TYPE_PIN} or {@link StorageManager#CRYPT_TYPE_PASSWORD}.
+     */
+    public int getStorageCryptType() {
+        if (isNone()) {
+            return StorageManager.CRYPT_TYPE_DEFAULT;
+        }
+        if (isPattern()) {
+            return StorageManager.CRYPT_TYPE_PATTERN;
+        }
+        if (isPassword()) {
+            return mQuality == PASSWORD_QUALITY_NUMERIC
+                    ? StorageManager.CRYPT_TYPE_PIN : StorageManager.CRYPT_TYPE_PASSWORD;
+        }
+        throw new IllegalStateException("Unhandled credential type");
+    }
+
+    /** Returns whether this is an empty credential */
+    public boolean isNone() {
+        ensureNotZeroized();
+        return mType == CREDENTIAL_TYPE_NONE;
+    }
+
+    /** Returns whether this is a pattern credential */
+    public boolean isPattern() {
+        ensureNotZeroized();
+        return mType == CREDENTIAL_TYPE_PATTERN;
+    }
+
+    /** Returns whether this is a password credential */
+    public boolean isPassword() {
+        ensureNotZeroized();
+        return mType == CREDENTIAL_TYPE_PASSWORD;
+    }
+
+    /** Returns the length of the credential */
+    public int size() {
+        ensureNotZeroized();
+        return mCredential.length;
+    }
+
+    /** Create a copy of the credential */
+    public LockscreenCredential duplicate() {
+        return new LockscreenCredential(mType, mQuality,
+                mCredential != null ? Arrays.copyOf(mCredential, mCredential.length) : null);
+    }
+
+    /**
+     * Zeroize the credential bytes.
+     */
+    public void zeroize() {
+        if (mCredential != null) {
+            Arrays.fill(mCredential, (byte) 0);
+            mCredential = null;
+        }
+    }
+
+    /**
+     * Check if the credential meets minimal length requirement.
+     *
+     * @throws IllegalArgumentException if the credential is too short.
+     */
+    public void checkLength() {
+        if (isNone()) {
+            return;
+        }
+        if (isPattern()) {
+            if (size() < LockPatternUtils.MIN_LOCK_PATTERN_SIZE) {
+                throw new IllegalArgumentException("pattern must not be null and at least "
+                        + LockPatternUtils.MIN_LOCK_PATTERN_SIZE + " dots long.");
+            }
+            return;
+        }
+        if (isPassword()) {
+            if (size() < LockPatternUtils.MIN_LOCK_PASSWORD_SIZE) {
+                throw new IllegalArgumentException("password must not be null and at least "
+                        + "of length " + LockPatternUtils.MIN_LOCK_PASSWORD_SIZE);
+            }
+            return;
+        }
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(mType);
+        dest.writeInt(mQuality);
+        dest.writeByteArray(mCredential);
+    }
+
+    public static final Parcelable.Creator<LockscreenCredential> CREATOR =
+            new Parcelable.Creator<LockscreenCredential>() {
+
+        @Override
+        public LockscreenCredential createFromParcel(Parcel source) {
+            return new LockscreenCredential(source.readInt(), source.readInt(),
+                    source.createByteArray());
+        }
+
+        @Override
+        public LockscreenCredential[] newArray(int size) {
+            return new LockscreenCredential[size];
+        }
+    };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void close() {
+        zeroize();
+    }
+
+    @Override
+    public int hashCode() {
+        // Effective Java — Item 9
+        return ((17 + mType) * 31 + mQuality) * 31 + mCredential.hashCode();
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (o == this) return true;
+        if (!(o instanceof LockscreenCredential)) return false;
+        final LockscreenCredential other = (LockscreenCredential) o;
+        return mType == other.mType && mQuality == other.mQuality
+                && Arrays.equals(mCredential, other.mCredential);
+    }
+
+    /**
+     * Converts a CharSequence to a byte array without requiring a toString(), which creates an
+     * additional copy.
+     *
+     * @param chars The CharSequence to convert
+     * @return A byte array representing the input
+     */
+    private static byte[] charSequenceToByteArray(CharSequence chars) {
+        if (chars == null) {
+            return new byte[0];
+        }
+        byte[] bytes = new byte[chars.length()];
+        for (int i = 0; i < chars.length(); i++) {
+            bytes[i] = (byte) chars.charAt(i);
+        }
+        return bytes;
+    }
+}
diff --git a/core/java/com/android/server/SystemConfig.java b/core/java/com/android/server/SystemConfig.java
index 697825d..ea0389f 100644
--- a/core/java/com/android/server/SystemConfig.java
+++ b/core/java/com/android/server/SystemConfig.java
@@ -168,6 +168,10 @@
     // These are the permitted backup transport service components
     final ArraySet<ComponentName> mBackupTransportWhitelist = new ArraySet<>();
 
+    // These are packages mapped to maps of component class name to default enabled state.
+    final ArrayMap<String, ArrayMap<String, Boolean>> mPackageComponentEnabledState =
+            new ArrayMap<>();
+
     // Package names that are exempted from private API blacklisting
     final ArraySet<String> mHiddenApiPackageWhitelist = new ArraySet<>();
 
@@ -301,6 +305,10 @@
         return mBackupTransportWhitelist;
     }
 
+    public ArrayMap<String, Boolean> getComponentsEnabledStates(String packageName) {
+        return mPackageComponentEnabledState.get(packageName);
+    }
+
     public ArraySet<String> getDisabledUntilUsedPreinstalledCarrierApps() {
         return mDisabledUntilUsedPreinstalledCarrierApps;
     }
@@ -846,6 +854,14 @@
                         }
                         XmlUtils.skipCurrentTag(parser);
                     } break;
+                    case "component-override": {
+                        if (allowAppConfigs) {
+                            readComponentOverrides(parser, permFile);
+                        } else {
+                            logNotAllowedInPartition(name, permFile, parser);
+                        }
+                        XmlUtils.skipCurrentTag(parser);
+                    } break;
                     case "backup-transport-whitelisted-service": {
                         if (allowFeatures) {
                             String serviceName = parser.getAttributeValue(null, "service");
@@ -1269,6 +1285,54 @@
         }
     }
 
+    private void readComponentOverrides(XmlPullParser parser, File permFile)
+            throws IOException, XmlPullParserException {
+        String pkgname = parser.getAttributeValue(null, "package");
+        if (pkgname == null) {
+            Slog.w(TAG, "<component-override> without package in "
+                    + permFile + " at " + parser.getPositionDescription());
+            return;
+        }
+
+        pkgname = pkgname.intern();
+
+        final int depth = parser.getDepth();
+        while (XmlUtils.nextElementWithin(parser, depth)) {
+            String name = parser.getName();
+            if ("component".equals(name)) {
+                String clsname = parser.getAttributeValue(null, "class");
+                String enabled = parser.getAttributeValue(null, "enabled");
+                if (clsname == null) {
+                    Slog.w(TAG, "<component> without class in "
+                            + permFile + " at " + parser.getPositionDescription());
+                    return;
+                } else if (enabled == null) {
+                    Slog.w(TAG, "<component> without enabled in "
+                            + permFile + " at " + parser.getPositionDescription());
+                    return;
+                }
+
+                if (clsname.startsWith(".")) {
+                    clsname = pkgname + clsname;
+                }
+
+                clsname = clsname.intern();
+
+                ArrayMap<String, Boolean> componentEnabledStates =
+                        mPackageComponentEnabledState.get(pkgname);
+                if (componentEnabledStates == null) {
+                    componentEnabledStates = new ArrayMap<>();
+                    mPackageComponentEnabledState.put(pkgname,
+                            componentEnabledStates);
+                }
+
+                componentEnabledStates.put(clsname, !"false".equals(enabled));
+            } else {
+                XmlUtils.skipCurrentTag(parser);
+            }
+        }
+    }
+
     private static boolean isSystemProcess() {
         return Process.myUid() == Process.SYSTEM_UID;
     }
diff --git a/core/jni/android_os_Debug.cpp b/core/jni/android_os_Debug.cpp
index 9445319..d62d2d9 100644
--- a/core/jni/android_os_Debug.cpp
+++ b/core/jni/android_os_Debug.cpp
@@ -139,8 +139,8 @@
         "nativePrivateClean", "nativeSharedClean", "nativeSwappedOut", "nativeSwappedOutPss" }
 };
 
-jfieldID otherStats_field;
-jfieldID hasSwappedOutPss_field;
+static jfieldID otherStats_field;
+static jfieldID hasSwappedOutPss_field;
 
 struct stats_t {
     int pss;
diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp
index af47cb6..c7b36d0 100644
--- a/core/jni/android_util_AssetManager.cpp
+++ b/core/jni/android_util_AssetManager.cpp
@@ -108,7 +108,7 @@
   jmethodID put;
 } gArrayMapOffsets;
 
-jclass g_stringClass = nullptr;
+static jclass g_stringClass = nullptr;
 
 // ----------------------------------------------------------------------------
 
diff --git a/core/jni/android_util_Binder.cpp b/core/jni/android_util_Binder.cpp
index e406e22..2232393 100644
--- a/core/jni/android_util_Binder.cpp
+++ b/core/jni/android_util_Binder.cpp
@@ -992,6 +992,31 @@
     return IPCThreadState::self()->blockUntilThreadAvailable();
 }
 
+static jobject android_os_Binder_waitForService(
+        JNIEnv *env,
+        jclass /* clazzObj */,
+        jstring serviceNameObj) {
+
+    const jchar* serviceName = env->GetStringCritical(serviceNameObj, nullptr);
+    if (!serviceName) {
+        signalExceptionForError(env, nullptr, BAD_VALUE, true /*canThrowRemoteException*/);
+        return nullptr;
+    }
+    String16 nameCopy = String16(reinterpret_cast<const char16_t *>(serviceName),
+            env->GetStringLength(serviceNameObj));
+    env->ReleaseStringCritical(serviceNameObj, serviceName);
+
+    auto sm = android::defaultServiceManager();
+    sp<IBinder> service = sm->waitForService(nameCopy);
+
+    if (!service) {
+        signalExceptionForError(env, nullptr, NAME_NOT_FOUND, true /*canThrowRemoteException*/);
+        return nullptr;
+    }
+
+    return javaObjectForIBinder(env, service);
+}
+
 // ----------------------------------------------------------------------------
 
 static const JNINativeMethod gBinderMethods[] = {
@@ -1019,7 +1044,8 @@
     { "flushPendingCommands", "()V", (void*)android_os_Binder_flushPendingCommands },
     { "getNativeBBinderHolder", "()J", (void*)android_os_Binder_getNativeBBinderHolder },
     { "getNativeFinalizer", "()J", (void*)android_os_Binder_getNativeFinalizer },
-    { "blockUntilThreadAvailable", "()V", (void*)android_os_Binder_blockUntilThreadAvailable }
+    { "blockUntilThreadAvailable", "()V", (void*)android_os_Binder_blockUntilThreadAvailable },
+    { "waitForService", "(Ljava/lang/String;)Landroid/os/IBinder;", (void*)android_os_Binder_waitForService }
 };
 
 const char* const kBinderPathName = "android/os/Binder";
diff --git a/core/jni/android_view_InputChannel.cpp b/core/jni/android_view_InputChannel.cpp
index af34e7b7..bf1cea8 100644
--- a/core/jni/android_view_InputChannel.cpp
+++ b/core/jni/android_view_InputChannel.cpp
@@ -112,7 +112,9 @@
 }
 
 static jobject android_view_InputChannel_createInputChannel(JNIEnv* env,
-        std::unique_ptr<NativeInputChannel> nativeInputChannel) {
+        sp<InputChannel> inputChannel) {
+    std::unique_ptr<NativeInputChannel> nativeInputChannel =
+            std::make_unique<NativeInputChannel>(inputChannel);
     jobject inputChannelObj = env->NewObject(gInputChannelClassInfo.clazz,
             gInputChannelClassInfo.ctor);
     if (inputChannelObj) {
@@ -143,14 +145,12 @@
         return nullptr;
     }
 
-    jobject serverChannelObj = android_view_InputChannel_createInputChannel(env,
-            std::make_unique<NativeInputChannel>(serverChannel));
+    jobject serverChannelObj = android_view_InputChannel_createInputChannel(env, serverChannel);
     if (env->ExceptionCheck()) {
         return nullptr;
     }
 
-    jobject clientChannelObj = android_view_InputChannel_createInputChannel(env,
-            std::make_unique<NativeInputChannel>(clientChannel));
+    jobject clientChannelObj = android_view_InputChannel_createInputChannel(env, clientChannel);
     if (env->ExceptionCheck()) {
         return nullptr;
     }
diff --git a/core/proto/android/service/procstats.proto b/core/proto/android/service/procstats.proto
index f49a044..ad7299d 100644
--- a/core/proto/android/service/procstats.proto
+++ b/core/proto/android/service/procstats.proto
@@ -241,12 +241,25 @@
     repeated StateStats active_state_stats = 6;
 }
 
-// Next Tag: 3
+// Next Tag: 7
 message PackageAssociationProcessStatsProto {
     option (android.msg_privacy).dest = DEST_AUTOMATIC;
 
     // Name of the target component.
     optional string component_name = 1;
+
+    // Total count of the times this association appeared.
+    optional int32 total_count = 3;
+
+    // Millisecond uptime total duration this association was around.
+    optional int64 total_duration_ms = 4;
+
+    // Total count of the times this association became actively impacting its target process.
+    optional int32 active_count = 5;
+
+    // Millisecond uptime total duration this association was around.
+    optional int64 active_duration_ms = 6;
+
     // Information on one source in this association.
     repeated PackageAssociationSourceProcessStatsProto sources = 2;
 }
diff --git a/core/proto/android/stats/devicepolicy/device_policy_enums.proto b/core/proto/android/stats/devicepolicy/device_policy_enums.proto
index 0821d14..15813a1 100644
--- a/core/proto/android/stats/devicepolicy/device_policy_enums.proto
+++ b/core/proto/android/stats/devicepolicy/device_policy_enums.proto
@@ -113,9 +113,9 @@
   PROVISIONING_PREPROVISIONING_ACTIVITY_TIME_MS = 87;
   PROVISIONING_ENCRYPT_DEVICE_ACTIVITY_TIME_MS = 88;
   PROVISIONING_WEB_ACTIVITY_TIME_MS = 89;
-  PROVISIONING_TRAMPOLINE_ACTIVITY_TIME_MS = 90;
-  PROVISIONING_POST_ENCRYPTION_ACTIVITY_TIME_MS = 91;
-  PROVISIONING_FINALIZATION_ACTIVITY_TIME_MS = 92;
+  PROVISIONING_TRAMPOLINE_ACTIVITY_TIME_MS = 90 [deprecated=true];
+  PROVISIONING_POST_ENCRYPTION_ACTIVITY_TIME_MS = 91 [deprecated=true];
+  PROVISIONING_FINALIZATION_ACTIVITY_TIME_MS = 92 [deprecated=true];
   PROVISIONING_NETWORK_TYPE = 93;
   PROVISIONING_ACTION = 94;
   PROVISIONING_EXTRAS = 95;
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index b030b33..5c1e13b 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1624,7 +1624,7 @@
          @hide This should only be used by Settings and SystemUI.
     -->
     <permission android:name="android.permission.NETWORK_SETTINGS"
-        android:protectionLevel="signature" />
+        android:protectionLevel="signature|telephony" />
 
     <!-- Allows SetupWizard to call methods in Networking services
          <p>Not for use by any other third-party or privileged applications.
@@ -2138,12 +2138,12 @@
 
     <!-- Must be required by a telephony data service to ensure that only the
          system can bind to it.
-         <p>Protection level: signature
+         <p>Protection level: signature|telephony
          @SystemApi
          @hide
     -->
     <permission android:name="android.permission.BIND_TELEPHONY_DATA_SERVICE"
-        android:protectionLevel="signature" />
+        android:protectionLevel="signature|telephony" />
 
     <!-- Must be required by a NetworkService to ensure that only the
          system can bind to it.
@@ -2164,11 +2164,11 @@
 
     <!-- @SystemApi Must be required by an EuiccService to ensure that only the system can bind to
          it.
-         <p>Protection level: signature
+         <p>Protection level: signature|telephony
          @hide
     -->
     <permission android:name="android.permission.BIND_EUICC_SERVICE"
-                android:protectionLevel="signature" />
+                android:protectionLevel="signature|telephony" />
 
     <!-- ================================== -->
     <!-- Permissions for sdcard interaction -->
@@ -2955,7 +2955,7 @@
          @hide
     -->
     <permission android:name="android.permission.INTERNAL_SYSTEM_WINDOW"
-        android:protectionLevel="signature" />
+        android:protectionLevel="signature|telephony|wifi" />
 
     <!-- @SystemApi Allows an application to use
          {@link android.view.WindowManager.LayoutsParams#SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS}
@@ -3740,7 +3740,7 @@
         @hide
     -->
    <permission android:name="android.permission.DEVICE_POWER"
-        android:protectionLevel="signature" />
+        android:protectionLevel="signature|telephony" />
 
     <!-- Allows toggling battery saver on the system.
          Superseded by DEVICE_POWER permission. @hide @SystemApi
@@ -3775,13 +3775,13 @@
          <p>Not for use by third-party applications.
     -->
     <permission android:name="android.permission.BROADCAST_SMS"
-        android:protectionLevel="signature" />
+        android:protectionLevel="signature|telephony" />
 
     <!-- Allows an application to broadcast a WAP PUSH receipt notification.
          <p>Not for use by third-party applications.
     -->
     <permission android:name="android.permission.BROADCAST_WAP_PUSH"
-        android:protectionLevel="signature" />
+        android:protectionLevel="signature|telephony" />
 
     <!-- @SystemApi Allows an application to broadcast privileged networking requests.
          <p>Not for use by third-party applications.
@@ -4396,13 +4396,13 @@
          {@link android.provider.BlockedNumberContract}.
          @hide -->
     <permission android:name="android.permission.READ_BLOCKED_NUMBERS"
-                android:protectionLevel="signature" />
+                android:protectionLevel="signature|telephony" />
 
     <!-- Allows the holder to write blocked numbers. See
          {@link android.provider.BlockedNumberContract}.
          @hide -->
     <permission android:name="android.permission.WRITE_BLOCKED_NUMBERS"
-                android:protectionLevel="signature" />
+                android:protectionLevel="signature|telephony" />
 
     <!-- Must be required by an {@link android.service.vr.VrListenerService}, to ensure that only
          the system can bind to it.
diff --git a/core/res/res/layout/resolve_list_item.xml b/core/res/res/layout/resolve_list_item.xml
index 0bdb25a..4857095 100644
--- a/core/res/res/layout/resolve_list_item.xml
+++ b/core/res/res/layout/resolve_list_item.xml
@@ -22,8 +22,6 @@
               android:layout_height="wrap_content"
               android:layout_width="match_parent"
               android:minHeight="?attr/listPreferredItemHeightSmall"
-              android:paddingTop="4dp"
-              android:paddingBottom="4dp"
               android:background="?attr/activatedBackgroundIndicator">
 
     <!-- Activity icon when presenting dialog
@@ -32,7 +30,8 @@
                android:layout_width="@dimen/resolver_icon_size"
                android:layout_height="@dimen/resolver_icon_size"
                android:layout_gravity="start|center_vertical"
-               android:layout_marginStart="?attr/listPreferredItemPaddingStart"
+               android:layout_marginStart="@dimen/resolver_icon_margin"
+               android:layout_marginEnd="@dimen/resolver_icon_margin"
                android:layout_marginTop="12dp"
                android:layout_marginBottom="12dp"
                android:scaleType="fitCenter" />
@@ -40,8 +39,7 @@
     <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
               android:gravity="start|center_vertical"
               android:orientation="vertical"
-              android:paddingStart="?attr/listPreferredItemPaddingStart"
-              android:paddingEnd="?attr/listPreferredItemPaddingEnd"
+              android:paddingEnd="@dimen/resolver_edge_margin"
               android:layout_height="wrap_content"
               android:layout_width="wrap_content"
               android:layout_gravity="start|center_vertical">
@@ -49,14 +47,20 @@
         <TextView android:id="@android:id/text1"
                   android:layout_width="wrap_content"
                   android:layout_height="wrap_content"
-                  android:textAppearance="?attr/textAppearanceMedium"
-                  android:textColor="?attr/textColorPrimary"
+                  android:layout_gravity="start|center_vertical"
+                  android:textColor="?android:attr/textColorPrimary"
+                  android:fontFamily="@android:string/config_bodyFontFamily"
+                  android:textSize="16sp"
                   android:minLines="1"
                   android:maxLines="1"
                   android:ellipsize="marquee" />
         <!-- Extended activity info to distinguish between duplicate activity names -->
         <TextView android:id="@android:id/text2"
-                  android:textAppearance="?android:attr/textAppearanceSmall"
+                  android:textColor="?android:attr/textColorSecondary"
+                  android:fontFamily="@android:string/config_bodyFontFamily"
+                  android:layout_gravity="start|center_vertical"
+                  android:textSize="14sp"
+                  android:visibility="gone"
                   android:layout_width="wrap_content"
                   android:layout_height="wrap_content"
                   android:minLines="1"
diff --git a/core/res/res/layout/resolver_different_item_header.xml b/core/res/res/layout/resolver_different_item_header.xml
index 7d9ffd7..0a35edc 100644
--- a/core/res/res/layout/resolver_different_item_header.xml
+++ b/core/res/res/layout/resolver_different_item_header.xml
@@ -22,12 +22,12 @@
     android:layout_height="wrap_content"
     android:layout_alwaysShow="true"
     android:text="@string/use_a_different_app"
-    android:minHeight="56dp"
-    android:textAppearance="?android:attr/textAppearanceMedium"
+    android:textColor="?android:attr/textColorPrimary"
+    android:fontFamily="@android:string/config_headlineFontFamilyMedium"
+    android:textSize="16sp"
     android:gravity="start|center_vertical"
-    android:paddingStart="16dp"
-    android:paddingEnd="16dp"
-    android:paddingTop="8dp"
-    android:paddingBottom="8dp"
-    android:elevation="8dp"
-    />
+    android:paddingStart="@dimen/resolver_edge_margin"
+    android:paddingEnd="@dimen/resolver_edge_margin"
+    android:paddingTop="@dimen/resolver_small_margin"
+    android:paddingBottom="@dimen/resolver_edge_margin"
+    android:elevation="1dp" />
diff --git a/core/res/res/layout/resolver_list.xml b/core/res/res/layout/resolver_list.xml
index 1dd4207..6e45e7a 100644
--- a/core/res/res/layout/resolver_list.xml
+++ b/core/res/res/layout/resolver_list.xml
@@ -29,16 +29,18 @@
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:layout_alwaysShow="true"
-        android:elevation="8dp"
-        android:background="?attr/colorBackgroundFloating">
+        android:elevation="@dimen/resolver_elevation"
+        android:paddingTop="@dimen/resolver_small_margin"
+        android:paddingStart="@dimen/resolver_edge_margin"
+        android:paddingEnd="@dimen/resolver_edge_margin"
+        android:paddingBottom="@dimen/resolver_edge_margin"
+        android:background="@drawable/bottomsheet_background">
 
         <TextView
             android:id="@+id/profile_button"
             android:layout_width="wrap_content"
             android:layout_height="48dp"
             android:layout_marginEnd="8dp"
-            android:paddingStart="8dp"
-            android:paddingEnd="8dp"
             android:visibility="gone"
             style="?attr/borderlessButtonStyle"
             android:textAppearance="?attr/textAppearanceButton"
@@ -50,36 +52,49 @@
 
         <TextView
             android:id="@+id/title"
-            android:layout_width="wrap_content"
+            android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            android:minHeight="56dp"
-            android:textAppearance="?attr/textAppearanceMedium"
-            android:gravity="start|center_vertical"
-            android:paddingStart="?attr/dialogPreferredPadding"
-            android:paddingEnd="?attr/dialogPreferredPadding"
-            android:paddingTop="8dp"
             android:layout_below="@id/profile_button"
             android:layout_alignParentStart="true"
-            android:paddingBottom="8dp" />
+            android:textColor="?android:attr/textColorPrimary"
+            android:fontFamily="@android:string/config_headlineFontFamilyMedium"
+            android:textSize="16sp"
+            android:gravity="start|center_vertical" />
     </RelativeLayout>
 
+    <View
+        android:layout_alwaysShow="true"
+        android:layout_width="match_parent"
+        android:layout_height="1dp"
+        android:background="?attr/colorBackgroundFloating"
+        android:foreground="?attr/dividerVertical" />
     <ListView
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:id="@+id/resolver_list"
         android:clipToPadding="false"
-        android:scrollbarStyle="outsideOverlay"
         android:background="?attr/colorBackgroundFloating"
-        android:elevation="8dp"
+        android:elevation="@dimen/resolver_elevation"
         android:nestedScrollingEnabled="true"
+        android:scrollbarStyle="outsideOverlay"
         android:scrollIndicators="top|bottom"
-        android:divider="@null" />
+        android:divider="?attr/dividerVertical"
+        android:footerDividersEnabled="false"
+        android:headerDividersEnabled="false"
+        android:dividerHeight="1dp" />
+    <View
+        android:layout_alwaysShow="true"
+        android:layout_width="match_parent"
+        android:layout_height="1dp"
+        android:background="?attr/colorBackgroundFloating"
+        android:foreground="?attr/dividerVertical" />
+
 
     <TextView android:id="@+id/empty"
               android:layout_width="match_parent"
               android:layout_height="wrap_content"
               android:background="?attr/colorBackgroundFloating"
-              android:elevation="8dp"
+              android:elevation="@dimen/resolver_elevation"
               android:layout_alwaysShow="true"
               android:text="@string/noApplications"
               android:padding="32dp"
@@ -102,18 +117,19 @@
         android:background="?attr/colorBackgroundFloating"
         android:paddingTop="@dimen/resolver_button_bar_spacing"
         android:paddingBottom="@dimen/resolver_button_bar_spacing"
-        android:paddingStart="12dp"
-        android:paddingEnd="12dp"
-        android:elevation="8dp">
+        android:paddingStart="@dimen/resolver_edge_margin"
+        android:paddingEnd="@dimen/resolver_small_margin"
+        android:elevation="@dimen/resolver_elevation">
 
         <Button
             android:id="@+id/button_once"
             android:layout_width="wrap_content"
             android:layout_gravity="start"
             android:maxLines="2"
-            style="?attr/buttonBarNegativeButtonStyle"
-            android:minHeight="@dimen/alert_dialog_button_bar_height"
+            style="?attr/buttonBarButtonStyle"
+            android:fontFamily="@android:string/config_headlineFontFamilyMedium"
             android:layout_height="wrap_content"
+            android:textAllCaps="false"
             android:enabled="false"
             android:text="@string/activity_resolver_use_once"
             android:onClick="onButtonClick" />
@@ -123,8 +139,9 @@
             android:layout_width="wrap_content"
             android:layout_gravity="end"
             android:maxLines="2"
-            android:minHeight="@dimen/alert_dialog_button_bar_height"
-            style="?attr/buttonBarPositiveButtonStyle"
+            style="?attr/buttonBarButtonStyle"
+            android:fontFamily="@android:string/config_headlineFontFamilyMedium"
+            android:textAllCaps="false"
             android:layout_height="wrap_content"
             android:enabled="false"
             android:text="@string/activity_resolver_use_always"
diff --git a/core/res/res/layout/resolver_list_with_default.xml b/core/res/res/layout/resolver_list_with_default.xml
index 740a7eb..dbba0b7 100644
--- a/core/res/res/layout/resolver_list_with_default.xml
+++ b/core/res/res/layout/resolver_list_with_default.xml
@@ -29,22 +29,22 @@
         android:layout_height="wrap_content"
         android:layout_alwaysShow="true"
         android:orientation="vertical"
-        android:background="?attr/colorBackgroundFloating"
-        android:elevation="8dp">
+        android:background="@drawable/bottomsheet_background"
+        android:paddingTop="@dimen/resolver_small_margin"
+        android:elevation="@dimen/resolver_elevation">
 
         <LinearLayout
             android:layout_width="match_parent"
-            android:layout_height="64dp"
-            android:orientation="horizontal">
-
+            android:layout_height="wrap_content"
+            android:orientation="horizontal"
+            android:paddingBottom="@dimen/resolver_edge_margin"
+            android:paddingEnd="@dimen/resolver_edge_margin">
             <ImageView
                 android:id="@+id/icon"
-                android:layout_width="24dp"
-                android:layout_height="24dp"
+                android:layout_width="@dimen/resolver_icon_size"
+                android:layout_height="@dimen/resolver_icon_size"
                 android:layout_gravity="start|top"
-                android:layout_marginStart="16dp"
-                android:layout_marginEnd="16dp"
-                android:layout_marginTop="20dp"
+                android:layout_marginStart="@dimen/resolver_icon_margin"
                 android:src="@drawable/resolver_icon_placeholder"
                 android:scaleType="fitCenter" />
 
@@ -52,9 +52,11 @@
                 android:id="@+id/title"
                 android:layout_width="0dp"
                 android:layout_weight="1"
-                android:layout_height="?attr/listPreferredItemHeight"
-                android:layout_marginStart="16dp"
-                android:textAppearance="?attr/textAppearanceMedium"
+                android:layout_height="wrap_content"
+                android:layout_marginStart="@dimen/resolver_icon_margin"
+                android:textColor="?android:attr/textColorPrimary"
+                android:fontFamily="@android:string/config_headlineFontFamilyMedium"
+                android:textSize="16sp"
                 android:gravity="start|center_vertical"
                 android:paddingEnd="16dp" />
 
@@ -107,21 +109,22 @@
             android:orientation="horizontal"
             android:layoutDirection="locale"
             android:measureWithLargestChild="true"
-            android:paddingTop="8dp"
-            android:paddingBottom="8dp"
-            android:paddingStart="12dp"
-            android:paddingEnd="12dp"
-            android:elevation="8dp">
+            android:paddingTop="@dimen/resolver_button_bar_spacing"
+            android:paddingBottom="@dimen/resolver_button_bar_spacing"
+            android:paddingStart="@dimen/resolver_edge_margin"
+            android:paddingEnd="@dimen/resolver_small_margin"
+            android:elevation="@dimen/resolver_elevation">
 
             <Button
                 android:id="@+id/button_once"
                 android:layout_width="wrap_content"
                 android:layout_gravity="start"
                 android:maxLines="2"
-                style="?attr/buttonBarNegativeButtonStyle"
-                android:minHeight="@dimen/alert_dialog_button_bar_height"
+                style="?attr/buttonBarButtonStyle"
+                android:fontFamily="@android:string/config_headlineFontFamilyMedium"
                 android:layout_height="wrap_content"
                 android:enabled="false"
+                android:textAllCaps="false"
                 android:text="@string/activity_resolver_use_once"
                 android:onClick="onButtonClick" />
 
@@ -130,29 +133,40 @@
                 android:layout_width="wrap_content"
                 android:layout_gravity="end"
                 android:maxLines="2"
-                android:minHeight="@dimen/alert_dialog_button_bar_height"
-                style="?attr/buttonBarPositiveButtonStyle"
+                style="?attr/buttonBarButtonStyle"
+                android:fontFamily="@android:string/config_headlineFontFamilyMedium"
                 android:layout_height="wrap_content"
                 android:enabled="false"
+                android:textAllCaps="false"
                 android:text="@string/activity_resolver_use_always"
                 android:onClick="onButtonClick" />
         </LinearLayout>
-
-        <View
-            android:layout_width="match_parent"
-            android:layout_height="1dp"
-            android:background="?attr/dividerVertical" />
     </LinearLayout>
 
+    <View
+        android:layout_alwaysShow="true"
+        android:layout_width="match_parent"
+        android:layout_height="1dp"
+        android:background="?attr/colorBackgroundFloating"
+        android:foreground="?attr/dividerVertical" />
     <ListView
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:id="@+id/resolver_list"
         android:clipToPadding="false"
-        android:scrollbarStyle="outsideOverlay"
         android:background="?attr/colorBackgroundFloating"
-        android:elevation="8dp"
+        android:elevation="@dimen/resolver_elevation"
         android:nestedScrollingEnabled="true"
-        android:divider="@null" />
-
+        android:scrollbarStyle="outsideOverlay"
+        android:scrollIndicators="top|bottom"
+        android:divider="?attr/dividerVertical"
+        android:footerDividersEnabled="false"
+        android:headerDividersEnabled="false"
+        android:dividerHeight="1dp" />
+    <View
+        android:layout_alwaysShow="true"
+        android:layout_width="match_parent"
+        android:layout_height="1dp"
+        android:background="?attr/colorBackgroundFloating"
+        android:foreground="?attr/dividerVertical" />
 </com.android.internal.widget.ResolverDrawerLayout>
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index a301702..ffcfe43 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -295,6 +295,12 @@
         <!-- Additional flag from base permission type: this permission can be automatically
             granted to the system app predictor -->
         <flag name="appPredictor" value="0x200000" />
+        <!-- Additional flag from base permission type: this permission can be automatically
+            granted to the system telephony apps -->
+        <flag name="telephony" value="0x400000" />
+        <!-- Additional flag from base permission type: this permission can be automatically
+            granted to the system wifi app-->
+        <flag name="wifi" value="0x800000" />
     </attr>
 
     <!-- Flags indicating more context for a permission group. -->
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 3fef7a2d..5605246 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -3687,6 +3687,21 @@
      -->
     <string name="config_defaultWellbeingPackage" translatable="false"></string>
 
+    <!-- The package name for the system telephony apps.
+         This package must be trusted, as it will be granted with permissions with special telephony
+         protection level. Note, framework by default support multiple telephony apps, each package
+         name is separated by comma.
+         Example: "com.android.phone,com.android.stk,com.android.providers.telephony"
+     -->
+    <string name="config_telephonyPackages" translatable="false">"com.android.phone,com.android.stk,com.android.providers.telephony,com.android.ons"</string>
+
+    <!-- The package name for the default system wifi app.
+         This package must be trusted, as it has the permissions to control wifi
+         connectivity on the device.
+         Example: "com.android.wifi"
+     -->
+    <string name="config_wifiPackage" translatable="false">"com.android.wifi"</string>
+
     <!-- The component name for the default system attention service.
          This service must be trusted, as it can be activated without explicit consent of the user.
          See android.attention.AttentionManagerService.
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 609659b..a01bbe3 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -750,7 +750,7 @@
 
     <dimen name="seekbar_thumb_exclusion_max_size">48dp</dimen>
 
-    <!-- chooser (sharesheet) spacing -->
+    <!-- chooser/resolver (sharesheet) spacing -->
     <dimen name="chooser_corner_radius">8dp</dimen>
     <dimen name="chooser_row_text_option_translate">25dp</dimen>
     <dimen name="chooser_view_spacing">18dp</dimen>
@@ -759,11 +759,15 @@
     <dimen name="chooser_preview_image_font_size">20sp</dimen>
     <dimen name="chooser_preview_image_border">1dp</dimen>
     <dimen name="chooser_preview_width">-1px</dimen>
-    <dimen name="resolver_icon_size">42dp</dimen>
-    <dimen name="resolver_button_bar_spacing">8dp</dimen>
-    <dimen name="resolver_badge_size">18dp</dimen>
     <dimen name="chooser_target_width">90dp</dimen>
     <dimen name="chooser_header_scroll_elevation">4dp</dimen>
     <dimen name="chooser_max_collapsed_height">288dp</dimen>
     <dimen name="chooser_direct_share_label_placeholder_max_width">72dp</dimen>
+    <dimen name="resolver_icon_size">32dp</dimen>
+    <dimen name="resolver_button_bar_spacing">8dp</dimen>
+    <dimen name="resolver_badge_size">18dp</dimen>
+    <dimen name="resolver_icon_margin">16dp</dimen>
+    <dimen name="resolver_small_margin">18dp</dimen>
+    <dimen name="resolver_edge_margin">24dp</dimen>
+    <dimen name="resolver_elevation">1dp</dimen>
 </resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 363bc9d..42cd2cd 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3467,6 +3467,8 @@
   <java-symbol type="string" name="config_defaultAutofillService" />
   <java-symbol type="string" name="config_defaultTextClassifierPackage" />
   <java-symbol type="string" name="config_defaultWellbeingPackage" />
+  <java-symbol type="string" name="config_telephonyPackages" />
+  <java-symbol type="string" name="config_wifiPackage" />
   <java-symbol type="string" name="config_defaultContentCaptureService" />
   <java-symbol type="string" name="config_defaultAugmentedAutofillService" />
   <java-symbol type="string" name="config_defaultAppPredictionService" />
@@ -3819,6 +3821,10 @@
   <java-symbol type="dimen" name="resolver_icon_size"/>
   <java-symbol type="dimen" name="resolver_badge_size"/>
   <java-symbol type="dimen" name="resolver_button_bar_spacing"/>
+  <java-symbol type="dimen" name="resolver_icon_margin"/>
+  <java-symbol type="dimen" name="resolver_small_margin"/>
+  <java-symbol type="dimen" name="resolver_edge_margin"/>
+  <java-symbol type="dimen" name="resolver_elevation"/>
 
   <!-- For DropBox -->
   <java-symbol type="integer" name="config_dropboxLowPriorityBroadcastRateLimitPeriod" />
diff --git a/core/tests/coretests/src/android/util/TimestampedValueTest.java b/core/tests/coretests/src/android/util/TimestampedValueTest.java
index 6e3ab79..6fc2400 100644
--- a/core/tests/coretests/src/android/util/TimestampedValueTest.java
+++ b/core/tests/coretests/src/android/util/TimestampedValueTest.java
@@ -55,12 +55,12 @@
         TimestampedValue<String> stringValue = new TimestampedValue<>(1000, "Hello");
         Parcel parcel = Parcel.obtain();
         try {
-            TimestampedValue.writeToParcel(parcel, stringValue);
+            parcel.writeParcelable(stringValue, 0);
 
             parcel.setDataPosition(0);
 
             TimestampedValue<String> stringValueCopy =
-                    TimestampedValue.readFromParcel(parcel, null /* classLoader */, String.class);
+                    parcel.readParcelable(null /* classLoader */);
             assertEquals(stringValue, stringValueCopy);
         } finally {
             parcel.recycle();
@@ -72,12 +72,12 @@
         TimestampedValue<String> stringValue = new TimestampedValue<>(1000, "Hello");
         Parcel parcel = Parcel.obtain();
         try {
-            TimestampedValue.writeToParcel(parcel, stringValue);
+            parcel.writeParcelable(stringValue, 0);
 
             parcel.setDataPosition(0);
 
-            TimestampedValue<Object> stringValueCopy =
-                    TimestampedValue.readFromParcel(parcel, null /* classLoader */, Object.class);
+            TimestampedValue<String> stringValueCopy =
+                    parcel.readParcelable(null /* classLoader */);
             assertEquals(stringValue, stringValueCopy);
         } finally {
             parcel.recycle();
@@ -85,15 +85,15 @@
     }
 
     @Test
-    public void testParceling_valueClassIncompatible() {
-        TimestampedValue<String> stringValue = new TimestampedValue<>(1000, "Hello");
+    public void testParceling_valueClassNotParcelable() {
+        // This class is not one supported by Parcel.writeValue().
+        class NotParcelable {}
+
+        TimestampedValue<NotParcelable> notParcelableValue =
+                new TimestampedValue<>(1000, new NotParcelable());
         Parcel parcel = Parcel.obtain();
         try {
-            TimestampedValue.writeToParcel(parcel, stringValue);
-
-            parcel.setDataPosition(0);
-
-            TimestampedValue.readFromParcel(parcel, null /* classLoader */, Double.class);
+            parcel.writeParcelable(notParcelableValue, 0);
             fail();
         } catch (RuntimeException expected) {
         } finally {
@@ -106,12 +106,11 @@
         TimestampedValue<String> nullValue = new TimestampedValue<>(1000, null);
         Parcel parcel = Parcel.obtain();
         try {
-            TimestampedValue.writeToParcel(parcel, nullValue);
+            parcel.writeParcelable(nullValue, 0);
 
             parcel.setDataPosition(0);
 
-            TimestampedValue<Object> nullValueCopy =
-                    TimestampedValue.readFromParcel(parcel, null /* classLoader */, String.class);
+            TimestampedValue<String> nullValueCopy = parcel.readParcelable(null /* classLoader */);
             assertEquals(nullValue, nullValueCopy);
         } finally {
             parcel.recycle();
diff --git a/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java b/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java
index abee1da2..7b405434 100644
--- a/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java
+++ b/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java
@@ -345,9 +345,9 @@
         accessibilityShortcutController.performAccessibilityShortcut();
         accessibilityShortcutController.performAccessibilityShortcut();
         verify(mToast).show();
-        assertEquals(WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS,
+        assertEquals(WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS,
                 mLayoutParams.privateFlags
-                        & WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS);
+                        & WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS);
         verify(mAccessibilityManagerService, times(1)).performAccessibilityShortcut();
     }
 
diff --git a/core/tests/coretests/src/com/android/internal/widget/LockscreenCredentialTest.java b/core/tests/coretests/src/com/android/internal/widget/LockscreenCredentialTest.java
new file mode 100644
index 0000000..5eec91c
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/widget/LockscreenCredentialTest.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.widget;
+
+
+import android.test.AndroidTestCase;
+
+import java.util.Arrays;
+
+
+public class LockscreenCredentialTest extends AndroidTestCase {
+
+    public void testEmptyCredential() {
+        LockscreenCredential empty = LockscreenCredential.createNone();
+
+        assertTrue(empty.isNone());
+        assertEquals(0, empty.size());
+        assertNotNull(empty.getCredential());
+
+        assertFalse(empty.isPassword());
+        assertFalse(empty.isPattern());
+    }
+
+    public void testPasswordCredential() {
+        LockscreenCredential password = LockscreenCredential.createPassword("password");
+
+        assertTrue(password.isPassword());
+        assertEquals(8, password.size());
+        assertTrue(Arrays.equals("password".getBytes(), password.getCredential()));
+
+        assertFalse(password.isNone());
+        assertFalse(password.isPattern());
+    }
+
+    public void testPatternCredential() {
+        LockscreenCredential pattern = LockscreenCredential.createPattern(Arrays.asList(
+                LockPatternView.Cell.of(0, 0),
+                LockPatternView.Cell.of(0, 1),
+                LockPatternView.Cell.of(0, 2),
+                LockPatternView.Cell.of(1, 2),
+                LockPatternView.Cell.of(2, 2)
+                ));
+
+        assertTrue(pattern.isPattern());
+        assertEquals(5, pattern.size());
+        assertTrue(Arrays.equals("12369".getBytes(), pattern.getCredential()));
+
+        assertFalse(pattern.isNone());
+        assertFalse(pattern.isPassword());
+    }
+
+    public void testPasswordOrNoneCredential() {
+        assertEquals(LockscreenCredential.createNone(),
+                LockscreenCredential.createPasswordOrNone(null));
+        assertEquals(LockscreenCredential.createNone(),
+                LockscreenCredential.createPasswordOrNone(""));
+        assertEquals(LockscreenCredential.createPassword("abcd"),
+                LockscreenCredential.createPasswordOrNone("abcd"));
+    }
+
+    public void testSanitize() {
+        LockscreenCredential password = LockscreenCredential.createPassword("password");
+        password.zeroize();
+
+        try {
+            password.isNone();
+            fail("Sanitized credential still accessible");
+        } catch (IllegalStateException expected) { }
+
+        try {
+            password.isPattern();
+            fail("Sanitized credential still accessible");
+        } catch (IllegalStateException expected) { }
+        try {
+            password.isPassword();
+            fail("Sanitized credential still accessible");
+        } catch (IllegalStateException expected) { }
+        try {
+            password.size();
+            fail("Sanitized credential still accessible");
+        } catch (IllegalStateException expected) { }
+        try {
+            password.getCredential();
+            fail("Sanitized credential still accessible");
+        } catch (IllegalStateException expected) { }
+    }
+
+    public void testEquals() {
+        assertEquals(LockscreenCredential.createNone(), LockscreenCredential.createNone());
+        assertEquals(LockscreenCredential.createPassword("1234"),
+                LockscreenCredential.createPassword("1234"));
+        assertEquals(LockscreenCredential.createPin("4321"),
+                LockscreenCredential.createPin("4321"));
+        assertEquals(createPattern("1234"), createPattern("1234"));
+
+        assertNotSame(LockscreenCredential.createPassword("1234"),
+                LockscreenCredential.createNone());
+        assertNotSame(LockscreenCredential.createPassword("1234"),
+                LockscreenCredential.createPassword("4321"));
+        assertNotSame(LockscreenCredential.createPassword("1234"),
+                createPattern("1234"));
+        assertNotSame(LockscreenCredential.createPassword("1234"),
+                LockscreenCredential.createPin("1234"));
+
+        assertNotSame(LockscreenCredential.createPin("1111"),
+                LockscreenCredential.createNone());
+        assertNotSame(LockscreenCredential.createPin("1111"),
+                LockscreenCredential.createPin("2222"));
+        assertNotSame(LockscreenCredential.createPin("1111"),
+                createPattern("1111"));
+        assertNotSame(LockscreenCredential.createPin("1111"),
+                LockscreenCredential.createPassword("1111"));
+
+        assertNotSame(createPattern("5678"),
+                LockscreenCredential.createNone());
+        assertNotSame(createPattern("5678"),
+                createPattern("1234"));
+        assertNotSame(createPattern("5678"),
+                LockscreenCredential.createPassword("5678"));
+        assertNotSame(createPattern("5678"),
+                LockscreenCredential.createPin("5678"));
+    }
+
+    public void testDuplicate() {
+        LockscreenCredential credential;
+
+        credential = LockscreenCredential.createNone();
+        assertEquals(credential, credential.duplicate());
+        credential = LockscreenCredential.createPassword("abcd");
+        assertEquals(credential, credential.duplicate());
+        credential = LockscreenCredential.createPin("1234");
+        assertEquals(credential, credential.duplicate());
+        credential = createPattern("5678");
+        assertEquals(credential, credential.duplicate());
+    }
+
+    private LockscreenCredential createPattern(String patternString) {
+        return LockscreenCredential.createPattern(LockPatternUtils.byteArrayToPattern(
+                patternString.getBytes()));
+    }
+}
diff --git a/data/etc/car/com.google.android.car.kitchensink.xml b/data/etc/car/com.google.android.car.kitchensink.xml
index d36a826..61281ee 100644
--- a/data/etc/car/com.google.android.car.kitchensink.xml
+++ b/data/etc/car/com.google.android.car.kitchensink.xml
@@ -16,17 +16,30 @@
   -->
 <permissions>
     <privapp-permissions package="com.google.android.car.kitchensink">
+        <permission name="android.permission.ACCESS_NETWORK_STATE"/>
+        <permission name="android.permission.ACCESS_WIFI_STATE"/>
+        <permission name="android.permission.ACTIVITY_EMBEDDING"/>
+        <permission name="android.permission.INJECT_EVENTS"/>
+        <!-- use for CarServiceUnitTest and CarServiceTest -->
+        <permission name="android.permission.INTERACT_ACROSS_USERS"/>
+        <!-- use for CarServiceUnitTest -->
+        <permission name="android.permission.INTERACT_ACROSS_USERS_FULL"/>
         <permission name="android.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS"/>
         <permission name="android.permission.LOCATION_HARDWARE"/>
         <permission name="android.permission.MANAGE_USB"/>
         <permission name="android.permission.MANAGE_USERS"/>
+        <!-- use for CarServiceTest -->
+        <permission name="android.permission.MEDIA_CONTENT_CONTROL"/>
         <permission name="android.permission.MODIFY_AUDIO_ROUTING"/>
         <permission name="android.permission.MODIFY_DAY_NIGHT_MODE"/>
         <permission name="android.permission.MODIFY_PHONE_STATE"/>
         <permission name="android.permission.PROVIDE_TRUST_AGENT"/>
+        <permission name="android.permission.OVERRIDE_WIFI_CONFIG"/>
         <permission name="android.permission.REAL_GET_TASKS"/>
         <permission name="android.permission.READ_LOGS"/>
         <permission name="android.permission.REBOOT"/>
+        <!-- use for CarServiceTest -->
+        <permission name="android.permission.SET_ACTIVITY_WATCHER"/>
         <permission name="android.permission.WRITE_SECURE_SETTINGS"/>
     </privapp-permissions>
 </permissions>
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 10f6b69..4b4e416 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -121,6 +121,7 @@
         <permission name="android.permission.APPROVE_INCIDENT_REPORTS"/>
         <permission name="android.permission.READ_PRIVILEGED_PHONE_STATE" />
         <permission name="android.permission.SUBSTITUTE_NOTIFICATION_APP_NAME" />
+        <permission name="android.permission.PACKAGE_USAGE_STATS" />
     </privapp-permissions>
 
     <privapp-permissions package="com.android.phone">
@@ -359,6 +360,7 @@
         <permission name="android.permission.CONNECTIVITY_INTERNAL"/>
         <permission name="android.permission.DUMP"/>
         <permission name="android.permission.INTERACT_ACROSS_USERS"/>
+        <permission name="android.permission.INTERNAL_SYSTEM_WINDOW"/>
         <permission name="android.permission.LOCAL_MAC_ADDRESS"/>
         <permission name="android.permission.MANAGE_USERS"/>
         <permission name="android.permission.PACKAGE_USAGE_STATS"/>
diff --git a/data/fonts/Android.mk b/data/fonts/Android.mk
index 454dceb..4226e08 100644
--- a/data/fonts/Android.mk
+++ b/data/fonts/Android.mk
@@ -83,8 +83,8 @@
 
 ################################
 # Copies the font configuration file into system/etc for the product as fonts.xml.
-# In the case where $(ADDITIONAL_FONTS_FILE) is defined, the content of $(ADDITIONAL_FONTS_FILE)
-# is added to the $(AOSP_FONTS_FILE).
+# Additional fonts should be installed to /product/fonts/ alongside a corresponding
+# fonts_customiztion.xml in /product/etc/
 include $(CLEAR_VARS)
 
 LOCAL_MODULE := fonts.xml
diff --git a/graphics/java/android/graphics/pdf/TEST_MAPPING b/graphics/java/android/graphics/pdf/TEST_MAPPING
new file mode 100644
index 0000000..d763598
--- /dev/null
+++ b/graphics/java/android/graphics/pdf/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+  "presubmit": [
+    {
+      "name": "CtsPdfTestCases"
+    }
+  ]
+}
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index ae90f11..61b72cf 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -26,8 +26,10 @@
         // a problem
         "-Wno-free-nonheap-object",
 
-        // clang's warning is broken, see: https://llvm.org/bugs/show_bug.cgi?id=21629
-        "-Wno-missing-braces",
+        // Clang is producing non-determistic binary when the new pass manager is
+        // enabled. Disable the new PM as a temporary workaround.
+        // b/142372146
+        "-fno-experimental-new-pass-manager",
     ],
 
     include_dirs: [
diff --git a/libs/hwui/DamageAccumulator.h b/libs/hwui/DamageAccumulator.h
index 7d0b687..030a20f 100644
--- a/libs/hwui/DamageAccumulator.h
+++ b/libs/hwui/DamageAccumulator.h
@@ -27,7 +27,7 @@
 // Smaller than INT_MIN/INT_MAX because we offset these values
 // and thus don't want to be adding offsets to INT_MAX, that's bad
 #define DIRTY_MIN (-0x7ffffff - 1)
-#define DIRTY_MAX (0x7ffffff)
+#define DIRTY_MAX (0x8000000)
 
 namespace android {
 namespace uirenderer {
diff --git a/location/java/android/location/AbstractListenerManager.java b/location/java/android/location/AbstractListenerManager.java
index c41023e..944ebf9 100644
--- a/location/java/android/location/AbstractListenerManager.java
+++ b/location/java/android/location/AbstractListenerManager.java
@@ -42,8 +42,8 @@
         @Nullable private volatile T mListener;
 
         private Registration(Executor executor, T listener) {
-            Preconditions.checkArgument(listener != null);
-            Preconditions.checkArgument(executor != null);
+            Preconditions.checkArgument(listener != null, "invalid null listener/callback");
+            Preconditions.checkArgument(executor != null, "invalid null executor");
             mExecutor = executor;
             mListener = listener;
         }
@@ -83,16 +83,18 @@
         return addInternal(listener, executor);
     }
 
-    protected final boolean addInternal(Object listener, Handler handler) throws RemoteException {
+    protected final boolean addInternal(@NonNull Object listener, @NonNull Handler handler)
+            throws RemoteException {
         return addInternal(listener, new HandlerExecutor(handler));
     }
 
-    protected final boolean addInternal(Object listener, Executor executor) throws RemoteException {
+    protected final boolean addInternal(@NonNull Object listener, @NonNull Executor executor)
+            throws RemoteException {
+        Preconditions.checkArgument(listener != null, "invalid null listener/callback");
         return addInternal(listener, new Registration<>(executor, convertKey(listener)));
     }
 
     private boolean addInternal(Object key, Registration<T> registration) throws RemoteException {
-        Preconditions.checkNotNull(key);
         Preconditions.checkNotNull(registration);
 
         synchronized (mListeners) {
diff --git a/location/java/android/location/ILocationManager.aidl b/location/java/android/location/ILocationManager.aidl
index d06ba12..daa2e08 100644
--- a/location/java/android/location/ILocationManager.aidl
+++ b/location/java/android/location/ILocationManager.aidl
@@ -42,11 +42,11 @@
 interface ILocationManager
 {
     void requestLocationUpdates(in LocationRequest request, in ILocationListener listener,
-            in PendingIntent intent, String packageName);
+            in PendingIntent intent, String packageName, String listenerIdentifier);
     void removeUpdates(in ILocationListener listener, in PendingIntent intent, String packageName);
 
     void requestGeofence(in LocationRequest request, in Geofence geofence,
-            in PendingIntent intent, String packageName);
+            in PendingIntent intent, String packageName, String listenerIdentifier);
     void removeGeofence(in Geofence fence, in PendingIntent intent, String packageName);
 
     Location getLastLocation(in LocationRequest request, String packageName);
@@ -64,22 +64,23 @@
 
     boolean sendNiResponse(int notifId, int userResponse);
 
-    boolean addGnssMeasurementsListener(in IGnssMeasurementsListener listener, in String packageName);
+    boolean addGnssMeasurementsListener(in IGnssMeasurementsListener listener,
+             String packageName, String listenerIdentifier);
     void injectGnssMeasurementCorrections(in GnssMeasurementCorrections corrections,
             in String packageName);
     long getGnssCapabilities(in String packageName);
     void removeGnssMeasurementsListener(in IGnssMeasurementsListener listener);
 
-    boolean addGnssNavigationMessageListener(
-            in IGnssNavigationMessageListener listener,
-            in String packageName);
+    boolean addGnssNavigationMessageListener(in IGnssNavigationMessageListener listener,
+             String packageName, String listenerIdentifier);
     void removeGnssNavigationMessageListener(in IGnssNavigationMessageListener listener);
 
     int getGnssYearOfHardware();
     String getGnssHardwareModelName();
 
     int getGnssBatchSize(String packageName);
-    boolean addGnssBatchingCallback(in IBatchedLocationCallback callback, String packageName);
+    boolean addGnssBatchingCallback(in IBatchedLocationCallback callback, String packageName,
+             String listenerIdentifier);
     void removeGnssBatchingCallback();
     boolean startGnssBatch(long periodNanos, boolean wakeOnFifoFull, String packageName);
     void flushGnssBatch(String packageName);
@@ -92,6 +93,7 @@
     String getBestProvider(in Criteria criteria, boolean enabledOnly);
     ProviderProperties getProviderProperties(String provider);
     boolean isProviderPackage(String packageName);
+    List<String> getProviderPackages(String provider);
 
     void setExtraLocationControllerPackage(String packageName);
     String getExtraLocationControllerPackage();
diff --git a/location/java/android/location/Location.java b/location/java/android/location/Location.java
index 9c36d76..6824be8 100644
--- a/location/java/android/location/Location.java
+++ b/location/java/android/location/Location.java
@@ -16,6 +16,7 @@
 
 package android.location;
 
+import android.annotation.Nullable;
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
 import android.annotation.UnsupportedAppUsage;
@@ -79,6 +80,8 @@
      *
      * @hide
      */
+    @TestApi
+    @SystemApi
     public static final String EXTRA_NO_GPS_LOCATION = "noGPSLocation";
 
     /**
@@ -1214,8 +1217,9 @@
      * @param value the Location to attach
      * @hide
      */
-    @UnsupportedAppUsage
-    public void setExtraLocation(String key, Location value) {
+    @TestApi
+    @SystemApi
+    public void setExtraLocation(@Nullable String key, @Nullable Location value) {
         if (mExtras == null) {
             mExtras = new Bundle();
         }
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index 90e29df..0b3e1c3 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -51,6 +51,7 @@
 import com.android.internal.location.ProviderProperties;
 import com.android.internal.util.Preconditions;
 
+import java.util.Collections;
 import java.util.List;
 import java.util.concurrent.Executor;
 import java.util.concurrent.RejectedExecutionException;
@@ -542,6 +543,23 @@
     }
 
     /**
+     * Create a string that allows an app to identify a listener
+     *
+     * @param listener The listener
+     *
+     * @return A identifying string
+     */
+    private static String getListenerIdentifier(@NonNull Object listener) {
+        StringBuilder sb = new StringBuilder();
+
+        sb.append(listener.getClass().getName());
+        sb.append('@');
+        sb.append(Integer.toHexString(System.identityHashCode(listener)));
+
+        return sb.toString();
+    }
+
+    /**
      * Register for a single location update using the named provider and
      * a callback.
      *
@@ -981,7 +999,7 @@
             boolean registered = false;
             try {
                 mService.requestLocationUpdates(locationRequest, transport, null,
-                        mContext.getPackageName());
+                        mContext.getPackageName(), getListenerIdentifier(listener));
                 registered = true;
             } catch (RemoteException e) {
                 throw e.rethrowFromSystemServer();
@@ -1026,7 +1044,7 @@
 
         try {
             mService.requestLocationUpdates(locationRequest, null, pendingIntent,
-                    mContext.getPackageName());
+                    mContext.getPackageName(), getListenerIdentifier(pendingIntent));
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -1235,6 +1253,24 @@
     }
 
     /**
+     * Returns a list of packages associated with the given provider,
+     * and an empty list if no package is associated with the provider.
+     *
+     * @hide
+     */
+    @TestApi
+    @RequiresPermission(Manifest.permission.READ_DEVICE_CONFIG)
+    @Nullable
+    public List<String> getProviderPackages(@NonNull String provider) {
+        try {
+            return mService.getProviderPackages(provider);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+            return Collections.emptyList();
+        }
+    }
+
+    /**
      * Sends additional commands to a location provider. Can be used to support provider specific
      * extensions to the Location Manager API.
      *
@@ -1471,7 +1507,8 @@
         Geofence fence = Geofence.createCircle(latitude, longitude, radius);
         LocationRequest request = new LocationRequest().setExpireIn(expiration);
         try {
-            mService.requestGeofence(request, fence, intent, mContext.getPackageName());
+            mService.requestGeofence(request, fence, intent, mContext.getPackageName(),
+                    getListenerIdentifier(intent));
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -1548,7 +1585,8 @@
         Preconditions.checkArgument(fence != null, "invalid null geofence");
 
         try {
-            mService.requestGeofence(request, fence, intent, mContext.getPackageName());
+            mService.requestGeofence(request, fence, intent, mContext.getPackageName(),
+                    getListenerIdentifier(intent));
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -1762,6 +1800,7 @@
      * @param handler  a handler with a looper that the callback runs on
      * @return true if the listener was successfully added
      *
+     * @throws IllegalArgumentException if callback is null
      * @throws SecurityException if the ACCESS_FINE_LOCATION permission is not present
      */
     @RequiresPermission(ACCESS_FINE_LOCATION)
@@ -1781,10 +1820,12 @@
     /**
      * Registers a GNSS status callback.
      *
-     * @param callback GNSS status callback object to register
      * @param executor the executor that the callback runs on
+     * @param callback GNSS status callback object to register
      * @return true if the listener was successfully added
      *
+     * @throws IllegalArgumentException if executor is null
+     * @throws IllegalArgumentException if callback is null
      * @throws SecurityException if the ACCESS_FINE_LOCATION permission is not present
      */
     @RequiresPermission(ACCESS_FINE_LOCATION)
@@ -1853,6 +1894,8 @@
      * @param listener a {@link OnNmeaMessageListener} object to register
      * @param handler  a handler with the looper that the listener runs on.
      * @return true if the listener was successfully added
+     *
+     * @throws IllegalArgumentException if listener is null
      * @throws SecurityException if the ACCESS_FINE_LOCATION permission is not present
      */
     @RequiresPermission(ACCESS_FINE_LOCATION)
@@ -1874,6 +1917,9 @@
      * @param listener a {@link OnNmeaMessageListener} object to register
      * @param executor the {@link Executor} that the listener runs on.
      * @return true if the listener was successfully added
+     *
+     * @throws IllegalArgumentException if executor is null
+     * @throws IllegalArgumentException if listener is null
      * @throws SecurityException if the ACCESS_FINE_LOCATION permission is not present
      */
     @RequiresPermission(ACCESS_FINE_LOCATION)
@@ -1946,6 +1992,9 @@
      * @param callback a {@link GnssMeasurementsEvent.Callback} object to register.
      * @param handler  the handler that the callback runs on.
      * @return {@code true} if the callback was added successfully, {@code false} otherwise.
+     *
+     * @throws IllegalArgumentException if callback is null
+     * @throws SecurityException if the ACCESS_FINE_LOCATION permission is not present
      */
     @RequiresPermission(ACCESS_FINE_LOCATION)
     public boolean registerGnssMeasurementsCallback(
@@ -1966,6 +2015,10 @@
      * @param callback a {@link GnssMeasurementsEvent.Callback} object to register.
      * @param executor the executor that the callback runs on.
      * @return {@code true} if the callback was added successfully, {@code false} otherwise.
+     *
+     * @throws IllegalArgumentException if executor is null
+     * @throws IllegalArgumentException if callback is null
+     * @throws SecurityException if the ACCESS_FINE_LOCATION permission is not present
      */
     @RequiresPermission(ACCESS_FINE_LOCATION)
     public boolean registerGnssMeasurementsCallback(
@@ -1983,6 +2036,9 @@
      *
      * @param measurementCorrections a {@link GnssMeasurementCorrections} object with the GNSS
      *     measurement corrections to be injected into the GNSS chipset.
+     *
+     * @throws IllegalArgumentException if measurementCorrections is null
+     * @throws SecurityException if the ACCESS_FINE_LOCATION permission is not present
      * @hide
      */
     @SystemApi
@@ -2057,6 +2113,9 @@
      * @param callback a {@link GnssNavigationMessage.Callback} object to register.
      * @param handler  the handler that the callback runs on.
      * @return {@code true} if the callback was added successfully, {@code false} otherwise.
+     *
+     * @throws IllegalArgumentException if callback is null
+     * @throws SecurityException if the ACCESS_FINE_LOCATION permission is not present
      */
     @RequiresPermission(ACCESS_FINE_LOCATION)
     public boolean registerGnssNavigationMessageCallback(
@@ -2078,6 +2137,10 @@
      * @param callback a {@link GnssNavigationMessage.Callback} object to register.
      * @param executor the looper that the callback runs on.
      * @return {@code true} if the callback was added successfully, {@code false} otherwise.
+     *
+     * @throws IllegalArgumentException if executor is null
+     * @throws IllegalArgumentException if callback is null
+     * @throws SecurityException if the ACCESS_FINE_LOCATION permission is not present
      */
     @RequiresPermission(ACCESS_FINE_LOCATION)
     public boolean registerGnssNavigationMessageCallback(
@@ -2538,7 +2601,7 @@
 
             mListenerTransport = new GnssMeasurementsListener();
             return mService.addGnssMeasurementsListener(mListenerTransport,
-                    mContext.getPackageName());
+                    mContext.getPackageName(), "gnss measurement callback");
         }
 
         @Override
@@ -2574,7 +2637,7 @@
 
             mListenerTransport = new GnssNavigationMessageListener();
             return mService.addGnssNavigationMessageListener(mListenerTransport,
-                    mContext.getPackageName());
+                    mContext.getPackageName(), "gnss navigation callback");
         }
 
         @Override
@@ -2609,7 +2672,8 @@
             Preconditions.checkState(mListenerTransport == null);
 
             mListenerTransport = new BatchedLocationCallback();
-            return mService.addGnssBatchingCallback(mListenerTransport, mContext.getPackageName());
+            return mService.addGnssBatchingCallback(mListenerTransport, mContext.getPackageName(),
+                     "batched location callback");
         }
 
         @Override
diff --git a/media/java/android/media/ExifInterface.java b/media/java/android/media/ExifInterface.java
index 5341d07..33ddfa7 100644
--- a/media/java/android/media/ExifInterface.java
+++ b/media/java/android/media/ExifInterface.java
@@ -2776,7 +2776,7 @@
         updateImageSizeValues(in, IFD_TYPE_THUMBNAIL);
 
         // Check if each image data is in valid position.
-        validateImages(in);
+        validateImages();
 
         if (mMimeType == IMAGE_TYPE_PEF) {
             // PEF files contain a MakerNote data, which contains the data for ColorSpace tag.
@@ -3143,8 +3143,11 @@
         // 2.1. Integers and byte order
         in.setByteOrder(ByteOrder.BIG_ENDIAN);
 
+        int bytesRead = 0;
+
         // Skip the signature bytes
         in.seek(PNG_SIGNATURE.length);
+        bytesRead += PNG_SIGNATURE.length;
 
         try {
             while (true) {
@@ -3159,12 +3162,14 @@
                 // See PNG (Portable Network Graphics) Specification, Version 1.2,
                 // 3.2. Chunk layout
                 int length = in.readInt();
+                bytesRead += 4;
 
                 byte[] type = new byte[PNG_CHUNK_LENGTH_BYTE_LENGTH];
                 if (in.read(type) != type.length) {
                     throw new IOException("Encountered invalid length while parsing PNG chunk"
                             + "type");
                 }
+                bytesRead += PNG_CHUNK_LENGTH_BYTE_LENGTH;
 
                 if (Arrays.equals(type, PNG_CHUNK_TYPE_IEND)) {
                     // IEND marks the end of the image.
@@ -3177,12 +3182,17 @@
                                 + "type: " + byteArrayToHexString(type));
                     }
                     readExifSegment(data, IFD_TYPE_PRIMARY);
+
+                    validateImages();
                     break;
                 } else {
                     // Skip to next chunk
                     in.skipBytes(length + PNG_CHUNK_CRC_BYTE_LENGTH);
+                    bytesRead += length + PNG_CHUNK_CRC_BYTE_LENGTH;
                 }
             }
+            // Save offset values for handleThumbnailFromJfif() function
+            mExifOffset = bytesRead;
         } catch (EOFException e) {
             // Should not reach here. Will only reach here if the file is corrupted or
             // does not follow the PNG specifications
@@ -3675,7 +3685,7 @@
             int thumbnailLength = jpegInterchangeFormatLengthAttribute.getIntValue(mExifByteOrder);
 
             if (mMimeType == IMAGE_TYPE_JPEG || mMimeType == IMAGE_TYPE_RAF
-                    || mMimeType == IMAGE_TYPE_RW2) {
+                    || mMimeType == IMAGE_TYPE_RW2 || mMimeType == IMAGE_TYPE_PNG) {
                 thumbnailOffset += mExifOffset;
             } else if (mMimeType == IMAGE_TYPE_ORF) {
                 // Update offset value since RAF files have IFD data preceding MakerNote data.
@@ -3819,12 +3829,13 @@
     }
 
     // Validate primary, preview, thumbnail image data by comparing image size
-    private void validateImages(InputStream in) throws IOException {
+    private void validateImages() throws IOException {
         // Swap images based on size (primary > preview > thumbnail)
         swapBasedOnImageSize(IFD_TYPE_PRIMARY, IFD_TYPE_PREVIEW);
         swapBasedOnImageSize(IFD_TYPE_PRIMARY, IFD_TYPE_THUMBNAIL);
         swapBasedOnImageSize(IFD_TYPE_PREVIEW, IFD_TYPE_THUMBNAIL);
 
+        // TODO (b/142296453): Revise image width/height setting logic
         // Check if image has PixelXDimension/PixelYDimension tags, which contain valid image
         // sizes, excluding padding at the right end or bottom end of the image to make sure that
         // the values are multiples of 64. See JEITA CP-3451C Table 5 and Section 4.8.1. B.
diff --git a/media/java/android/media/IMediaRoute2Provider.aidl b/media/java/android/media/IMediaRoute2Provider.aidl
index f132cef..66764c7 100644
--- a/media/java/android/media/IMediaRoute2Provider.aidl
+++ b/media/java/android/media/IMediaRoute2Provider.aidl
@@ -27,4 +27,6 @@
     void selectRoute(String packageName, String id);
     void unselectRoute(String packageName, String id);
     void notifyControlRequestSent(String id, in Intent request);
+    void requestSetVolume(String id, int volume);
+    void requestUpdateVolume(String id, int delta);
 }
diff --git a/media/java/android/media/IMediaRouterService.aidl b/media/java/android/media/IMediaRouterService.aidl
index 81213b9..7b7a34e 100644
--- a/media/java/android/media/IMediaRouterService.aidl
+++ b/media/java/android/media/IMediaRouterService.aidl
@@ -46,6 +46,8 @@
     void registerClient2(IMediaRouter2Client client, String packageName);
     void unregisterClient2(IMediaRouter2Client client);
     void sendControlRequest(IMediaRouter2Client client, in MediaRoute2Info route, in Intent request);
+    void requestSetVolume2(IMediaRouter2Client client, in MediaRoute2Info route, int volume);
+    void requestUpdateVolume2(IMediaRouter2Client client, in MediaRoute2Info route, int direction);
     /**
      * Changes the selected route of the client.
      *
@@ -66,4 +68,9 @@
      */
     void selectClientRoute2(IMediaRouter2Manager manager, String packageName,
             in @nullable MediaRoute2Info route);
+
+    void requestSetVolume2Manager(IMediaRouter2Manager manager,
+            in MediaRoute2Info route, int volume);
+    void requestUpdateVolume2Manager(IMediaRouter2Manager manager,
+            in MediaRoute2Info route, int direction);
 }
diff --git a/media/java/android/media/MediaCodecInfo.java b/media/java/android/media/MediaCodecInfo.java
index 26e7936..91d644b 100644
--- a/media/java/android/media/MediaCodecInfo.java
+++ b/media/java/android/media/MediaCodecInfo.java
@@ -21,6 +21,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.SuppressLint;
 import android.annotation.TestApi;
 import android.annotation.UnsupportedAppUsage;
 import android.os.Build;
@@ -3750,8 +3751,11 @@
         public static final int DolbyVisionProfileDvheStn = 0x20;
         public static final int DolbyVisionProfileDvheDth = 0x40;
         public static final int DolbyVisionProfileDvheDtb = 0x80;
-        public static final int DolbyVisionProfileDvheSt = 0x100;
-        public static final int DolbyVisionProfileDvavSe = 0x200;
+        public static final int DolbyVisionProfileDvheSt  = 0x100;
+        public static final int DolbyVisionProfileDvavSe  = 0x200;
+        /** Dolby Vision AV1 profile */
+        @SuppressLint("AllUpper")
+        public static final int DolbyVisionProfileDvav110 = 0x400;
 
         public static final int DolbyVisionLevelHd24    = 0x1;
         public static final int DolbyVisionLevelHd30    = 0x2;
diff --git a/media/java/android/media/MediaRoute2ProviderService.java b/media/java/android/media/MediaRoute2ProviderService.java
index e8e0f82..58deff2 100644
--- a/media/java/android/media/MediaRoute2ProviderService.java
+++ b/media/java/android/media/MediaRoute2ProviderService.java
@@ -81,6 +81,20 @@
     public abstract void onControlRequest(String routeId, Intent request);
 
     /**
+     * Called when requestSetVolume is called on a route of the provider
+     * @param routeId the id of the route
+     * @param volume the target volume
+     */
+    public abstract void onSetVolume(String routeId, int volume);
+
+    /**
+     * Called when requestUpdateVolume is called on a route of the provider
+     * @param routeId id of the route
+     * @param delta the delta to add to the current volume
+     */
+    public abstract void onUpdateVolume(String routeId, int delta);
+
+    /**
      * Updates provider info and publishes routes
      */
     public final void setProviderInfo(MediaRoute2ProviderInfo info) {
@@ -130,5 +144,17 @@
             mHandler.sendMessage(obtainMessage(MediaRoute2ProviderService::onControlRequest,
                     MediaRoute2ProviderService.this, id, request));
         }
+
+        @Override
+        public void requestSetVolume(String id, int volume) {
+            mHandler.sendMessage(obtainMessage(MediaRoute2ProviderService::onSetVolume,
+                    MediaRoute2ProviderService.this, id, volume));
+        }
+
+        @Override
+        public void requestUpdateVolume(String id, int delta) {
+            mHandler.sendMessage(obtainMessage(MediaRoute2ProviderService::onUpdateVolume,
+                    MediaRoute2ProviderService.this, id, delta));
+        }
     }
 }
diff --git a/media/java/android/media/MediaRouter2.java b/media/java/android/media/MediaRouter2.java
index ed35ef6..aca40d8 100644
--- a/media/java/android/media/MediaRouter2.java
+++ b/media/java/android/media/MediaRouter2.java
@@ -265,6 +265,54 @@
         }
     }
 
+    /**
+     * Requests a volume change for the route asynchronously.
+     * <p>
+     * It may have no effect if the route is currently not selected.
+     * </p>
+     *
+     * @param volume The new volume value between 0 and {@link MediaRoute2Info#getVolumeMax}.
+     */
+    public void requestSetVolume(@NonNull MediaRoute2Info route, int volume) {
+        Objects.requireNonNull(route, "route must not be null");
+
+        Client client;
+        synchronized (sLock) {
+            client = mClient;
+        }
+        if (client != null) {
+            try {
+                mMediaRouterService.requestSetVolume2(client, route, volume);
+            } catch (RemoteException ex) {
+                Log.e(TAG, "Unable to send control request.", ex);
+            }
+        }
+    }
+
+    /**
+     * Requests an incremental volume update  for the route asynchronously.
+     * <p>
+     * It may have no effect if the route is currently not selected.
+     * </p>
+     *
+     * @param delta The delta to add to the current volume.
+     */
+    public void requestUpdateVolume(@NonNull MediaRoute2Info route, int delta) {
+        Objects.requireNonNull(route, "route must not be null");
+
+        Client client;
+        synchronized (sLock) {
+            client = mClient;
+        }
+        if (client != null) {
+            try {
+                mMediaRouterService.requestUpdateVolume2(client, route, delta);
+            } catch (RemoteException ex) {
+                Log.e(TAG, "Unable to send control request.", ex);
+            }
+        }
+    }
+
     @GuardedBy("mCallbackRecords")
     private int findCallbackRecordIndexLocked(Callback callback) {
         final int count = mCallbackRecords.size();
@@ -310,6 +358,7 @@
             List<MediaRoute2Info> outRoutes) {
         if (provider == null || !provider.isValid()) {
             Log.w(TAG, "Ignoring invalid provider : " + provider);
+            return;
         }
 
         final Collection<MediaRoute2Info> routes = provider.getRoutes();
@@ -321,10 +370,21 @@
             if (!route.supportsControlCategory(controlCategories)) {
                 continue;
             }
+            MediaRoute2Info preRoute = findRouteById(route.getId());
+            if (!route.equals(preRoute)) {
+                notifyRouteChanged(route);
+            }
             outRoutes.add(route);
         }
     }
 
+    MediaRoute2Info findRouteById(String id) {
+        for (MediaRoute2Info route : mRoutes) {
+            if (route.getId().equals(id)) return route;
+        }
+        return null;
+    }
+
     void notifyRouteListChanged(List<MediaRoute2Info> routes) {
         for (CallbackRecord record: mCallbackRecords) {
             record.mExecutor.execute(
@@ -332,10 +392,18 @@
         }
     }
 
+    void notifyRouteChanged(MediaRoute2Info route) {
+        for (CallbackRecord record: mCallbackRecords) {
+            record.mExecutor.execute(
+                    () -> record.mCallback.onRouteChanged(route));
+        }
+    }
+
     /**
      * Interface for receiving events about media routing changes.
      */
     public static class Callback {
+        //TODO: clean up these callbacks
         /**
          * Called when a route is added.
          */
@@ -369,7 +437,7 @@
         void notifyRoutes() {
             final List<MediaRoute2Info> routes = mRoutes;
             // notify only when bound to media router service.
-            //TODO: Correct the condition when control category, default rotue, .. are finalized.
+            //TODO: Correct the condition when control category, default route, .. are finalized.
             if (routes.size() > 0) {
                 mExecutor.execute(() -> mCallback.onRoutesChanged(routes));
             }
diff --git a/media/java/android/media/MediaRouter2Manager.java b/media/java/android/media/MediaRouter2Manager.java
index 0b64569..4f2a295 100644
--- a/media/java/android/media/MediaRouter2Manager.java
+++ b/media/java/android/media/MediaRouter2Manager.java
@@ -234,6 +234,54 @@
         }
     }
 
+    /**
+     * Requests a volume change for the route asynchronously.
+     * <p>
+     * It may have no effect if the route is currently not selected.
+     * </p>
+     *
+     * @param volume The new volume value between 0 and {@link MediaRoute2Info#getVolumeMax}.
+     */
+    public void requestSetVolume(@NonNull MediaRoute2Info route, int volume) {
+        Objects.requireNonNull(route, "route must not be null");
+
+        Client client;
+        synchronized (sLock) {
+            client = mClient;
+        }
+        if (client != null) {
+            try {
+                mMediaRouterService.requestSetVolume2Manager(client, route, volume);
+            } catch (RemoteException ex) {
+                Log.e(TAG, "Unable to send control request.", ex);
+            }
+        }
+    }
+
+    /**
+     * Requests an incremental volume update  for the route asynchronously.
+     * <p>
+     * It may have no effect if the route is currently not selected.
+     * </p>
+     *
+     * @param delta The delta to add to the current volume.
+     */
+    public void requestUpdateVolume(@NonNull MediaRoute2Info route, int delta) {
+        Objects.requireNonNull(route, "route must not be null");
+
+        Client client;
+        synchronized (sLock) {
+            client = mClient;
+        }
+        if (client != null) {
+            try {
+                mMediaRouterService.requestUpdateVolume2Manager(client, route, delta);
+            } catch (RemoteException ex) {
+                Log.e(TAG, "Unable to send control request.", ex);
+            }
+        }
+    }
+
     int findProviderIndex(MediaRoute2ProviderInfo provider) {
         final int count = mProviders.size();
         for (int i = 0; i < count; i++) {
diff --git a/media/java/android/media/MediaScanner.java b/media/java/android/media/MediaScanner.java
index 771628c..ca96c9a 100644
--- a/media/java/android/media/MediaScanner.java
+++ b/media/java/android/media/MediaScanner.java
@@ -17,106 +17,14 @@
 package android.media;
 
 import android.annotation.UnsupportedAppUsage;
-import android.content.ContentProviderClient;
-import android.content.ContentResolver;
-import android.content.ContentUris;
 import android.content.ContentValues;
 import android.content.Context;
-import android.content.SharedPreferences;
-import android.database.Cursor;
-import android.database.SQLException;
-import android.drm.DrmManagerClient;
-import android.graphics.BitmapFactory;
-import android.mtp.MtpConstants;
 import android.net.Uri;
 import android.os.Build;
-import android.os.Environment;
 import android.os.RemoteException;
-import android.os.SystemProperties;
-import android.provider.MediaStore;
-import android.provider.MediaStore.Audio;
-import android.provider.MediaStore.Audio.Playlists;
-import android.provider.MediaStore.Files;
-import android.provider.MediaStore.Files.FileColumns;
-import android.provider.MediaStore.Images;
-import android.provider.MediaStore.Video;
-import android.provider.Settings;
-import android.provider.Settings.SettingNotFoundException;
-import android.sax.Element;
-import android.sax.ElementListener;
-import android.sax.RootElement;
-import android.system.ErrnoException;
-import android.system.Os;
-import android.text.TextUtils;
-import android.util.Log;
-import android.util.Xml;
-
-import dalvik.system.CloseGuard;
-
-import org.xml.sax.Attributes;
-import org.xml.sax.ContentHandler;
-import org.xml.sax.SAXException;
-
-import java.io.BufferedReader;
-import java.io.File;
-import java.io.FileDescriptor;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStreamReader;
-import java.text.ParseException;
-import java.text.SimpleDateFormat;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.Locale;
-import java.util.TimeZone;
-import java.util.concurrent.atomic.AtomicBoolean;
 
 /**
- * Internal service helper that no-one should use directly.
- *
- * The way the scan currently works is:
- * - The Java MediaScannerService creates a MediaScanner (this class), and calls
- *   MediaScanner.scanDirectories on it.
- * - scanDirectories() calls the native processDirectory() for each of the specified directories.
- * - the processDirectory() JNI method wraps the provided mediascanner client in a native
- *   'MyMediaScannerClient' class, then calls processDirectory() on the native MediaScanner
- *   object (which got created when the Java MediaScanner was created).
- * - native MediaScanner.processDirectory() calls
- *   doProcessDirectory(), which recurses over the folder, and calls
- *   native MyMediaScannerClient.scanFile() for every file whose extension matches.
- * - native MyMediaScannerClient.scanFile() calls back on Java MediaScannerClient.scanFile,
- *   which calls doScanFile, which after some setup calls back down to native code, calling
- *   MediaScanner.processFile().
- * - MediaScanner.processFile() calls one of several methods, depending on the type of the
- *   file: parseMP3, parseMP4, parseMidi, parseOgg or parseWMA.
- * - each of these methods gets metadata key/value pairs from the file, and repeatedly
- *   calls native MyMediaScannerClient.handleStringTag, which calls back up to its Java
- *   counterparts in this file.
- * - Java handleStringTag() gathers the key/value pairs that it's interested in.
- * - once processFile returns and we're back in Java code in doScanFile(), it calls
- *   Java MyMediaScannerClient.endFile(), which takes all the data that's been
- *   gathered and inserts an entry in to the database.
- *
- * In summary:
- * Java MediaScannerService calls
- * Java MediaScanner scanDirectories, which calls
- * Java MediaScanner processDirectory (native method), which calls
- * native MediaScanner processDirectory, which calls
- * native MyMediaScannerClient scanFile, which calls
- * Java MyMediaScannerClient scanFile, which calls
- * Java MediaScannerClient doScanFile, which calls
- * Java MediaScanner processFile (native method), which calls
- * native MediaScanner processFile, which calls
- * native parseMP3, parseMP4, parseMidi, parseOgg or parseWMA, which calls
- * native MyMediaScanner handleStringTag, which calls
- * Java MyMediaScanner handleStringTag.
- * Once MediaScanner processFile returns, an entry is inserted in to the database.
- *
- * The MediaScanner class is not thread-safe, so it should only be used in a single threaded manner.
- *
- * {@hide}
- *
+ * @hide
  * @deprecated this media scanner has served faithfully for many years, but it's
  *             become tedious to test and maintain, mainly due to the way it
  *             weaves obscurely between managed and native code. It's been
@@ -125,1876 +33,182 @@
  */
 @Deprecated
 public class MediaScanner implements AutoCloseable {
-    static {
-        System.loadLibrary("media_jni");
-        native_init();
-    }
-
-    private final static String TAG = "MediaScanner";
-
-    @UnsupportedAppUsage
+    @Deprecated
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "All scanning requests should be performed through {@link android.media.MediaScannerConnection}")
     private static final String[] FILES_PRESCAN_PROJECTION = new String[] {
-            Files.FileColumns._ID, // 0
-            Files.FileColumns.DATA, // 1
-            Files.FileColumns.FORMAT, // 2
-            Files.FileColumns.DATE_MODIFIED, // 3
-            Files.FileColumns.MEDIA_TYPE, // 4
     };
 
-    private static final String[] ID_PROJECTION = new String[] {
-            Files.FileColumns._ID,
-    };
-
-    private static final int FILES_PRESCAN_ID_COLUMN_INDEX = 0;
-    private static final int FILES_PRESCAN_PATH_COLUMN_INDEX = 1;
-    private static final int FILES_PRESCAN_FORMAT_COLUMN_INDEX = 2;
-    private static final int FILES_PRESCAN_DATE_MODIFIED_COLUMN_INDEX = 3;
-    private static final int FILES_PRESCAN_MEDIA_TYPE_COLUMN_INDEX = 4;
-
-    private static final String[] PLAYLIST_MEMBERS_PROJECTION = new String[] {
-            Audio.Playlists.Members.PLAYLIST_ID, // 0
-     };
-
-    private static final int ID_PLAYLISTS_COLUMN_INDEX = 0;
-    private static final int PATH_PLAYLISTS_COLUMN_INDEX = 1;
-    private static final int DATE_MODIFIED_PLAYLISTS_COLUMN_INDEX = 2;
-
-    private static final String RINGTONES_DIR = "/ringtones/";
-    private static final String NOTIFICATIONS_DIR = "/notifications/";
-    private static final String ALARMS_DIR = "/alarms/";
-    private static final String MUSIC_DIR = "/music/";
-    private static final String PODCASTS_DIR = "/podcasts/";
-    private static final String AUDIOBOOKS_DIR = "/audiobooks/";
-
-    public static final String SCANNED_BUILD_PREFS_NAME = "MediaScanBuild";
-    public static final String LAST_INTERNAL_SCAN_FINGERPRINT = "lastScanFingerprint";
-    private static final String SYSTEM_SOUNDS_DIR = Environment.getRootDirectory() + "/media/audio";
-    private static final String OEM_SOUNDS_DIR = Environment.getOemDirectory() + "/media/audio";
-    private static final String PRODUCT_SOUNDS_DIR = Environment.getProductDirectory() + "/media/audio";
-    private static String sLastInternalScanFingerprint;
-
-    private static final String[] ID3_GENRES = {
-        // ID3v1 Genres
-        "Blues",
-        "Classic Rock",
-        "Country",
-        "Dance",
-        "Disco",
-        "Funk",
-        "Grunge",
-        "Hip-Hop",
-        "Jazz",
-        "Metal",
-        "New Age",
-        "Oldies",
-        "Other",
-        "Pop",
-        "R&B",
-        "Rap",
-        "Reggae",
-        "Rock",
-        "Techno",
-        "Industrial",
-        "Alternative",
-        "Ska",
-        "Death Metal",
-        "Pranks",
-        "Soundtrack",
-        "Euro-Techno",
-        "Ambient",
-        "Trip-Hop",
-        "Vocal",
-        "Jazz+Funk",
-        "Fusion",
-        "Trance",
-        "Classical",
-        "Instrumental",
-        "Acid",
-        "House",
-        "Game",
-        "Sound Clip",
-        "Gospel",
-        "Noise",
-        "AlternRock",
-        "Bass",
-        "Soul",
-        "Punk",
-        "Space",
-        "Meditative",
-        "Instrumental Pop",
-        "Instrumental Rock",
-        "Ethnic",
-        "Gothic",
-        "Darkwave",
-        "Techno-Industrial",
-        "Electronic",
-        "Pop-Folk",
-        "Eurodance",
-        "Dream",
-        "Southern Rock",
-        "Comedy",
-        "Cult",
-        "Gangsta",
-        "Top 40",
-        "Christian Rap",
-        "Pop/Funk",
-        "Jungle",
-        "Native American",
-        "Cabaret",
-        "New Wave",
-        "Psychadelic",
-        "Rave",
-        "Showtunes",
-        "Trailer",
-        "Lo-Fi",
-        "Tribal",
-        "Acid Punk",
-        "Acid Jazz",
-        "Polka",
-        "Retro",
-        "Musical",
-        "Rock & Roll",
-        "Hard Rock",
-        // The following genres are Winamp extensions
-        "Folk",
-        "Folk-Rock",
-        "National Folk",
-        "Swing",
-        "Fast Fusion",
-        "Bebob",
-        "Latin",
-        "Revival",
-        "Celtic",
-        "Bluegrass",
-        "Avantgarde",
-        "Gothic Rock",
-        "Progressive Rock",
-        "Psychedelic Rock",
-        "Symphonic Rock",
-        "Slow Rock",
-        "Big Band",
-        "Chorus",
-        "Easy Listening",
-        "Acoustic",
-        "Humour",
-        "Speech",
-        "Chanson",
-        "Opera",
-        "Chamber Music",
-        "Sonata",
-        "Symphony",
-        "Booty Bass",
-        "Primus",
-        "Porn Groove",
-        "Satire",
-        "Slow Jam",
-        "Club",
-        "Tango",
-        "Samba",
-        "Folklore",
-        "Ballad",
-        "Power Ballad",
-        "Rhythmic Soul",
-        "Freestyle",
-        "Duet",
-        "Punk Rock",
-        "Drum Solo",
-        "A capella",
-        "Euro-House",
-        "Dance Hall",
-        // The following ones seem to be fairly widely supported as well
-        "Goa",
-        "Drum & Bass",
-        "Club-House",
-        "Hardcore",
-        "Terror",
-        "Indie",
-        "Britpop",
-        null,
-        "Polsk Punk",
-        "Beat",
-        "Christian Gangsta",
-        "Heavy Metal",
-        "Black Metal",
-        "Crossover",
-        "Contemporary Christian",
-        "Christian Rock",
-        "Merengue",
-        "Salsa",
-        "Thrash Metal",
-        "Anime",
-        "JPop",
-        "Synthpop",
-        // 148 and up don't seem to have been defined yet.
-    };
-
-    private long mNativeContext;
-    @UnsupportedAppUsage
+    @Deprecated
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "All scanning requests should be performed through {@link android.media.MediaScannerConnection}")
     private final Context mContext;
-    @UnsupportedAppUsage
+    @Deprecated
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "All scanning requests should be performed through {@link android.media.MediaScannerConnection}")
     private final String mPackageName;
-    private final String mVolumeName;
-    private final ContentProviderClient mMediaProvider;
-    @UnsupportedAppUsage
+    @Deprecated
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "All scanning requests should be performed through {@link android.media.MediaScannerConnection}")
     private final Uri mAudioUri;
-    private final Uri mVideoUri;
-    private final Uri mImagesUri;
-    private final Uri mPlaylistsUri;
-    @UnsupportedAppUsage
+    @Deprecated
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "All scanning requests should be performed through {@link android.media.MediaScannerConnection}")
     private final Uri mFilesUri;
-    private final Uri mFilesFullUri;
-    private final boolean mProcessPlaylists;
-    private final boolean mProcessGenres;
-    private int mMtpObjectHandle;
 
-    private final AtomicBoolean mClosed = new AtomicBoolean();
-    private final CloseGuard mCloseGuard = CloseGuard.get();
-
-    /** whether to use bulk inserts or individual inserts for each item */
-    private static final boolean ENABLE_BULK_INSERTS = true;
-
-    // used when scanning the image database so we know whether we have to prune
-    // old thumbnail files
-    private int mOriginalCount;
-    /** Whether the scanner has set a default sound for the ringer ringtone. */
-    private boolean mDefaultRingtoneSet;
-    /** Whether the scanner has set a default sound for the notification ringtone. */
-    private boolean mDefaultNotificationSet;
-    /** Whether the scanner has set a default sound for the alarm ringtone. */
-    private boolean mDefaultAlarmSet;
-    /** The filename for the default sound for the ringer ringtone. */
-    @UnsupportedAppUsage
+    @Deprecated
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "All scanning requests should be performed through {@link android.media.MediaScannerConnection}")
     private String mDefaultRingtoneFilename;
-    /** The filename for the default sound for the notification ringtone. */
-    @UnsupportedAppUsage
+    @Deprecated
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "All scanning requests should be performed through {@link android.media.MediaScannerConnection}")
     private String mDefaultNotificationFilename;
-    /** The filename for the default sound for the alarm ringtone. */
-    @UnsupportedAppUsage
+    @Deprecated
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "All scanning requests should be performed through {@link android.media.MediaScannerConnection}")
     private String mDefaultAlarmAlertFilename;
-    /**
-     * The prefix for system properties that define the default sound for
-     * ringtones. Concatenate the name of the setting from Settings
-     * to get the full system property.
-     */
-    private static final String DEFAULT_RINGTONE_PROPERTY_PREFIX = "ro.config.";
-
-    private final BitmapFactory.Options mBitmapOptions = new BitmapFactory.Options();
 
     private static class FileEntry {
-        @UnsupportedAppUsage
+        @Deprecated
+        @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "All scanning requests should be performed through {@link android.media.MediaScannerConnection}")
         long mRowId;
-        String mPath;
-        long mLastModified;
-        int mFormat;
-        int mMediaType;
-        @UnsupportedAppUsage
+        @Deprecated
+        @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "All scanning requests should be performed through {@link android.media.MediaScannerConnection}")
         boolean mLastModifiedChanged;
 
-        /** @deprecated kept intact for lame apps using reflection */
         @Deprecated
-        @UnsupportedAppUsage
+        @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "All scanning requests should be performed through {@link android.media.MediaScannerConnection}")
         FileEntry(long rowId, String path, long lastModified, int format) {
-            this(rowId, path, lastModified, format, FileColumns.MEDIA_TYPE_NONE);
-        }
-
-        FileEntry(long rowId, String path, long lastModified, int format, int mediaType) {
-            mRowId = rowId;
-            mPath = path;
-            mLastModified = lastModified;
-            mFormat = format;
-            mMediaType = mediaType;
-            mLastModifiedChanged = false;
-        }
-
-        @Override
-        public String toString() {
-            return mPath + " mRowId: " + mRowId;
+            throw new UnsupportedOperationException();
         }
     }
 
-    private static class PlaylistEntry {
-        String path;
-        long bestmatchid;
-        int bestmatchlevel;
-    }
-
-    private final ArrayList<PlaylistEntry> mPlaylistEntries = new ArrayList<>();
-    private final ArrayList<FileEntry> mPlayLists = new ArrayList<>();
-
-    @UnsupportedAppUsage
+    @Deprecated
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "All scanning requests should be performed through {@link android.media.MediaScannerConnection}")
     private MediaInserter mMediaInserter;
 
-    private DrmManagerClient mDrmManagerClient = null;
-
-    @UnsupportedAppUsage
+    @Deprecated
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "All scanning requests should be performed through {@link android.media.MediaScannerConnection}")
     public MediaScanner(Context c, String volumeName) {
-        native_setup();
-        mContext = c;
-        mPackageName = c.getPackageName();
-        mVolumeName = volumeName;
-
-        mBitmapOptions.inSampleSize = 1;
-        mBitmapOptions.inJustDecodeBounds = true;
-
-        setDefaultRingtoneFileNames();
-
-        mMediaProvider = mContext.getContentResolver()
-                .acquireContentProviderClient(MediaStore.AUTHORITY);
-
-        if (sLastInternalScanFingerprint == null) {
-            final SharedPreferences scanSettings =
-                    mContext.getSharedPreferences(SCANNED_BUILD_PREFS_NAME, Context.MODE_PRIVATE);
-            sLastInternalScanFingerprint =
-                    scanSettings.getString(LAST_INTERNAL_SCAN_FINGERPRINT, new String());
-        }
-
-        mAudioUri = Audio.Media.getContentUri(volumeName);
-        mVideoUri = Video.Media.getContentUri(volumeName);
-        mImagesUri = Images.Media.getContentUri(volumeName);
-        mFilesUri = Files.getContentUri(volumeName);
-
-        Uri filesFullUri = mFilesUri.buildUpon().appendQueryParameter("nonotify", "1").build();
-        filesFullUri = MediaStore.setIncludePending(filesFullUri);
-        filesFullUri = MediaStore.setIncludeTrashed(filesFullUri);
-        mFilesFullUri = filesFullUri;
-
-        if (!volumeName.equals("internal")) {
-            // we only support playlists on external media
-            mProcessPlaylists = true;
-            mProcessGenres = true;
-            mPlaylistsUri = Playlists.getContentUri(volumeName);
-        } else {
-            mProcessPlaylists = false;
-            mProcessGenres = false;
-            mPlaylistsUri = null;
-        }
-
-        final Locale locale = mContext.getResources().getConfiguration().locale;
-        if (locale != null) {
-            String language = locale.getLanguage();
-            String country = locale.getCountry();
-            if (language != null) {
-                if (country != null) {
-                    setLocale(language + "_" + country);
-                } else {
-                    setLocale(language);
-                }
-            }
-        }
-
-        mCloseGuard.open("close");
+        throw new UnsupportedOperationException();
     }
 
-    private void setDefaultRingtoneFileNames() {
-        mDefaultRingtoneFilename = SystemProperties.get(DEFAULT_RINGTONE_PROPERTY_PREFIX
-                + Settings.System.RINGTONE);
-        mDefaultNotificationFilename = SystemProperties.get(DEFAULT_RINGTONE_PROPERTY_PREFIX
-                + Settings.System.NOTIFICATION_SOUND);
-        mDefaultAlarmAlertFilename = SystemProperties.get(DEFAULT_RINGTONE_PROPERTY_PREFIX
-                + Settings.System.ALARM_ALERT);
-    }
-
-    @UnsupportedAppUsage
+    @Deprecated
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "All scanning requests should be performed through {@link android.media.MediaScannerConnection}")
     private final MyMediaScannerClient mClient = new MyMediaScannerClient();
 
-    @UnsupportedAppUsage
+    @Deprecated
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "All scanning requests should be performed through {@link android.media.MediaScannerConnection}")
     private boolean isDrmEnabled() {
-        String prop = SystemProperties.get("drm.service.enabled");
-        return prop != null && prop.equals("true");
+        throw new UnsupportedOperationException();
     }
 
     private class MyMediaScannerClient implements MediaScannerClient {
-
-        private final SimpleDateFormat mDateFormatter;
-
-        private String mArtist;
-        private String mAlbumArtist;    // use this if mArtist is missing
-        private String mAlbum;
-        private String mTitle;
-        private String mComposer;
-        private String mGenre;
-        @UnsupportedAppUsage
-        private String mMimeType;
-        /** @deprecated file types no longer exist */
         @Deprecated
-        @UnsupportedAppUsage
+        @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "All scanning requests should be performed through {@link android.media.MediaScannerConnection}")
+        private String mMimeType;
+        @Deprecated
+        @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "All scanning requests should be performed through {@link android.media.MediaScannerConnection}")
         private int mFileType;
-        private int mTrack;
-        private int mYear;
-        private int mDuration;
-        @UnsupportedAppUsage
+        @Deprecated
+        @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "All scanning requests should be performed through {@link android.media.MediaScannerConnection}")
         private String mPath;
-        private long mDate;
-        private long mLastModified;
-        private long mFileSize;
-        private String mWriter;
-        private int mCompilation;
-        @UnsupportedAppUsage
+        @Deprecated
+        @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "All scanning requests should be performed through {@link android.media.MediaScannerConnection}")
         private boolean mIsDrm;
-        @UnsupportedAppUsage
-        private boolean mNoMedia;   // flag to suppress file from appearing in media tables
-        private boolean mScanSuccess;
-        private int mWidth;
-        private int mHeight;
-        private int mColorStandard;
-        private int mColorTransfer;
-        private int mColorRange;
+        @Deprecated
+        @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "All scanning requests should be performed through {@link android.media.MediaScannerConnection}")
+        private boolean mNoMedia;
 
         public MyMediaScannerClient() {
-            mDateFormatter = new SimpleDateFormat("yyyyMMdd'T'HHmmss");
-            mDateFormatter.setTimeZone(TimeZone.getTimeZone("UTC"));
+            throw new UnsupportedOperationException();
         }
 
-        @UnsupportedAppUsage
+        @Deprecated
+        @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "All scanning requests should be performed through {@link android.media.MediaScannerConnection}")
         public FileEntry beginFile(String path, String mimeType, long lastModified,
                 long fileSize, boolean isDirectory, boolean noMedia) {
-            mMimeType = mimeType;
-            mFileSize = fileSize;
-            mIsDrm = false;
-            mScanSuccess = true;
-
-            if (!isDirectory) {
-                if (!noMedia && isNoMediaFile(path)) {
-                    noMedia = true;
-                }
-                mNoMedia = noMedia;
-
-                // if mimeType was not specified, compute file type based on file extension.
-                if (mMimeType == null) {
-                    mMimeType = MediaFile.getMimeTypeForFile(path);
-                }
-
-                if (isDrmEnabled() && MediaFile.isDrmMimeType(mMimeType)) {
-                    getMimeTypeFromDrm(path);
-                }
-            }
-
-            FileEntry entry = makeEntryFor(path);
-            // add some slack to avoid a rounding error
-            long delta = (entry != null) ? (lastModified - entry.mLastModified) : 0;
-            boolean wasModified = delta > 1 || delta < -1;
-            if (entry == null || wasModified) {
-                if (wasModified) {
-                    entry.mLastModified = lastModified;
-                } else {
-                    entry = new FileEntry(0, path, lastModified,
-                            (isDirectory ? MtpConstants.FORMAT_ASSOCIATION : 0),
-                            FileColumns.MEDIA_TYPE_NONE);
-                }
-                entry.mLastModifiedChanged = true;
-            }
-
-            if (mProcessPlaylists && MediaFile.isPlayListMimeType(mMimeType)) {
-                mPlayLists.add(entry);
-                // we don't process playlists in the main scan, so return null
-                return null;
-            }
-
-            // clear all the metadata
-            mArtist = null;
-            mAlbumArtist = null;
-            mAlbum = null;
-            mTitle = null;
-            mComposer = null;
-            mGenre = null;
-            mTrack = 0;
-            mYear = 0;
-            mDuration = 0;
-            mPath = path;
-            mDate = 0;
-            mLastModified = lastModified;
-            mWriter = null;
-            mCompilation = 0;
-            mWidth = 0;
-            mHeight = 0;
-            mColorStandard = -1;
-            mColorTransfer = -1;
-            mColorRange = -1;
-
-            return entry;
+            throw new UnsupportedOperationException();
         }
 
-        @Override
-        @UnsupportedAppUsage
+        @Deprecated
+        @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "All scanning requests should be performed through {@link android.media.MediaScannerConnection}")
         public void scanFile(String path, long lastModified, long fileSize,
                 boolean isDirectory, boolean noMedia) {
-            // This is the callback funtion from native codes.
-            // Log.v(TAG, "scanFile: "+path);
-            doScanFile(path, null, lastModified, fileSize, isDirectory, false, noMedia);
+            throw new UnsupportedOperationException();
         }
 
-        @UnsupportedAppUsage
+        @Deprecated
+        @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "All scanning requests should be performed through {@link android.media.MediaScannerConnection}")
         public Uri doScanFile(String path, String mimeType, long lastModified,
                 long fileSize, boolean isDirectory, boolean scanAlways, boolean noMedia) {
-            Uri result = null;
-//            long t1 = System.currentTimeMillis();
-            try {
-                FileEntry entry = beginFile(path, mimeType, lastModified,
-                        fileSize, isDirectory, noMedia);
-
-                if (entry == null) {
-                    return null;
-                }
-
-                // if this file was just inserted via mtp, set the rowid to zero
-                // (even though it already exists in the database), to trigger
-                // the correct code path for updating its entry
-                if (mMtpObjectHandle != 0) {
-                    entry.mRowId = 0;
-                }
-
-                if (entry.mPath != null) {
-                    if (((!mDefaultNotificationSet &&
-                                doesPathHaveFilename(entry.mPath, mDefaultNotificationFilename))
-                        || (!mDefaultRingtoneSet &&
-                                doesPathHaveFilename(entry.mPath, mDefaultRingtoneFilename))
-                        || (!mDefaultAlarmSet &&
-                                doesPathHaveFilename(entry.mPath, mDefaultAlarmAlertFilename)))) {
-                        Log.w(TAG, "forcing rescan of " + entry.mPath +
-                                "since ringtone setting didn't finish");
-                        scanAlways = true;
-                    } else if (isSystemSoundWithMetadata(entry.mPath)
-                            && !Build.FINGERPRINT.equals(sLastInternalScanFingerprint)) {
-                        // file is located on the system partition where the date cannot be trusted:
-                        // rescan if the build fingerprint has changed since the last scan.
-                        Log.i(TAG, "forcing rescan of " + entry.mPath
-                                + " since build fingerprint changed");
-                        scanAlways = true;
-                    }
-                }
-
-                // rescan for metadata if file was modified since last scan
-                if (entry != null && (entry.mLastModifiedChanged || scanAlways)) {
-                    if (noMedia) {
-                        result = endFile(entry, false, false, false, false, false, false);
-                    } else {
-                        boolean isaudio = MediaFile.isAudioMimeType(mMimeType);
-                        boolean isvideo = MediaFile.isVideoMimeType(mMimeType);
-                        boolean isimage = MediaFile.isImageMimeType(mMimeType);
-
-                        if (isaudio || isvideo || isimage) {
-                            path = Environment.maybeTranslateEmulatedPathToInternal(new File(path))
-                                    .getAbsolutePath();
-                        }
-
-                        // we only extract metadata for audio and video files
-                        if (isaudio || isvideo) {
-                            mScanSuccess = processFile(path, mimeType, this);
-                        }
-
-                        if (isimage) {
-                            mScanSuccess = processImageFile(path);
-                        }
-
-                        String lowpath = path.toLowerCase(Locale.ROOT);
-                        boolean ringtones = mScanSuccess && (lowpath.indexOf(RINGTONES_DIR) > 0);
-                        boolean notifications = mScanSuccess &&
-                                (lowpath.indexOf(NOTIFICATIONS_DIR) > 0);
-                        boolean alarms = mScanSuccess && (lowpath.indexOf(ALARMS_DIR) > 0);
-                        boolean podcasts = mScanSuccess && (lowpath.indexOf(PODCASTS_DIR) > 0);
-                        boolean audiobooks = mScanSuccess && (lowpath.indexOf(AUDIOBOOKS_DIR) > 0);
-                        boolean music = mScanSuccess && ((lowpath.indexOf(MUSIC_DIR) > 0) ||
-                            (!ringtones && !notifications && !alarms && !podcasts && !audiobooks));
-
-                        result = endFile(entry, ringtones, notifications, alarms, podcasts,
-                                audiobooks, music);
-                    }
-                }
-            } catch (RemoteException e) {
-                Log.e(TAG, "RemoteException in MediaScanner.scanFile()", e);
-            }
-//            long t2 = System.currentTimeMillis();
-//            Log.v(TAG, "scanFile: " + path + " took " + (t2-t1));
-            return result;
+            throw new UnsupportedOperationException();
         }
 
-        private long parseDate(String date) {
-            try {
-              return mDateFormatter.parse(date).getTime();
-            } catch (ParseException e) {
-              return 0;
-            }
-        }
-
-        private int parseSubstring(String s, int start, int defaultValue) {
-            int length = s.length();
-            if (start == length) return defaultValue;
-
-            char ch = s.charAt(start++);
-            // return defaultValue if we have no integer at all
-            if (ch < '0' || ch > '9') return defaultValue;
-
-            int result = ch - '0';
-            while (start < length) {
-                ch = s.charAt(start++);
-                if (ch < '0' || ch > '9') return result;
-                result = result * 10 + (ch - '0');
-            }
-
-            return result;
-        }
-
-        @UnsupportedAppUsage
+        @Deprecated
+        @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "All scanning requests should be performed through {@link android.media.MediaScannerConnection}")
         public void handleStringTag(String name, String value) {
-            if (name.equalsIgnoreCase("title") || name.startsWith("title;")) {
-                // Don't trim() here, to preserve the special \001 character
-                // used to force sorting. The media provider will trim() before
-                // inserting the title in to the database.
-                mTitle = value;
-            } else if (name.equalsIgnoreCase("artist") || name.startsWith("artist;")) {
-                mArtist = value.trim();
-            } else if (name.equalsIgnoreCase("albumartist") || name.startsWith("albumartist;")
-                    || name.equalsIgnoreCase("band") || name.startsWith("band;")) {
-                mAlbumArtist = value.trim();
-            } else if (name.equalsIgnoreCase("album") || name.startsWith("album;")) {
-                mAlbum = value.trim();
-            } else if (name.equalsIgnoreCase("composer") || name.startsWith("composer;")) {
-                mComposer = value.trim();
-            } else if (mProcessGenres &&
-                    (name.equalsIgnoreCase("genre") || name.startsWith("genre;"))) {
-                mGenre = getGenreName(value);
-            } else if (name.equalsIgnoreCase("year") || name.startsWith("year;")) {
-                mYear = parseSubstring(value, 0, 0);
-            } else if (name.equalsIgnoreCase("tracknumber") || name.startsWith("tracknumber;")) {
-                // track number might be of the form "2/12"
-                // we just read the number before the slash
-                int num = parseSubstring(value, 0, 0);
-                mTrack = (mTrack / 1000) * 1000 + num;
-            } else if (name.equalsIgnoreCase("discnumber") ||
-                    name.equals("set") || name.startsWith("set;")) {
-                // set number might be of the form "1/3"
-                // we just read the number before the slash
-                int num = parseSubstring(value, 0, 0);
-                mTrack = (num * 1000) + (mTrack % 1000);
-            } else if (name.equalsIgnoreCase("duration")) {
-                mDuration = parseSubstring(value, 0, 0);
-            } else if (name.equalsIgnoreCase("writer") || name.startsWith("writer;")) {
-                mWriter = value.trim();
-            } else if (name.equalsIgnoreCase("compilation")) {
-                mCompilation = parseSubstring(value, 0, 0);
-            } else if (name.equalsIgnoreCase("isdrm")) {
-                mIsDrm = (parseSubstring(value, 0, 0) == 1);
-            } else if (name.equalsIgnoreCase("date")) {
-                mDate = parseDate(value);
-            } else if (name.equalsIgnoreCase("width")) {
-                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 + ")");
-            }
+            throw new UnsupportedOperationException();
         }
 
-        private boolean convertGenreCode(String input, String expected) {
-            String output = getGenreName(input);
-            if (output.equals(expected)) {
-                return true;
-            } else {
-                Log.d(TAG, "'" + input + "' -> '" + output + "', expected '" + expected + "'");
-                return false;
-            }
-        }
-        private void testGenreNameConverter() {
-            convertGenreCode("2", "Country");
-            convertGenreCode("(2)", "Country");
-            convertGenreCode("(2", "(2");
-            convertGenreCode("2 Foo", "Country");
-            convertGenreCode("(2) Foo", "Country");
-            convertGenreCode("(2 Foo", "(2 Foo");
-            convertGenreCode("2Foo", "2Foo");
-            convertGenreCode("(2)Foo", "Country");
-            convertGenreCode("200 Foo", "Foo");
-            convertGenreCode("(200) Foo", "Foo");
-            convertGenreCode("200Foo", "200Foo");
-            convertGenreCode("(200)Foo", "Foo");
-            convertGenreCode("200)Foo", "200)Foo");
-            convertGenreCode("200) Foo", "200) Foo");
-        }
-
-        public String getGenreName(String genreTagValue) {
-
-            if (genreTagValue == null) {
-                return null;
-            }
-            final int length = genreTagValue.length();
-
-            if (length > 0) {
-                boolean parenthesized = false;
-                StringBuffer number = new StringBuffer();
-                int i = 0;
-                for (; i < length; ++i) {
-                    char c = genreTagValue.charAt(i);
-                    if (i == 0 && c == '(') {
-                        parenthesized = true;
-                    } else if (Character.isDigit(c)) {
-                        number.append(c);
-                    } else {
-                        break;
-                    }
-                }
-                char charAfterNumber = i < length ? genreTagValue.charAt(i) : ' ';
-                if ((parenthesized && charAfterNumber == ')')
-                        || !parenthesized && Character.isWhitespace(charAfterNumber)) {
-                    try {
-                        short genreIndex = Short.parseShort(number.toString());
-                        if (genreIndex >= 0) {
-                            if (genreIndex < ID3_GENRES.length && ID3_GENRES[genreIndex] != null) {
-                                return ID3_GENRES[genreIndex];
-                            } else if (genreIndex == 0xFF) {
-                                return null;
-                            } else if (genreIndex < 0xFF && (i + 1) < length) {
-                                // genre is valid but unknown,
-                                // if there is a string after the value we take it
-                                if (parenthesized && charAfterNumber == ')') {
-                                    i++;
-                                }
-                                String ret = genreTagValue.substring(i).trim();
-                                if (ret.length() != 0) {
-                                    return ret;
-                                }
-                            } else {
-                                // else return the number, without parentheses
-                                return number.toString();
-                            }
-                        }
-                    } catch (NumberFormatException e) {
-                    }
-                }
-            }
-
-            return genreTagValue;
-        }
-
-        private boolean processImageFile(String path) {
-            try {
-                mBitmapOptions.outWidth = 0;
-                mBitmapOptions.outHeight = 0;
-                BitmapFactory.decodeFile(path, mBitmapOptions);
-                mWidth = mBitmapOptions.outWidth;
-                mHeight = mBitmapOptions.outHeight;
-                return mWidth > 0 && mHeight > 0;
-            } catch (Throwable th) {
-                // ignore;
-            }
-            return false;
-        }
-
-        @UnsupportedAppUsage
+        @Deprecated
+        @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "All scanning requests should be performed through {@link android.media.MediaScannerConnection}")
         public void setMimeType(String mimeType) {
-            if ("audio/mp4".equals(mMimeType) &&
-                    mimeType.startsWith("video")) {
-                // for feature parity with Donut, we force m4a files to keep the
-                // audio/mp4 mimetype, even if they are really "enhanced podcasts"
-                // with a video track
-                return;
-            }
-            mMimeType = mimeType;
+            throw new UnsupportedOperationException();
         }
 
-        /**
-         * Formats the data into a values array suitable for use with the Media
-         * Content Provider.
-         *
-         * @return a map of values
-         */
-        @UnsupportedAppUsage
+        @Deprecated
+        @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "All scanning requests should be performed through {@link android.media.MediaScannerConnection}")
         private ContentValues toValues() {
-            ContentValues map = new ContentValues();
-
-            map.put(MediaStore.MediaColumns.DATA, mPath);
-            map.put(MediaStore.MediaColumns.TITLE, mTitle);
-            map.put(MediaStore.MediaColumns.DATE_MODIFIED, mLastModified);
-            map.put(MediaStore.MediaColumns.SIZE, mFileSize);
-            map.put(MediaStore.MediaColumns.MIME_TYPE, mMimeType);
-            map.put(MediaStore.MediaColumns.IS_DRM, mIsDrm);
-            map.putNull(MediaStore.MediaColumns.HASH);
-
-            String resolution = null;
-            if (mWidth > 0 && mHeight > 0) {
-                map.put(MediaStore.MediaColumns.WIDTH, mWidth);
-                map.put(MediaStore.MediaColumns.HEIGHT, mHeight);
-                resolution = mWidth + "x" + mHeight;
-            }
-
-            if (!mNoMedia) {
-                if (MediaFile.isVideoMimeType(mMimeType)) {
-                    map.put(Video.Media.ARTIST, (mArtist != null && mArtist.length() > 0
-                            ? mArtist : MediaStore.UNKNOWN_STRING));
-                    map.put(Video.Media.ALBUM, (mAlbum != null && mAlbum.length() > 0
-                            ? mAlbum : MediaStore.UNKNOWN_STRING));
-                    map.put(Video.Media.DURATION, mDuration);
-                    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);
-                    }
-                } else if (MediaFile.isImageMimeType(mMimeType)) {
-                    // FIXME - add DESCRIPTION
-                } else if (MediaFile.isAudioMimeType(mMimeType)) {
-                    map.put(Audio.Media.ARTIST, (mArtist != null && mArtist.length() > 0) ?
-                            mArtist : MediaStore.UNKNOWN_STRING);
-                    map.put(Audio.Media.ALBUM_ARTIST, (mAlbumArtist != null &&
-                            mAlbumArtist.length() > 0) ? mAlbumArtist : null);
-                    map.put(Audio.Media.ALBUM, (mAlbum != null && mAlbum.length() > 0) ?
-                            mAlbum : MediaStore.UNKNOWN_STRING);
-                    map.put(Audio.Media.COMPOSER, mComposer);
-                    map.put(Audio.Media.GENRE, mGenre);
-                    if (mYear != 0) {
-                        map.put(Audio.Media.YEAR, mYear);
-                    }
-                    map.put(Audio.Media.TRACK, mTrack);
-                    map.put(Audio.Media.DURATION, mDuration);
-                    map.put(Audio.Media.COMPILATION, mCompilation);
-                }
-            }
-            return map;
+            throw new UnsupportedOperationException();
         }
 
-        @UnsupportedAppUsage
+        @Deprecated
+        @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "All scanning requests should be performed through {@link android.media.MediaScannerConnection}")
         private Uri endFile(FileEntry entry, boolean ringtones, boolean notifications,
                 boolean alarms, boolean podcasts, boolean audiobooks, boolean music)
                 throws RemoteException {
-            // update database
-
-            // use album artist if artist is missing
-            if (mArtist == null || mArtist.length() == 0) {
-                mArtist = mAlbumArtist;
-            }
-
-            ContentValues values = toValues();
-            String title = values.getAsString(MediaStore.MediaColumns.TITLE);
-            if (title == null || TextUtils.isEmpty(title.trim())) {
-                title = MediaFile.getFileTitle(values.getAsString(MediaStore.MediaColumns.DATA));
-                values.put(MediaStore.MediaColumns.TITLE, title);
-            }
-            String album = values.getAsString(Audio.Media.ALBUM);
-            if (MediaStore.UNKNOWN_STRING.equals(album)) {
-                album = values.getAsString(MediaStore.MediaColumns.DATA);
-                // extract last path segment before file name
-                int lastSlash = album.lastIndexOf('/');
-                if (lastSlash >= 0) {
-                    int previousSlash = 0;
-                    while (true) {
-                        int idx = album.indexOf('/', previousSlash + 1);
-                        if (idx < 0 || idx >= lastSlash) {
-                            break;
-                        }
-                        previousSlash = idx;
-                    }
-                    if (previousSlash != 0) {
-                        album = album.substring(previousSlash + 1, lastSlash);
-                        values.put(Audio.Media.ALBUM, album);
-                    }
-                }
-            }
-            long rowId = entry.mRowId;
-            if (MediaFile.isAudioMimeType(mMimeType) && (rowId == 0 || mMtpObjectHandle != 0)) {
-                // Only set these for new entries. For existing entries, they
-                // may have been modified later, and we want to keep the current
-                // values so that custom ringtones still show up in the ringtone
-                // picker.
-                values.put(Audio.Media.IS_RINGTONE, ringtones);
-                values.put(Audio.Media.IS_NOTIFICATION, notifications);
-                values.put(Audio.Media.IS_ALARM, alarms);
-                values.put(Audio.Media.IS_MUSIC, music);
-                values.put(Audio.Media.IS_PODCAST, podcasts);
-                values.put(Audio.Media.IS_AUDIOBOOK, audiobooks);
-            } else if (MediaFile.isExifMimeType(mMimeType) && !mNoMedia) {
-                ExifInterface exif = null;
-                try {
-                    exif = new ExifInterface(entry.mPath);
-                } catch (Exception ex) {
-                    // exif is null
-                }
-                if (exif != null) {
-                    long time = exif.getGpsDateTime();
-                    if (time != -1) {
-                        values.put(Images.Media.DATE_TAKEN, time);
-                    } else {
-                        // If no time zone information is available, we should consider using
-                        // EXIF local time as taken time if the difference between file time
-                        // and EXIF local time is not less than 1 Day, otherwise MediaProvider
-                        // will use file time as taken time.
-                        time = exif.getDateTime();
-                        if (time != -1 && Math.abs(mLastModified * 1000 - time) >= 86400000) {
-                            values.put(Images.Media.DATE_TAKEN, time);
-                        }
-                    }
-
-                    int orientation = exif.getAttributeInt(
-                        ExifInterface.TAG_ORIENTATION, -1);
-                    if (orientation != -1) {
-                        // We only recognize a subset of orientation tag values.
-                        int degree;
-                        switch(orientation) {
-                            case ExifInterface.ORIENTATION_ROTATE_90:
-                                degree = 90;
-                                break;
-                            case ExifInterface.ORIENTATION_ROTATE_180:
-                                degree = 180;
-                                break;
-                            case ExifInterface.ORIENTATION_ROTATE_270:
-                                degree = 270;
-                                break;
-                            default:
-                                degree = 0;
-                                break;
-                        }
-                        values.put(Images.Media.ORIENTATION, degree);
-                    }
-                }
-            }
-
-            Uri tableUri = mFilesUri;
-            int mediaType = FileColumns.MEDIA_TYPE_NONE;
-            MediaInserter inserter = mMediaInserter;
-            if (!mNoMedia) {
-                if (MediaFile.isVideoMimeType(mMimeType)) {
-                    tableUri = mVideoUri;
-                    mediaType = FileColumns.MEDIA_TYPE_VIDEO;
-                } else if (MediaFile.isImageMimeType(mMimeType)) {
-                    tableUri = mImagesUri;
-                    mediaType = FileColumns.MEDIA_TYPE_IMAGE;
-                } else if (MediaFile.isAudioMimeType(mMimeType)) {
-                    tableUri = mAudioUri;
-                    mediaType = FileColumns.MEDIA_TYPE_AUDIO;
-                } else if (MediaFile.isPlayListMimeType(mMimeType)) {
-                    tableUri = mPlaylistsUri;
-                    mediaType = FileColumns.MEDIA_TYPE_PLAYLIST;
-                }
-            }
-            Uri result = null;
-            boolean needToSetSettings = false;
-            // Setting a flag in order not to use bulk insert for the file related with
-            // notifications, ringtones, and alarms, because the rowId of the inserted file is
-            // needed.
-            if (notifications && !mDefaultNotificationSet) {
-                if (TextUtils.isEmpty(mDefaultNotificationFilename) ||
-                        doesPathHaveFilename(entry.mPath, mDefaultNotificationFilename)) {
-                    needToSetSettings = true;
-                }
-            } else if (ringtones && !mDefaultRingtoneSet) {
-                if (TextUtils.isEmpty(mDefaultRingtoneFilename) ||
-                        doesPathHaveFilename(entry.mPath, mDefaultRingtoneFilename)) {
-                    needToSetSettings = true;
-                }
-            } else if (alarms && !mDefaultAlarmSet) {
-                if (TextUtils.isEmpty(mDefaultAlarmAlertFilename) ||
-                        doesPathHaveFilename(entry.mPath, mDefaultAlarmAlertFilename)) {
-                    needToSetSettings = true;
-                }
-            }
-
-            if (rowId == 0) {
-                if (mMtpObjectHandle != 0) {
-                    values.put(MediaStore.MediaColumns.MEDIA_SCANNER_NEW_OBJECT_ID, mMtpObjectHandle);
-                }
-                if (tableUri == mFilesUri) {
-                    int format = entry.mFormat;
-                    if (format == 0) {
-                        format = MediaFile.getFormatCode(entry.mPath, mMimeType);
-                    }
-                    values.put(Files.FileColumns.FORMAT, format);
-                }
-                // New file, insert it.
-                // Directories need to be inserted before the files they contain, so they
-                // get priority when bulk inserting.
-                // If the rowId of the inserted file is needed, it gets inserted immediately,
-                // bypassing the bulk inserter.
-                if (inserter == null || needToSetSettings) {
-                    if (inserter != null) {
-                        inserter.flushAll();
-                    }
-                    result = mMediaProvider.insert(tableUri, values);
-                } else if (entry.mFormat == MtpConstants.FORMAT_ASSOCIATION) {
-                    inserter.insertwithPriority(tableUri, values);
-                } else {
-                    inserter.insert(tableUri, values);
-                }
-
-                if (result != null) {
-                    rowId = ContentUris.parseId(result);
-                    entry.mRowId = rowId;
-                }
-            } else {
-                // updated file
-                result = ContentUris.withAppendedId(tableUri, rowId);
-                // path should never change, and we want to avoid replacing mixed cased paths
-                // with squashed lower case paths
-                values.remove(MediaStore.MediaColumns.DATA);
-
-                if (!mNoMedia) {
-                    // Changing media type must be done as separate update
-                    if (mediaType != entry.mMediaType) {
-                        final ContentValues mediaTypeValues = new ContentValues();
-                        mediaTypeValues.put(FileColumns.MEDIA_TYPE, mediaType);
-                        mMediaProvider.update(ContentUris.withAppendedId(mFilesUri, rowId),
-                                mediaTypeValues, null, null);
-                    }
-                }
-
-                mMediaProvider.update(result, values, null, null);
-            }
-
-            if(needToSetSettings) {
-                if (notifications) {
-                    setRingtoneIfNotSet(Settings.System.NOTIFICATION_SOUND, tableUri, rowId);
-                    mDefaultNotificationSet = true;
-                } else if (ringtones) {
-                    setRingtoneIfNotSet(Settings.System.RINGTONE, tableUri, rowId);
-                    mDefaultRingtoneSet = true;
-                } else if (alarms) {
-                    setRingtoneIfNotSet(Settings.System.ALARM_ALERT, tableUri, rowId);
-                    mDefaultAlarmSet = true;
-                }
-            }
-
-            return result;
+            throw new UnsupportedOperationException();
         }
 
-        private boolean doesPathHaveFilename(String path, String filename) {
-            int pathFilenameStart = path.lastIndexOf(File.separatorChar) + 1;
-            int filenameLength = filename.length();
-            return path.regionMatches(pathFilenameStart, filename, 0, filenameLength) &&
-                    pathFilenameStart + filenameLength == path.length();
-        }
-
-        private void setRingtoneIfNotSet(String settingName, Uri uri, long rowId) {
-            if (wasRingtoneAlreadySet(settingName)) {
-                return;
-            }
-
-            ContentResolver cr = mContext.getContentResolver();
-            String existingSettingValue = Settings.System.getString(cr, settingName);
-            if (TextUtils.isEmpty(existingSettingValue)) {
-                final Uri settingUri = Settings.System.getUriFor(settingName);
-                final Uri ringtoneUri = ContentUris.withAppendedId(uri, rowId);
-                RingtoneManager.setActualDefaultRingtoneUri(mContext,
-                        RingtoneManager.getDefaultType(settingUri), ringtoneUri);
-            }
-            Settings.System.putInt(cr, settingSetIndicatorName(settingName), 1);
-        }
-
-        /** @deprecated file types no longer exist */
         @Deprecated
-        @UnsupportedAppUsage
+        @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "All scanning requests should be performed through {@link android.media.MediaScannerConnection}")
         private int getFileTypeFromDrm(String path) {
-            return 0;
-        }
-
-        private void getMimeTypeFromDrm(String path) {
-            mMimeType = null;
-
-            if (mDrmManagerClient == null) {
-                mDrmManagerClient = new DrmManagerClient(mContext);
-            }
-
-            if (mDrmManagerClient.canHandle(path, null)) {
-                mIsDrm = true;
-                mMimeType = mDrmManagerClient.getOriginalMimeType(path);
-            }
-
-            if (mMimeType == null) {
-                mMimeType = ContentResolver.MIME_TYPE_DEFAULT;
-            }
-        }
-
-    }; // end of anonymous MediaScannerClient instance
-
-    private static boolean isSystemSoundWithMetadata(String path) {
-        if (path.startsWith(SYSTEM_SOUNDS_DIR + ALARMS_DIR)
-                || path.startsWith(SYSTEM_SOUNDS_DIR + RINGTONES_DIR)
-                || path.startsWith(SYSTEM_SOUNDS_DIR + NOTIFICATIONS_DIR)
-                || path.startsWith(OEM_SOUNDS_DIR + ALARMS_DIR)
-                || path.startsWith(OEM_SOUNDS_DIR + RINGTONES_DIR)
-                || path.startsWith(OEM_SOUNDS_DIR + NOTIFICATIONS_DIR)
-                || path.startsWith(PRODUCT_SOUNDS_DIR + ALARMS_DIR)
-                || path.startsWith(PRODUCT_SOUNDS_DIR + RINGTONES_DIR)
-                || path.startsWith(PRODUCT_SOUNDS_DIR + NOTIFICATIONS_DIR)) {
-            return true;
-        }
-        return false;
-    }
-
-    private String settingSetIndicatorName(String base) {
-        return base + "_set";
-    }
-
-    private boolean wasRingtoneAlreadySet(String name) {
-        ContentResolver cr = mContext.getContentResolver();
-        String indicatorName = settingSetIndicatorName(name);
-        try {
-            return Settings.System.getInt(cr, indicatorName) != 0;
-        } catch (SettingNotFoundException e) {
-            return false;
+            throw new UnsupportedOperationException();
         }
     }
 
-    @UnsupportedAppUsage
+    @Deprecated
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "All scanning requests should be performed through {@link android.media.MediaScannerConnection}")
     private void prescan(String filePath, boolean prescanFiles) throws RemoteException {
-        Cursor c = null;
-        String where = null;
-        String[] selectionArgs = null;
-
-        mPlayLists.clear();
-
-        if (filePath != null) {
-            // query for only one file
-            where = MediaStore.Files.FileColumns._ID + ">?" +
-                " AND " + Files.FileColumns.DATA + "=?";
-            selectionArgs = new String[] { "", filePath };
-        } else {
-            where = MediaStore.Files.FileColumns._ID + ">?";
-            selectionArgs = new String[] { "" };
-        }
-
-        mDefaultRingtoneSet = wasRingtoneAlreadySet(Settings.System.RINGTONE);
-        mDefaultNotificationSet = wasRingtoneAlreadySet(Settings.System.NOTIFICATION_SOUND);
-        mDefaultAlarmSet = wasRingtoneAlreadySet(Settings.System.ALARM_ALERT);
-
-        // Tell the provider to not delete the file.
-        // If the file is truly gone the delete is unnecessary, and we want to avoid
-        // accidentally deleting files that are really there (this may happen if the
-        // filesystem is mounted and unmounted while the scanner is running).
-        Uri.Builder builder = mFilesUri.buildUpon();
-        builder.appendQueryParameter(MediaStore.PARAM_DELETE_DATA, "false");
-        MediaBulkDeleter deleter = new MediaBulkDeleter(mMediaProvider, builder.build());
-
-        // Build the list of files from the content provider
-        try {
-            if (prescanFiles) {
-                // First read existing files from the files table.
-                // Because we'll be deleting entries for missing files as we go,
-                // we need to query the database in small batches, to avoid problems
-                // with CursorWindow positioning.
-                long lastId = Long.MIN_VALUE;
-                Uri limitUri = mFilesUri.buildUpon()
-                        .appendQueryParameter(MediaStore.PARAM_LIMIT, "1000").build();
-
-                while (true) {
-                    selectionArgs[0] = "" + lastId;
-                    if (c != null) {
-                        c.close();
-                        c = null;
-                    }
-                    c = mMediaProvider.query(limitUri, FILES_PRESCAN_PROJECTION,
-                            where, selectionArgs, MediaStore.Files.FileColumns._ID, null);
-                    if (c == null) {
-                        break;
-                    }
-
-                    int num = c.getCount();
-
-                    if (num == 0) {
-                        break;
-                    }
-                    while (c.moveToNext()) {
-                        long rowId = c.getLong(FILES_PRESCAN_ID_COLUMN_INDEX);
-                        String path = c.getString(FILES_PRESCAN_PATH_COLUMN_INDEX);
-                        int format = c.getInt(FILES_PRESCAN_FORMAT_COLUMN_INDEX);
-                        long lastModified = c.getLong(FILES_PRESCAN_DATE_MODIFIED_COLUMN_INDEX);
-                        lastId = rowId;
-
-                        // Only consider entries with absolute path names.
-                        // This allows storing URIs in the database without the
-                        // media scanner removing them.
-                        if (path != null && path.startsWith("/")) {
-                            boolean exists = false;
-                            try {
-                                exists = Os.access(path, android.system.OsConstants.F_OK);
-                            } catch (ErrnoException e1) {
-                            }
-                            if (!exists && !MtpConstants.isAbstractObject(format)) {
-                                // do not delete missing playlists, since they may have been
-                                // modified by the user.
-                                // The user can delete them in the media player instead.
-                                // instead, clear the path and lastModified fields in the row
-                                String mimeType = MediaFile.getMimeTypeForFile(path);
-                                if (!MediaFile.isPlayListMimeType(mimeType)) {
-                                    deleter.delete(rowId);
-                                    if (path.toLowerCase(Locale.US).endsWith("/.nomedia")) {
-                                        deleter.flush();
-                                        String parent = new File(path).getParent();
-                                        mMediaProvider.call(MediaStore.UNHIDE_CALL, parent, null);
-                                    }
-                                }
-                            }
-                        }
-                    }
-                }
-            }
-        }
-        finally {
-            if (c != null) {
-                c.close();
-            }
-            deleter.flush();
-        }
-
-        // compute original size of images
-        mOriginalCount = 0;
-        c = mMediaProvider.query(mImagesUri, ID_PROJECTION, null, null, null, null);
-        if (c != null) {
-            mOriginalCount = c.getCount();
-            c.close();
-        }
+        throw new UnsupportedOperationException();
     }
 
-    static class MediaBulkDeleter {
-        StringBuilder whereClause = new StringBuilder();
-        ArrayList<String> whereArgs = new ArrayList<String>(100);
-        final ContentProviderClient mProvider;
-        final Uri mBaseUri;
-
-        public MediaBulkDeleter(ContentProviderClient provider, Uri baseUri) {
-            mProvider = provider;
-            mBaseUri = baseUri;
-        }
-
-        public void delete(long id) throws RemoteException {
-            if (whereClause.length() != 0) {
-                whereClause.append(",");
-            }
-            whereClause.append("?");
-            whereArgs.add("" + id);
-            if (whereArgs.size() > 100) {
-                flush();
-            }
-        }
-        public void flush() throws RemoteException {
-            int size = whereArgs.size();
-            if (size > 0) {
-                String [] foo = new String [size];
-                foo = whereArgs.toArray(foo);
-                int numrows = mProvider.delete(mBaseUri,
-                        MediaStore.MediaColumns._ID + " IN (" +
-                        whereClause.toString() + ")", foo);
-                //Log.i("@@@@@@@@@", "rows deleted: " + numrows);
-                whereClause.setLength(0);
-                whereArgs.clear();
-            }
-        }
-    }
-
-    @UnsupportedAppUsage
+    @Deprecated
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "All scanning requests should be performed through {@link android.media.MediaScannerConnection}")
     private void postscan(final String[] directories) throws RemoteException {
-
-        // handle playlists last, after we know what media files are on the storage.
-        if (mProcessPlaylists) {
-            processPlayLists();
-        }
-
-        // allow GC to clean up
-        mPlayLists.clear();
+        throw new UnsupportedOperationException();
     }
 
-    private void releaseResources() {
-        // release the DrmManagerClient resources
-        if (mDrmManagerClient != null) {
-            mDrmManagerClient.close();
-            mDrmManagerClient = null;
-        }
-    }
-
-    public void scanDirectories(String[] directories) {
-        try {
-            long start = System.currentTimeMillis();
-            prescan(null, true);
-            long prescan = System.currentTimeMillis();
-
-            if (ENABLE_BULK_INSERTS) {
-                // create MediaInserter for bulk inserts
-                mMediaInserter = new MediaInserter(mMediaProvider, 500);
-            }
-
-            for (int i = 0; i < directories.length; i++) {
-                processDirectory(directories[i], mClient);
-            }
-
-            if (ENABLE_BULK_INSERTS) {
-                // flush remaining inserts
-                mMediaInserter.flushAll();
-                mMediaInserter = null;
-            }
-
-            long scan = System.currentTimeMillis();
-            postscan(directories);
-            long end = System.currentTimeMillis();
-
-            if (false) {
-                Log.d(TAG, " prescan time: " + (prescan - start) + "ms\n");
-                Log.d(TAG, "    scan time: " + (scan - prescan) + "ms\n");
-                Log.d(TAG, "postscan time: " + (end - scan) + "ms\n");
-                Log.d(TAG, "   total time: " + (end - start) + "ms\n");
-            }
-        } catch (SQLException e) {
-            // this might happen if the SD card is removed while the media scanner is running
-            Log.e(TAG, "SQLException in MediaScanner.scan()", e);
-        } catch (UnsupportedOperationException e) {
-            // this might happen if the SD card is removed while the media scanner is running
-            Log.e(TAG, "UnsupportedOperationException in MediaScanner.scan()", e);
-        } catch (RemoteException e) {
-            Log.e(TAG, "RemoteException in MediaScanner.scan()", e);
-        } finally {
-            releaseResources();
-        }
-    }
-
-    // this function is used to scan a single file
-    @UnsupportedAppUsage
+    @Deprecated
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "All scanning requests should be performed through {@link android.media.MediaScannerConnection}")
     public Uri scanSingleFile(String path, String mimeType) {
-        try {
-            prescan(path, true);
-
-            File file = new File(path);
-            if (!file.exists() || !file.canRead()) {
-                return null;
-            }
-
-            // lastModified is in milliseconds on Files.
-            long lastModifiedSeconds = file.lastModified() / 1000;
-
-            // always scan the file, so we can return the content://media Uri for existing files
-            return mClient.doScanFile(path, mimeType, lastModifiedSeconds, file.length(),
-                    false, true, MediaScanner.isNoMediaPath(path));
-        } catch (RemoteException e) {
-            Log.e(TAG, "RemoteException in MediaScanner.scanFile()", e);
-            return null;
-        } finally {
-            releaseResources();
-        }
+        throw new UnsupportedOperationException();
     }
 
-    private static boolean isNoMediaFile(String path) {
-        File file = new File(path);
-        if (file.isDirectory()) return false;
-
-        // special case certain file names
-        // I use regionMatches() instead of substring() below
-        // to avoid memory allocation
-        int lastSlash = path.lastIndexOf('/');
-        if (lastSlash >= 0 && lastSlash + 2 < path.length()) {
-            // ignore those ._* files created by MacOS
-            if (path.regionMatches(lastSlash + 1, "._", 0, 2)) {
-                return true;
-            }
-
-            // ignore album art files created by Windows Media Player:
-            // Folder.jpg, AlbumArtSmall.jpg, AlbumArt_{...}_Large.jpg
-            // and AlbumArt_{...}_Small.jpg
-            if (path.regionMatches(true, path.length() - 4, ".jpg", 0, 4)) {
-                if (path.regionMatches(true, lastSlash + 1, "AlbumArt_{", 0, 10) ||
-                        path.regionMatches(true, lastSlash + 1, "AlbumArt.", 0, 9)) {
-                    return true;
-                }
-                int length = path.length() - lastSlash - 1;
-                if ((length == 17 && path.regionMatches(
-                        true, lastSlash + 1, "AlbumArtSmall", 0, 13)) ||
-                        (length == 10
-                         && path.regionMatches(true, lastSlash + 1, "Folder", 0, 6))) {
-                    return true;
-                }
-            }
-        }
-        return false;
-    }
-
-    private static HashMap<String,String> mNoMediaPaths = new HashMap<String,String>();
-    private static HashMap<String,String> mMediaPaths = new HashMap<String,String>();
-
-    /* MediaProvider calls this when a .nomedia file is added or removed */
-    public static void clearMediaPathCache(boolean clearMediaPaths, boolean clearNoMediaPaths) {
-        synchronized (MediaScanner.class) {
-            if (clearMediaPaths) {
-                mMediaPaths.clear();
-            }
-            if (clearNoMediaPaths) {
-                mNoMediaPaths.clear();
-            }
-        }
-    }
-
-    @UnsupportedAppUsage
+    @Deprecated
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "All scanning requests should be performed through {@link android.media.MediaScannerConnection}")
     public static boolean isNoMediaPath(String path) {
-        if (path == null) {
-            return false;
-        }
-        // return true if file or any parent directory has name starting with a dot
-        if (path.indexOf("/.") >= 0) {
-            return true;
-        }
-
-        int firstSlash = path.lastIndexOf('/');
-        if (firstSlash <= 0) {
-            return false;
-        }
-        String parent = path.substring(0,  firstSlash);
-
-        synchronized (MediaScanner.class) {
-            if (mNoMediaPaths.containsKey(parent)) {
-                return true;
-            } else if (!mMediaPaths.containsKey(parent)) {
-                // check to see if any parent directories have a ".nomedia" file
-                // start from 1 so we don't bother checking in the root directory
-                int offset = 1;
-                while (offset >= 0) {
-                    int slashIndex = path.indexOf('/', offset);
-                    if (slashIndex > offset) {
-                        slashIndex++; // move past slash
-                        File file = new File(path.substring(0, slashIndex) + ".nomedia");
-                        if (file.exists()) {
-                            // we have a .nomedia in one of the parent directories
-                            mNoMediaPaths.put(parent, "");
-                            return true;
-                        }
-                    }
-                    offset = slashIndex;
-                }
-                mMediaPaths.put(parent, "");
-            }
-        }
-
-        return isNoMediaFile(path);
+        throw new UnsupportedOperationException();
     }
 
-    public void scanMtpFile(String path, int objectHandle, int format) {
-        String mimeType = MediaFile.getMimeType(path, format);
-        File file = new File(path);
-        long lastModifiedSeconds = file.lastModified() / 1000;
-
-        if (!MediaFile.isAudioMimeType(mimeType) && !MediaFile.isVideoMimeType(mimeType) &&
-            !MediaFile.isImageMimeType(mimeType) && !MediaFile.isPlayListMimeType(mimeType) &&
-            !MediaFile.isDrmMimeType(mimeType)) {
-
-            // no need to use the media scanner, but we need to update last modified and file size
-            ContentValues values = new ContentValues();
-            values.put(Files.FileColumns.SIZE, file.length());
-            values.put(Files.FileColumns.DATE_MODIFIED, lastModifiedSeconds);
-            try {
-                String[] whereArgs = new String[] {  Integer.toString(objectHandle) };
-                mMediaProvider.update(Files.getMtpObjectsUri(mVolumeName), values,
-                        "_id=?", whereArgs);
-            } catch (RemoteException e) {
-                Log.e(TAG, "RemoteException in scanMtpFile", e);
-            }
-            return;
-        }
-
-        mMtpObjectHandle = objectHandle;
-        Cursor fileList = null;
-        try {
-            if (MediaFile.isPlayListMimeType(mimeType)) {
-                // build file cache so we can look up tracks in the playlist
-                prescan(null, true);
-
-                FileEntry entry = makeEntryFor(path);
-                if (entry != null) {
-                    fileList = mMediaProvider.query(mFilesUri,
-                            FILES_PRESCAN_PROJECTION, null, null, null, null);
-                    processPlayList(entry, fileList);
-                }
-            } else {
-                // MTP will create a file entry for us so we don't want to do it in prescan
-                prescan(path, false);
-
-                // always scan the file, so we can return the content://media Uri for existing files
-                mClient.doScanFile(path, mimeType, lastModifiedSeconds, file.length(),
-                    (format == MtpConstants.FORMAT_ASSOCIATION), true, isNoMediaPath(path));
-            }
-        } catch (RemoteException e) {
-            Log.e(TAG, "RemoteException in MediaScanner.scanFile()", e);
-        } finally {
-            mMtpObjectHandle = 0;
-            if (fileList != null) {
-                fileList.close();
-            }
-            releaseResources();
-        }
-    }
-
-    @UnsupportedAppUsage
+    @Deprecated
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "All scanning requests should be performed through {@link android.media.MediaScannerConnection}")
     FileEntry makeEntryFor(String path) {
-        String where;
-        String[] selectionArgs;
-
-        Cursor c = null;
-        try {
-            where = Files.FileColumns.DATA + "=?";
-            selectionArgs = new String[] { path };
-            c = mMediaProvider.query(mFilesFullUri, FILES_PRESCAN_PROJECTION,
-                    where, selectionArgs, null, null);
-            if (c != null && c.moveToFirst()) {
-                long rowId = c.getLong(FILES_PRESCAN_ID_COLUMN_INDEX);
-                long lastModified = c.getLong(FILES_PRESCAN_DATE_MODIFIED_COLUMN_INDEX);
-                int format = c.getInt(FILES_PRESCAN_FORMAT_COLUMN_INDEX);
-                int mediaType = c.getInt(FILES_PRESCAN_MEDIA_TYPE_COLUMN_INDEX);
-                return new FileEntry(rowId, path, lastModified, format, mediaType);
-            }
-        } catch (RemoteException e) {
-        } finally {
-            if (c != null) {
-                c.close();
-            }
-        }
-        return null;
+        throw new UnsupportedOperationException();
     }
 
-    // returns the number of matching file/directory names, starting from the right
-    private int matchPaths(String path1, String path2) {
-        int result = 0;
-        int end1 = path1.length();
-        int end2 = path2.length();
-
-        while (end1 > 0 && end2 > 0) {
-            int slash1 = path1.lastIndexOf('/', end1 - 1);
-            int slash2 = path2.lastIndexOf('/', end2 - 1);
-            int backSlash1 = path1.lastIndexOf('\\', end1 - 1);
-            int backSlash2 = path2.lastIndexOf('\\', end2 - 1);
-            int start1 = (slash1 > backSlash1 ? slash1 : backSlash1);
-            int start2 = (slash2 > backSlash2 ? slash2 : backSlash2);
-            if (start1 < 0) start1 = 0; else start1++;
-            if (start2 < 0) start2 = 0; else start2++;
-            int length = end1 - start1;
-            if (end2 - start2 != length) break;
-            if (path1.regionMatches(true, start1, path2, start2, length)) {
-                result++;
-                end1 = start1 - 1;
-                end2 = start2 - 1;
-            } else break;
-        }
-
-        return result;
+    @Deprecated
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "All scanning requests should be performed through {@link android.media.MediaScannerConnection}")
+    private void setLocale(String locale) {
+        throw new UnsupportedOperationException();
     }
 
-    private boolean matchEntries(long rowId, String data) {
-
-        int len = mPlaylistEntries.size();
-        boolean done = true;
-        for (int i = 0; i < len; i++) {
-            PlaylistEntry entry = mPlaylistEntries.get(i);
-            if (entry.bestmatchlevel == Integer.MAX_VALUE) {
-                continue; // this entry has been matched already
-            }
-            done = false;
-            if (data.equalsIgnoreCase(entry.path)) {
-                entry.bestmatchid = rowId;
-                entry.bestmatchlevel = Integer.MAX_VALUE;
-                continue; // no need for path matching
-            }
-
-            int matchLength = matchPaths(data, entry.path);
-            if (matchLength > entry.bestmatchlevel) {
-                entry.bestmatchid = rowId;
-                entry.bestmatchlevel = matchLength;
-            }
-        }
-        return done;
-    }
-
-    private void cachePlaylistEntry(String line, String playListDirectory) {
-        PlaylistEntry entry = new PlaylistEntry();
-        // watch for trailing whitespace
-        int entryLength = line.length();
-        while (entryLength > 0 && Character.isWhitespace(line.charAt(entryLength - 1))) entryLength--;
-        // path should be longer than 3 characters.
-        // avoid index out of bounds errors below by returning here.
-        if (entryLength < 3) return;
-        if (entryLength < line.length()) line = line.substring(0, entryLength);
-
-        // does entry appear to be an absolute path?
-        // look for Unix or DOS absolute paths
-        char ch1 = line.charAt(0);
-        boolean fullPath = (ch1 == '/' ||
-                (Character.isLetter(ch1) && line.charAt(1) == ':' && line.charAt(2) == '\\'));
-        // if we have a relative path, combine entry with playListDirectory
-        if (!fullPath)
-            line = playListDirectory + line;
-        entry.path = line;
-        //FIXME - should we look for "../" within the path?
-
-        mPlaylistEntries.add(entry);
-    }
-
-    private void processCachedPlaylist(Cursor fileList, ContentValues values, Uri playlistUri) {
-        fileList.moveToPosition(-1);
-        while (fileList.moveToNext()) {
-            long rowId = fileList.getLong(FILES_PRESCAN_ID_COLUMN_INDEX);
-            String data = fileList.getString(FILES_PRESCAN_PATH_COLUMN_INDEX);
-            if (matchEntries(rowId, data)) {
-                break;
-            }
-        }
-
-        int len = mPlaylistEntries.size();
-        int index = 0;
-        for (int i = 0; i < len; i++) {
-            PlaylistEntry entry = mPlaylistEntries.get(i);
-            if (entry.bestmatchlevel > 0) {
-                try {
-                    values.clear();
-                    values.put(MediaStore.Audio.Playlists.Members.PLAY_ORDER, Integer.valueOf(index));
-                    values.put(MediaStore.Audio.Playlists.Members.AUDIO_ID, Long.valueOf(entry.bestmatchid));
-                    mMediaProvider.insert(playlistUri, values);
-                    index++;
-                } catch (RemoteException e) {
-                    Log.e(TAG, "RemoteException in MediaScanner.processCachedPlaylist()", e);
-                    return;
-                }
-            }
-        }
-        mPlaylistEntries.clear();
-    }
-
-    private void processM3uPlayList(String path, String playListDirectory, Uri uri,
-            ContentValues values, Cursor fileList) {
-        BufferedReader reader = null;
-        try {
-            File f = new File(path);
-            if (f.exists()) {
-                reader = new BufferedReader(
-                        new InputStreamReader(new FileInputStream(f)), 8192);
-                String line = reader.readLine();
-                mPlaylistEntries.clear();
-                while (line != null) {
-                    // ignore comment lines, which begin with '#'
-                    if (line.length() > 0 && line.charAt(0) != '#') {
-                        cachePlaylistEntry(line, playListDirectory);
-                    }
-                    line = reader.readLine();
-                }
-
-                processCachedPlaylist(fileList, values, uri);
-            }
-        } catch (IOException e) {
-            Log.e(TAG, "IOException in MediaScanner.processM3uPlayList()", e);
-        } finally {
-            try {
-                if (reader != null)
-                    reader.close();
-            } catch (IOException e) {
-                Log.e(TAG, "IOException in MediaScanner.processM3uPlayList()", e);
-            }
-        }
-    }
-
-    private void processPlsPlayList(String path, String playListDirectory, Uri uri,
-            ContentValues values, Cursor fileList) {
-        BufferedReader reader = null;
-        try {
-            File f = new File(path);
-            if (f.exists()) {
-                reader = new BufferedReader(
-                        new InputStreamReader(new FileInputStream(f)), 8192);
-                String line = reader.readLine();
-                mPlaylistEntries.clear();
-                while (line != null) {
-                    // ignore comment lines, which begin with '#'
-                    if (line.startsWith("File")) {
-                        int equals = line.indexOf('=');
-                        if (equals > 0) {
-                            cachePlaylistEntry(line.substring(equals + 1), playListDirectory);
-                        }
-                    }
-                    line = reader.readLine();
-                }
-
-                processCachedPlaylist(fileList, values, uri);
-            }
-        } catch (IOException e) {
-            Log.e(TAG, "IOException in MediaScanner.processPlsPlayList()", e);
-        } finally {
-            try {
-                if (reader != null)
-                    reader.close();
-            } catch (IOException e) {
-                Log.e(TAG, "IOException in MediaScanner.processPlsPlayList()", e);
-            }
-        }
-    }
-
-    class WplHandler implements ElementListener {
-
-        final ContentHandler handler;
-        String playListDirectory;
-
-        public WplHandler(String playListDirectory, Uri uri, Cursor fileList) {
-            this.playListDirectory = playListDirectory;
-
-            RootElement root = new RootElement("smil");
-            Element body = root.getChild("body");
-            Element seq = body.getChild("seq");
-            Element media = seq.getChild("media");
-            media.setElementListener(this);
-
-            this.handler = root.getContentHandler();
-        }
-
-        @Override
-        public void start(Attributes attributes) {
-            String path = attributes.getValue("", "src");
-            if (path != null) {
-                cachePlaylistEntry(path, playListDirectory);
-            }
-        }
-
-       @Override
-       public void end() {
-       }
-
-        ContentHandler getContentHandler() {
-            return handler;
-        }
-    }
-
-    private void processWplPlayList(String path, String playListDirectory, Uri uri,
-            ContentValues values, Cursor fileList) {
-        FileInputStream fis = null;
-        try {
-            File f = new File(path);
-            if (f.exists()) {
-                fis = new FileInputStream(f);
-
-                mPlaylistEntries.clear();
-                Xml.parse(fis, Xml.findEncodingByName("UTF-8"),
-                        new WplHandler(playListDirectory, uri, fileList).getContentHandler());
-
-                processCachedPlaylist(fileList, values, uri);
-            }
-        } catch (SAXException e) {
-            e.printStackTrace();
-        } catch (IOException e) {
-            e.printStackTrace();
-        } finally {
-            try {
-                if (fis != null)
-                    fis.close();
-            } catch (IOException e) {
-                Log.e(TAG, "IOException in MediaScanner.processWplPlayList()", e);
-            }
-        }
-    }
-
-    private void processPlayList(FileEntry entry, Cursor fileList) throws RemoteException {
-        String path = entry.mPath;
-        ContentValues values = new ContentValues();
-        int lastSlash = path.lastIndexOf('/');
-        if (lastSlash < 0) throw new IllegalArgumentException("bad path " + path);
-        Uri uri, membersUri;
-        long rowId = entry.mRowId;
-
-        // make sure we have a name
-        String name = values.getAsString(MediaStore.Audio.Playlists.NAME);
-        if (name == null) {
-            name = values.getAsString(MediaStore.MediaColumns.TITLE);
-            if (name == null) {
-                // extract name from file name
-                int lastDot = path.lastIndexOf('.');
-                name = (lastDot < 0 ? path.substring(lastSlash + 1)
-                        : path.substring(lastSlash + 1, lastDot));
-            }
-        }
-
-        values.put(MediaStore.Audio.Playlists.NAME, name);
-        values.put(MediaStore.Audio.Playlists.DATE_MODIFIED, entry.mLastModified);
-
-        if (rowId == 0) {
-            values.put(MediaStore.Audio.Playlists.DATA, path);
-            uri = mMediaProvider.insert(mPlaylistsUri, values);
-            rowId = ContentUris.parseId(uri);
-            membersUri = Uri.withAppendedPath(uri, Playlists.Members.CONTENT_DIRECTORY);
-        } else {
-            uri = ContentUris.withAppendedId(mPlaylistsUri, rowId);
-            mMediaProvider.update(uri, values, null, null);
-
-            // delete members of existing playlist
-            membersUri = Uri.withAppendedPath(uri, Playlists.Members.CONTENT_DIRECTORY);
-            mMediaProvider.delete(membersUri, null, null);
-        }
-
-        String playListDirectory = path.substring(0, lastSlash + 1);
-        String mimeType = MediaFile.getMimeTypeForFile(path);
-        switch (mimeType) {
-            case "application/vnd.ms-wpl":
-                processWplPlayList(path, playListDirectory, membersUri, values, fileList);
-                break;
-            case "audio/x-mpegurl":
-                processM3uPlayList(path, playListDirectory, membersUri, values, fileList);
-                break;
-            case "audio/x-scpls":
-                processPlsPlayList(path, playListDirectory, membersUri, values, fileList);
-                break;
-        }
-    }
-
-    private void processPlayLists() throws RemoteException {
-        Iterator<FileEntry> iterator = mPlayLists.iterator();
-        Cursor fileList = null;
-        try {
-            // use the files uri and projection because we need the format column,
-            // but restrict the query to just audio files
-            fileList = mMediaProvider.query(mFilesUri, FILES_PRESCAN_PROJECTION,
-                    "media_type=2", null, null, null);
-            while (iterator.hasNext()) {
-                FileEntry entry = iterator.next();
-                // only process playlist files if they are new or have been modified since the last scan
-                if (entry.mLastModifiedChanged) {
-                    processPlayList(entry, fileList);
-                }
-            }
-        } catch (RemoteException e1) {
-        } finally {
-            if (fileList != null) {
-                fileList.close();
-            }
-        }
-    }
-
-    private native void processDirectory(String path, MediaScannerClient client);
-    private native boolean processFile(String path, String mimeType, MediaScannerClient client);
-    @UnsupportedAppUsage
-    private native void setLocale(String locale);
-
-    public native byte[] extractAlbumArt(FileDescriptor fd);
-
-    private static native final void native_init();
-    private native final void native_setup();
-    private native final void native_finalize();
-
     @Override
     public void close() {
-        mCloseGuard.close();
-        if (mClosed.compareAndSet(false, true)) {
-            mMediaProvider.close();
-            native_finalize();
-        }
-    }
-
-    @Override
-    protected void finalize() throws Throwable {
-        try {
-            if (mCloseGuard != null) {
-                mCloseGuard.warnIfOpen();
-            }
-
-            close();
-        } finally {
-            super.finalize();
-        }
+        throw new UnsupportedOperationException();
     }
 }
diff --git a/media/java/android/media/RingtoneManager.java b/media/java/android/media/RingtoneManager.java
index 435d8d7..ff40442 100644
--- a/media/java/android/media/RingtoneManager.java
+++ b/media/java/android/media/RingtoneManager.java
@@ -846,7 +846,7 @@
      * Adds an audio file to the list of ringtones.
      *
      * After making sure the given file is an audio file, copies the file to the ringtone storage,
-     * and asks the {@link android.media.MediaScanner} to scan that file. This call will block until
+     * and asks the system to scan that file. This call will block until
      * the scan is completed.
      *
      * The directory where the copied file is stored is the directory that matches the ringtone's
diff --git a/media/jni/Android.bp b/media/jni/Android.bp
index 45ee210..bd9ea13 100644
--- a/media/jni/Android.bp
+++ b/media/jni/Android.bp
@@ -17,7 +17,6 @@
         "android_media_MediaPlayer.cpp",
         "android_media_MediaProfiles.cpp",
         "android_media_MediaRecorder.cpp",
-        "android_media_MediaScanner.cpp",
         "android_media_MediaSync.cpp",
         "android_media_ResampleInputStream.cpp",
         "android_media_Streams.cpp",
diff --git a/media/jni/android_media_MediaPlayer.cpp b/media/jni/android_media_MediaPlayer.cpp
index d24edc7..94299bc 100644
--- a/media/jni/android_media_MediaPlayer.cpp
+++ b/media/jni/android_media_MediaPlayer.cpp
@@ -1446,7 +1446,6 @@
 extern int register_android_media_MediaMetadataRetriever(JNIEnv *env);
 extern int register_android_media_MediaMuxer(JNIEnv *env);
 extern int register_android_media_MediaRecorder(JNIEnv *env);
-extern int register_android_media_MediaScanner(JNIEnv *env);
 extern int register_android_media_MediaSync(JNIEnv *env);
 extern int register_android_media_ResampleInputStream(JNIEnv *env);
 extern int register_android_media_MediaProfiles(JNIEnv *env);
@@ -1485,11 +1484,6 @@
         goto bail;
     }
 
-    if (register_android_media_MediaScanner(env) < 0) {
-        ALOGE("ERROR: MediaScanner native registration failed\n");
-        goto bail;
-    }
-
     if (register_android_media_MediaMetadataRetriever(env) < 0) {
         ALOGE("ERROR: MediaMetadataRetriever native registration failed\n");
         goto bail;
diff --git a/media/jni/android_media_MediaScanner.cpp b/media/jni/android_media_MediaScanner.cpp
deleted file mode 100644
index 58044c0..0000000
--- a/media/jni/android_media_MediaScanner.cpp
+++ /dev/null
@@ -1,468 +0,0 @@
-/*
-**
-** Copyright 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.
-*/
-
-//#define LOG_NDEBUG 0
-#define LOG_TAG "MediaScannerJNI"
-#include <utils/Log.h>
-#include <utils/threads.h>
-#include <media/mediascanner.h>
-#include <media/stagefright/StagefrightMediaScanner.h>
-#include <private/media/VideoFrame.h>
-
-#include "jni.h"
-#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;
-
-
-static const char* const kClassMediaScannerClient =
-        "android/media/MediaScannerClient";
-
-static const char* const kClassMediaScanner =
-        "android/media/MediaScanner";
-
-static const char* const kRunTimeException =
-        "java/lang/RuntimeException";
-
-static const char* const kIllegalArgumentException =
-        "java/lang/IllegalArgumentException";
-
-struct fields_t {
-    jfieldID    context;
-};
-static fields_t fields;
-
-static status_t checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName) {
-    if (env->ExceptionCheck()) {
-        ALOGE("An exception was thrown by callback '%s'.", methodName);
-        LOGE_EX(env);
-        env->ExceptionClear();
-        return UNKNOWN_ERROR;
-    }
-    return OK;
-}
-
-// stolen from dalvik/vm/checkJni.cpp
-static bool isValidUtf8(const char* bytes) {
-    while (*bytes != '\0') {
-        unsigned char utf8 = *(bytes++);
-        // Switch on the high four bits.
-        switch (utf8 >> 4) {
-        case 0x00:
-        case 0x01:
-        case 0x02:
-        case 0x03:
-        case 0x04:
-        case 0x05:
-        case 0x06:
-        case 0x07:
-            // Bit pattern 0xxx. No need for any extra bytes.
-            break;
-        case 0x08:
-        case 0x09:
-        case 0x0a:
-        case 0x0b:
-        case 0x0f:
-            /*
-             * Bit pattern 10xx or 1111, which are illegal start bytes.
-             * Note: 1111 is valid for normal UTF-8, but not the
-             * modified UTF-8 used here.
-             */
-            return false;
-        case 0x0e:
-            // Bit pattern 1110, so there are two additional bytes.
-            utf8 = *(bytes++);
-            if ((utf8 & 0xc0) != 0x80) {
-                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.
-            utf8 = *(bytes++);
-            if ((utf8 & 0xc0) != 0x80) {
-                return false;
-            }
-            break;
-        }
-    }
-    return true;
-}
-
-class MyMediaScannerClient : public MediaScannerClient
-{
-public:
-    MyMediaScannerClient(JNIEnv *env, jobject client)
-        :   mEnv(env),
-            mClient(env->NewGlobalRef(client)),
-            mScanFileMethodID(0),
-            mHandleStringTagMethodID(0),
-            mSetMimeTypeMethodID(0)
-    {
-        ALOGV("MyMediaScannerClient constructor");
-        jclass mediaScannerClientInterface =
-                env->FindClass(kClassMediaScannerClient);
-
-        if (mediaScannerClientInterface == NULL) {
-            ALOGE("Class %s not found", kClassMediaScannerClient);
-        } else {
-            mScanFileMethodID = env->GetMethodID(
-                                    mediaScannerClientInterface,
-                                    "scanFile",
-                                    "(Ljava/lang/String;JJZZ)V");
-
-            mHandleStringTagMethodID = env->GetMethodID(
-                                    mediaScannerClientInterface,
-                                    "handleStringTag",
-                                    "(Ljava/lang/String;Ljava/lang/String;)V");
-
-            mSetMimeTypeMethodID = env->GetMethodID(
-                                    mediaScannerClientInterface,
-                                    "setMimeType",
-                                    "(Ljava/lang/String;)V");
-        }
-    }
-
-    virtual ~MyMediaScannerClient()
-    {
-        ALOGV("MyMediaScannerClient destructor");
-        mEnv->DeleteGlobalRef(mClient);
-    }
-
-    virtual status_t scanFile(const char* path, long long lastModified,
-            long long fileSize, bool isDirectory, bool noMedia)
-    {
-        ALOGV("scanFile: path(%s), time(%lld), size(%lld) and isDir(%d)",
-            path, lastModified, fileSize, isDirectory);
-
-        jstring pathStr;
-        if ((pathStr = mEnv->NewStringUTF(path)) == NULL) {
-            mEnv->ExceptionClear();
-            return NO_MEMORY;
-        }
-
-        mEnv->CallVoidMethod(mClient, mScanFileMethodID, pathStr, lastModified,
-                fileSize, isDirectory, noMedia);
-
-        mEnv->DeleteLocalRef(pathStr);
-        return checkAndClearExceptionFromCallback(mEnv, "scanFile");
-    }
-
-    virtual status_t handleStringTag(const char* name, const char* value)
-    {
-        ALOGV("handleStringTag: name(%s) and value(%s)", name, value);
-        jstring nameStr, valueStr;
-        if ((nameStr = mEnv->NewStringUTF(name)) == NULL) {
-            mEnv->ExceptionClear();
-            return NO_MEMORY;
-        }
-        char *cleaned = NULL;
-        if (!isValidUtf8(value)) {
-            cleaned = strdup(value);
-            char *chp = cleaned;
-            char ch;
-            while ((ch = *chp)) {
-                if (ch & 0x80) {
-                    *chp = '?';
-                }
-                chp++;
-            }
-            value = cleaned;
-        }
-        valueStr = mEnv->NewStringUTF(value);
-        free(cleaned);
-        if (valueStr == NULL) {
-            mEnv->DeleteLocalRef(nameStr);
-            mEnv->ExceptionClear();
-            return NO_MEMORY;
-        }
-
-        mEnv->CallVoidMethod(
-            mClient, mHandleStringTagMethodID, nameStr, valueStr);
-
-        mEnv->DeleteLocalRef(nameStr);
-        mEnv->DeleteLocalRef(valueStr);
-        return checkAndClearExceptionFromCallback(mEnv, "handleStringTag");
-    }
-
-    virtual status_t setMimeType(const char* mimeType)
-    {
-        ALOGV("setMimeType: %s", mimeType);
-        jstring mimeTypeStr;
-        if ((mimeTypeStr = mEnv->NewStringUTF(mimeType)) == NULL) {
-            mEnv->ExceptionClear();
-            return NO_MEMORY;
-        }
-
-        mEnv->CallVoidMethod(mClient, mSetMimeTypeMethodID, mimeTypeStr);
-
-        mEnv->DeleteLocalRef(mimeTypeStr);
-        return checkAndClearExceptionFromCallback(mEnv, "setMimeType");
-    }
-
-private:
-    JNIEnv *mEnv;
-    jobject mClient;
-    jmethodID mScanFileMethodID;
-    jmethodID mHandleStringTagMethodID;
-    jmethodID mSetMimeTypeMethodID;
-};
-
-
-static MediaScanner *getNativeScanner_l(JNIEnv* env, jobject thiz)
-{
-    return (MediaScanner *) env->GetLongField(thiz, fields.context);
-}
-
-static void setNativeScanner_l(JNIEnv* env, jobject thiz, MediaScanner *s)
-{
-    env->SetLongField(thiz, fields.context, (jlong)s);
-}
-
-static void
-android_media_MediaScanner_processDirectory(
-        JNIEnv *env, jobject thiz, jstring path, jobject client)
-{
-    ALOGV("processDirectory");
-    MediaScanner *mp = getNativeScanner_l(env, thiz);
-    if (mp == NULL) {
-        jniThrowException(env, kRunTimeException, "No scanner available");
-        return;
-    }
-
-    if (path == NULL) {
-        jniThrowException(env, kIllegalArgumentException, NULL);
-        return;
-    }
-
-    const char *pathStr = env->GetStringUTFChars(path, NULL);
-    if (pathStr == NULL) {  // Out of memory
-        return;
-    }
-
-    MyMediaScannerClient myClient(env, client);
-    MediaScanResult result = mp->processDirectory(pathStr, myClient);
-    if (result == MEDIA_SCAN_RESULT_ERROR) {
-        ALOGE("An error occurred while scanning directory '%s'.", pathStr);
-    }
-    env->ReleaseStringUTFChars(path, pathStr);
-}
-
-static jboolean
-android_media_MediaScanner_processFile(
-        JNIEnv *env, jobject thiz, jstring path,
-        jstring mimeType, jobject client)
-{
-    ALOGV("processFile");
-
-    // Lock already hold by processDirectory
-    MediaScanner *mp = getNativeScanner_l(env, thiz);
-    if (mp == NULL) {
-        jniThrowException(env, kRunTimeException, "No scanner available");
-        return false;
-    }
-
-    if (path == NULL) {
-        jniThrowException(env, kIllegalArgumentException, NULL);
-        return false;
-    }
-
-    const char *pathStr = env->GetStringUTFChars(path, NULL);
-    if (pathStr == NULL) {  // Out of memory
-        return false;
-    }
-
-    const char *mimeTypeStr =
-        (mimeType ? env->GetStringUTFChars(mimeType, NULL) : NULL);
-    if (mimeType && mimeTypeStr == NULL) {  // Out of memory
-        // ReleaseStringUTFChars can be called with an exception pending.
-        env->ReleaseStringUTFChars(path, pathStr);
-        return false;
-    }
-
-    MyMediaScannerClient myClient(env, client);
-    MediaScanResult result = mp->processFile(pathStr, mimeTypeStr, myClient);
-    if (result == MEDIA_SCAN_RESULT_ERROR) {
-        ALOGE("An error occurred while scanning file '%s'.", pathStr);
-    }
-    env->ReleaseStringUTFChars(path, pathStr);
-    if (mimeType) {
-        env->ReleaseStringUTFChars(mimeType, mimeTypeStr);
-    }
-    return result != MEDIA_SCAN_RESULT_ERROR;
-}
-
-static void
-android_media_MediaScanner_setLocale(
-        JNIEnv *env, jobject thiz, jstring locale)
-{
-    ALOGV("setLocale");
-    MediaScanner *mp = getNativeScanner_l(env, thiz);
-    if (mp == NULL) {
-        jniThrowException(env, kRunTimeException, "No scanner available");
-        return;
-    }
-
-    if (locale == NULL) {
-        jniThrowException(env, kIllegalArgumentException, NULL);
-        return;
-    }
-    const char *localeStr = env->GetStringUTFChars(locale, NULL);
-    if (localeStr == NULL) {  // Out of memory
-        return;
-    }
-    mp->setLocale(localeStr);
-
-    env->ReleaseStringUTFChars(locale, localeStr);
-}
-
-static jbyteArray
-android_media_MediaScanner_extractAlbumArt(
-        JNIEnv *env, jobject thiz, jobject fileDescriptor)
-{
-    ALOGV("extractAlbumArt");
-    MediaScanner *mp = getNativeScanner_l(env, thiz);
-    if (mp == NULL) {
-        jniThrowException(env, kRunTimeException, "No scanner available");
-        return NULL;
-    }
-
-    if (fileDescriptor == NULL) {
-        jniThrowException(env, kIllegalArgumentException, NULL);
-        return NULL;
-    }
-
-    int fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
-    MediaAlbumArt* mediaAlbumArt = mp->extractAlbumArt(fd);
-    if (mediaAlbumArt == NULL) {
-        return NULL;
-    }
-
-    jbyteArray array = env->NewByteArray(mediaAlbumArt->size());
-    if (array != NULL) {
-        const jbyte* data =
-                reinterpret_cast<const jbyte*>(mediaAlbumArt->data());
-        env->SetByteArrayRegion(array, 0, mediaAlbumArt->size(), data);
-    }
-
-    free(mediaAlbumArt);
-    // if NewByteArray() returned NULL, an out-of-memory
-    // exception will have been raised. I just want to
-    // return null in that case.
-    env->ExceptionClear();
-    return array;
-}
-
-// This function gets a field ID, which in turn causes class initialization.
-// It is called from a static block in MediaScanner, which won't run until the
-// first time an instance of this class is used.
-static void
-android_media_MediaScanner_native_init(JNIEnv *env)
-{
-    ALOGV("native_init");
-    jclass clazz = env->FindClass(kClassMediaScanner);
-    if (clazz == NULL) {
-        return;
-    }
-
-    fields.context = env->GetFieldID(clazz, "mNativeContext", "J");
-    if (fields.context == NULL) {
-        return;
-    }
-}
-
-static void
-android_media_MediaScanner_native_setup(JNIEnv *env, jobject thiz)
-{
-    ALOGV("native_setup");
-    MediaScanner *mp = new StagefrightMediaScanner;
-
-    if (mp == NULL) {
-        jniThrowException(env, kRunTimeException, "Out of memory");
-        return;
-    }
-
-    env->SetLongField(thiz, fields.context, (jlong)mp);
-}
-
-static void
-android_media_MediaScanner_native_finalize(JNIEnv *env, jobject thiz)
-{
-    ALOGV("native_finalize");
-    MediaScanner *mp = getNativeScanner_l(env, thiz);
-    if (mp == 0) {
-        return;
-    }
-    delete mp;
-    setNativeScanner_l(env, thiz, 0);
-}
-
-static const JNINativeMethod gMethods[] = {
-    {
-        "processDirectory",
-        "(Ljava/lang/String;Landroid/media/MediaScannerClient;)V",
-        (void *)android_media_MediaScanner_processDirectory
-    },
-
-    {
-        "processFile",
-        "(Ljava/lang/String;Ljava/lang/String;Landroid/media/MediaScannerClient;)Z",
-        (void *)android_media_MediaScanner_processFile
-    },
-
-    {
-        "setLocale",
-        "(Ljava/lang/String;)V",
-        (void *)android_media_MediaScanner_setLocale
-    },
-
-    {
-        "extractAlbumArt",
-        "(Ljava/io/FileDescriptor;)[B",
-        (void *)android_media_MediaScanner_extractAlbumArt
-    },
-
-    {
-        "native_init",
-        "()V",
-        (void *)android_media_MediaScanner_native_init
-    },
-
-    {
-        "native_setup",
-        "()V",
-        (void *)android_media_MediaScanner_native_setup
-    },
-
-    {
-        "native_finalize",
-        "()V",
-        (void *)android_media_MediaScanner_native_finalize
-    },
-};
-
-// This function only registers the native methods, and is called from
-// JNI_OnLoad in android_media_MediaPlayer.cpp
-int register_android_media_MediaScanner(JNIEnv *env)
-{
-    return AndroidRuntime::registerNativeMethods(env,
-                kClassMediaScanner, gMethods, NELEM(gMethods));
-}
diff --git a/media/tests/MediaRouteProvider/src/com/android/mediarouteprovider/example/SampleMediaRoute2ProviderService.java b/media/tests/MediaRouteProvider/src/com/android/mediarouteprovider/example/SampleMediaRoute2ProviderService.java
index 8d39a93..680c879 100644
--- a/media/tests/MediaRouteProvider/src/com/android/mediarouteprovider/example/SampleMediaRoute2ProviderService.java
+++ b/media/tests/MediaRouteProvider/src/com/android/mediarouteprovider/example/SampleMediaRoute2ProviderService.java
@@ -130,6 +130,33 @@
         }
     }
 
+    @Override
+    public void onSetVolume(String routeId, int volume) {
+        MediaRoute2Info route = mRoutes.get(routeId);
+        if (route == null) {
+            return;
+        }
+        volume = Math.min(volume, Math.max(0, route.getVolumeMax()));
+        mRoutes.put(routeId, new MediaRoute2Info.Builder(route)
+                .setVolume(volume)
+                .build());
+        publishRoutes();
+    }
+
+    @Override
+    public void onUpdateVolume(String routeId, int delta) {
+        MediaRoute2Info route = mRoutes.get(routeId);
+        if (route == null) {
+            return;
+        }
+        int volume = route.getVolume() + delta;
+        volume = Math.min(volume, Math.max(0, route.getVolumeMax()));
+        mRoutes.put(routeId, new MediaRoute2Info.Builder(route)
+                .setVolume(volume)
+                .build());
+        publishRoutes();
+    }
+
     void publishRoutes() {
         MediaRoute2ProviderInfo info = new MediaRoute2ProviderInfo.Builder()
                 .addRoutes(mRoutes.values())
diff --git a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java
index da832ac..ca43d04 100644
--- a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java
+++ b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java
@@ -47,8 +47,10 @@
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.Executor;
 import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
 
 @RunWith(AndroidJUnit4.class)
 @SmallTest
@@ -190,17 +192,8 @@
         MediaRouter2Manager.Callback mockCallback = mock(MediaRouter2Manager.Callback.class);
         mManager.registerCallback(mExecutor, mockCallback);
 
-        MediaRouter2.Callback mockRouterCallback = mock(MediaRouter2.Callback.class);
-
-        mRouter2.setControlCategories(CONTROL_CATEGORIES_SPECIAL);
-        mRouter2.registerCallback(mExecutor, mockRouterCallback);
-        mRouter2.unregisterCallback(mockRouterCallback);
-
-        verify(mockCallback, timeout(TIMEOUT_MS))
-                .onRoutesChanged(argThat(routes -> routes.size() > 0));
-
         Map<String, MediaRoute2Info> routes =
-                createRouteMap(mManager.getAvailableRoutes(mPackageName));
+                waitAndGetRoutesWithManager(CONTROL_CATEGORIES_SPECIAL);
 
         Assert.assertEquals(1, routes.size());
         Assert.assertNotNull(routes.get(ROUTE_ID_SPECIAL_CATEGORY));
@@ -214,12 +207,10 @@
     @Test
     public void testGetRoutes() throws Exception {
         MediaRouter2.Callback mockCallback = mock(MediaRouter2.Callback.class);
-
-        mRouter2.setControlCategories(CONTROL_CATEGORIES_SPECIAL);
         mRouter2.registerCallback(mExecutor, mockCallback);
-        verify(mockCallback, timeout(TIMEOUT_MS).atLeastOnce())
-                .onRoutesChanged(argThat(routes -> routes.size() > 0));
-        Map<String, MediaRoute2Info> routes = createRouteMap(mRouter2.getRoutes());
+
+        Map<String, MediaRoute2Info> routes = waitAndGetRoutes(CONTROL_CATEGORIES_SPECIAL);
+
         Assert.assertEquals(1, routes.size());
         Assert.assertNotNull(routes.get(ROUTE_ID_SPECIAL_CATEGORY));
 
@@ -228,51 +219,40 @@
 
     @Test
     public void testOnRouteSelected() throws Exception {
-        MediaRouter2.Callback mockRouterCallback = mock(MediaRouter2.Callback.class);
+        MediaRouter2.Callback routerCallback = new MediaRouter2.Callback();
         MediaRouter2Manager.Callback managerCallback = mock(MediaRouter2Manager.Callback.class);
 
         mManager.registerCallback(mExecutor, managerCallback);
-        mRouter2.setControlCategories(CONTROL_CATEGORIES_ALL);
-        mRouter2.registerCallback(mExecutor, mockRouterCallback);
+        mRouter2.registerCallback(mExecutor, routerCallback);
 
-        verify(managerCallback, timeout(TIMEOUT_MS))
-                .onRoutesChanged(argThat(routes -> routes.size() > 0));
-
-        Map<String, MediaRoute2Info> routes =
-                createRouteMap(mManager.getAvailableRoutes(mPackageName));
+        Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(CONTROL_CATEGORIES_ALL);
 
         MediaRoute2Info routeToSelect = routes.get(ROUTE_ID1);
-        mManager.selectRoute(mPackageName, routeToSelect);
-
         assertNotNull(routeToSelect);
+
+        mManager.selectRoute(mPackageName, routeToSelect);
         verify(managerCallback, timeout(TIMEOUT_MS))
                 .onRouteAdded(argThat(route -> route.equals(routeToSelect)));
 
+        mRouter2.unregisterCallback(routerCallback);
         mManager.unregisterCallback(managerCallback);
-        mRouter2.unregisterCallback(mockRouterCallback);
     }
 
     /**
      * Tests selecting and unselecting routes of a single provider.
      */
     @Test
-    public void testSingleProviderSelect() {
+    public void testSingleProviderSelect() throws Exception {
         MediaRouter2Manager.Callback managerCallback = mock(MediaRouter2Manager.Callback.class);
         MediaRouter2.Callback routerCallback = mock(MediaRouter2.Callback.class);
 
         mManager.registerCallback(mExecutor, managerCallback);
-        mRouter2.setControlCategories(CONTROL_CATEGORIES_ALL);
         mRouter2.registerCallback(mExecutor, routerCallback);
 
-        verify(managerCallback, timeout(TIMEOUT_MS))
-                .onRoutesChanged(argThat(routes -> routes.size() > 0));
-
-        Map<String, MediaRoute2Info> routes =
-                createRouteMap(mManager.getAvailableRoutes(mPackageName));
+        Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(CONTROL_CATEGORIES_ALL);
 
         mManager.selectRoute(mPackageName, routes.get(ROUTE_ID1));
-        verify(managerCallback, timeout(TIMEOUT_MS)
-        )
+        verify(managerCallback, timeout(TIMEOUT_MS))
                 .onRouteChanged(argThat(routeInfo -> TextUtils.equals(ROUTE_ID1, routeInfo.getId())
                         && TextUtils.equals(routeInfo.getClientPackageName(), mPackageName)));
 
@@ -291,14 +271,67 @@
     }
 
     @Test
-    public void testVolumeHandling() {
+    public void testControlVolumeWithRouter() throws Exception {
         MediaRouter2.Callback mockCallback = mock(MediaRouter2.Callback.class);
 
-        mRouter2.setControlCategories(CONTROL_CATEGORIES_ALL);
+        Map<String, MediaRoute2Info> routes = waitAndGetRoutes(CONTROL_CATEGORIES_ALL);
         mRouter2.registerCallback(mExecutor, mockCallback);
+
+        MediaRoute2Info volRoute = routes.get(ROUTE_ID_VARIABLE_VOLUME);
+        int originalVolume = volRoute.getVolume();
+        int deltaVolume = (originalVolume == volRoute.getVolumeMax() ? -1 : 1);
+        int targetVolume = originalVolume + deltaVolume;
+
+        mRouter2.requestSetVolume(volRoute, targetVolume);
         verify(mockCallback, timeout(TIMEOUT_MS).atLeastOnce())
-                .onRoutesChanged(argThat(routes -> routes.size() > 0));
-        Map<String, MediaRoute2Info> routes = createRouteMap(mRouter2.getRoutes());
+                .onRouteChanged(argThat(route ->
+                        route.getId().equals(volRoute.getId())
+                                && route.getVolume() == targetVolume));
+
+        mRouter2.requestUpdateVolume(volRoute, -deltaVolume);
+        verify(mockCallback, timeout(TIMEOUT_MS).atLeastOnce())
+                .onRouteChanged(argThat(route ->
+                        route.getId().equals(volRoute.getId())
+                                && route.getVolume() == originalVolume));
+
+        mRouter2.unregisterCallback(mockCallback);
+    }
+
+    @Test
+    public void testControlVolumeWithManager() throws Exception {
+        MediaRouter2Manager.Callback managerCallback = mock(MediaRouter2Manager.Callback.class);
+        MediaRouter2.Callback mockCallback = mock(MediaRouter2.Callback.class);
+
+        mManager.registerCallback(mExecutor, managerCallback);
+        mRouter2.registerCallback(mExecutor, mockCallback);
+        Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(CONTROL_CATEGORIES_ALL);
+
+        MediaRoute2Info volRoute = routes.get(ROUTE_ID_VARIABLE_VOLUME);
+        int originalVolume = volRoute.getVolume();
+        int deltaVolume = (originalVolume == volRoute.getVolumeMax() ? -1 : 1);
+        int targetVolume = originalVolume + deltaVolume;
+
+        mManager.requestSetVolume(volRoute, targetVolume);
+        verify(mockCallback, timeout(TIMEOUT_MS).atLeastOnce())
+                .onRouteChanged(argThat(route ->
+                        route.getId().equals(volRoute.getId())
+                                && route.getVolume() == targetVolume));
+
+        mManager.requestUpdateVolume(volRoute, -deltaVolume);
+        verify(mockCallback, timeout(TIMEOUT_MS).atLeastOnce())
+                .onRouteChanged(argThat(route ->
+                        route.getId().equals(volRoute.getId())
+                                && route.getVolume() == originalVolume));
+
+        mRouter2.unregisterCallback(mockCallback);
+        mManager.unregisterCallback(managerCallback);
+    }
+
+    @Test
+    public void testVolumeHandling() throws Exception {
+        MediaRouter2.Callback mockCallback = mock(MediaRouter2.Callback.class);
+        mRouter2.registerCallback(mExecutor, mockCallback);
+        Map<String, MediaRoute2Info> routes = waitAndGetRoutes(CONTROL_CATEGORIES_ALL);
 
         MediaRoute2Info fixedVolumeRoute = routes.get(ROUTE_ID_FIXED_VOLUME);
         MediaRoute2Info variableVolumeRoute = routes.get(ROUTE_ID_VARIABLE_VOLUME);
@@ -310,6 +343,48 @@
         mRouter2.unregisterCallback(mockCallback);
     }
 
+    Map<String, MediaRoute2Info> waitAndGetRoutes(List<String> controlCategories) throws Exception {
+        CountDownLatch latch = new CountDownLatch(1);
+        MediaRouter2.Callback callback = new MediaRouter2.Callback() {
+            @Override
+            public void onRoutesChanged(List<MediaRoute2Info> routes) {
+                if (routes.size() > 0) latch.countDown();
+            }
+        };
+        mRouter2.setControlCategories(controlCategories);
+        mRouter2.registerCallback(mExecutor, callback);
+        try {
+            latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS);
+            return createRouteMap(mRouter2.getRoutes());
+        } finally {
+            mRouter2.unregisterCallback(callback);
+        }
+    }
+
+    Map<String, MediaRoute2Info> waitAndGetRoutesWithManager(List<String> controlCategories)
+            throws Exception {
+        CountDownLatch latch = new CountDownLatch(1);
+
+        // Dummy callback is required to send control category info.
+        MediaRouter2.Callback routerCallback = new MediaRouter2.Callback();
+        MediaRouter2Manager.Callback managerCallback = new MediaRouter2Manager.Callback() {
+            @Override
+            public void onRoutesChanged(List<MediaRoute2Info> routes) {
+                if (routes.size() > 0) latch.countDown();
+            }
+        };
+        mManager.registerCallback(mExecutor, managerCallback);
+        mRouter2.setControlCategories(controlCategories);
+        mRouter2.registerCallback(mExecutor, routerCallback);
+        try {
+            latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS);
+            return createRouteMap(mManager.getAvailableRoutes(mPackageName));
+        } finally {
+            mRouter2.unregisterCallback(routerCallback);
+            mManager.unregisterCallback(managerCallback);
+        }
+    }
+
     // Helper for getting routes easily
     static Map<String, MediaRoute2Info> createRouteMap(List<MediaRoute2Info> routes) {
         Map<String, MediaRoute2Info> routeMap = new HashMap<>();
diff --git a/packages/CarSystemUI/res/layout/car_top_navigation_bar_unprovisioned.xml b/packages/CarSystemUI/res/layout/car_top_navigation_bar_unprovisioned.xml
new file mode 100644
index 0000000..8247211
--- /dev/null
+++ b/packages/CarSystemUI/res/layout/car_top_navigation_bar_unprovisioned.xml
@@ -0,0 +1,147 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2019 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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.statusbar.car.CarNavigationBarView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:systemui="http://schemas.android.com/apk/res-auto"
+    android:id="@+id/car_top_bar"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:background="@drawable/system_bar_background"
+    android:orientation="vertical">
+
+  <RelativeLayout
+      android:layout_width="match_parent"
+      android:layout_height="wrap_content"
+      android:layout_weight="1">
+
+    <FrameLayout
+        android:id="@+id/left_hvac_container"
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent"
+        android:layout_alignParentStart="true"
+        >
+
+      <com.android.systemui.statusbar.car.CarNavigationButton
+          android:id="@+id/hvacleft"
+          android:layout_width="match_parent"
+          android:layout_height="match_parent"
+          android:background="@null"
+          systemui:broadcast="true"
+          systemui:intent="intent:#Intent;action=android.car.intent.action.TOGGLE_HVAC_CONTROLS;end"
+          />
+
+      <com.android.systemui.statusbar.hvac.AnimatedTemperatureView
+          android:id="@+id/lefttext"
+          android:layout_width="wrap_content"
+          android:layout_height="match_parent"
+          android:paddingStart="@*android:dimen/car_padding_4"
+          android:paddingEnd="16dp"
+          android:gravity="center_vertical|start"
+          android:minEms="4"
+          android:textAppearance="@style/TextAppearance.CarStatus"
+          systemui:hvacAreaId="49"
+          systemui:hvacMaxText="@string/hvac_max_text"
+          systemui:hvacMaxValue="@dimen/hvac_max_value"
+          systemui:hvacMinText="@string/hvac_min_text"
+          systemui:hvacMinValue="@dimen/hvac_min_value"
+          systemui:hvacPivotOffset="60dp"
+          systemui:hvacPropertyId="358614275"
+          systemui:hvacTempFormat="%.0f\u00B0"
+          />
+    </FrameLayout>
+
+    <FrameLayout
+        android:id="@+id/clock_container"
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent"
+        android:layout_centerInParent="true">
+      <com.android.systemui.statusbar.car.CarNavigationButton
+          android:id="@+id/qs"
+          android:layout_width="match_parent"
+          android:layout_height="match_parent"
+          android:background="@null"/>
+      <com.android.systemui.statusbar.policy.Clock
+          android:id="@+id/clock"
+          android:layout_width="wrap_content"
+          android:layout_height="wrap_content"
+          android:layout_gravity="center"
+          android:elevation="5dp"
+          android:singleLine="true"
+          android:textAppearance="@style/TextAppearance.StatusBar.Clock"/>
+    </FrameLayout>
+
+    <LinearLayout
+        android:id="@+id/system_icon_area"
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent"
+        android:layout_centerHorizontal="true"
+        android:layout_centerVertical="true"
+        android:layout_toEndOf="@+id/clock_container"
+        android:paddingStart="@*android:dimen/car_padding_1"
+        android:gravity="center_vertical"
+        android:orientation="horizontal"
+        >
+
+      <include
+          layout="@layout/system_icons"
+          android:layout_width="wrap_content"
+          android:layout_height="wrap_content"
+          android:layout_weight="1"
+          android:paddingStart="4dp"
+          android:gravity="center_vertical"
+          />
+    </LinearLayout>
+
+    <FrameLayout
+        android:id="@+id/right_hvac_container"
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent"
+        android:layout_alignParentEnd="true"
+        >
+
+      <com.android.systemui.statusbar.car.CarNavigationButton
+          android:id="@+id/hvacright"
+          android:layout_width="match_parent"
+          android:layout_height="match_parent"
+          android:background="@null"
+          systemui:broadcast="true"
+          systemui:intent="intent:#Intent;action=android.car.intent.action.TOGGLE_HVAC_CONTROLS;end"
+          />
+
+      <com.android.systemui.statusbar.hvac.AnimatedTemperatureView
+          android:id="@+id/righttext"
+          android:layout_width="wrap_content"
+          android:layout_height="match_parent"
+          android:paddingStart="16dp"
+          android:paddingEnd="@*android:dimen/car_padding_4"
+          android:gravity="center_vertical|end"
+          android:minEms="4"
+          android:textAppearance="@style/TextAppearance.CarStatus"
+          systemui:hvacAreaId="68"
+          systemui:hvacMaxText="@string/hvac_max_text"
+          systemui:hvacMaxValue="@dimen/hvac_max_value"
+          systemui:hvacMinText="@string/hvac_min_text"
+          systemui:hvacMinValue="@dimen/hvac_min_value"
+          systemui:hvacPivotOffset="60dp"
+          systemui:hvacPropertyId="358614275"
+          systemui:hvacTempFormat="%.0f\u00B0"
+          />
+    </FrameLayout>
+  </RelativeLayout>
+
+</com.android.systemui.statusbar.car.CarNavigationBarView>
diff --git a/packages/CarSystemUI/res/layout/super_status_bar.xml b/packages/CarSystemUI/res/layout/super_status_bar.xml
index 08d48bf..37cd1d4 100644
--- a/packages/CarSystemUI/res/layout/super_status_bar.xml
+++ b/packages/CarSystemUI/res/layout/super_status_bar.xml
@@ -69,10 +69,10 @@
             android:visibility="gone"
         />
 
-        <include layout="@layout/car_top_navigation_bar"
+        <FrameLayout
+            android:id="@+id/car_top_navigation_bar_container"
             android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-        />
+            android:layout_height="wrap_content"/>
     </LinearLayout>
 
     <include layout="@layout/brightness_mirror"/>
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
index a585727..9d47cdc 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
@@ -162,9 +162,11 @@
     private BatteryMeterView mBatteryMeterView;
     private Drawable mNotificationPanelBackground;
 
+    private ViewGroup mTopNavigationBarContainer;
     private ViewGroup mNavigationBarWindow;
     private ViewGroup mLeftNavigationBarWindow;
     private ViewGroup mRightNavigationBarWindow;
+    private CarNavigationBarView mTopNavigationBarView;
     private CarNavigationBarView mNavigationBarView;
     private CarNavigationBarView mLeftNavigationBarView;
     private CarNavigationBarView mRightNavigationBarView;
@@ -176,7 +178,7 @@
     private CarFacetButtonController mCarFacetButtonController;
     private ActivityManagerWrapper mActivityManagerWrapper;
     private DeviceProvisionedController mDeviceProvisionedController;
-    private boolean mDeviceIsProvisioned = true;
+    private boolean mDeviceIsSetUpForUser = true;
     private HvacController mHvacController;
     private DrivingStateHelper mDrivingStateHelper;
     private PowerManagerHelper mPowerManagerHelper;
@@ -197,6 +199,9 @@
     private boolean mNotificationListAtBottom;
     // Was the notification list at the bottom when the user first touched the screen
     private boolean mNotificationListAtBottomAtTimeOfTouch;
+    // To be attached to the top navigation bar (i.e. status bar) to pull down the notification
+    // panel.
+    private View.OnTouchListener mTopNavBarNotificationTouchListener;
     // To be attached to the navigation bars such that they can close the notification panel if
     // it's open.
     private View.OnTouchListener mNavBarNotificationTouchListener;
@@ -364,7 +369,7 @@
         // get the provisioned state before calling the parent class since it's that flow that
         // builds the nav bar
         mDeviceProvisionedController = Dependency.get(DeviceProvisionedController.class);
-        mDeviceIsProvisioned = mDeviceProvisionedController.isDeviceProvisioned();
+        mDeviceIsSetUpForUser = mDeviceProvisionedController.isCurrentUserSetup();
 
         // Keyboard related setup, before nav bars are created.
         mHideNavBarForKeyboard = mContext.getResources().getBoolean(
@@ -376,6 +381,10 @@
         mScreenLifecycle = Dependency.get(ScreenLifecycle.class);
         mScreenLifecycle.addObserver(mScreenObserver);
 
+        // Need to initialize HVAC controller before calling super.start - before system bars are
+        // created.
+        mHvacController = new HvacController(mContext);
+
         super.start();
         mTaskStackListener = new TaskStackListenerImpl();
         mActivityManagerWrapper = ActivityManagerWrapper.getInstance();
@@ -394,25 +403,18 @@
 
         mHvacController.connectToCarService();
 
-        CarSystemUIFactory factory = SystemUIFactory.getInstance();
-        if (!mDeviceIsProvisioned) {
-            mDeviceProvisionedController.addCallback(
-                    new DeviceProvisionedController.DeviceProvisionedListener() {
-                        @Override
-                        public void onDeviceProvisionedChanged() {
-                            mHandler.post(() -> {
-                                // on initial boot we are getting a call even though the value
-                                // is the same so we are confirming the reset is needed
-                                boolean deviceProvisioned =
-                                        mDeviceProvisionedController.isDeviceProvisioned();
-                                if (mDeviceIsProvisioned != deviceProvisioned) {
-                                    mDeviceIsProvisioned = deviceProvisioned;
-                                    restartNavBars();
-                                }
-                            });
-                        }
-                    });
-        }
+        mDeviceProvisionedController.addCallback(
+                new DeviceProvisionedController.DeviceProvisionedListener() {
+                    @Override
+                    public void onUserSetupChanged() {
+                        mHandler.post(() -> restartNavBarsIfNecessary());
+                    }
+
+                    @Override
+                    public void onUserSwitched() {
+                        mHandler.post(() -> restartNavBarsIfNecessary());
+                    }
+                });
 
         // Register a listener for driving state changes.
         mDrivingStateHelper = new DrivingStateHelper(mContext, this::onDrivingStateChanged);
@@ -424,6 +426,14 @@
         mSwitchToGuestTimer = new SwitchToGuestTimer(mContext);
     }
 
+    private void restartNavBarsIfNecessary() {
+        boolean currentUserSetup = mDeviceProvisionedController.isCurrentUserSetup();
+        if (mDeviceIsSetUpForUser != currentUserSetup) {
+            mDeviceIsSetUpForUser = currentUserSetup;
+            restartNavBars();
+        }
+    }
+
     /**
      * Remove all content from navbars and rebuild them. Used to allow for different nav bars
      * before and after the device is provisioned. . Also for change of density and font size.
@@ -432,8 +442,8 @@
         // remove and reattach all hvac components such that we don't keep a reference to unused
         // ui elements
         mHvacController.removeAllComponents();
-        addTemperatureViewToController(mStatusBarWindow);
         mCarFacetButtonController.removeAll();
+
         if (mNavigationBarWindow != null) {
             mNavigationBarWindow.removeAllViews();
             mNavigationBarView = null;
@@ -527,7 +537,6 @@
     @Override
     protected void makeStatusBarView(@Nullable RegisterStatusBarResult result) {
         super.makeStatusBarView(result);
-        mHvacController = new HvacController(mContext);
 
         CarSystemUIFactory factory = SystemUIFactory.getInstance();
         mCarFacetButtonController = factory.getCarDependencyComponent()
@@ -552,7 +561,8 @@
      * touch listeners needed for opening and closing the notification panel
      */
     private void connectNotificationsUI() {
-        // Attached to the status bar to detect pull down of the notification shade.
+        // Attached to the top navigation bar (i.e. status bar) to detect pull down of the
+        // notification shade.
         GestureDetector openGestureDetector = new GestureDetector(mContext,
                 new OpenNotificationGestureListener() {
                     @Override
@@ -585,6 +595,18 @@
         GestureDetector handleBarCloseNotificationGestureDetector = new GestureDetector(mContext,
                 new HandleBarCloseNotificationGestureListener());
 
+        mTopNavBarNotificationTouchListener = (v, event) -> {
+            if (!mDeviceIsSetUpForUser) {
+                return true;
+            }
+            boolean consumed = openGestureDetector.onTouchEvent(event);
+            if (consumed) {
+                return true;
+            }
+            maybeCompleteAnimation(event);
+            return true;
+        };
+
         mNavBarNotificationTouchListener =
                 (v, event) -> {
                     boolean consumed = navBarCloseNotificationGestureDetector.onTouchEvent(event);
@@ -595,21 +617,6 @@
                     return true;
                 };
 
-        // The following are the ui elements that the user would call the status bar.
-        // This will set the status bar so it they can make call backs.
-        CarNavigationBarView topBar = mStatusBarWindow.findViewById(R.id.car_top_bar);
-        topBar.setStatusBar(this);
-        topBar.setStatusBarWindowTouchListener((v1, event1) -> {
-
-                    boolean consumed = openGestureDetector.onTouchEvent(event1);
-                    if (consumed) {
-                        return true;
-                    }
-                    maybeCompleteAnimation(event1);
-                    return true;
-                }
-        );
-
         mNotificationClickHandlerFactory = new NotificationClickHandlerFactory(mBarService);
         mNotificationClickHandlerFactory.registerClickListener((launchResult, alertEntry) -> {
             if (launchResult == ActivityManager.START_TASK_TO_FRONT
@@ -916,23 +923,30 @@
     }
 
     private void buildNavBarContent() {
+        // Always build top bar.
+        buildTopBar((mDeviceIsSetUpForUser) ? R.layout.car_top_navigation_bar :
+                R.layout.car_top_navigation_bar_unprovisioned);
+
         if (mShowBottom) {
-            buildBottomBar((mDeviceIsProvisioned) ? R.layout.car_navigation_bar :
+            buildBottomBar((mDeviceIsSetUpForUser) ? R.layout.car_navigation_bar :
                     R.layout.car_navigation_bar_unprovisioned);
         }
 
         if (mShowLeft) {
-            buildLeft((mDeviceIsProvisioned) ? R.layout.car_left_navigation_bar :
+            buildLeft((mDeviceIsSetUpForUser) ? R.layout.car_left_navigation_bar :
                     R.layout.car_left_navigation_bar_unprovisioned);
         }
 
         if (mShowRight) {
-            buildRight((mDeviceIsProvisioned) ? R.layout.car_right_navigation_bar :
+            buildRight((mDeviceIsSetUpForUser) ? R.layout.car_right_navigation_bar :
                     R.layout.car_right_navigation_bar_unprovisioned);
         }
     }
 
     private void buildNavBarWindows() {
+        mTopNavigationBarContainer = mStatusBarWindow
+            .findViewById(R.id.car_top_navigation_bar_container);
+
         if (mShowBottom) {
             mNavigationBarWindow = (ViewGroup) View.inflate(mContext,
                     R.layout.navigation_bar_window, null);
@@ -965,15 +979,25 @@
         }
 
         boolean isKeyboardVisible = (vis & InputMethodService.IME_VISIBLE) != 0;
-        if (!isKeyboardVisible) {
-            attachBottomNavBarWindow();
-        } else {
-            detachBottomNavBarWindow();
-        }
+        showBottomNavBarWindow(isKeyboardVisible);
     }
 
     private void attachNavBarWindows() {
-        attachBottomNavBarWindow();
+        if (mShowBottom && !mBottomNavBarVisible) {
+            mBottomNavBarVisible = true;
+
+            WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
+                    LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT,
+                    WindowManager.LayoutParams.TYPE_NAVIGATION_BAR,
+                    WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+                            | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
+                            | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
+                            | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
+                    PixelFormat.TRANSLUCENT);
+            lp.setTitle("CarNavigationBar");
+            lp.windowAnimations = 0;
+            mWindowManager.addView(mNavigationBarWindow, lp);
+        }
 
         if (mShowLeft) {
             int width = mContext.getResources().getDimensionPixelSize(
@@ -1011,47 +1035,32 @@
         }
     }
 
-    /**
-     * Attaches the bottom nav bar window. Can be extended to modify the specific behavior of
-     * attaching the bottom nav bar.
-     */
-    protected void attachBottomNavBarWindow() {
+    private void showBottomNavBarWindow(boolean isKeyboardVisible) {
         if (!mShowBottom) {
             return;
         }
 
-        if (mBottomNavBarVisible) {
+        // If keyboard is visible and bottom nav bar not visible, this is the correct state, so do
+        // nothing. Same with if keyboard is not visible and bottom nav bar is visible.
+        if (isKeyboardVisible ^ mBottomNavBarVisible) {
             return;
         }
-        mBottomNavBarVisible = true;
 
-        WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
-                LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT,
-                WindowManager.LayoutParams.TYPE_NAVIGATION_BAR,
-                WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
-                        | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
-                        | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
-                        | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
-                PixelFormat.TRANSLUCENT);
-        lp.setTitle("CarNavigationBar");
-        lp.windowAnimations = 0;
-        mWindowManager.addView(mNavigationBarWindow, lp);
+        mNavigationBarWindow.setVisibility(isKeyboardVisible ? View.GONE : View.VISIBLE);
+        mBottomNavBarVisible = !isKeyboardVisible;
     }
 
-    /**
-     * Detaches the bottom nav bar window. Can be extended to modify the specific behavior of
-     * detaching the bottom nav bar.
-     */
-    protected void detachBottomNavBarWindow() {
-        if (!mShowBottom) {
-            return;
+    private void buildTopBar(int layout) {
+        mTopNavigationBarContainer.removeAllViews();
+        View.inflate(mContext, layout, mTopNavigationBarContainer);
+        mTopNavigationBarView = (CarNavigationBarView) mTopNavigationBarContainer.getChildAt(0);
+        if (mTopNavigationBarView == null) {
+            Log.e(TAG, "CarStatusBar failed inflate for R.layout.car_top_navigation_bar");
+            throw new RuntimeException("Unable to build top nav bar due to missing layout");
         }
-
-        if (!mBottomNavBarVisible) {
-            return;
-        }
-        mBottomNavBarVisible = false;
-        mWindowManager.removeView(mNavigationBarWindow);
+        mTopNavigationBarView.setStatusBar(this);
+        addTemperatureViewToController(mTopNavigationBarView);
+        mTopNavigationBarView.setStatusBarWindowTouchListener(mTopNavBarNotificationTouchListener);
     }
 
     private void buildBottomBar(int layout) {
@@ -1062,7 +1071,7 @@
         mNavigationBarView = (CarNavigationBarView) mNavigationBarWindow.getChildAt(0);
         if (mNavigationBarView == null) {
             Log.e(TAG, "CarStatusBar failed inflate for R.layout.car_navigation_bar");
-            throw new RuntimeException("Unable to build botom nav bar due to missing layout");
+            throw new RuntimeException("Unable to build bottom nav bar due to missing layout");
         }
         mNavigationBarView.setStatusBar(this);
         addTemperatureViewToController(mNavigationBarView);
@@ -1073,7 +1082,7 @@
         View.inflate(mContext, layout, mLeftNavigationBarWindow);
         mLeftNavigationBarView = (CarNavigationBarView) mLeftNavigationBarWindow.getChildAt(0);
         if (mLeftNavigationBarView == null) {
-            Log.e(TAG, "CarStatusBar failed inflate for R.layout.car_navigation_bar");
+            Log.e(TAG, "CarStatusBar failed inflate for R.layout.car_left_navigation_bar");
             throw new RuntimeException("Unable to build left nav bar due to missing layout");
         }
         mLeftNavigationBarView.setStatusBar(this);
@@ -1086,7 +1095,7 @@
         View.inflate(mContext, layout, mRightNavigationBarWindow);
         mRightNavigationBarView = (CarNavigationBarView) mRightNavigationBarWindow.getChildAt(0);
         if (mRightNavigationBarView == null) {
-            Log.e(TAG, "CarStatusBar failed inflate for R.layout.car_navigation_bar");
+            Log.e(TAG, "CarStatusBar failed inflate for R.layout.car_right_navigation_bar");
             throw new RuntimeException("Unable to build right nav bar due to missing layout");
         }
         mRightNavigationBarView.setStatusBar(this);
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/privacy/OngoingPrivacyChip.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/privacy/OngoingPrivacyChip.java
index ead1de2..88d641e 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/privacy/OngoingPrivacyChip.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/privacy/OngoingPrivacyChip.java
@@ -47,6 +47,7 @@
 public class OngoingPrivacyChip extends LinearLayout implements View.OnClickListener {
 
     private Context mContext;
+    private Handler mHandler;
 
     private LinearLayout mIconsContainer;
     private List<PrivacyItem> mPrivacyItems;
@@ -88,6 +89,7 @@
 
     private void init(Context context) {
         mContext = context;
+        mHandler = new Handler(Looper.getMainLooper());
         mPrivacyItems = new ArrayList<>();
         sAppOpsController = Dependency.get(AppOpsController.class);
         mUserManager = mContext.getSystemService(UserManager.class);
@@ -131,8 +133,7 @@
     @Override
     public void onClick(View v) {
         updatePrivacyList();
-        Handler mUiHandler = new Handler(Looper.getMainLooper());
-        mUiHandler.post(() -> {
+        mHandler.post(() -> {
             mActivityStarter.postStartActivityDismissingKeyguard(
                     new Intent(Intent.ACTION_REVIEW_ONGOING_PERMISSION_USAGE), 0);
         });
@@ -152,21 +153,17 @@
     }
 
     private void updatePrivacyList() {
-        mPrivacyItems = mCurrentUserIds.stream()
+        List<PrivacyItem> privacyItems = mCurrentUserIds.stream()
                 .flatMap(item -> sAppOpsController.getActiveAppOpsForUser(item).stream())
                 .filter(Objects::nonNull)
                 .map(item -> toPrivacyItem(item))
                 .filter(Objects::nonNull)
                 .collect(Collectors.toList());
-        mPrivacyDialogBuilder = new PrivacyDialogBuilder(mContext, mPrivacyItems);
-
-        Handler refresh = new Handler(Looper.getMainLooper());
-        refresh.post(new Runnable() {
-            @Override
-            public void run() {
-                updateView();
-            }
-        });
+        if (!privacyItems.equals(mPrivacyItems)) {
+            mPrivacyItems = privacyItems;
+            mPrivacyDialogBuilder = new PrivacyDialogBuilder(mContext, mPrivacyItems);
+            mHandler.post(this::updateView);
+        }
     }
 
     private PrivacyItem toPrivacyItem(AppOpItem appOpItem) {
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/privacy/PrivacyApplication.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/privacy/PrivacyApplication.java
index 5ec7a77..820bea9 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/privacy/PrivacyApplication.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/privacy/PrivacyApplication.java
@@ -23,16 +23,20 @@
 import android.graphics.drawable.Drawable;
 import android.util.Log;
 
+import java.util.Objects;
+
 /**
  * Class to hold the data for the applications that are using the AppOps permissions.
  */
 public class PrivacyApplication {
     private static final String TAG = "PrivacyApplication";
 
+    private String mPackageName;
     private Drawable mIcon;
     private String mApplicationName;
 
     public PrivacyApplication(String packageName, Context context) {
+        mPackageName = packageName;
         try {
             CarUserManagerHelper carUserManagerHelper = new CarUserManagerHelper(context);
             ApplicationInfo app = context.getPackageManager()
@@ -59,4 +63,17 @@
     public String getApplicationName() {
         return mApplicationName;
     }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        PrivacyApplication that = (PrivacyApplication) o;
+        return mPackageName.equals(that.mPackageName);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mPackageName);
+    }
 }
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/privacy/PrivacyItem.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/privacy/PrivacyItem.java
index fca1373..d3e123e 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/privacy/PrivacyItem.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/privacy/PrivacyItem.java
@@ -16,6 +16,8 @@
 
 package com.android.systemui.statusbar.car.privacy;
 
+import java.util.Objects;
+
 /**
  * Class for holding the data of each privacy item displayed in {@link OngoingPrivacyDialog}
  */
@@ -43,4 +45,18 @@
     public PrivacyType getPrivacyType() {
         return mPrivacyType;
     }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        PrivacyItem that = (PrivacyItem) o;
+        return mPrivacyType == that.mPrivacyType
+                && mPrivacyApplication.equals(that.mPrivacyApplication);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mPrivacyType, mPrivacyApplication);
+    }
 }
diff --git a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java
index 81c5bcd..642dc82 100644
--- a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java
+++ b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java
@@ -35,7 +35,6 @@
 import android.net.http.SslError;
 import android.os.Bundle;
 import android.telephony.CarrierConfigManager;
-import android.telephony.Rlog;
 import android.telephony.SubscriptionManager;
 import android.text.TextUtils;
 import android.util.ArrayMap;
@@ -477,11 +476,11 @@
     }
 
     private static void logd(String s) {
-        Rlog.d(TAG, s);
+        Log.d(TAG, s);
     }
 
     private static void loge(String s) {
-        Rlog.d(TAG, s);
+        Log.d(TAG, s);
     }
 
 }
diff --git a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CustomConfigLoader.java b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CustomConfigLoader.java
index 02c61d7..46b1d5f 100644
--- a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CustomConfigLoader.java
+++ b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CustomConfigLoader.java
@@ -19,7 +19,6 @@
 import android.content.Intent;
 import android.os.PersistableBundle;
 import android.telephony.CarrierConfigManager;
-import android.telephony.Rlog;
 import android.text.TextUtils;
 import android.util.Log;
 
@@ -27,7 +26,6 @@
 import com.android.internal.util.ArrayUtils;
 
 import java.util.ArrayList;
-import java.util.HashMap;
 import java.util.List;
 
 /**
@@ -44,7 +42,7 @@
     private static final String INTER_GROUP_DELIMITER = "\\s*:\\s*";
 
     private static final String TAG = CustomConfigLoader.class.getSimpleName();
-    private static final boolean VDBG = Rlog.isLoggable(TAG, Log.VERBOSE);
+    private static final boolean VDBG = Log.isLoggable(TAG, Log.VERBOSE);
 
     /**
      * loads and parses the carrier config, return a list of carrier action for the given signal
@@ -70,7 +68,7 @@
         // return an empty list if no match found
         List<Integer> actionList = new ArrayList<>();
         if (carrierConfigManager == null) {
-            Rlog.e(TAG, "load carrier config failure with carrier config manager uninitialized");
+            Log.e(TAG, "load carrier config failure with carrier config manager uninitialized");
             return actionList;
         }
         PersistableBundle b = carrierConfigManager.getConfig();
@@ -101,8 +99,8 @@
                             .EXTRA_DEFAULT_NETWORK_AVAILABLE_KEY, false));
                     break;
                 default:
-                    Rlog.e(TAG, "load carrier config failure with un-configured key: " +
-                            intent.getAction());
+                    Log.e(TAG, "load carrier config failure with un-configured key: "
+                            + intent.getAction());
                     break;
             }
             if (!ArrayUtils.isEmpty(configs)) {
@@ -111,12 +109,12 @@
                     matchConfig(config, arg1, arg2, actionList);
                     if (!actionList.isEmpty()) {
                         // return the first match
-                        if (VDBG) Rlog.d(TAG, "found match action list: " + actionList.toString());
+                        if (VDBG) Log.d(TAG, "found match action list: " + actionList.toString());
                         return actionList;
                     }
                 }
             }
-            Rlog.d(TAG, "no matching entry for signal: " + intent.getAction() + "arg1: " + arg1
+            Log.d(TAG, "no matching entry for signal: " + intent.getAction() + "arg1: " + arg1
                     + "arg2: " + arg2);
         }
         return actionList;
@@ -166,7 +164,7 @@
                 try {
                     actionList.add(Integer.parseInt(idx));
                 } catch (NumberFormatException e) {
-                    Rlog.e(TAG, "NumberFormatException(string: " + idx + " config:" + config + "): "
+                    Log.e(TAG, "NumberFormatException(string: " + idx + " config:" + config + "): "
                             + e);
                 }
             }
diff --git a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
index 1b27b52..48d34ae 100644
--- a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
+++ b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
@@ -16,6 +16,7 @@
 
 package com.android.externalstorage;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.usage.StorageStatsManager;
 import android.content.ContentResolver;
@@ -298,6 +299,53 @@
         return projection != null ? projection : DEFAULT_ROOT_PROJECTION;
     }
 
+    /**
+     * Check that the directory is the root of storage or blocked file from tree.
+     *
+     * @param docId the docId of the directory to be checked
+     * @return true, should be blocked from tree. Otherwise, false.
+     */
+    @Override
+    protected boolean shouldBlockFromTree(@NonNull String docId) {
+        try {
+            final File dir = getFileForDocId(docId, true /* visible */).getCanonicalFile();
+            if (!dir.isDirectory()) {
+                return false;
+            }
+
+            final String path = dir.getAbsolutePath();
+
+            // Block Download folder from tree
+            if (MediaStore.Downloads.isDownloadDir(path)) {
+                return true;
+            }
+
+            final ArrayMap<String, RootInfo> roots = new ArrayMap<>();
+
+            synchronized (mRootsLock) {
+                roots.putAll(mRoots);
+            }
+
+            // block root of storage
+            for (int i = 0; i < roots.size(); i++) {
+                RootInfo rootInfo = roots.valueAt(i);
+                // skip home root
+                if (TextUtils.equals(rootInfo.rootId, ROOT_ID_HOME)) {
+                    continue;
+                }
+
+                // block the root of storage
+                if (TextUtils.equals(path, rootInfo.visiblePath.getAbsolutePath())) {
+                    return true;
+                }
+            }
+            return false;
+        } catch (IOException e) {
+            throw new IllegalArgumentException(
+                    "Failed to determine if " + docId + " should block from tree " + ": " + e);
+        }
+    }
+
     @Override
     protected String getDocIdForFile(File file) throws FileNotFoundException {
         return getDocIdForFileMaybeCreate(file, false);
diff --git a/packages/PackageInstaller/res/values-television/themes.xml b/packages/PackageInstaller/res/values-television/themes.xml
new file mode 100644
index 0000000..5ae4957
--- /dev/null
+++ b/packages/PackageInstaller/res/values-television/themes.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2019 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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>
+
+    <style name="Theme.AlertDialogActivity.NoAnimation">
+        <item name="android:windowAnimationStyle">@null</item>
+    </style>
+
+    <style name="Theme.AlertDialogActivity"
+           parent="@android:style/Theme.DeviceDefault.Light.Dialog.Alert" />
+
+    <style name="Theme.AlertDialogActivity.NoActionBar"
+           parent="@android:style/Theme.DeviceDefault.Light.NoActionBar">
+    </style>
+
+</resources>
diff --git a/packages/PrintSpooler/TEST_MAPPING b/packages/PrintSpooler/TEST_MAPPING
new file mode 100644
index 0000000..4fa8822
--- /dev/null
+++ b/packages/PrintSpooler/TEST_MAPPING
@@ -0,0 +1,12 @@
+{
+  "presubmit": [
+    {
+      "name": "CtsPrintTestCases",
+      "options": [
+        {
+          "include-annotation": "android.platform.test.annotations.Presubmit"
+        }
+      ]
+    }
+  ]
+}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
index 5e2b7c8..17c621e 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
@@ -51,6 +51,7 @@
 import com.android.internal.util.XmlUtils;
 import com.android.internal.widget.LockPatternUtils;
 import com.android.internal.widget.LockPatternView;
+import com.android.internal.widget.LockscreenCredential;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
@@ -1992,8 +1993,11 @@
                 try {
                     LockPatternUtils lpu = new LockPatternUtils(mContext);
                     List<LockPatternView.Cell> cellPattern =
-                            LockPatternUtils.stringToPattern(lockPattern);
-                    lpu.saveLockPattern(cellPattern, null, UserHandle.USER_SYSTEM);
+                            LockPatternUtils.byteArrayToPattern(lockPattern.getBytes());
+                    lpu.setLockCredential(
+                            LockscreenCredential.createPattern(cellPattern),
+                            LockscreenCredential.createNone(),
+                            UserHandle.USER_SYSTEM);
                 } catch (IllegalArgumentException e) {
                     // Don't want corrupted lock pattern to hang the reboot process
                 }
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java
index 2ef0422..748f356 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java
@@ -115,4 +115,15 @@
             Log.e(TAG, "Failed to clean up screenshot of recents animation", e);
         }
     }
+
+    /**
+     * @see {{@link IRecentsAnimationController#setWillFinishToHome(boolean)}}.
+     */
+    public void setWillFinishToHome(boolean willFinishToHome) {
+        try {
+            mAnimationController.setWillFinishToHome(willFinishToHome);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Failed to set overview reached state", e);
+        }
+    }
 }
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java
index d45603f..ebac3d9 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java
@@ -33,10 +33,9 @@
 import com.android.internal.util.LatencyTracker;
 import com.android.internal.widget.LockPatternChecker;
 import com.android.internal.widget.LockPatternUtils;
+import com.android.internal.widget.LockscreenCredential;
 import com.android.systemui.R;
 
-import java.util.Arrays;
-
 /**
  * Base class for PIN and password unlock screens.
  */
@@ -132,19 +131,20 @@
     protected void verifyPasswordAndUnlock() {
         if (mDismissing) return; // already verified but haven't been dismissed; don't do it again.
 
-        final byte[] entry = getPasswordText();
+        final LockscreenCredential password =
+                LockscreenCredential.createPassword(getPasswordText());
         setPasswordEntryInputEnabled(false);
         if (mPendingLockCheck != null) {
             mPendingLockCheck.cancel(false);
         }
 
         final int userId = KeyguardUpdateMonitor.getCurrentUser();
-        if (entry.length <= MINIMUM_PASSWORD_LENGTH_BEFORE_REPORT) {
+        if (password.size() <= MINIMUM_PASSWORD_LENGTH_BEFORE_REPORT) {
             // to avoid accidental lockout, only count attempts that are long enough to be a
             // real password. This may require some tweaking.
             setPasswordEntryInputEnabled(true);
             onPasswordChecked(userId, false /* matched */, 0, false /* not valid - too short */);
-            Arrays.fill(entry, (byte) 0);
+            password.zeroize();
             return;
         }
 
@@ -152,9 +152,9 @@
             LatencyTracker.getInstance(mContext).onActionStart(ACTION_CHECK_CREDENTIAL);
             LatencyTracker.getInstance(mContext).onActionStart(ACTION_CHECK_CREDENTIAL_UNLOCKED);
         }
-        mPendingLockCheck = LockPatternChecker.checkPassword(
+        mPendingLockCheck = LockPatternChecker.checkCredential(
                 mLockPatternUtils,
-                entry,
+                password,
                 userId,
                 new LockPatternChecker.OnCheckCallback() {
 
@@ -166,7 +166,7 @@
                         }
                         onPasswordChecked(userId, true /* matched */, 0 /* timeoutMs */,
                                 true /* isValidPassword */);
-                        Arrays.fill(entry, (byte) 0);
+                        password.zeroize();
                     }
 
                     @Override
@@ -181,7 +181,7 @@
                             onPasswordChecked(userId, false /* matched */, timeoutMs,
                                     true /* isValidPassword */);
                         }
-                        Arrays.fill(entry, (byte) 0);
+                        password.zeroize();
                     }
 
                     @Override
@@ -192,7 +192,7 @@
                             LatencyTracker.getInstance(mContext).onActionEnd(
                                     ACTION_CHECK_CREDENTIAL_UNLOCKED);
                         }
-                        Arrays.fill(entry, (byte) 0);
+                        password.zeroize();
                     }
                 });
     }
@@ -223,7 +223,7 @@
     }
 
     protected abstract void resetPasswordText(boolean animate, boolean announce);
-    protected abstract byte[] getPasswordText();
+    protected abstract CharSequence getPasswordText();
     protected abstract void setPasswordEntryEnabled(boolean enabled);
     protected abstract void setPasswordEntryInputEnabled(boolean enabled);
 
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java
index e3ac0f6..12c9fc9 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java
@@ -243,8 +243,8 @@
     }
 
     @Override
-    protected byte[] getPasswordText() {
-        return charSequenceToByteArray(mPasswordEntry.getText());
+    protected CharSequence getPasswordText() {
+        return mPasswordEntry.getText();
     }
 
     @Override
@@ -379,18 +379,4 @@
         return getContext().getString(
                 com.android.internal.R.string.keyguard_accessibility_password_unlock);
     }
-
-    /*
-     * This method avoids creating a new string when getting a byte array from EditView#getText().
-     */
-    private static byte[] charSequenceToByteArray(CharSequence chars) {
-        if (chars == null) {
-            return null;
-        }
-        byte[] bytes = new byte[chars.length()];
-        for (int i = 0; i < chars.length(); i++) {
-            bytes[i] = (byte) chars.charAt(i);
-        }
-        return bytes;
-    }
 }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java
index 297052f..9eb168a 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java
@@ -39,6 +39,7 @@
 import com.android.internal.widget.LockPatternChecker;
 import com.android.internal.widget.LockPatternUtils;
 import com.android.internal.widget.LockPatternView;
+import com.android.internal.widget.LockscreenCredential;
 import com.android.settingslib.animation.AppearAnimationCreator;
 import com.android.settingslib.animation.AppearAnimationUtils;
 import com.android.settingslib.animation.DisappearAnimationUtils;
@@ -297,9 +298,9 @@
                 LatencyTracker.getInstance(mContext).onActionStart(ACTION_CHECK_CREDENTIAL);
                 LatencyTracker.getInstance(mContext).onActionStart(ACTION_CHECK_CREDENTIAL_UNLOCKED);
             }
-            mPendingLockCheck = LockPatternChecker.checkPattern(
+            mPendingLockCheck = LockPatternChecker.checkCredential(
                     mLockPatternUtils,
-                    pattern,
+                    LockscreenCredential.createPattern(pattern),
                     userId,
                     new LockPatternChecker.OnCheckCallback() {
 
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java
index 274f739..8e9df55 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java
@@ -167,8 +167,8 @@
     }
 
     @Override
-    protected byte[] getPasswordText() {
-        return charSequenceToByteArray(mPasswordEntry.getText());
+    protected CharSequence getPasswordText() {
+        return mPasswordEntry.getText();
     }
 
     @Override
@@ -266,18 +266,4 @@
         return getContext().getString(
                 com.android.internal.R.string.keyguard_accessibility_pin_unlock);
     }
-
-    /*
-     * This method avoids creating a new string when getting a byte array from EditView#getText().
-     */
-    private static byte[] charSequenceToByteArray(CharSequence chars) {
-        if (chars == null) {
-            return null;
-        }
-        byte[] bytes = new byte[chars.length()];
-        for (int i = 0; i < chars.length(); i++) {
-            bytes[i] = (byte) chars.charAt(i);
-        }
-        return bytes;
-    }
 }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
index af4e61b..5d35169 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
@@ -31,6 +31,7 @@
 import android.content.Context;
 import android.graphics.Color;
 import android.graphics.drawable.Drawable;
+import android.graphics.text.LineBreaker;
 import android.net.Uri;
 import android.os.Trace;
 import android.provider.Settings;
@@ -152,6 +153,7 @@
         mRowWithHeaderTextSize = mContext.getResources().getDimensionPixelSize(
                 R.dimen.header_row_font_size);
         mTitle.setOnClickListener(this);
+        mTitle.setBreakStrategy(LineBreaker.BREAK_STRATEGY_BALANCED);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
index 3a7a7f7..ad20986 100644
--- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
+++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
@@ -460,7 +460,7 @@
                         | WindowManager.LayoutParams.FLAG_SLIPPERY
                         | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN,
                 PixelFormat.TRANSLUCENT);
-        lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS
+        lp.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS
                 | WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION;
 
         if (!DEBUG_SCREENSHOT_ROUNDED_CORNERS) {
diff --git a/packages/SystemUI/src/com/android/systemui/SizeCompatModeActivityController.java b/packages/SystemUI/src/com/android/systemui/SizeCompatModeActivityController.java
index a8591bb..10009f5 100644
--- a/packages/SystemUI/src/com/android/systemui/SizeCompatModeActivityController.java
+++ b/packages/SystemUI/src/com/android/systemui/SizeCompatModeActivityController.java
@@ -203,7 +203,7 @@
             mWinParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
                     | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
             mWinParams.format = PixelFormat.TRANSLUCENT;
-            mWinParams.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
+            mWinParams.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
             mWinParams.setTitle(SizeCompatModeActivityController.class.getSimpleName()
                     + context.getDisplayId());
         }
diff --git a/packages/SystemUI/src/com/android/systemui/SysUIToast.java b/packages/SystemUI/src/com/android/systemui/SysUIToast.java
index 8bcf057..0f7f1be 100644
--- a/packages/SystemUI/src/com/android/systemui/SysUIToast.java
+++ b/packages/SystemUI/src/com/android/systemui/SysUIToast.java
@@ -31,7 +31,7 @@
     public static Toast makeText(Context context, CharSequence text, @Duration int duration) {
         Toast toast = Toast.makeText(context, text, duration);
         toast.getWindowParams().privateFlags |=
-                WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
+                WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
         return toast;
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistModule.java b/packages/SystemUI/src/com/android/systemui/assist/AssistModule.java
index 739eade..6f5a17d 100644
--- a/packages/SystemUI/src/com/android/systemui/assist/AssistModule.java
+++ b/packages/SystemUI/src/com/android/systemui/assist/AssistModule.java
@@ -21,6 +21,7 @@
 import android.os.HandlerThread;
 import android.os.SystemClock;
 
+import androidx.annotation.Nullable;
 import androidx.slice.Clock;
 
 import com.android.internal.app.AssistUtils;
@@ -68,6 +69,7 @@
     }
 
     @Provides
+    @Nullable
     static AssistHandleViewController provideAssistHandleViewController(
             NavigationBarController navigationBarController) {
         return navigationBarController.getAssistHandlerViewController();
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
index a9359d4..f1abdb3 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
@@ -580,7 +580,7 @@
                 WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL,
                 WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED,
                 PixelFormat.TRANSLUCENT);
-        lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
+        lp.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
         lp.setTitle("BiometricPrompt");
         lp.token = windowToken;
         return lp;
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPasswordView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPasswordView.java
index 8df072e..bebaa4b 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPasswordView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPasswordView.java
@@ -26,7 +26,7 @@
 import android.widget.TextView;
 
 import com.android.internal.widget.LockPatternChecker;
-import com.android.internal.widget.LockPatternUtils;
+import com.android.internal.widget.LockscreenCredential;
 import com.android.systemui.R;
 
 /**
@@ -96,13 +96,16 @@
     }
 
     private void checkPasswordAndUnlock() {
-        final byte[] password = LockPatternUtils.charSequenceToByteArray(mPasswordField.getText());
-        if (password == null || password.length == 0) {
-            return;
-        }
+        try (LockscreenCredential password =  mCredentialType == Utils.CREDENTIAL_PIN
+                ? LockscreenCredential.createPinOrNone(mPasswordField.getText())
+                : LockscreenCredential.createPasswordOrNone(mPasswordField.getText())) {
+            if (password.isNone()) {
+                return;
+            }
 
-        mPendingLockCheck = LockPatternChecker.checkPassword(mLockPatternUtils,
-                password, mUserId, this::onCredentialChecked);
+            mPendingLockCheck = LockPatternChecker.checkCredential(mLockPatternUtils,
+                    password, mUserId, this::onCredentialChecked);
+        }
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPatternView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPatternView.java
index 6c36f82..14414a4 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPatternView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPatternView.java
@@ -22,6 +22,7 @@
 import com.android.internal.widget.LockPatternChecker;
 import com.android.internal.widget.LockPatternUtils;
 import com.android.internal.widget.LockPatternView;
+import com.android.internal.widget.LockscreenCredential;
 import com.android.systemui.R;
 
 import java.util.List;
@@ -64,11 +65,13 @@
                 return;
             }
 
-            mPendingLockCheck = LockPatternChecker.checkPattern(
-                    mLockPatternUtils,
-                    pattern,
-                    mUserId,
-                    this::onPatternChecked);
+            try (LockscreenCredential credential = LockscreenCredential.createPattern(pattern)) {
+                mPendingLockCheck = LockPatternChecker.checkCredential(
+                        mLockPatternUtils,
+                        credential,
+                        mUserId,
+                        this::onPatternChecked);
+            }
         }
 
         private void onPatternChecked(boolean matched, int timeoutMs) {
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java b/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java
index ca4ec6d..b069ba3 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java
@@ -75,7 +75,7 @@
                         handler, wakeLock, machine, dockManager, dozeLog),
                 createDozeUi(context, host, wakeLock, machine, handler, alarmManager, params,
                         dozeLog),
-                new DozeScreenState(wrappedService, handler, params, wakeLock),
+                new DozeScreenState(wrappedService, handler, host, params, wakeLock),
                 createDozeScreenBrightness(context, wrappedService, sensorManager, host, params,
                         handler),
                 new DozeWallpaperState(context, getBiometricUnlockController(dozeService)),
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java b/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java
index 07dd2cd..1a6bd60 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java
@@ -63,9 +63,10 @@
     void setDozeScreenBrightness(int value);
 
     /**
-     * Makes scrims black and changes animation durations.
+     * Fade out screen before switching off the display power mode.
+     * @param onDisplayOffCallback Executed when the display is black.
      */
-    default void prepareForGentleWakeUp() {}
+    void prepareForGentleSleep(Runnable onDisplayOffCallback);
 
     void onIgnoreTouchWhilePulsing(boolean ignore);
 
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java
index 38ee2fe..95c42fc 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java
@@ -16,6 +16,12 @@
 
 package com.android.systemui.doze;
 
+import static com.android.systemui.doze.DozeMachine.State.DOZE;
+import static com.android.systemui.doze.DozeMachine.State.DOZE_AOD;
+import static com.android.systemui.doze.DozeMachine.State.DOZE_AOD_PAUSED;
+import static com.android.systemui.doze.DozeMachine.State.DOZE_AOD_PAUSING;
+import static com.android.systemui.doze.DozeMachine.State.DOZE_PULSE_DONE;
+
 import android.os.Handler;
 import android.util.Log;
 import android.view.Display;
@@ -48,21 +54,24 @@
     private final Handler mHandler;
     private final Runnable mApplyPendingScreenState = this::applyPendingScreenState;
     private final DozeParameters mParameters;
+    private final DozeHost mDozeHost;
 
     private int mPendingScreenState = Display.STATE_UNKNOWN;
     private SettableWakeLock mWakeLock;
 
-    public DozeScreenState(DozeMachine.Service service, Handler handler,
+    public DozeScreenState(DozeMachine.Service service, Handler handler, DozeHost host,
             DozeParameters parameters, WakeLock wakeLock) {
         mDozeService = service;
         mHandler = handler;
         mParameters = parameters;
+        mDozeHost = host;
         mWakeLock = new SettableWakeLock(wakeLock, TAG);
     }
 
     @Override
     public void transitionTo(DozeMachine.State oldState, DozeMachine.State newState) {
         int screenState = newState.screenState(mParameters);
+        mDozeHost.prepareForGentleSleep(null);
 
         if (newState == DozeMachine.State.FINISH) {
             // Make sure not to apply the screen state after DozeService was destroyed.
@@ -79,12 +88,13 @@
             return;
         }
 
-        boolean messagePending = mHandler.hasCallbacks(mApplyPendingScreenState);
-        boolean pulseEnding = oldState  == DozeMachine.State.DOZE_PULSE_DONE
-                && newState == DozeMachine.State.DOZE_AOD;
-        boolean turningOn = (oldState == DozeMachine.State.DOZE_AOD_PAUSED
-                || oldState  == DozeMachine.State.DOZE) && newState == DozeMachine.State.DOZE_AOD;
-        boolean justInitialized = oldState == DozeMachine.State.INITIALIZED;
+        final boolean messagePending = mHandler.hasCallbacks(mApplyPendingScreenState);
+        final boolean pulseEnding = oldState  == DOZE_PULSE_DONE && newState == DOZE_AOD;
+        final boolean turningOn = (oldState == DOZE_AOD_PAUSED
+                || oldState  == DOZE) && newState == DOZE_AOD;
+        final boolean turningOff = (oldState == DOZE_AOD && newState == DOZE)
+                || (oldState == DOZE_AOD_PAUSING && newState == DOZE_AOD_PAUSED);
+        final boolean justInitialized = oldState == DozeMachine.State.INITIALIZED;
         if (messagePending || justInitialized || pulseEnding || turningOn) {
             // During initialization, we hide the navigation bar. That is however only applied after
             // a traversal; setting the screen state here is immediate however, so it can happen
@@ -93,7 +103,7 @@
             mPendingScreenState = screenState;
 
             // Delay screen state transitions even longer while animations are running.
-            boolean shouldDelayTransition = newState == DozeMachine.State.DOZE_AOD
+            boolean shouldDelayTransition = newState == DOZE_AOD
                     && mParameters.shouldControlScreenOff() && !turningOn;
 
             if (shouldDelayTransition) {
@@ -114,6 +124,8 @@
             } else if (DEBUG) {
                 Log.d(TAG, "Pending display state change to " + screenState);
             }
+        } else if (turningOff) {
+            mDozeHost.prepareForGentleSleep(() -> applyScreenState(screenState));
         } else {
             applyScreenState(screenState);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java b/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java
index 2c0ccd21..f155783 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java
@@ -135,7 +135,6 @@
                 break;
             case DOZE:
             case DOZE_AOD_PAUSED:
-                mHost.prepareForGentleWakeUp();
                 unscheduleTimeTick();
                 break;
             case DOZE_REQUEST_PULSE:
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipDismissViewController.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipDismissViewController.java
index 8224365..3f15966 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipDismissViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipDismissViewController.java
@@ -107,7 +107,7 @@
                             | LayoutParams.FLAG_NOT_FOCUSABLE,
                     PixelFormat.TRANSLUCENT);
             lp.setTitle("pip-dismiss-overlay");
-            lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
+            lp.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
             lp.gravity = Gravity.TOP | Gravity.CENTER_HORIZONTAL;
             mWindowManager.addView(mDismissView, lp);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index ae83567..2e24403 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -28,10 +28,12 @@
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Message;
+import android.provider.Settings;
 import android.service.quicksettings.Tile;
 import android.util.AttributeSet;
 import android.view.LayoutInflater;
 import android.view.View;
+import android.widget.FrameLayout;
 import android.widget.LinearLayout;
 
 import com.android.internal.logging.MetricsLogger;
@@ -49,6 +51,8 @@
 import com.android.systemui.qs.external.CustomTile;
 import com.android.systemui.settings.BrightnessController;
 import com.android.systemui.settings.ToggleSliderView;
+import com.android.systemui.shared.plugins.PluginManager;
+import com.android.systemui.statusbar.phone.NPVPluginManager;
 import com.android.systemui.statusbar.policy.BrightnessMirrorController;
 import com.android.systemui.statusbar.policy.BrightnessMirrorController.BrightnessMirrorListener;
 import com.android.systemui.tuner.TunerService;
@@ -98,6 +102,10 @@
     private BrightnessMirrorController mBrightnessMirrorController;
     private View mDivider;
 
+    private FrameLayout mPluginFrame;
+    private final PluginManager mPluginManager;
+    private NPVPluginManager mNPVPluginManager;
+
     public QSPanel(Context context) {
         this(context, null);
     }
@@ -106,9 +114,13 @@
         this(context, attrs, null);
     }
 
+    public QSPanel(Context context, AttributeSet attrs, DumpController dumpController) {
+        this(context, attrs, dumpController, null);
+    }
+
     @Inject
     public QSPanel(@Named(VIEW_CONTEXT) Context context, AttributeSet attrs,
-            DumpController dumpController) {
+            DumpController dumpController, PluginManager pluginManager) {
         super(context, attrs);
         mContext = context;
 
@@ -136,6 +148,15 @@
         mBrightnessController = new BrightnessController(getContext(),
                 findViewById(R.id.brightness_slider));
         mDumpController = dumpController;
+        mPluginManager = pluginManager;
+        if (mPluginManager != null && Settings.System.getInt(
+                mContext.getContentResolver(), "npv_plugin_flag", 0) == 2) {
+            mPluginFrame = (FrameLayout) LayoutInflater.from(mContext).inflate(
+                    R.layout.status_bar_expanded_plugin_frame, this, false);
+            addView(mPluginFrame);
+            mNPVPluginManager = new NPVPluginManager(mPluginFrame, mPluginManager);
+        }
+
     }
 
     protected void addDivider() {
@@ -377,6 +398,7 @@
         if (mListening) {
             refreshAllTiles();
         }
+        if (mNPVPluginManager != null) mNPVPluginManager.setListening(listening);
     }
 
     public void setListening(boolean listening, boolean expanded) {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsOnboarding.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsOnboarding.java
index c1ce163..aa64449 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsOnboarding.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsOnboarding.java
@@ -497,7 +497,7 @@
                 WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY,
                 flags,
                 PixelFormat.TRANSLUCENT);
-        lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
+        lp.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
         lp.setTitle("RecentsOnboarding");
         lp.gravity = gravity;
         return lp;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java b/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
index 0f277ca..2d1c087 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
@@ -128,7 +128,7 @@
                         | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
                 PixelFormat.TRANSLUCENT);
         lp.token = new Binder();
-        lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
+        lp.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
         lp.setTitle("ScreenPinningConfirmation");
         lp.gravity = Gravity.FILL;
         return lp;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NavigationBarController.java b/packages/SystemUI/src/com/android/systemui/statusbar/NavigationBarController.java
index e09e9cd..5144a95 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NavigationBarController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NavigationBarController.java
@@ -233,11 +233,13 @@
     }
 
     /** @return {@link NavigationBarFragment} on the default display. */
+    @Nullable
     public NavigationBarFragment getDefaultNavigationBarFragment() {
         return mNavigationBars.get(DEFAULT_DISPLAY);
     }
 
     /** @return {@link AssistHandleViewController} (only on the default display). */
+    @Nullable
     public AssistHandleViewController getAssistHandlerViewController() {
         NavigationBarFragment navBar = getDefaultNavigationBarFragment();
         return navBar == null ? null : navBar.getAssistHandlerViewController();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DoubleTapHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DoubleTapHelper.java
index 0d62703..78ea5c0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DoubleTapHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DoubleTapHelper.java
@@ -100,6 +100,7 @@
                                     event.getY() - mActivationY);
                         }
                         if (withinDoubleTapSlop) {
+                            makeInactive();
                             if (!mDoubleTapListener.onDoubleTap()) {
                                 return false;
                             }
@@ -134,6 +135,7 @@
         if (mActivated) {
             mActivated = false;
             mActivationListener.onActiveChanged(false);
+            mView.removeCallbacks(mTapTimeoutRunnable);
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java
index ca7c227..442c089 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java
@@ -276,7 +276,7 @@
                             | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN,
                     PixelFormat.TRANSLUCENT);
             mEdgePanelLp.privateFlags |=
-                    WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
+                    WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
             mEdgePanelLp.setTitle(TAG + mDisplayId);
             mEdgePanelLp.accessibilityTitle = mContext.getString(R.string.nav_bar_edge_panel);
             mEdgePanelLp.windowAnimations = 0;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/FloatingRotationButton.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/FloatingRotationButton.java
index deb314b..a784984 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/FloatingRotationButton.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/FloatingRotationButton.java
@@ -82,7 +82,7 @@
         final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(mDiameter, mDiameter,
                 mMargin, mMargin, WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL, flags,
                 PixelFormat.TRANSLUCENT);
-        lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
+        lp.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
         lp.setTitle("FloatingRotationButton");
         switch (mWindowManager.getDefaultDisplay().getRotation()) {
             case Surface.ROTATION_0:
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 177294b..9ab635c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -43,6 +43,7 @@
 import android.os.PowerManager;
 import android.os.SystemClock;
 import android.provider.DeviceConfig;
+import android.provider.Settings;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.util.MathUtils;
@@ -541,7 +542,10 @@
         mQsNavbarScrim = findViewById(R.id.qs_navbar_scrim);
         mLastOrientation = getResources().getConfiguration().orientation;
         mPluginFrame = findViewById(R.id.plugin_frame);
-        mNPVPluginManager = new NPVPluginManager(mPluginFrame, mPluginManager);
+        if (Settings.System.getInt(
+                mContext.getContentResolver(), "npv_plugin_flag", 0) == 1) {
+            mNPVPluginManager = new NPVPluginManager(mPluginFrame, mPluginManager);
+        }
 
 
         initBottomArea();
@@ -778,7 +782,7 @@
             mPluginFrame.setLayoutParams(lp);
         }
 
-        mNPVPluginManager.replaceFrameLayout(mPluginFrame);
+        if (mNPVPluginManager != null) mNPVPluginManager.replaceFrameLayout(mPluginFrame);
     }
 
     private void initBottomArea() {
@@ -808,8 +812,10 @@
         int oldMaxHeight = mQsMaxExpansionHeight;
         if (mQs != null) {
             mQsMinExpansionHeight = mKeyguardShowing ? 0 : mQs.getQsMinExpansionHeight();
-            mNPVPluginManager.setYOffset(mQsMinExpansionHeight);
-            mQsMinExpansionHeight += mNPVPluginManager.getHeight();
+            if (mNPVPluginManager != null) {
+                mNPVPluginManager.setYOffset(mQsMinExpansionHeight);
+                mQsMinExpansionHeight += mNPVPluginManager.getHeight();
+            }
             mQsMaxExpansionHeight = mQs.getDesiredHeight();
             mNotificationStackScroller.setMaxTopPadding(
                     mQsMaxExpansionHeight + mQsNotificationTopPadding);
@@ -1915,9 +1921,11 @@
                 mBarState != StatusBarState.KEYGUARD && (!mQsExpanded
                         || mQsExpansionFromOverscroll));
         updateEmptyShadeView();
-        mNPVPluginManager.changeVisibility((mBarState != StatusBarState.KEYGUARD)
-                ? View.VISIBLE
-                : View.INVISIBLE);
+        if (mNPVPluginManager != null) {
+            mNPVPluginManager.changeVisibility((mBarState != StatusBarState.KEYGUARD)
+                    ? View.VISIBLE
+                    : View.INVISIBLE);
+        }
         mQsNavbarScrim.setVisibility(mBarState == StatusBarState.SHADE && mQsExpanded
                 && !mStackScrollerOverscrolling && mQsScrimEnabled
                 ? View.VISIBLE
@@ -1975,7 +1983,9 @@
         float qsExpansionFraction = getQsExpansionFraction();
         mQs.setQsExpansion(qsExpansionFraction, getHeaderTranslation());
         int heightDiff = mQs.getDesiredHeight() - mQs.getQsMinExpansionHeight();
-        mNPVPluginManager.setExpansion(qsExpansionFraction, getHeaderTranslation(), heightDiff);
+        if (mNPVPluginManager != null) {
+            mNPVPluginManager.setExpansion(qsExpansionFraction, getHeaderTranslation(), heightDiff);
+        }
         mNotificationStackScroller.setQsExpansionFraction(qsExpansionFraction);
     }
 
@@ -2396,7 +2406,7 @@
                 appearAmount = mNotificationStackScroller.calculateAppearFractionBypass();
             }
             startHeight = -mQs.getQsMinExpansionHeight();
-            startHeight -= mNPVPluginManager.getHeight();
+            if (mNPVPluginManager != null) startHeight -= mNPVPluginManager.getHeight();
         }
         float translation = MathUtils.lerp(startHeight, 0,
                 Math.min(1.0f, appearAmount))
@@ -2540,7 +2550,7 @@
         mKeyguardStatusBar.setListening(listening);
         if (mQs == null) return;
         mQs.setListening(listening);
-        mNPVPluginManager.setListening(listening);
+        if (mNPVPluginManager != null) mNPVPluginManager.setListening(listening);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index 5ba19cc..e7d896c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -258,7 +258,7 @@
 
         final ScrimState oldState = mState;
         mState = state;
-        Trace.traceCounter(Trace.TRACE_TAG_APP, "scrim_state", mState.getIndex());
+        Trace.traceCounter(Trace.TRACE_TAG_APP, "scrim_state", mState.ordinal());
 
         if (mCallback != null) {
             mCallback.onCancelled();
@@ -519,22 +519,6 @@
     }
 
     /**
-     * Set front scrim to black, cancelling animations, in order to prepare to fade them
-     * away once the display turns on.
-     */
-    public void prepareForGentleWakeUp() {
-        if (mState == ScrimState.AOD && mDozeParameters.getAlwaysOn()) {
-            mInFrontAlpha = 1f;
-            mInFrontTint = Color.BLACK;
-            mBehindTint = Color.BLACK;
-            mAnimateChange = false;
-            updateScrims();
-            mAnimateChange = true;
-            mAnimationDuration = ANIMATION_DURATION_LONG;
-        }
-    }
-
-    /**
      * If the lock screen sensor is active.
      */
     public void setWakeLockScreenSensorActive(boolean active) {
@@ -595,6 +579,8 @@
         setScrimAlpha(mScrimInFront, mInFrontAlpha);
         setScrimAlpha(mScrimBehind, mBehindAlpha);
         setScrimAlpha(mScrimForBubble, mBubbleAlpha);
+        // The animation could have all already finished, let's call onFinished just in case
+        onFinished();
         dispatchScrimsVisible();
     }
 
@@ -693,9 +679,9 @@
 
             @Override
             public void onAnimationEnd(Animator animation) {
+                scrim.setTag(TAG_KEY_ANIM, null);
                 onFinished(lastCallback);
 
-                scrim.setTag(TAG_KEY_ANIM, null);
                 dispatchScrimsVisible();
 
                 if (!mDeferFinishedListener && mOnAnimationFinished != null) {
@@ -759,9 +745,9 @@
     }
 
     private void onFinished(Callback callback) {
-        if (!hasReachedFinalState(mScrimBehind)
-            || !hasReachedFinalState(mScrimInFront)
-            || !hasReachedFinalState(mScrimForBubble)) {
+        if (isAnimating(mScrimBehind)
+            || isAnimating(mScrimInFront)
+            || isAnimating(mScrimForBubble)) {
             if (callback != null && callback != mCallback) {
                 // Since we only notify the callback that we're finished once everything has
                 // finished, we need to make sure that any changing callbacks are also invoked
@@ -794,11 +780,6 @@
         }
     }
 
-    private boolean hasReachedFinalState(ScrimView scrim) {
-        return scrim.getViewAlpha() == getCurrentScrimAlpha(scrim)
-                && scrim.getTint() == getCurrentScrimTint(scrim);
-    }
-
     private boolean isAnimating(View scrim) {
         return scrim.getTag(TAG_KEY_ANIM) != null;
     }
@@ -854,10 +835,7 @@
             } else {
                 // update the alpha directly
                 updateScrimColor(scrim, alpha, getCurrentScrimTint(scrim));
-                onFinished();
             }
-        } else {
-            onFinished();
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
index 7463c7c..e0597159 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
@@ -30,12 +30,30 @@
     /**
      * Initial state.
      */
-    UNINITIALIZED(-1),
+    UNINITIALIZED,
+
+    /**
+     * When turned off by sensors (prox, presence.)
+     */
+    OFF {
+        @Override
+        public void prepare(ScrimState previousState) {
+            mFrontTint = Color.BLACK;
+            mBehindTint = previousState.mBehindTint;
+            mBubbleTint = previousState.mBubbleTint;
+
+            mFrontAlpha = 1f;
+            mBehindAlpha = previousState.mBehindAlpha;
+            mBubbleAlpha = previousState.mBubbleAlpha;
+
+            mAnimationDuration = ScrimController.ANIMATION_DURATION_LONG;
+        }
+    },
 
     /**
      * On the lock screen.
      */
-    KEYGUARD(0) {
+    KEYGUARD {
         @Override
         public void prepare(ScrimState previousState) {
             mBlankScreen = false;
@@ -65,7 +83,7 @@
     /**
      * Showing password challenge on the keyguard.
      */
-    BOUNCER(1) {
+    BOUNCER {
         @Override
         public void prepare(ScrimState previousState) {
             mBehindAlpha = ScrimController.GRADIENT_SCRIM_ALPHA_BUSY;
@@ -77,7 +95,7 @@
     /**
      * Showing password challenge on top of a FLAG_SHOW_WHEN_LOCKED activity.
      */
-    BOUNCER_SCRIMMED(2) {
+    BOUNCER_SCRIMMED {
         @Override
         public void prepare(ScrimState previousState) {
             mBehindAlpha = 0;
@@ -89,7 +107,7 @@
     /**
      * Changing screen brightness from quick settings.
      */
-    BRIGHTNESS_MIRROR(3) {
+    BRIGHTNESS_MIRROR {
         @Override
         public void prepare(ScrimState previousState) {
             mBehindAlpha = 0;
@@ -101,7 +119,7 @@
     /**
      * Always on display or screen off.
      */
-    AOD(4) {
+    AOD {
         @Override
         public void prepare(ScrimState previousState) {
             final boolean alwaysOnEnabled = mDozeParameters.getAlwaysOn();
@@ -136,7 +154,7 @@
     /**
      * When phone wakes up because you received a notification.
      */
-    PULSING(5) {
+    PULSING {
         @Override
         public void prepare(ScrimState previousState) {
             mFrontAlpha = mAodFrontScrimAlpha;
@@ -164,7 +182,7 @@
     /**
      * Unlocked on top of an app (launcher or any other activity.)
      */
-    UNLOCKED(6) {
+    UNLOCKED {
         @Override
         public void prepare(ScrimState previousState) {
             // State that UI will sync to.
@@ -201,7 +219,7 @@
     /**
      * Unlocked with a bubble expanded.
      */
-    BUBBLE_EXPANDED(7) {
+    BUBBLE_EXPANDED {
         @Override
         public void prepare(ScrimState previousState) {
             mFrontTint = Color.TRANSPARENT;
@@ -237,17 +255,12 @@
     DozeParameters mDozeParameters;
     boolean mDisplayRequiresBlanking;
     boolean mWallpaperSupportsAmbientMode;
-    int mIndex;
     boolean mHasBackdrop;
     boolean mLaunchingAffordanceWithPreview;
     boolean mWakeLockScreenSensorActive;
     boolean mKeyguardFadingAway;
     long mKeyguardFadingAwayDuration;
 
-    ScrimState(int index) {
-        mIndex = index;
-    }
-
     public void init(ScrimView scrimInFront, ScrimView scrimBehind, ScrimView scrimForBubble,
             DozeParameters dozeParameters) {
         mScrimInFront = scrimInFront;
@@ -262,10 +275,6 @@
     public void prepare(ScrimState previousState) {
     }
 
-    public int getIndex() {
-        return mIndex;
-    }
-
     public float getFrontAlpha() {
         return mFrontAlpha;
     }
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 9093687..d1fe46e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -4018,6 +4018,13 @@
         } else if (isPulsing()) {
             mScrimController.transitionTo(ScrimState.PULSING,
                     mDozeScrimController.getScrimCallback());
+        } else if (mDozeServiceHost.hasPendingScreenOffCallback()) {
+            mScrimController.transitionTo(ScrimState.OFF, new ScrimController.Callback() {
+                @Override
+                public void onFinished() {
+                    mDozeServiceHost.executePendingScreenOffCallback();
+                }
+            });
         } else if (mDozing && !unlocking) {
             mScrimController.transitionTo(ScrimState.AOD);
         } else if (mIsKeyguard && !unlocking) {
@@ -4044,6 +4051,7 @@
         private boolean mAnimateWakeup;
         private boolean mAnimateScreenOff;
         private boolean mIgnoreTouchWhilePulsing;
+        private Runnable mPendingScreenOffCallback;
         @VisibleForTesting
         boolean mWakeLockScreenPerformsAuth = SystemProperties.getBoolean(
                 "persist.sysui.wake_performs_auth", true);
@@ -4266,8 +4274,33 @@
         }
 
         @Override
-        public void prepareForGentleWakeUp() {
-            mScrimController.prepareForGentleWakeUp();
+        public void prepareForGentleSleep(Runnable onDisplayOffCallback) {
+            if (onDisplayOffCallback != null) {
+                Log.w(TAG, "Overlapping onDisplayOffCallback. Ignoring previous one.");
+            }
+            mPendingScreenOffCallback = onDisplayOffCallback;
+            updateScrimController();
+        }
+
+        /**
+         * When the dozing host is waiting for scrims to fade out to change the display state.
+         */
+        boolean hasPendingScreenOffCallback() {
+            return mPendingScreenOffCallback != null;
+        }
+
+        /**
+         * Executes an nullifies the pending display state callback.
+         *
+         * @see #hasPendingScreenOffCallback()
+         * @see #prepareForGentleSleep(Runnable)
+         */
+        void executePendingScreenOffCallback() {
+            if (mPendingScreenOffCallback == null) {
+                return;
+            }
+            mPendingScreenOffCallback.run();
+            mPendingScreenOffCallback = null;
         }
 
         private void dispatchTap(View view, float x, float y) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java
index ce929b7..44be6bc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java
@@ -93,10 +93,10 @@
     public static void setShowForAllUsers(Dialog dialog, boolean show) {
         if (show) {
             dialog.getWindow().getAttributes().privateFlags |=
-                    WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
+                    WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
         } else {
             dialog.getWindow().getAttributes().privateFlags &=
-                    ~WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
+                    ~WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
index 43795dc..cca9479 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
@@ -23,12 +23,16 @@
 import android.app.Notification;
 import android.app.PendingIntent;
 import android.app.RemoteInput;
+import android.content.ClipDescription;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.PackageManager;
 import android.content.pm.ShortcutManager;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
+import android.net.Uri;
 import android.os.Bundle;
+import android.os.ServiceManager;
 import android.os.SystemClock;
 import android.os.UserHandle;
 import android.text.Editable;
@@ -53,8 +57,13 @@
 import android.widget.ProgressBar;
 import android.widget.TextView;
 
+import androidx.core.view.inputmethod.EditorInfoCompat;
+import androidx.core.view.inputmethod.InputConnectionCompat;
+import androidx.core.view.inputmethod.InputContentInfoCompat;
+
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto;
+import com.android.internal.statusbar.IStatusBarService;
 import com.android.systemui.Dependency;
 import com.android.systemui.Interpolators;
 import com.android.systemui.R;
@@ -65,6 +74,7 @@
 import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
 import com.android.systemui.statusbar.phone.LightBarController;
 
+import java.util.HashMap;
 import java.util.function.Consumer;
 
 /**
@@ -88,6 +98,8 @@
     private RemoteInputController mController;
     private RemoteInputQuickSettingsDisabler mRemoteInputQuickSettingsDisabler;
 
+    private IStatusBarService mStatusBarManagerService;
+
     private NotificationEntry mEntry;
 
     private boolean mRemoved;
@@ -103,6 +115,8 @@
     public RemoteInputView(Context context, AttributeSet attrs) {
         super(context, attrs);
         mRemoteInputQuickSettingsDisabler = Dependency.get(RemoteInputQuickSettingsDisabler.class);
+        mStatusBarManagerService = IStatusBarService.Stub.asInterface(
+                ServiceManager.getService(Context.STATUS_BAR_SERVICE));
     }
 
     @Override
@@ -128,7 +142,7 @@
 
                 if (isSoftImeEvent || isKeyboardEnterKey) {
                     if (mEditText.length() > 0) {
-                        sendRemoteInput();
+                        sendRemoteInput(prepareRemoteInputFromText());
                     }
                     // Consume action to prevent IME from closing.
                     return true;
@@ -141,7 +155,7 @@
         mEditText.mRemoteInputView = this;
     }
 
-    private void sendRemoteInput() {
+    protected Intent prepareRemoteInputFromText() {
         Bundle results = new Bundle();
         results.putString(mRemoteInput.getResultKey(), mEditText.getText().toString());
         Intent fillInIntent = new Intent().addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
@@ -153,6 +167,25 @@
             RemoteInput.setResultsSource(fillInIntent, RemoteInput.SOURCE_CHOICE);
         }
 
+        return fillInIntent;
+    }
+
+    protected Intent prepareRemoteInputFromData(String contentType, Uri data) {
+        HashMap<String, Uri> results = new HashMap<>();
+        results.put(contentType, data);
+        try {
+            mStatusBarManagerService.grantInlineReplyUriPermission(
+                    mEntry.notification.getKey(), data);
+        } catch (Exception e) {
+            Log.e(TAG, "Failed to grant URI permissions:" + e.getMessage(), e);
+        }
+        Intent fillInIntent = new Intent().addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+        RemoteInput.addDataResultToIntent(mRemoteInput, fillInIntent, results);
+
+        return fillInIntent;
+    }
+
+    private void sendRemoteInput(Intent intent) {
         mEditText.setEnabled(false);
         mSendButton.setVisibility(INVISIBLE);
         mProgressBar.setVisibility(VISIBLE);
@@ -176,7 +209,7 @@
         MetricsLogger.action(mContext, MetricsProto.MetricsEvent.ACTION_REMOTE_INPUT_SEND,
                 mEntry.notification.getPackageName());
         try {
-            mPendingIntent.send(mContext, 0, fillInIntent);
+            mPendingIntent.send(mContext, 0, intent);
         } catch (PendingIntent.CanceledException e) {
             Log.i(TAG, "Unable to send remote input result", e);
             MetricsLogger.action(mContext, MetricsProto.MetricsEvent.ACTION_REMOTE_INPUT_FAIL,
@@ -195,7 +228,9 @@
                 LayoutInflater.from(context).inflate(R.layout.remote_input, root, false);
         v.mController = controller;
         v.mEntry = entry;
-        v.mEditText.setTextOperationUser(computeTextOperationUser(entry.notification.getUser()));
+        UserHandle user = computeTextOperationUser(entry.notification.getUser());
+        v.mEditText.mUser = user;
+        v.mEditText.setTextOperationUser(user);
         v.setTag(VIEW_TAG);
 
         return v;
@@ -204,7 +239,7 @@
     @Override
     public void onClick(View v) {
         if (v == mSendButton) {
-            sendRemoteInput();
+            sendRemoteInput(prepareRemoteInputFromText());
         }
     }
 
@@ -518,6 +553,7 @@
         private RemoteInputView mRemoteInputView;
         boolean mShowImeOnInputConnection;
         private LightBarController mLightBarController;
+        UserHandle mUser;
 
         public RemoteEditText(Context context, AttributeSet attrs) {
             super(context, attrs);
@@ -617,11 +653,47 @@
 
         @Override
         public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
+            String[] allowedDataTypes = mRemoteInputView.mRemoteInput.getAllowedDataTypes()
+                    .toArray(new String[0]);
+            EditorInfoCompat.setContentMimeTypes(outAttrs, allowedDataTypes);
             final InputConnection inputConnection = super.onCreateInputConnection(outAttrs);
 
-            if (mShowImeOnInputConnection && inputConnection != null) {
+            final InputConnectionCompat.OnCommitContentListener callback =
+                    new InputConnectionCompat.OnCommitContentListener() {
+                        @Override
+                        public boolean onCommitContent(
+                                InputContentInfoCompat inputContentInfoCompat, int i,
+                                Bundle bundle) {
+                            Uri contentUri = inputContentInfoCompat.getContentUri();
+                            ClipDescription description = inputContentInfoCompat.getDescription();
+                            String mimeType = null;
+                            if (description != null && description.getMimeTypeCount() > 0) {
+                                mimeType = description.getMimeType(0);
+                            }
+                            if (mimeType != null) {
+                                Intent dataIntent = mRemoteInputView.prepareRemoteInputFromData(
+                                        mimeType, contentUri);
+                                mRemoteInputView.sendRemoteInput(dataIntent);
+                            }
+                            return true;
+                        }
+                    };
+
+            InputConnection ic = InputConnectionCompat.createWrapper(
+                    inputConnection, outAttrs, callback);
+
+            Context userContext = null;
+            try {
+                userContext = mContext.createPackageContextAsUser(
+                        mContext.getPackageName(), 0, mUser);
+            } catch (PackageManager.NameNotFoundException e) {
+                Log.e(TAG, "Unable to create user context:" + e.getMessage(), e);
+            }
+
+            if (mShowImeOnInputConnection && ic != null) {
+                Context targetContext = userContext != null ? userContext : getContext();
                 final InputMethodManager imm =
-                        getContext().getSystemService(InputMethodManager.class);
+                        targetContext.getSystemService(InputMethodManager.class);
                 if (imm != null) {
                     // onCreateInputConnection is called by InputMethodManager in the middle of
                     // setting up the connection to the IME; wait with requesting the IME until that
@@ -636,7 +708,7 @@
                 }
             }
 
-            return inputConnection;
+            return ic;
         }
 
         @Override
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeDockHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeDockHandlerTest.java
index 1ce0172..98ec4594 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeDockHandlerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeDockHandlerTest.java
@@ -20,13 +20,11 @@
 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.never;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
-import android.app.Instrumentation;
 import android.hardware.display.AmbientDisplayConfiguration;
 import android.os.Handler;
 import android.os.Looper;
@@ -34,7 +32,6 @@
 import android.testing.TestableLooper;
 import android.testing.TestableLooper.RunWithLooper;
 
-import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.SysuiTestCase;
@@ -45,23 +42,24 @@
 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)
 @RunWithLooper
 public class DozeDockHandlerTest extends SysuiTestCase {
-    private DozeDockHandler mDockHandler;
+    @Mock
     private DozeMachine mMachine;
-    private DozeHostFake mHost;
+    @Mock
+    private DozeHost mHost;
     private AmbientDisplayConfiguration mConfig;
-    private Instrumentation mInstrumentation;
     private DockManagerFake mDockManagerFake;
+    private DozeDockHandler mDockHandler;
 
     @Before
     public void setUp() throws Exception {
-        mInstrumentation = InstrumentationRegistry.getInstrumentation();
-        mMachine = mock(DozeMachine.class);
-        mHost = spy(new DozeHostFake());
+        MockitoAnnotations.initMocks(this);
         mConfig = DozeConfigurationUtil.createMockConfig();
         doReturn(false).when(mConfig).alwaysOnEnabled(anyInt());
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeHostFake.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeHostFake.java
deleted file mode 100644
index abfa755..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeHostFake.java
+++ /dev/null
@@ -1,120 +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.doze;
-
-import android.annotation.NonNull;
-
-/**
- * A rudimentary fake for DozeHost.
- */
-class DozeHostFake implements DozeHost {
-    Callback callback;
-    boolean pulseExtended;
-    boolean animateWakeup;
-    boolean animateScreenOff;
-    boolean dozing;
-    float doubleTapX;
-    float doubleTapY;
-    float aodDimmingScrimOpacity;
-
-    @Override
-    public void addCallback(@NonNull Callback callback) {
-        this.callback = callback;
-    }
-
-    @Override
-    public void removeCallback(@NonNull Callback callback) {
-        this.callback = null;
-    }
-
-    @Override
-    public void startDozing() {
-        dozing = true;
-    }
-
-    @Override
-    public void pulseWhileDozing(@NonNull PulseCallback callback, int reason) {
-        throw new RuntimeException("not implemented");
-    }
-
-    @Override
-    public void stopDozing() {
-        dozing = false;
-    }
-
-    @Override
-    public void dozeTimeTick() {
-        // Nothing to do in here. Real host would just update the UI.
-    }
-
-    @Override
-    public boolean isPowerSaveActive() {
-        return false;
-    }
-
-    @Override
-    public boolean isPulsingBlocked() {
-        return false;
-    }
-
-    @Override
-    public boolean isProvisioned() {
-        return false;
-    }
-
-    @Override
-    public boolean isBlockingDoze() {
-        return false;
-    }
-
-    @Override
-    public void onIgnoreTouchWhilePulsing(boolean ignore) {
-    }
-
-    @Override
-    public void extendPulse(int reason) {
-        pulseExtended = true;
-    }
-
-    @Override
-    public void stopPulsing() {}
-
-    @Override
-    public void setAnimateWakeup(boolean animateWakeup) {
-        this.animateWakeup = animateWakeup;
-    }
-
-    @Override
-    public void setAnimateScreenOff(boolean animateScreenOff) {
-        this.animateScreenOff = animateScreenOff;
-    }
-
-    @Override
-    public void onSlpiTap(float x, float y) {
-        doubleTapX = y;
-        doubleTapY = y;
-    }
-
-    @Override
-    public void setDozeScreenBrightness(int value) {
-    }
-
-    @Override
-    public void setAodDimmingScrim(float scrimOpacity) {
-        aodDimmingScrimOpacity = scrimOpacity;
-    }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java
index aa62e9a..316b891 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java
@@ -30,6 +30,11 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotEquals;
 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.reset;
+import static org.mockito.Mockito.verify;
 
 import android.content.Intent;
 import android.os.PowerManager;
@@ -45,6 +50,8 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
 
 @RunWith(AndroidJUnit4.class)
 @SmallTest
@@ -55,22 +62,27 @@
     static final int[] SENSOR_TO_OPACITY = new int[]{-1, 10, 0, 0, 0};
 
     DozeServiceFake mServiceFake;
-    DozeScreenBrightness mScreen;
     FakeSensorManager.FakeGenericSensor mSensor;
     FakeSensorManager mSensorManager;
-    DozeHostFake mHostFake;
+    @Mock
+    DozeHost mDozeHost;
+    DozeScreenBrightness mScreen;
 
     @Before
     public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
         Settings.System.putIntForUser(mContext.getContentResolver(),
                 Settings.System.SCREEN_BRIGHTNESS, DEFAULT_BRIGHTNESS,
                 UserHandle.USER_CURRENT);
+        doAnswer(invocation -> {
+            ((Runnable) invocation.getArgument(0)).run();
+            return null;
+        }).when(mDozeHost).prepareForGentleSleep(any());
         mServiceFake = new DozeServiceFake();
-        mHostFake = new DozeHostFake();
         mSensorManager = new FakeSensorManager(mContext);
         mSensor = mSensorManager.getFakeLightSensor();
         mScreen = new DozeScreenBrightness(mContext, mServiceFake, mSensorManager,
-                mSensor.getSensor(), mHostFake, null /* handler */,
+                mSensor.getSensor(), mDozeHost, null /* handler */,
                 DEFAULT_BRIGHTNESS, SENSOR_TO_BRIGHTNESS, SENSOR_TO_OPACITY,
                 true /* debuggable */);
     }
@@ -173,7 +185,7 @@
     @Test
     public void testNullSensor() throws Exception {
         mScreen = new DozeScreenBrightness(mContext, mServiceFake, mSensorManager,
-                null /* sensor */, mHostFake, null /* handler */,
+                null /* sensor */, mDozeHost, null /* handler */,
                 DEFAULT_BRIGHTNESS, SENSOR_TO_BRIGHTNESS, SENSOR_TO_OPACITY,
                 true /* debuggable */);
 
@@ -203,26 +215,7 @@
         mSensor.sendSensorEvent(0);
 
         assertEquals(1, mServiceFake.screenBrightness);
-        assertEquals(10/255f, mHostFake.aodDimmingScrimOpacity, 0.001f /* delta */);
-    }
-
-    @Test
-    public void pausingAod_softBlanks() throws Exception {
-        mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
-        mScreen.transitionTo(INITIALIZED, DOZE_AOD);
-
-        mSensor.sendSensorEvent(2);
-
-        mScreen.transitionTo(DOZE_AOD, DOZE_AOD_PAUSING);
-        mScreen.transitionTo(DOZE_AOD_PAUSING, DOZE_AOD_PAUSED);
-
-        assertEquals(1f, mHostFake.aodDimmingScrimOpacity, 0.001f /* delta */);
-
-        mSensor.sendSensorEvent(0);
-        assertEquals(1f, mHostFake.aodDimmingScrimOpacity, 0.001f /* delta */);
-
-        mScreen.transitionTo(DOZE_AOD_PAUSED, DOZE_AOD);
-        assertEquals(1f, mHostFake.aodDimmingScrimOpacity, 0.001f /* delta */);
+        verify(mDozeHost).setAodDimmingScrim(eq(10f / 255f));
     }
 
     @Test
@@ -232,8 +225,9 @@
         mScreen.transitionTo(DOZE_AOD, DOZE_AOD_PAUSING);
         mScreen.transitionTo(DOZE_AOD_PAUSING, DOZE_AOD_PAUSED);
 
+        reset(mDozeHost);
         mSensor.sendSensorEvent(1);
-        assertEquals(1f, mHostFake.aodDimmingScrimOpacity, 0.001f /* delta */);
+        verify(mDozeHost).setAodDimmingScrim(eq(1f));
     }
 
     @Test
@@ -241,11 +235,12 @@
         mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
         mScreen.transitionTo(INITIALIZED, DOZE_AOD);
         mScreen.transitionTo(DOZE_AOD, DOZE);
-        assertEquals(1f, mHostFake.aodDimmingScrimOpacity, 0.001f /* delta */);
+        verify(mDozeHost).setAodDimmingScrim(eq(1f));
 
+        reset(mDozeHost);
         mScreen.transitionTo(DOZE, DOZE_AOD);
         mSensor.sendSensorEvent(2);
-        assertEquals(0f, mHostFake.aodDimmingScrimOpacity, 0.001f /* delta */);
+        verify(mDozeHost).setAodDimmingScrim(eq(0f));
     }
 
     @Test
@@ -260,11 +255,10 @@
 
         mSensor.sendSensorEvent(0);
 
+        reset(mDozeHost);
         mScreen.transitionTo(DOZE_AOD_PAUSED, DOZE_AOD);
-
         mSensor.sendSensorEvent(2);
-
-        assertEquals(0f, mHostFake.aodDimmingScrimOpacity, 0.001f /* delta */);
+        verify(mDozeHost).setAodDimmingScrim(eq(0f));
     }
 
     @Test
@@ -273,11 +267,11 @@
         mScreen.transitionTo(INITIALIZED, DOZE_AOD);
 
         mSensor.sendSensorEvent(2);
-
         mScreen.transitionTo(DOZE_AOD, DOZE_AOD_PAUSING);
         mScreen.transitionTo(DOZE_AOD_PAUSING, DOZE_AOD_PAUSED);
-        mScreen.transitionTo(DOZE_AOD_PAUSED, DOZE_AOD);
 
-        assertEquals(0f, mHostFake.aodDimmingScrimOpacity, 0.001f /* delta */);
+        reset(mDozeHost);
+        mScreen.transitionTo(DOZE_AOD_PAUSED, DOZE_AOD);
+        verify(mDozeHost).setAodDimmingScrim(eq(0f));
     }
 }
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenStateTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenStateTest.java
index bfd448a..b92f173 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenStateTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenStateTest.java
@@ -18,6 +18,8 @@
 
 import static com.android.systemui.doze.DozeMachine.State.DOZE;
 import static com.android.systemui.doze.DozeMachine.State.DOZE_AOD;
+import static com.android.systemui.doze.DozeMachine.State.DOZE_AOD_PAUSED;
+import static com.android.systemui.doze.DozeMachine.State.DOZE_AOD_PAUSING;
 import static com.android.systemui.doze.DozeMachine.State.DOZE_PULSING;
 import static com.android.systemui.doze.DozeMachine.State.DOZE_REQUEST_PULSE;
 import static com.android.systemui.doze.DozeMachine.State.FINISH;
@@ -30,6 +32,9 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertThat;
 import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import android.os.Looper;
@@ -46,6 +51,7 @@
 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;
 
@@ -53,12 +59,14 @@
 @SmallTest
 public class DozeScreenStateTest extends SysuiTestCase {
 
-    DozeServiceFake mServiceFake;
-    DozeScreenState mScreen;
-    FakeHandler mHandlerFake;
+    private DozeServiceFake mServiceFake;
+    private FakeHandler mHandlerFake;
     @Mock
-    DozeParameters mDozeParameters;
-    WakeLockFake mWakeLock;
+    private DozeHost mDozeHost;
+    @Mock
+    private DozeParameters mDozeParameters;
+    private WakeLockFake mWakeLock;
+    private DozeScreenState mScreen;
 
     @Before
     public void setUp() throws Exception {
@@ -68,7 +76,8 @@
         mServiceFake = new DozeServiceFake();
         mHandlerFake = new FakeHandler(Looper.getMainLooper());
         mWakeLock = new WakeLockFake();
-        mScreen = new DozeScreenState(mServiceFake, mHandlerFake, mDozeParameters, mWakeLock);
+        mScreen = new DozeScreenState(mServiceFake, mHandlerFake, mDozeHost, mDozeParameters,
+                mWakeLock);
     }
 
     @Test
@@ -183,4 +192,20 @@
         assertThat(mWakeLock.isHeld(), is(false));
     }
 
+    @Test
+    public void test_animatesPausing() {
+        ArgumentCaptor<Runnable> captor = ArgumentCaptor.forClass(Runnable.class);
+        doAnswer(invocation -> null).when(mDozeHost).prepareForGentleSleep(captor.capture());
+        mHandlerFake.setMode(QUEUEING);
+
+        mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
+        mScreen.transitionTo(INITIALIZED, DOZE_AOD_PAUSING);
+        mScreen.transitionTo(DOZE_AOD_PAUSING, DOZE_AOD_PAUSED);
+
+        mHandlerFake.dispatchQueuedMessages();
+        verify(mDozeHost).prepareForGentleSleep(eq(captor.getValue()));
+        captor.getValue().run();
+        assertEquals(Display.STATE_OFF, mServiceFake.screenState);
+    }
+
 }
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
index e5ae6d5..777fec7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
@@ -20,6 +20,7 @@
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.spy;
@@ -50,14 +51,22 @@
 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;
 
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
 @RunWithLooper(setAsMainLooper = true)
 public class DozeTriggersTest extends SysuiTestCase {
-    private DozeTriggers mTriggers;
+
+    @Mock
     private DozeMachine mMachine;
-    private DozeHostFake mHost;
+    @Mock
+    private DozeHost mHost;
+    @Mock
+    private AlarmManager mAlarmManager;
+    private DozeTriggers mTriggers;
     private FakeSensorManager mSensors;
     private Sensor mTapSensor;
     private DockManager mDockManagerFake;
@@ -65,9 +74,7 @@
 
     @Before
     public void setUp() throws Exception {
-        mMachine = mock(DozeMachine.class);
-        AlarmManager alarmManager = mock(AlarmManager.class);
-        mHost = spy(new DozeHostFake());
+        MockitoAnnotations.initMocks(this);
         AmbientDisplayConfiguration config = DozeConfigurationUtil.createMockConfig();
         DozeParameters parameters = DozeConfigurationUtil.createMockParameters();
         mSensors = spy(new FakeSensorManager(mContext));
@@ -78,7 +85,7 @@
                 new AsyncSensorManager(mSensors, null, new Handler());
         mProximitySensor = new FakeProximitySensor(getContext(), asyncSensorManager);
 
-        mTriggers = new DozeTriggers(mContext, mMachine, mHost, alarmManager, config, parameters,
+        mTriggers = new DozeTriggers(mContext, mMachine, mHost, mAlarmManager, config, parameters,
                 asyncSensorManager, Handler.createAsync(Looper.myLooper()), wakeLock, true,
                 mDockManagerFake, mProximitySensor, mock(DozeLog.class));
         waitForSensorManager();
@@ -87,19 +94,21 @@
     @Test
     public void testOnNotification_stillWorksAfterOneFailedProxCheck() throws Exception {
         when(mMachine.getState()).thenReturn(DozeMachine.State.DOZE);
+        ArgumentCaptor<DozeHost.Callback> captor = ArgumentCaptor.forClass(DozeHost.Callback.class);
+        doAnswer(invocation -> null).when(mHost).addCallback(captor.capture());
 
         mTriggers.transitionTo(DozeMachine.State.UNINITIALIZED, DozeMachine.State.INITIALIZED);
         mTriggers.transitionTo(DozeMachine.State.INITIALIZED, DozeMachine.State.DOZE);
         clearInvocations(mMachine);
 
         mProximitySensor.setLastEvent(new ProximitySensor.ProximityEvent(true, 1));
-        mHost.callback.onNotificationAlerted(null /* pulseSuppressedListener */);
+        captor.getValue().onNotificationAlerted(null /* pulseSuppressedListener */);
         mProximitySensor.alertListeners();
 
         verify(mMachine, never()).requestState(any());
         verify(mMachine, never()).requestPulse(anyInt());
 
-        mHost.callback.onNotificationAlerted(null /* pulseSuppressedListener */);
+        captor.getValue().onNotificationAlerted(null /* pulseSuppressedListener */);
         waitForSensorManager();
         mProximitySensor.setLastEvent(new ProximitySensor.ProximityEvent(false, 2));
         mProximitySensor.alertListeners();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DoubleTapHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DoubleTapHelperTest.java
new file mode 100644
index 0000000..df1233a
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DoubleTapHelperTest.java
@@ -0,0 +1,369 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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 org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyFloat;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.res.Resources;
+import android.os.SystemClock;
+import android.testing.AndroidTestingRunner;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewConfiguration;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.R;
+import com.android.systemui.SysuiTestCase;
+
+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;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class DoubleTapHelperTest extends SysuiTestCase {
+
+    private DoubleTapHelper mDoubleTapHelper;
+    private int mTouchSlop;
+    private int mDoubleTouchSlop;
+    @Mock private View mView;
+    @Mock private DoubleTapHelper.ActivationListener mActivationListener;
+    @Mock private DoubleTapHelper.DoubleTapListener mDoubleTapListener;
+    @Mock private DoubleTapHelper.SlideBackListener mSlideBackListener;
+    @Mock private DoubleTapHelper.DoubleTapLogListener mDoubleTapLogListener;
+    @Mock private Resources mResources;
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+        mTouchSlop = ViewConfiguration.get(mContext).getScaledTouchSlop();
+        // The double tap slop has to be less than the regular slop, otherwise it has no effect.
+        mDoubleTouchSlop = mTouchSlop - 1;
+        when(mView.getContext()).thenReturn(mContext);
+        when(mView.getResources()).thenReturn(mResources);
+        when(mResources.getDimension(R.dimen.double_tap_slop))
+                .thenReturn((float) mDoubleTouchSlop);
+
+        mDoubleTapHelper = new DoubleTapHelper(mView,
+                                               mActivationListener,
+                                               mDoubleTapListener,
+                                               mSlideBackListener, mDoubleTapLogListener);
+    }
+
+    @Test
+    public void testDoubleTap_success() {
+        long downtimeA = SystemClock.uptimeMillis();
+        long downtimeB = downtimeA + 100;
+
+        MotionEvent evDownA = MotionEvent.obtain(downtimeA,
+                                                 downtimeA,
+                                                 MotionEvent.ACTION_DOWN,
+                                                 1,
+                                                 1,
+                                                 0);
+        MotionEvent evUpA = MotionEvent.obtain(downtimeA,
+                                               downtimeA + 1,
+                                               MotionEvent.ACTION_UP,
+                                               1,
+                                               1,
+                                               0);
+        MotionEvent evDownB = MotionEvent.obtain(downtimeB,
+                                                 downtimeB,
+                                                 MotionEvent.ACTION_DOWN,
+                                                 1,
+                                                 1,
+                                                 0);
+        MotionEvent evUpB = MotionEvent.obtain(downtimeB,
+                                               downtimeB + 1,
+                                               MotionEvent.ACTION_UP,
+                                               1,
+                                               1,
+                                               0);
+
+        mDoubleTapHelper.onTouchEvent(evDownA);
+        mDoubleTapHelper.onTouchEvent(evUpA);
+        verify(mActivationListener).onActiveChanged(true);
+        verify(mView).postDelayed(any(Runnable.class), anyLong());
+        verify(mDoubleTapLogListener, never()).onDoubleTapLog(anyBoolean(), anyFloat(), anyFloat());
+        verify(mDoubleTapListener, never()).onDoubleTap();
+
+        mDoubleTapHelper.onTouchEvent(evDownB);
+        mDoubleTapHelper.onTouchEvent(evUpB);
+        verify(mDoubleTapLogListener).onDoubleTapLog(true, 0, 0);
+        verify(mDoubleTapListener).onDoubleTap();
+
+        evDownA.recycle();
+        evUpA.recycle();
+        evDownB.recycle();
+        evUpB.recycle();
+    }
+
+    @Test
+    public void testSingleTap_timeout() {
+        long downtimeA = SystemClock.uptimeMillis();
+
+        MotionEvent evDownA = MotionEvent.obtain(downtimeA,
+                                                 downtimeA,
+                                                 MotionEvent.ACTION_DOWN,
+                                                 1,
+                                                 1,
+                                                 0);
+        MotionEvent evUpA = MotionEvent.obtain(downtimeA,
+                                               downtimeA + 1,
+                                               MotionEvent.ACTION_UP,
+                                               1,
+                                               1,
+                                               0);
+
+        ArgumentCaptor<Runnable> runnableCaptor = ArgumentCaptor.forClass(Runnable.class);
+        mDoubleTapHelper.onTouchEvent(evDownA);
+        mDoubleTapHelper.onTouchEvent(evUpA);
+        verify(mActivationListener).onActiveChanged(true);
+        verify(mView).postDelayed(runnableCaptor.capture(), anyLong());
+        runnableCaptor.getValue().run();
+        verify(mActivationListener).onActiveChanged(true);
+
+        evDownA.recycle();
+        evUpA.recycle();
+    }
+
+    @Test
+    public void testSingleTap_slop() {
+        long downtimeA = SystemClock.uptimeMillis();
+
+        MotionEvent evDownA = MotionEvent.obtain(downtimeA,
+                                                 downtimeA,
+                                                 MotionEvent.ACTION_DOWN,
+                                                 1,
+                                                 1,
+                                                 0);
+        MotionEvent evUpA = MotionEvent.obtain(downtimeA,
+                                               downtimeA + 1,
+                                               MotionEvent.ACTION_UP,
+                                               1 + mTouchSlop,
+                                               1,
+                                               0);
+
+        ArgumentCaptor<Runnable> runnableCaptor = ArgumentCaptor.forClass(Runnable.class);
+        mDoubleTapHelper.onTouchEvent(evDownA);
+        mDoubleTapHelper.onTouchEvent(evUpA);
+        verify(mActivationListener, never()).onActiveChanged(true);
+        verify(mDoubleTapListener, never()).onDoubleTap();
+
+        evDownA.recycle();
+        evUpA.recycle();
+    }
+
+    @Test
+    public void testDoubleTap_slop() {
+        long downtimeA = SystemClock.uptimeMillis();
+        long downtimeB = downtimeA + 100;
+
+        MotionEvent evDownA = MotionEvent.obtain(downtimeA,
+                                                 downtimeA,
+                                                 MotionEvent.ACTION_DOWN,
+                                                 1,
+                                                 1,
+                                                 0);
+        MotionEvent evUpA = MotionEvent.obtain(downtimeA,
+                                               downtimeA + 1,
+                                               MotionEvent.ACTION_UP,
+                                               1,
+                                               1,
+                                               0);
+        MotionEvent evDownB = MotionEvent.obtain(downtimeB,
+                                                 downtimeB,
+                                                 MotionEvent.ACTION_DOWN,
+                                                 1,
+                                                 1,
+                                                 0);
+        MotionEvent evUpB = MotionEvent.obtain(downtimeB,
+                                               downtimeB + 1,
+                                               MotionEvent.ACTION_UP,
+                                               1,
+                                               1 + mDoubleTouchSlop,
+                                               0);
+
+        mDoubleTapHelper.onTouchEvent(evDownA);
+        mDoubleTapHelper.onTouchEvent(evUpA);
+        verify(mActivationListener).onActiveChanged(true);
+        verify(mView).postDelayed(any(Runnable.class), anyLong());
+
+        mDoubleTapHelper.onTouchEvent(evDownB);
+        mDoubleTapHelper.onTouchEvent(evUpB);
+        verify(mDoubleTapLogListener).onDoubleTapLog(false, 0, mDoubleTouchSlop);
+        verify(mActivationListener).onActiveChanged(false);
+        verify(mDoubleTapListener, never()).onDoubleTap();
+
+        evDownA.recycle();
+        evUpA.recycle();
+        evDownB.recycle();
+        evUpB.recycle();
+    }
+
+    @Test
+    public void testSlideBack() {
+        long downtimeA = SystemClock.uptimeMillis();
+        long downtimeB = downtimeA + 100;
+
+        MotionEvent evDownA = MotionEvent.obtain(downtimeA,
+                                                 downtimeA,
+                                                 MotionEvent.ACTION_DOWN,
+                                                 1,
+                                                 1,
+                                                 0);
+        MotionEvent evUpA = MotionEvent.obtain(downtimeA,
+                                               downtimeA + 1,
+                                               MotionEvent.ACTION_UP,
+                                               1,
+                                               1,
+                                               0);
+        MotionEvent evDownB = MotionEvent.obtain(downtimeB,
+                                                 downtimeB,
+                                                 MotionEvent.ACTION_DOWN,
+                                                 1,
+                                                 1,
+                                                 0);
+        MotionEvent evUpB = MotionEvent.obtain(downtimeB,
+                                               downtimeB + 1,
+                                               MotionEvent.ACTION_UP,
+                                               1,
+                                               1,
+                                               0);
+
+        when(mSlideBackListener.onSlideBack()).thenReturn(true);
+
+        mDoubleTapHelper.onTouchEvent(evDownA);
+        mDoubleTapHelper.onTouchEvent(evUpA);
+        verify(mActivationListener, never()).onActiveChanged(true);
+        verify(mActivationListener, never()).onActiveChanged(false);
+        verify(mDoubleTapListener, never()).onDoubleTap();
+        mDoubleTapHelper.onTouchEvent(evDownB);
+        mDoubleTapHelper.onTouchEvent(evUpB);
+        verify(mActivationListener, never()).onActiveChanged(true);
+        verify(mActivationListener, never()).onActiveChanged(false);
+        verify(mDoubleTapListener, never()).onDoubleTap();
+
+        evDownA.recycle();
+        evUpA.recycle();
+        evDownB.recycle();
+        evUpB.recycle();
+    }
+
+
+    @Test
+    public void testMoreThanTwoTaps() {
+        long downtimeA = SystemClock.uptimeMillis();
+        long downtimeB = downtimeA + 100;
+        long downtimeC = downtimeB + 100;
+        long downtimeD = downtimeC + 100;
+
+        MotionEvent evDownA = MotionEvent.obtain(downtimeA,
+                                                 downtimeA,
+                                                 MotionEvent.ACTION_DOWN,
+                                                 1,
+                                                 1,
+                                                 0);
+        MotionEvent evUpA = MotionEvent.obtain(downtimeA,
+                                               downtimeA + 1,
+                                               MotionEvent.ACTION_UP,
+                                               1,
+                                               1,
+                                               0);
+        MotionEvent evDownB = MotionEvent.obtain(downtimeB,
+                                                 downtimeB,
+                                                 MotionEvent.ACTION_DOWN,
+                                                 1,
+                                                 1,
+                                                 0);
+        MotionEvent evUpB = MotionEvent.obtain(downtimeB,
+                                               downtimeB + 1,
+                                               MotionEvent.ACTION_UP,
+                                               1,
+                                               1,
+                                               0);
+        MotionEvent evDownC = MotionEvent.obtain(downtimeC,
+                                                 downtimeC,
+                                                 MotionEvent.ACTION_DOWN,
+                                                 1,
+                                                 1,
+                                                 0);
+        MotionEvent evUpC = MotionEvent.obtain(downtimeC,
+                                               downtimeC + 1,
+                                               MotionEvent.ACTION_UP,
+                                               1,
+                                               1,
+                                               0);
+        MotionEvent evDownD = MotionEvent.obtain(downtimeD,
+                                                 downtimeD,
+                                                 MotionEvent.ACTION_DOWN,
+                                                 1,
+                                                 1,
+                                                 0);
+        MotionEvent evUpD = MotionEvent.obtain(downtimeD,
+                                               downtimeD + 1,
+                                               MotionEvent.ACTION_UP,
+                                               1,
+                                               1,
+                                               0);
+
+        mDoubleTapHelper.onTouchEvent(evDownA);
+        mDoubleTapHelper.onTouchEvent(evUpA);
+        verify(mActivationListener).onActiveChanged(true);
+        verify(mView).postDelayed(any(Runnable.class), anyLong());
+        verify(mDoubleTapLogListener, never()).onDoubleTapLog(anyBoolean(), anyFloat(), anyFloat());
+        verify(mDoubleTapListener, never()).onDoubleTap();
+
+        mDoubleTapHelper.onTouchEvent(evDownB);
+        mDoubleTapHelper.onTouchEvent(evUpB);
+        verify(mDoubleTapLogListener).onDoubleTapLog(true, 0, 0);
+        verify(mDoubleTapListener).onDoubleTap();
+
+        reset(mView);
+        reset(mActivationListener);
+        reset(mDoubleTapLogListener);
+        reset(mDoubleTapListener);
+
+        mDoubleTapHelper.onTouchEvent(evDownC);
+        mDoubleTapHelper.onTouchEvent(evUpC);
+        verify(mActivationListener).onActiveChanged(true);
+        verify(mView).postDelayed(any(Runnable.class), anyLong());
+        verify(mDoubleTapLogListener, never()).onDoubleTapLog(anyBoolean(), anyFloat(), anyFloat());
+        verify(mDoubleTapListener, never()).onDoubleTap();
+
+        mDoubleTapHelper.onTouchEvent(evDownD);
+        mDoubleTapHelper.onTouchEvent(evUpD);
+        verify(mDoubleTapLogListener).onDoubleTapLog(true, 0, 0);
+        verify(mDoubleTapListener).onDoubleTap();
+
+        evDownA.recycle();
+        evUpA.recycle();
+        evDownB.recycle();
+        evUpB.recycle();
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
index 214e26a..f3ff7ec 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
@@ -133,6 +133,20 @@
     }
 
     @Test
+    public void transitionToOff() {
+        mScrimController.transitionTo(ScrimState.OFF);
+        mScrimController.finishAnimationsImmediately();
+
+        assertScrimAlpha(OPAQUE /* front */,
+                SEMI_TRANSPARENT /* back */,
+                TRANSPARENT /* bubble */);
+
+        assertScrimTint(true /* front */,
+                true /* behind */,
+                false /* bubble */);
+    }
+
+    @Test
     public void transitionToAod_withRegularWallpaper() {
         mScrimController.transitionTo(ScrimState.AOD);
         mScrimController.finishAnimationsImmediately();
diff --git a/proto/src/metrics_constants/metrics_constants.proto b/proto/src/metrics_constants/metrics_constants.proto
index 5b826b1..b0e401b 100644
--- a/proto/src/metrics_constants/metrics_constants.proto
+++ b/proto/src/metrics_constants/metrics_constants.proto
@@ -2584,15 +2584,15 @@
 
     // ACTION: Logged when trampoline activity finishes.
     // TIME: Indicates total time taken by trampoline activity to finish in MS.
-    PROVISIONING_TRAMPOLINE_ACTIVITY_TIME_MS = 523;
+    PROVISIONING_TRAMPOLINE_ACTIVITY_TIME_MS = 523 [deprecated=true];
 
     // ACTION: Logged when encryption activity finishes.
     // TIME: Indicates total time taken by post encryption activity to finish in MS.
-    PROVISIONING_POST_ENCRYPTION_ACTIVITY_TIME_MS = 524;
+    PROVISIONING_POST_ENCRYPTION_ACTIVITY_TIME_MS = 524 [deprecated=true];
 
     // ACTION: Logged when finalization activity finishes.
     // TIME: Indicates time taken by finalization activity to finish in MS.
-    PROVISIONING_FINALIZATION_ACTIVITY_TIME_MS = 525;
+    PROVISIONING_FINALIZATION_ACTIVITY_TIME_MS = 525 [deprecated=true];
 
     // OPEN: Settings Support > Phone/Chat -> Disclaimer
     DIALOG_SUPPORT_DISCLAIMER = 526;
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 91269c7..e909e7a 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -16,13 +16,6 @@
 
 package com.android.server.accessibility;
 
-import static android.accessibilityservice.AccessibilityService.SHOW_MODE_AUTO;
-import static android.accessibilityservice.AccessibilityService.SHOW_MODE_HARD_KEYBOARD_ORIGINAL_VALUE;
-import static android.accessibilityservice.AccessibilityService.SHOW_MODE_HARD_KEYBOARD_OVERRIDDEN;
-import static android.accessibilityservice.AccessibilityService.SHOW_MODE_HIDDEN;
-import static android.accessibilityservice.AccessibilityService.SHOW_MODE_IGNORE_HARD_KEYBOARD;
-import static android.accessibilityservice.AccessibilityService.SHOW_MODE_MASK;
-
 import static com.android.internal.util.FunctionalUtils.ignoreRemoteException;
 import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
 
@@ -50,8 +43,6 @@
 import android.content.pm.ResolveInfo;
 import android.content.pm.ServiceInfo;
 import android.database.ContentObserver;
-import android.graphics.Point;
-import android.graphics.Rect;
 import android.graphics.Region;
 import android.hardware.display.DisplayManager;
 import android.hardware.fingerprint.IFingerprintService;
@@ -119,7 +110,6 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
-import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
@@ -136,6 +126,7 @@
  */
 public class AccessibilityManagerService extends IAccessibilityManager.Stub
         implements AbstractAccessibilityServiceConnection.SystemSupport,
+        AccessibilityUserState.ServiceInfoChangeListener,
         AccessibilityWindowManager.AccessibilityEventSender,
         AccessibilitySecurityPolicy.AccessibilityUserManager {
 
@@ -178,12 +169,6 @@
     private final SimpleStringSplitter mStringColonSplitter =
             new SimpleStringSplitter(COMPONENT_NAME_SEPARATOR);
 
-    private final Rect mTempRect = new Rect();
-
-    private final Rect mTempRect1 = new Rect();
-
-    private final Point mTempPoint = new Point();
-
     private final PackageManager mPackageManager;
 
     private final PowerManager mPowerManager;
@@ -226,7 +211,7 @@
     private final RemoteCallbackList<IAccessibilityManagerClient> mGlobalClients =
             new RemoteCallbackList<>();
 
-    private final SparseArray<UserState> mUserStates = new SparseArray<>();
+    private final SparseArray<AccessibilityUserState> mUserStates = new SparseArray<>();
 
     private final UiAutomationManager mUiAutomationManager = new UiAutomationManager(mLock);
 
@@ -237,7 +222,7 @@
 
     private boolean mIsAccessibilityButtonShown;
 
-    private UserState getCurrentUserStateLocked() {
+    private AccessibilityUserState getCurrentUserStateLocked() {
         return getUserStateLocked(mCurrentUserId);
     }
 
@@ -293,6 +278,11 @@
         return mIsAccessibilityButtonShown;
     }
 
+    @Override
+    public void onServiceInfoChangedLocked(AccessibilityUserState userState) {
+        scheduleNotifyClientsOfServicesStateChangeLocked(userState);
+    }
+
     @Nullable
     public FingerprintGestureDispatcher getFingerprintGestureDispatcher() {
         return mFingerprintGestureDispatcher;
@@ -307,40 +297,40 @@
         }
     }
 
-    private UserState getUserState(int userId) {
+    private AccessibilityUserState getUserState(int userId) {
         synchronized (mLock) {
             return getUserStateLocked(userId);
         }
     }
 
-    private UserState getUserStateLocked(int userId) {
-        UserState state = mUserStates.get(userId);
+    @NonNull
+    private AccessibilityUserState getUserStateLocked(int userId) {
+        AccessibilityUserState state = mUserStates.get(userId);
         if (state == null) {
-            state = new UserState(userId);
+            state = new AccessibilityUserState(userId, mContext, this);
             mUserStates.put(userId, state);
         }
         return state;
     }
 
     boolean getBindInstantServiceAllowed(int userId) {
-        final UserState userState = getUserState(userId);
-        if (userState == null) return false;
-        return userState.getBindInstantServiceAllowed();
+        synchronized (mLock) {
+            final AccessibilityUserState userState = getUserStateLocked(userId);
+            return userState.getBindInstantServiceAllowedLocked();
+        }
     }
 
     void setBindInstantServiceAllowed(int userId, boolean allowed) {
-        UserState userState;
+        mContext.enforceCallingOrSelfPermission(
+                Manifest.permission.MANAGE_BIND_INSTANT_SERVICE,
+                "setBindInstantServiceAllowed");
         synchronized (mLock) {
-            userState = getUserState(userId);
-            if (userState == null) {
-                if (!allowed) {
-                    return;
-                }
-                userState = new UserState(userId);
-                mUserStates.put(userId, userState);
+            final AccessibilityUserState userState = getUserStateLocked(userId);
+            if (allowed != userState.getBindInstantServiceAllowedLocked()) {
+                userState.setBindInstantServiceAllowedLocked(allowed);
+                onUserStateChangedLocked(userState);
             }
         }
-        userState.setBindInstantServiceAllowed(allowed);
     }
 
     private void registerBroadcastReceivers() {
@@ -354,7 +344,7 @@
                         return;
                     }
                     // We will update when the automation service dies.
-                    UserState userState = getCurrentUserStateLocked();
+                    AccessibilityUserState userState = getCurrentUserStateLocked();
                     // We have to reload the installed services since some services may
                     // have different attributes, resolve info (does not support equals),
                     // etc. Remove them then to force reload.
@@ -376,8 +366,8 @@
                     if (userId != mCurrentUserId) {
                         return;
                     }
-                    UserState userState = getUserStateLocked(userId);
-                    boolean reboundAService = userState.mBindingServices.removeIf(
+                    AccessibilityUserState userState = getUserStateLocked(userId);
+                    boolean reboundAService = userState.getBindingServicesLocked().removeIf(
                             component -> component != null
                                     && component.getPackageName().equals(packageName));
                     if (reboundAService) {
@@ -395,14 +385,14 @@
                     if (userId != mCurrentUserId) {
                         return;
                     }
-                    UserState userState = getUserStateLocked(userId);
+                    AccessibilityUserState userState = getUserStateLocked(userId);
                     Iterator<ComponentName> it = userState.mEnabledServices.iterator();
                     while (it.hasNext()) {
                         ComponentName comp = it.next();
                         String compPkg = comp.getPackageName();
                         if (compPkg.equals(packageName)) {
                             it.remove();
-                            userState.mBindingServices.remove(comp);
+                            userState.getBindingServicesLocked().remove(comp);
                             // Update the enabled services setting.
                             persistComponentNamesToSettingLocked(
                                     Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
@@ -430,7 +420,7 @@
                     if (userId != mCurrentUserId) {
                         return false;
                     }
-                    UserState userState = getUserStateLocked(userId);
+                    AccessibilityUserState userState = getUserStateLocked(userId);
                     Iterator<ComponentName> it = userState.mEnabledServices.iterator();
                     while (it.hasNext()) {
                         ComponentName comp = it.next();
@@ -441,7 +431,7 @@
                                     return true;
                                 }
                                 it.remove();
-                                userState.mBindingServices.remove(comp);
+                                userState.getBindingServicesLocked().remove(comp);
                                 persistComponentNamesToSettingLocked(
                                         Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
                                         userState.mEnabledServices, userId);
@@ -478,7 +468,7 @@
                 } else if (Intent.ACTION_USER_PRESENT.equals(action)) {
                     // We will update when the automation service dies.
                     synchronized (mLock) {
-                        UserState userState = getCurrentUserStateLocked();
+                        AccessibilityUserState userState = getCurrentUserStateLocked();
                         if (readConfigurationForUserStateLocked(userState)) {
                             onUserStateChangedLocked(userState);
                         }
@@ -509,7 +499,7 @@
             // If the client is from a process that runs across users such as
             // the system UI or the system we add it to the global state that
             // is shared across users.
-            UserState userState = getUserStateLocked(resolvedUserId);
+            AccessibilityUserState userState = getUserStateLocked(resolvedUserId);
             Client client = new Client(callback, Binder.getCallingUid(), userState);
             if (mSecurityPolicy.isCallerInteractingAcrossUsers(userId)) {
                 mGlobalClients.register(callback, client);
@@ -517,7 +507,7 @@
                     Slog.i(LOG_TAG, "Added global client for pid:" + Binder.getCallingPid());
                 }
                 return IntPair.of(
-                        userState.getClientState(),
+                        getClientStateLocked(userState),
                         client.mLastSentRelevantEventTypes);
             } else {
                 userState.mUserClients.register(callback, client);
@@ -529,7 +519,7 @@
                             + " and userId:" + mCurrentUserId);
                 }
                 return IntPair.of(
-                        (resolvedUserId == mCurrentUserId) ? userState.getClientState() : 0,
+                        (resolvedUserId == mCurrentUserId) ? getClientStateLocked(userState) : 0,
                         client.mLastSentRelevantEventTypes);
             }
         }
@@ -644,7 +634,7 @@
                     .resolveCallingUserIdEnforcingPermissionsLocked(userId);
 
             // The automation service can suppress other services.
-            final UserState userState = getUserStateLocked(resolvedUserId);
+            final AccessibilityUserState userState = getUserStateLocked(resolvedUserId);
             if (mUiAutomationManager.suppressingAccessibilityServicesLocked()) {
                 return Collections.emptyList();
             }
@@ -754,15 +744,15 @@
         }
         synchronized (mLock) {
             // Set the temporary state.
-            UserState userState = getCurrentUserStateLocked();
+            AccessibilityUserState userState = getCurrentUserStateLocked();
 
-            userState.mIsTouchExplorationEnabled = touchExplorationEnabled;
-            userState.mIsDisplayMagnificationEnabled = false;
-            userState.mIsNavBarMagnificationEnabled = false;
-            userState.mIsAutoclickEnabled = false;
+            userState.setTouchExplorationEnabledLocked(touchExplorationEnabled);
+            userState.setDisplayMagnificationEnabledLocked(false);
+            userState.setNavBarMagnificationEnabledLocked(false);
+            userState.setAutoclickEnabledLocked(false);
             userState.mEnabledServices.clear();
             userState.mEnabledServices.add(service);
-            userState.mBindingServices.clear();
+            userState.getBindingServicesLocked().clear();
             userState.mTouchExplorationGrantedServices.clear();
             userState.mTouchExplorationGrantedServices.add(service);
 
@@ -943,7 +933,7 @@
             }
 
             // Disconnect from services for the old user.
-            UserState oldUserState = getCurrentUserStateLocked();
+            AccessibilityUserState oldUserState = getCurrentUserStateLocked();
             oldUserState.onSwitchToAnotherUserLocked();
 
             // Disable the local managers for the old user.
@@ -960,7 +950,7 @@
             // The user changed.
             mCurrentUserId = userId;
 
-            UserState userState = getCurrentUserStateLocked();
+            AccessibilityUserState userState = getCurrentUserStateLocked();
 
             readConfigurationForUserStateLocked(userState);
             // Even if reading did not yield change, we have to update
@@ -979,8 +969,8 @@
 
     private void announceNewUserIfNeeded() {
         synchronized (mLock) {
-            UserState userState = getCurrentUserStateLocked();
-            if (userState.isHandlingAccessibilityEvents()) {
+            AccessibilityUserState userState = getCurrentUserStateLocked();
+            if (userState.isHandlingAccessibilityEventsLocked()) {
                 UserManager userManager = (UserManager) mContext.getSystemService(
                         Context.USER_SERVICE);
                 String message = mContext.getString(R.string.user_switched,
@@ -997,7 +987,7 @@
         synchronized (mLock) {
             int parentUserId = mSecurityPolicy.resolveProfileParentLocked(userId);
             if (parentUserId == mCurrentUserId) {
-                UserState userState = getUserStateLocked(mCurrentUserId);
+                AccessibilityUserState userState = getUserStateLocked(mCurrentUserId);
                 onUserStateChangedLocked(userState);
             }
         }
@@ -1015,7 +1005,7 @@
         readComponentNamesFromStringLocked(oldSetting, mTempComponentNameSet, false);
         readComponentNamesFromStringLocked(newSetting, mTempComponentNameSet, true);
 
-        UserState userState = getUserStateLocked(UserHandle.USER_SYSTEM);
+        AccessibilityUserState userState = getUserStateLocked(UserHandle.USER_SYSTEM);
         userState.mEnabledServices.clear();
         userState.mEnabledServices.addAll(mTempComponentNameSet);
         persistComponentNamesToSettingLocked(
@@ -1025,6 +1015,10 @@
         onUserStateChangedLocked(userState);
     }
 
+    private int getClientStateLocked(AccessibilityUserState userState) {
+        return userState.getClientStateLocked(mUiAutomationManager.isUiAutomationRunningLocked());
+    }
+
     private InteractionBridge getInteractionBridge() {
         synchronized (mLock) {
             if (mInteractionBridge == null) {
@@ -1044,7 +1038,7 @@
         //       gestures to avoid user frustration when different
         //       behavior is observed from different combinations of
         //       enabled accessibility services.
-        UserState state = getCurrentUserStateLocked();
+        AccessibilityUserState state = getCurrentUserStateLocked();
         for (int i = state.mBoundServices.size() - 1; i >= 0; i--) {
             AccessibilityServiceConnection service = state.mBoundServices.get(i);
             if (service.mRequestTouchExplorationMode && service.mIsDefault == isDefault) {
@@ -1056,7 +1050,7 @@
     }
 
     private void notifyClearAccessibilityCacheLocked() {
-        UserState state = getCurrentUserStateLocked();
+        AccessibilityUserState state = getCurrentUserStateLocked();
         for (int i = state.mBoundServices.size() - 1; i >= 0; i--) {
             AccessibilityServiceConnection service = state.mBoundServices.get(i);
             service.notifyClearAccessibilityNodeInfoCache();
@@ -1065,25 +1059,17 @@
 
     private void notifyMagnificationChangedLocked(int displayId, @NonNull Region region,
             float scale, float centerX, float centerY) {
-        final UserState state = getCurrentUserStateLocked();
+        final AccessibilityUserState state = getCurrentUserStateLocked();
         for (int i = state.mBoundServices.size() - 1; i >= 0; i--) {
             final AccessibilityServiceConnection service = state.mBoundServices.get(i);
             service.notifyMagnificationChangedLocked(displayId, region, scale, centerX, centerY);
         }
     }
 
-    private void notifySoftKeyboardShowModeChangedLocked(int showMode) {
-        final UserState state = getCurrentUserStateLocked();
-        for (int i = state.mBoundServices.size() - 1; i >= 0; i--) {
-            final AccessibilityServiceConnection service = state.mBoundServices.get(i);
-            service.notifySoftKeyboardShowModeChangedLocked(showMode);
-        }
-    }
-
     private void notifyAccessibilityButtonClickedLocked(int displayId) {
-        final UserState state = getCurrentUserStateLocked();
+        final AccessibilityUserState state = getCurrentUserStateLocked();
 
-        int potentialTargets = state.mIsNavBarMagnificationEnabled ? 1 : 0;
+        int potentialTargets = state.isNavBarMagnificationEnabledLocked() ? 1 : 0;
         for (int i = state.mBoundServices.size() - 1; i >= 0; i--) {
             final AccessibilityServiceConnection service = state.mBoundServices.get(i);
             if (service.mRequestAccessibilityButton) {
@@ -1095,7 +1081,7 @@
             return;
         }
         if (potentialTargets == 1) {
-            if (state.mIsNavBarMagnificationEnabled) {
+            if (state.isNavBarMagnificationEnabledLocked()) {
                 mMainHandler.sendMessage(obtainMessage(
                         AccessibilityManagerService::sendAccessibilityButtonToInputFilter, this,
                         displayId));
@@ -1110,13 +1096,13 @@
                 }
             }
         } else {
-            if (state.mServiceAssignedToAccessibilityButton == null
-                    && !state.mIsNavBarMagnificationAssignedToAccessibilityButton) {
+            if (state.getServiceAssignedToAccessibilityButtonLocked() == null
+                    && !state.isNavBarMagnificationAssignedToAccessibilityButtonLocked()) {
                 mMainHandler.sendMessage(obtainMessage(
                         AccessibilityManagerService::showAccessibilityButtonTargetSelection, this,
                         displayId));
-            } else if (state.mIsNavBarMagnificationEnabled
-                    && state.mIsNavBarMagnificationAssignedToAccessibilityButton) {
+            } else if (state.isNavBarMagnificationEnabledLocked()
+                    && state.isNavBarMagnificationAssignedToAccessibilityButtonLocked()) {
                 mMainHandler.sendMessage(obtainMessage(
                         AccessibilityManagerService::sendAccessibilityButtonToInputFilter, this,
                         displayId));
@@ -1125,7 +1111,7 @@
                 for (int i = state.mBoundServices.size() - 1; i >= 0; i--) {
                     final AccessibilityServiceConnection service = state.mBoundServices.get(i);
                     if (service.mRequestAccessibilityButton && (service.mComponentName.equals(
-                            state.mServiceAssignedToAccessibilityButton))) {
+                            state.getServiceAssignedToAccessibilityButtonLocked()))) {
                         service.notifyAccessibilityButtonClickedLocked(displayId);
                         return;
                     }
@@ -1154,7 +1140,7 @@
     }
 
     private void notifyAccessibilityButtonVisibilityChangedLocked(boolean available) {
-        final UserState state = getCurrentUserStateLocked();
+        final AccessibilityUserState state = getCurrentUserStateLocked();
         mIsAccessibilityButtonShown = available;
         for (int i = state.mBoundServices.size() - 1; i >= 0; i--) {
             final AccessibilityServiceConnection clientConnection = state.mBoundServices.get(i);
@@ -1165,7 +1151,7 @@
         }
     }
 
-    private boolean readInstalledAccessibilityServiceLocked(UserState userState) {
+    private boolean readInstalledAccessibilityServiceLocked(AccessibilityUserState userState) {
         mTempAccessibilityServiceInfoList.clear();
 
         int flags = PackageManager.GET_SERVICES
@@ -1174,7 +1160,7 @@
                 | PackageManager.MATCH_DIRECT_BOOT_AWARE
                 | PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
 
-        if (userState.getBindInstantServiceAllowed()) {
+        if (userState.getBindInstantServiceAllowedLocked()) {
             flags |= PackageManager.MATCH_INSTANT;
         }
 
@@ -1209,7 +1195,7 @@
         return false;
     }
 
-    private boolean readInstalledAccessibilityShortcutLocked(UserState userState) {
+    private boolean readInstalledAccessibilityShortcutLocked(AccessibilityUserState userState) {
         final List<AccessibilityShortcutInfo> shortcutInfos = AccessibilityManager
                 .getInstance(mContext).getInstalledAccessibilityShortcutListAsUser(
                         mContext, mCurrentUserId);
@@ -1221,7 +1207,7 @@
         return false;
     }
 
-    private boolean readEnabledAccessibilityServicesLocked(UserState userState) {
+    private boolean readEnabledAccessibilityServicesLocked(AccessibilityUserState userState) {
         mTempComponentNameSet.clear();
         readComponentNamesFromSettingLocked(Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
                 userState.mUserId, mTempComponentNameSet);
@@ -1236,7 +1222,7 @@
     }
 
     private boolean readTouchExplorationGrantedAccessibilityServicesLocked(
-            UserState userState) {
+            AccessibilityUserState userState) {
         mTempComponentNameSet.clear();
         readComponentNamesFromSettingLocked(
                 Settings.Secure.TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES,
@@ -1261,7 +1247,7 @@
     private void notifyAccessibilityServicesDelayedLocked(AccessibilityEvent event,
             boolean isDefault) {
         try {
-            UserState state = getCurrentUserStateLocked();
+            AccessibilityUserState state = getCurrentUserStateLocked();
             for (int i = 0, count = state.mBoundServices.size(); i < count; i++) {
                 AccessibilityServiceConnection service = state.mBoundServices.get(i);
 
@@ -1276,7 +1262,7 @@
         }
     }
 
-    private void updateRelevantEventsLocked(UserState userState) {
+    private void updateRelevantEventsLocked(AccessibilityUserState userState) {
         mMainHandler.post(() -> {
             broadcastToClients(userState, ignoreRemoteException(client -> {
                 int relevantEventTypes;
@@ -1296,7 +1282,7 @@
         });
     }
 
-    private int computeRelevantEventTypesLocked(UserState userState, Client client) {
+    private int computeRelevantEventTypesLocked(AccessibilityUserState userState, Client client) {
         int relevantEventTypes = 0;
 
         int serviceCount = userState.mBoundServices.size();
@@ -1342,20 +1328,11 @@
     }
 
     private void broadcastToClients(
-            UserState userState, Consumer<Client> clientAction) {
+            AccessibilityUserState userState, Consumer<Client> clientAction) {
         mGlobalClients.broadcastForEachCookie(clientAction);
         userState.mUserClients.broadcastForEachCookie(clientAction);
     }
 
-    private void unbindAllServicesLocked(UserState userState) {
-        List<AccessibilityServiceConnection> services = userState.mBoundServices;
-        for (int count = services.size(); count > 0; count--) {
-            // When the service is unbound, it disappears from the list, so there's no need to
-            // keep track of the index
-            services.get(0).unbindLocked();
-        }
-    }
-
     /**
      * Populates a set with the {@link ComponentName}s stored in a colon
      * separated value setting for a given user.
@@ -1422,7 +1399,7 @@
         }
     }
 
-    private void updateServicesLocked(UserState userState) {
+    private void updateServicesLocked(AccessibilityUserState userState) {
         Map<ComponentName, AccessibilityServiceConnection> componentNameToServiceMap =
                 userState.mComponentNameToServiceMap;
         boolean isUnlockingOrUnlocked = LocalServices.getService(UserManagerInternal.class)
@@ -1442,7 +1419,7 @@
             }
 
             // Wait for the binding if it is in process.
-            if (userState.mBindingServices.contains(componentName)) {
+            if (userState.getBindingServicesLocked().contains(componentName)) {
                 continue;
             }
             if (userState.mEnabledServices.contains(componentName)
@@ -1478,15 +1455,15 @@
         if (audioManager != null) {
             audioManager.setAccessibilityServiceUids(mTempIntArray);
         }
-        updateAccessibilityEnabledSetting(userState);
+        updateAccessibilityEnabledSettingLocked(userState);
     }
 
-    private void scheduleUpdateClientsIfNeededLocked(UserState userState) {
-        final int clientState = userState.getClientState();
-        if (userState.mLastSentClientState != clientState
+    private void scheduleUpdateClientsIfNeededLocked(AccessibilityUserState userState) {
+        final int clientState = getClientStateLocked(userState);
+        if (userState.getLastSentClientStateLocked() != clientState
                 && (mGlobalClients.getRegisteredCallbackCount() > 0
                         || userState.mUserClients.getRegisteredCallbackCount() > 0)) {
-            userState.mLastSentClientState = clientState;
+            userState.setLastSentClientStateLocked(clientState);
             mMainHandler.sendMessage(obtainMessage(
                     AccessibilityManagerService::sendStateToAllClients,
                     this, clientState, userState.mUserId));
@@ -1508,7 +1485,8 @@
                 client -> client.setState(clientState)));
     }
 
-    private void scheduleNotifyClientsOfServicesStateChangeLocked(UserState userState) {
+    private void scheduleNotifyClientsOfServicesStateChangeLocked(
+            AccessibilityUserState userState) {
         updateRecommendedUiTimeoutLocked(userState);
         mMainHandler.sendMessage(obtainMessage(
                 AccessibilityManagerService::sendServicesStateChanged,
@@ -1527,44 +1505,45 @@
                 client -> client.notifyServicesStateChanged(uiTimeout)));
     }
 
-    private void scheduleUpdateInputFilter(UserState userState) {
+    private void scheduleUpdateInputFilter(AccessibilityUserState userState) {
         mMainHandler.sendMessage(obtainMessage(
                 AccessibilityManagerService::updateInputFilter, this, userState));
     }
 
-    private void scheduleUpdateFingerprintGestureHandling(UserState userState) {
+    private void scheduleUpdateFingerprintGestureHandling(AccessibilityUserState userState) {
         mMainHandler.sendMessage(obtainMessage(
                 AccessibilityManagerService::updateFingerprintGestureHandling,
                 this, userState));
     }
 
-    private void updateInputFilter(UserState userState) {
+    private void updateInputFilter(AccessibilityUserState userState) {
         if (mUiAutomationManager.suppressingAccessibilityServicesLocked()) return;
 
         boolean setInputFilter = false;
         AccessibilityInputFilter inputFilter = null;
         synchronized (mLock) {
             int flags = 0;
-            if (userState.mIsDisplayMagnificationEnabled) {
+            if (userState.isDisplayMagnificationEnabledLocked()) {
                 flags |= AccessibilityInputFilter.FLAG_FEATURE_SCREEN_MAGNIFIER;
             }
-            if (userState.mIsNavBarMagnificationEnabled) {
+            if (userState.isNavBarMagnificationEnabledLocked()) {
                 flags |= AccessibilityInputFilter.FLAG_FEATURE_TRIGGERED_SCREEN_MAGNIFIER;
             }
             if (userHasMagnificationServicesLocked(userState)) {
                 flags |= AccessibilityInputFilter.FLAG_FEATURE_CONTROL_SCREEN_MAGNIFIER;
             }
             // Touch exploration without accessibility makes no sense.
-            if (userState.isHandlingAccessibilityEvents() && userState.mIsTouchExplorationEnabled) {
+            if (userState.isHandlingAccessibilityEventsLocked()
+                    && userState.isTouchExplorationEnabledLocked()) {
                 flags |= AccessibilityInputFilter.FLAG_FEATURE_TOUCH_EXPLORATION;
             }
-            if (userState.mIsFilterKeyEventsEnabled) {
+            if (userState.isFilterKeyEventsEnabledLocked()) {
                 flags |= AccessibilityInputFilter.FLAG_FEATURE_FILTER_KEY_EVENTS;
             }
-            if (userState.mIsAutoclickEnabled) {
+            if (userState.isAutoclickEnabledLocked()) {
                 flags |= AccessibilityInputFilter.FLAG_FEATURE_AUTOCLICK;
             }
-            if (userState.mIsPerformGesturesEnabled) {
+            if (userState.isPerformGesturesEnabledLocked()) {
                 flags |= AccessibilityInputFilter.FLAG_FEATURE_INJECT_MOTION_EVENTS;
             }
             if (flags != 0) {
@@ -1597,8 +1576,8 @@
             String label = service.getServiceInfo().getResolveInfo()
                     .loadLabel(mContext.getPackageManager()).toString();
 
-            final UserState userState = getCurrentUserStateLocked();
-            if (userState.mIsTouchExplorationEnabled) {
+            final AccessibilityUserState userState = getCurrentUserStateLocked();
+            if (userState.isTouchExplorationEnabledLocked()) {
                 return;
             }
             if (mEnableTouchExplorationDialog != null
@@ -1608,40 +1587,40 @@
             mEnableTouchExplorationDialog = new AlertDialog.Builder(mContext)
                 .setIconAttribute(android.R.attr.alertDialogIcon)
                 .setPositiveButton(android.R.string.ok, new OnClickListener() {
-                     @Override
-                     public void onClick(DialogInterface dialog, int which) {
-                         // The user allowed the service to toggle touch exploration.
-                         userState.mTouchExplorationGrantedServices.add(service.mComponentName);
-                         persistComponentNamesToSettingLocked(
-                                 Settings.Secure.TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES,
-                                 userState.mTouchExplorationGrantedServices, userState.mUserId);
-                         // Enable touch exploration.
-                         userState.mIsTouchExplorationEnabled = true;
-                         final long identity = Binder.clearCallingIdentity();
-                         try {
-                             Settings.Secure.putIntForUser(mContext.getContentResolver(),
-                                     Settings.Secure.TOUCH_EXPLORATION_ENABLED, 1,
-                                     userState.mUserId);
-                         } finally {
-                             Binder.restoreCallingIdentity(identity);
-                         }
-                         onUserStateChangedLocked(userState);
-                     }
-                 })
-                 .setNegativeButton(android.R.string.cancel, new OnClickListener() {
-                     @Override
-                     public void onClick(DialogInterface dialog, int which) {
-                         dialog.dismiss();
-                     }
-                 })
-                 .setTitle(R.string.enable_explore_by_touch_warning_title)
-                 .setMessage(mContext.getString(
-                         R.string.enable_explore_by_touch_warning_message, label))
-                 .create();
+                    @Override
+                    public void onClick(DialogInterface dialog, int which) {
+                        // The user allowed the service to toggle touch exploration.
+                        userState.mTouchExplorationGrantedServices.add(service.mComponentName);
+                        persistComponentNamesToSettingLocked(
+                                Settings.Secure.TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES,
+                                userState.mTouchExplorationGrantedServices, userState.mUserId);
+                        // Enable touch exploration.
+                        userState.setTouchExplorationEnabledLocked(true);
+                        final long identity = Binder.clearCallingIdentity();
+                        try {
+                            Settings.Secure.putIntForUser(mContext.getContentResolver(),
+                                    Settings.Secure.TOUCH_EXPLORATION_ENABLED, 1,
+                                    userState.mUserId);
+                        } finally {
+                            Binder.restoreCallingIdentity(identity);
+                        }
+                        onUserStateChangedLocked(userState);
+                    }
+                })
+                .setNegativeButton(android.R.string.cancel, new OnClickListener() {
+                    @Override
+                    public void onClick(DialogInterface dialog, int which) {
+                        dialog.dismiss();
+                    }
+                })
+                .setTitle(R.string.enable_explore_by_touch_warning_title)
+                .setMessage(mContext.getString(
+                        R.string.enable_explore_by_touch_warning_message, label))
+                .create();
              mEnableTouchExplorationDialog.getWindow().setType(
                      WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
              mEnableTouchExplorationDialog.getWindow().getAttributes().privateFlags
-                     |= WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
+                     |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
              mEnableTouchExplorationDialog.setCanceledOnTouchOutside(true);
              mEnableTouchExplorationDialog.show();
         }
@@ -1652,7 +1631,7 @@
      *
      * @param userState the new user state
      */
-    private void onUserStateChangedLocked(UserState userState) {
+    private void onUserStateChangedLocked(AccessibilityUserState userState) {
         // TODO: Remove this hack
         mInitialized = true;
         updateLegacyCapabilitiesLocked(userState);
@@ -1670,7 +1649,7 @@
         updateAccessibilityButtonTargetsLocked(userState);
     }
 
-    private void updateWindowsForAccessibilityCallbackLocked(UserState userState) {
+    private void updateWindowsForAccessibilityCallbackLocked(AccessibilityUserState userState) {
         // We observe windows for accessibility only if there is at least
         // one bound service that can retrieve window content that specified
         // it is interested in accessing such windows. For services that are
@@ -1702,7 +1681,7 @@
         }
     }
 
-    private void updateLegacyCapabilitiesLocked(UserState userState) {
+    private void updateLegacyCapabilitiesLocked(AccessibilityUserState userState) {
         // Up to JB-MR1 we had a white list with services that can enable touch
         // exploration. When a service is first started we show a dialog to the
         // use to get a permission to white list the service.
@@ -1724,20 +1703,20 @@
         }
     }
 
-    private void updatePerformGesturesLocked(UserState userState) {
+    private void updatePerformGesturesLocked(AccessibilityUserState userState) {
         final int serviceCount = userState.mBoundServices.size();
         for (int i = 0; i < serviceCount; i++) {
             AccessibilityServiceConnection service = userState.mBoundServices.get(i);
             if ((service.getCapabilities()
                     & AccessibilityServiceInfo.CAPABILITY_CAN_PERFORM_GESTURES) != 0) {
-                userState.mIsPerformGesturesEnabled = true;
+                userState.setPerformGesturesEnabledLocked(true);
                 return;
             }
         }
-        userState.mIsPerformGesturesEnabled = false;
+        userState.setPerformGesturesEnabledLocked(false);
     }
 
-    private void updateFilterKeyEventsLocked(UserState userState) {
+    private void updateFilterKeyEventsLocked(AccessibilityUserState userState) {
         final int serviceCount = userState.mBoundServices.size();
         for (int i = 0; i < serviceCount; i++) {
             AccessibilityServiceConnection service = userState.mBoundServices.get(i);
@@ -1745,14 +1724,14 @@
                     && (service.getCapabilities()
                             & AccessibilityServiceInfo
                             .CAPABILITY_CAN_REQUEST_FILTER_KEY_EVENTS) != 0) {
-                userState.mIsFilterKeyEventsEnabled = true;
+                userState.setFilterKeyEventsEnabledLocked(true);
                 return;
             }
         }
-        userState.mIsFilterKeyEventsEnabled = false;
+        userState.setFilterKeyEventsEnabledLocked(false);
     }
 
-    private boolean readConfigurationForUserStateLocked(UserState userState) {
+    private boolean readConfigurationForUserStateLocked(AccessibilityUserState userState) {
         boolean somethingChanged = readInstalledAccessibilityServiceLocked(userState);
         somethingChanged |= readInstalledAccessibilityShortcutLocked(userState);
         somethingChanged |= readEnabledAccessibilityServicesLocked(userState);
@@ -1767,10 +1746,10 @@
         return somethingChanged;
     }
 
-    private void updateAccessibilityEnabledSetting(UserState userState) {
+    private void updateAccessibilityEnabledSettingLocked(AccessibilityUserState userState) {
         final long identity = Binder.clearCallingIdentity();
         final boolean isA11yEnabled = mUiAutomationManager.isUiAutomationRunningLocked()
-                || userState.isHandlingAccessibilityEvents();
+                || userState.isHandlingAccessibilityEventsLocked();
         try {
             Settings.Secure.putIntForUser(mContext.getContentResolver(),
                     Settings.Secure.ACCESSIBILITY_ENABLED,
@@ -1781,18 +1760,18 @@
         }
     }
 
-    private boolean readTouchExplorationEnabledSettingLocked(UserState userState) {
+    private boolean readTouchExplorationEnabledSettingLocked(AccessibilityUserState userState) {
         final boolean touchExplorationEnabled = Settings.Secure.getIntForUser(
                 mContext.getContentResolver(),
                 Settings.Secure.TOUCH_EXPLORATION_ENABLED, 0, userState.mUserId) == 1;
-        if (touchExplorationEnabled != userState.mIsTouchExplorationEnabled) {
-            userState.mIsTouchExplorationEnabled = touchExplorationEnabled;
+        if (touchExplorationEnabled != userState.isTouchExplorationEnabledLocked()) {
+            userState.setTouchExplorationEnabledLocked(touchExplorationEnabled);
             return true;
         }
         return false;
     }
 
-    private boolean readMagnificationEnabledSettingsLocked(UserState userState) {
+    private boolean readMagnificationEnabledSettingsLocked(AccessibilityUserState userState) {
         final boolean displayMagnificationEnabled = Settings.Secure.getIntForUser(
                 mContext.getContentResolver(),
                 Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED,
@@ -1801,40 +1780,40 @@
                 mContext.getContentResolver(),
                 Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED,
                 0, userState.mUserId) == 1;
-        if ((displayMagnificationEnabled != userState.mIsDisplayMagnificationEnabled)
-                || (navBarMagnificationEnabled != userState.mIsNavBarMagnificationEnabled)) {
-            userState.mIsDisplayMagnificationEnabled = displayMagnificationEnabled;
-            userState.mIsNavBarMagnificationEnabled = navBarMagnificationEnabled;
+        if ((displayMagnificationEnabled != userState.isDisplayMagnificationEnabledLocked())
+                || (navBarMagnificationEnabled != userState.isNavBarMagnificationEnabledLocked())) {
+            userState.setDisplayMagnificationEnabledLocked(displayMagnificationEnabled);
+            userState.setNavBarMagnificationEnabledLocked(navBarMagnificationEnabled);
             return true;
         }
         return false;
     }
 
-    private boolean readAutoclickEnabledSettingLocked(UserState userState) {
+    private boolean readAutoclickEnabledSettingLocked(AccessibilityUserState userState) {
         final boolean autoclickEnabled = Settings.Secure.getIntForUser(
                 mContext.getContentResolver(),
                 Settings.Secure.ACCESSIBILITY_AUTOCLICK_ENABLED,
                 0, userState.mUserId) == 1;
-        if (autoclickEnabled != userState.mIsAutoclickEnabled) {
-            userState.mIsAutoclickEnabled = autoclickEnabled;
+        if (autoclickEnabled != userState.isAutoclickEnabledLocked()) {
+            userState.setAutoclickEnabledLocked(autoclickEnabled);
             return true;
         }
         return false;
     }
 
-    private boolean readHighTextContrastEnabledSettingLocked(UserState userState) {
+    private boolean readHighTextContrastEnabledSettingLocked(AccessibilityUserState userState) {
         final boolean highTextContrastEnabled = Settings.Secure.getIntForUser(
                 mContext.getContentResolver(),
                 Settings.Secure.ACCESSIBILITY_HIGH_TEXT_CONTRAST_ENABLED, 0,
                 userState.mUserId) == 1;
-        if (highTextContrastEnabled != userState.mIsTextHighContrastEnabled) {
-            userState.mIsTextHighContrastEnabled = highTextContrastEnabled;
+        if (highTextContrastEnabled != userState.isTextHighContrastEnabledLocked()) {
+            userState.setTextHighContrastEnabledLocked(highTextContrastEnabled);
             return true;
         }
         return false;
     }
 
-    private void updateTouchExplorationLocked(UserState userState) {
+    private void updateTouchExplorationLocked(AccessibilityUserState userState) {
         boolean enabled = mUiAutomationManager.isTouchExplorationEnabledLocked();
         final int serviceCount = userState.mBoundServices.size();
         for (int i = 0; i < serviceCount; i++) {
@@ -1844,8 +1823,8 @@
                 break;
             }
         }
-        if (enabled != userState.mIsTouchExplorationEnabled) {
-            userState.mIsTouchExplorationEnabled = enabled;
+        if (enabled != userState.isTouchExplorationEnabledLocked()) {
+            userState.setTouchExplorationEnabledLocked(enabled);
             final long identity = Binder.clearCallingIdentity();
             try {
                 Settings.Secure.putIntForUser(mContext.getContentResolver(),
@@ -1857,60 +1836,61 @@
         }
     }
 
-    private boolean readAccessibilityShortcutSettingLocked(UserState userState) {
+    private boolean readAccessibilityShortcutSettingLocked(AccessibilityUserState userState) {
         String componentNameToEnableString = AccessibilityShortcutController
                 .getTargetServiceComponentNameString(mContext, userState.mUserId);
         if ((componentNameToEnableString == null) || componentNameToEnableString.isEmpty()) {
-            if (userState.mServiceToEnableWithShortcut == null) {
+            if (userState.getServiceToEnableWithShortcutLocked() == null) {
                 return false;
             }
-            userState.mServiceToEnableWithShortcut = null;
+            userState.setServiceToEnableWithShortcutLocked(null);
             return true;
         }
         ComponentName componentNameToEnable =
             ComponentName.unflattenFromString(componentNameToEnableString);
         if ((componentNameToEnable != null)
-                && componentNameToEnable.equals(userState.mServiceToEnableWithShortcut)) {
+                && componentNameToEnable.equals(userState.getServiceToEnableWithShortcutLocked())) {
             return false;
         }
 
-        userState.mServiceToEnableWithShortcut = componentNameToEnable;
+        userState.setServiceToEnableWithShortcutLocked(componentNameToEnable);
         scheduleNotifyClientsOfServicesStateChangeLocked(userState);
         return true;
     }
 
-    private boolean readAccessibilityButtonSettingsLocked(UserState userState) {
+    private boolean readAccessibilityButtonSettingsLocked(AccessibilityUserState userState) {
         String componentId = Settings.Secure.getStringForUser(mContext.getContentResolver(),
                 Settings.Secure.ACCESSIBILITY_BUTTON_TARGET_COMPONENT, userState.mUserId);
         if (TextUtils.isEmpty(componentId)) {
-            if ((userState.mServiceAssignedToAccessibilityButton == null)
-                    && !userState.mIsNavBarMagnificationAssignedToAccessibilityButton) {
+            if ((userState.getServiceAssignedToAccessibilityButtonLocked() == null)
+                    && !userState.isNavBarMagnificationAssignedToAccessibilityButtonLocked()) {
                 return false;
             }
-            userState.mServiceAssignedToAccessibilityButton = null;
-            userState.mIsNavBarMagnificationAssignedToAccessibilityButton = false;
+            userState.setServiceAssignedToAccessibilityButtonLocked(null);
+            userState.setNavBarMagnificationAssignedToAccessibilityButtonLocked(false);
             return true;
         }
 
         if (componentId.equals(MagnificationController.class.getName())) {
-            if (userState.mIsNavBarMagnificationAssignedToAccessibilityButton) {
+            if (userState.isNavBarMagnificationAssignedToAccessibilityButtonLocked()) {
                 return false;
             }
-            userState.mServiceAssignedToAccessibilityButton = null;
-            userState.mIsNavBarMagnificationAssignedToAccessibilityButton = true;
+            userState.setServiceAssignedToAccessibilityButtonLocked(null);
+            userState.setNavBarMagnificationAssignedToAccessibilityButtonLocked(true);
             return true;
         }
 
         ComponentName componentName = ComponentName.unflattenFromString(componentId);
-        if (Objects.equals(componentName, userState.mServiceAssignedToAccessibilityButton)) {
+        if (Objects.equals(componentName,
+                userState.getServiceAssignedToAccessibilityButtonLocked())) {
             return false;
         }
-        userState.mServiceAssignedToAccessibilityButton = componentName;
-        userState.mIsNavBarMagnificationAssignedToAccessibilityButton = false;
+        userState.setServiceAssignedToAccessibilityButtonLocked(componentName);
+        userState.setNavBarMagnificationAssignedToAccessibilityButtonLocked(false);
         return true;
     }
 
-    private boolean readUserRecommendedUiTimeoutSettingsLocked(UserState userState) {
+    private boolean readUserRecommendedUiTimeoutSettingsLocked(AccessibilityUserState userState) {
         final int nonInteractiveUiTimeout = Settings.Secure.getIntForUser(
                 mContext.getContentResolver(),
                 Settings.Secure.ACCESSIBILITY_NON_INTERACTIVE_UI_TIMEOUT_MS, 0,
@@ -1919,10 +1899,10 @@
                 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;
+        if (nonInteractiveUiTimeout != userState.getUserNonInteractiveUiTimeoutLocked()
+                || interactiveUiTimeout != userState.getUserInteractiveUiTimeoutLocked()) {
+            userState.setUserNonInteractiveUiTimeoutLocked(nonInteractiveUiTimeout);
+            userState.setUserInteractiveUiTimeoutLocked(interactiveUiTimeout);
             scheduleNotifyClientsOfServicesStateChangeLocked(userState);
             return true;
         }
@@ -1936,22 +1916,22 @@
      *
      * @param userState
      */
-    private void updateAccessibilityShortcutLocked(UserState userState) {
-        if (userState.mServiceToEnableWithShortcut == null) {
+    private void updateAccessibilityShortcutLocked(AccessibilityUserState userState) {
+        if (userState.getServiceToEnableWithShortcutLocked() == null) {
             return;
         }
         boolean shortcutServiceIsInstalled =
                 AccessibilityShortcutController.getFrameworkShortcutFeaturesMap()
-                        .containsKey(userState.mServiceToEnableWithShortcut);
+                        .containsKey(userState.getServiceToEnableWithShortcutLocked());
         for (int i = 0; !shortcutServiceIsInstalled && (i < userState.mInstalledServices.size());
                 i++) {
             if (userState.mInstalledServices.get(i).getComponentName()
-                    .equals(userState.mServiceToEnableWithShortcut)) {
+                    .equals(userState.getServiceToEnableWithShortcutLocked())) {
                 shortcutServiceIsInstalled = true;
             }
         }
         if (!shortcutServiceIsInstalled) {
-            userState.mServiceToEnableWithShortcut = null;
+            userState.setServiceToEnableWithShortcutLocked(null);
             final long identity = Binder.clearCallingIdentity();
             try {
                 Settings.Secure.putStringForUser(mContext.getContentResolver(),
@@ -1967,7 +1947,7 @@
     }
 
     private boolean canRequestAndRequestsTouchExplorationLocked(
-            AccessibilityServiceConnection service, UserState userState) {
+            AccessibilityServiceConnection service, AccessibilityUserState userState) {
         // Service not ready or cannot request the feature - well nothing to do.
         if (!service.canReceiveEventsLocked() || !service.mRequestTouchExplorationMode) {
             return false;
@@ -1997,7 +1977,7 @@
         return false;
     }
 
-    private void updateMagnificationLocked(UserState userState) {
+    private void updateMagnificationLocked(AccessibilityUserState userState) {
         if (userState.mUserId != mCurrentUserId) {
             return;
         }
@@ -2012,8 +1992,8 @@
         // We would skip overlay display because it uses overlay window to simulate secondary
         // displays in one display. It's not a real display and there's no input events for it.
         final ArrayList<Display> displays = getValidDisplayList();
-        if (userState.mIsDisplayMagnificationEnabled
-                || userState.mIsNavBarMagnificationEnabled) {
+        if (userState.isDisplayMagnificationEnabledLocked()
+                || userState.isNavBarMagnificationEnabledLocked()) {
             for (int i = 0; i < displays.size(); i++) {
                 final Display display = displays.get(i);
                 getMagnificationController().register(display.getDisplayId());
@@ -2037,7 +2017,7 @@
      * Returns whether the specified user has any services that are capable of
      * controlling magnification.
      */
-    private boolean userHasMagnificationServicesLocked(UserState userState) {
+    private boolean userHasMagnificationServicesLocked(AccessibilityUserState userState) {
         final List<AccessibilityServiceConnection> services = userState.mBoundServices;
         for (int i = 0, count = services.size(); i < count; i++) {
             final AccessibilityServiceConnection service = services.get(i);
@@ -2052,7 +2032,7 @@
      * Returns whether the specified user has any services that are capable of
      * controlling magnification and are actively listening for magnification updates.
      */
-    private boolean userHasListeningMagnificationServicesLocked(UserState userState,
+    private boolean userHasListeningMagnificationServicesLocked(AccessibilityUserState userState,
             int displayId) {
         final List<AccessibilityServiceConnection> services = userState.mBoundServices;
         for (int i = 0, count = services.size(); i < count; i++) {
@@ -2065,7 +2045,7 @@
         return false;
     }
 
-    private void updateFingerprintGestureHandling(UserState userState) {
+    private void updateFingerprintGestureHandling(AccessibilityUserState userState) {
         final List<AccessibilityServiceConnection> services;
         synchronized (mLock) {
             services = userState.mBoundServices;
@@ -2097,7 +2077,7 @@
         }
     }
 
-    private void updateAccessibilityButtonTargetsLocked(UserState userState) {
+    private void updateAccessibilityButtonTargetsLocked(AccessibilityUserState userState) {
         for (int i = userState.mBoundServices.size() - 1; i >= 0; i--) {
             final AccessibilityServiceConnection service = userState.mBoundServices.get(i);
             if (service.mRequestAccessibilityButton) {
@@ -2107,9 +2087,9 @@
         }
     }
 
-    private void updateRecommendedUiTimeoutLocked(UserState userState) {
-        int newNonInteractiveUiTimeout = userState.mUserNonInteractiveUiTimeout;
-        int newInteractiveUiTimeout = userState.mUserInteractiveUiTimeout;
+    private void updateRecommendedUiTimeoutLocked(AccessibilityUserState userState) {
+        int newNonInteractiveUiTimeout = userState.getUserNonInteractiveUiTimeoutLocked();
+        int newInteractiveUiTimeout = userState.getUserInteractiveUiTimeoutLocked();
         // read from a11y services if user does not specify value
         if (newNonInteractiveUiTimeout == 0 || newInteractiveUiTimeout == 0) {
             int serviceNonInteractiveUiTimeout = 0;
@@ -2132,8 +2112,8 @@
                 newInteractiveUiTimeout = serviceInteractiveUiTimeout;
             }
         }
-        userState.mNonInteractiveUiTimeout = newNonInteractiveUiTimeout;
-        userState.mInteractiveUiTimeout = newInteractiveUiTimeout;
+        userState.setNonInteractiveUiTimeoutLocked(newNonInteractiveUiTimeout);
+        userState.setInteractiveUiTimeoutLocked(newInteractiveUiTimeout);
     }
 
     @GuardedBy("mLock")
@@ -2180,8 +2160,8 @@
         final Map<ComponentName, ToggleableFrameworkFeatureInfo> frameworkFeatureMap =
                 AccessibilityShortcutController.getFrameworkShortcutFeaturesMap();
         synchronized(mLock) {
-            final UserState userState = getUserStateLocked(mCurrentUserId);
-            final ComponentName serviceName = userState.mServiceToEnableWithShortcut;
+            final AccessibilityUserState userState = getUserStateLocked(mCurrentUserId);
+            final ComponentName serviceName = userState.getServiceToEnableWithShortcutLocked();
             if (serviceName == null) {
                 return;
             }
@@ -2218,11 +2198,11 @@
                     "getAccessibilityShortcutService requires the MANAGE_ACCESSIBILITY permission");
         }
         synchronized(mLock) {
-            final UserState userState = getUserStateLocked(mCurrentUserId);
-            if (userState.mServiceToEnableWithShortcut == null) {
+            final AccessibilityUserState userState = getUserStateLocked(mCurrentUserId);
+            if (userState.getServiceToEnableWithShortcutLocked() == null) {
                 return null;
             }
-            return userState.mServiceToEnableWithShortcut.flattenToString();
+            return userState.getServiceToEnableWithShortcutLocked().flattenToString();
         }
     }
 
@@ -2237,7 +2217,7 @@
                         userId);
         setting.write(ComponentNameSet.add(setting.read(), componentName));
 
-        UserState userState = getUserStateLocked(userId);
+        AccessibilityUserState userState = getUserStateLocked(userId);
         if (userState.mEnabledServices.add(componentName)) {
             onUserStateChangedLocked(userState);
         }
@@ -2254,7 +2234,7 @@
                         userId);
         setting.write(ComponentNameSet.remove(setting.read(), componentName));
 
-        UserState userState = getUserStateLocked(userId);
+        AccessibilityUserState userState = getUserStateLocked(userId);
         if (userState.mEnabledServices.remove(componentName)) {
             onUserStateChangedLocked(userState);
         }
@@ -2322,14 +2302,14 @@
     @Override
     public long getRecommendedTimeoutMillis() {
         synchronized(mLock) {
-            final UserState userState = getCurrentUserStateLocked();
+            final AccessibilityUserState userState = getCurrentUserStateLocked();
             return getRecommendedTimeoutMillisLocked(userState);
         }
     }
 
-    private long getRecommendedTimeoutMillisLocked(UserState userState) {
-        return IntPair.of(userState.mInteractiveUiTimeout,
-                userState.mNonInteractiveUiTimeout);
+    private long getRecommendedTimeoutMillisLocked(AccessibilityUserState userState) {
+        return IntPair.of(userState.getInteractiveUiTimeoutLocked(),
+                userState.getNonInteractiveUiTimeoutLocked());
     }
 
     @Override
@@ -2338,78 +2318,20 @@
         synchronized (mLock) {
             pw.println("ACCESSIBILITY MANAGER (dumpsys accessibility)");
             pw.println();
+            pw.append("currentUserId=").append(String.valueOf(mCurrentUserId));
+            pw.println();
             final int userCount = mUserStates.size();
             for (int i = 0; i < userCount; i++) {
-                UserState userState = mUserStates.valueAt(i);
-                pw.append("User state[attributes:{id=" + userState.mUserId);
-                pw.append(", currentUser=" + (userState.mUserId == mCurrentUserId));
-                pw.append(", touchExplorationEnabled=" + userState.mIsTouchExplorationEnabled);
-                pw.append(", displayMagnificationEnabled="
-                        + userState.mIsDisplayMagnificationEnabled);
-                pw.append(", navBarMagnificationEnabled="
-                        + userState.mIsNavBarMagnificationEnabled);
-                pw.append(", autoclickEnabled=" + userState.mIsAutoclickEnabled);
-                pw.append(", nonInteractiveUiTimeout=" + userState.mNonInteractiveUiTimeout);
-                pw.append(", interactiveUiTimeout=" + userState.mInteractiveUiTimeout);
-                pw.append(", installedServiceCount=" + userState.mInstalledServices.size());
-                if (mUiAutomationManager.isUiAutomationRunningLocked()) {
-                    pw.append(", ");
-                    mUiAutomationManager.dumpUiAutomationService(fd, pw, args);
-                    pw.println();
-                }
-                pw.append("}");
-                pw.println();
-                pw.append("     Bound services:{");
-                final int serviceCount = userState.mBoundServices.size();
-                for (int j = 0; j < serviceCount; j++) {
-                    if (j > 0) {
-                        pw.append(", ");
-                        pw.println();
-                        pw.append("                     ");
-                    }
-                    AccessibilityServiceConnection service = userState.mBoundServices.get(j);
-                    service.dump(fd, pw, args);
-                }
-                pw.println("}");
-                pw.append("     Enabled services:{");
-                Iterator<ComponentName> it = userState.mEnabledServices.iterator();
-                if (it.hasNext()) {
-                    ComponentName componentName = it.next();
-                    pw.append(componentName.toShortString());
-                    while (it.hasNext()) {
-                        componentName = it.next();
-                        pw.append(", ");
-                        pw.append(componentName.toShortString());
-                    }
-                }
-                pw.println("}");
-                pw.append("     Binding services:{");
-                it = userState.mBindingServices.iterator();
-                if (it.hasNext()) {
-                    ComponentName componentName = it.next();
-                    pw.append(componentName.toShortString());
-                    while (it.hasNext()) {
-                        componentName = it.next();
-                        pw.append(", ");
-                        pw.append(componentName.toShortString());
-                    }
-                }
-                pw.println("}]");
+                mUserStates.valueAt(i).dump(fd, pw, args);
+            }
+            if (mUiAutomationManager.isUiAutomationRunningLocked()) {
+                mUiAutomationManager.dumpUiAutomationService(fd, pw, args);
                 pw.println();
             }
             mA11yWindowManager.dump(fd, pw, args);
         }
     }
 
-    private void putSecureIntForUser(String key, int value, int userid) {
-        final long identity = Binder.clearCallingIdentity();
-        try {
-            Settings.Secure.putIntForUser(mContext.getContentResolver(), key, value, userid);
-        } finally {
-            Binder.restoreCallingIdentity(identity);
-        }
-    }
-
     //TODO remove after refactoring KeyEventDispatcherTest
     final class MainHandler extends Handler {
         public static final int MSG_SEND_KEY_EVENT_TO_INPUT_FILTER = 8;
@@ -2446,7 +2368,7 @@
 
     @Override
     public void onClientChangeLocked(boolean serviceInfoChanged) {
-        AccessibilityManagerService.UserState userState = getUserStateLocked(mCurrentUserId);
+        AccessibilityUserState userState = getUserStateLocked(mCurrentUserId);
         onUserStateChangedLocked(userState);
         if (serviceInfoChanged) {
             scheduleNotifyClientsOfServicesStateChangeLocked(userState);
@@ -2474,7 +2396,7 @@
             info.setCapabilities(AccessibilityServiceInfo.CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT);
             info.flags |= AccessibilityServiceInfo.FLAG_RETRIEVE_INTERACTIVE_WINDOWS;
             info.flags |= AccessibilityServiceInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS;
-            final UserState userState;
+            final AccessibilityUserState userState;
             synchronized (mLock) {
                 userState = getCurrentUserStateLocked();
             }
@@ -2593,7 +2515,7 @@
                 if (mInputFilter != null) {
                     mInputFilter.onDisplayChanged();
                 }
-                UserState userState = getCurrentUserStateLocked();
+                AccessibilityUserState userState = getCurrentUserStateLocked();
                 if (displayId != Display.DEFAULT_DISPLAY) {
                     final List<AccessibilityServiceConnection> services = userState.mBoundServices;
                     for (int i = 0; i < services.size(); i++) {
@@ -2618,7 +2540,7 @@
                 if (mInputFilter != null) {
                     mInputFilter.onDisplayChanged();
                 }
-                UserState userState = getCurrentUserStateLocked();
+                AccessibilityUserState userState = getCurrentUserStateLocked();
                 if (displayId != Display.DEFAULT_DISPLAY) {
                     final List<AccessibilityServiceConnection> services = userState.mBoundServices;
                     for (int i = 0; i < services.size(); i++) {
@@ -2658,7 +2580,8 @@
         final String[] mPackageNames;
         int mLastSentRelevantEventTypes;
 
-        private Client(IAccessibilityManagerClient callback, int clientUid, UserState userState) {
+        private Client(IAccessibilityManagerClient callback, int clientUid,
+                AccessibilityUserState userState) {
             mCallback = callback;
             mPackageNames = mPackageManager.getPackagesForUid(clientUid);
             synchronized (mLock) {
@@ -2667,316 +2590,6 @@
         }
     }
 
-    public class UserState {
-        public final int mUserId;
-
-        // Non-transient state.
-
-        public final RemoteCallbackList<IAccessibilityManagerClient> mUserClients =
-                new RemoteCallbackList<>();
-
-        // Transient state.
-
-        public final ArrayList<AccessibilityServiceConnection> mBoundServices = new ArrayList<>();
-
-        public final Map<ComponentName, AccessibilityServiceConnection> mComponentNameToServiceMap =
-                new HashMap<>();
-
-        public final List<AccessibilityServiceInfo> mInstalledServices =
-                new ArrayList<>();
-
-        public final List<AccessibilityShortcutInfo> mInstalledShortcuts = new ArrayList<>();
-
-        private final Set<ComponentName> mBindingServices = new HashSet<>();
-
-        public final Set<ComponentName> mEnabledServices = new HashSet<>();
-
-        public final Set<ComponentName> mTouchExplorationGrantedServices =
-                new HashSet<>();
-
-        public ComponentName mServiceChangingSoftKeyboardMode;
-
-        public ComponentName mServiceToEnableWithShortcut;
-
-        public int mLastSentClientState = -1;
-        public int mNonInteractiveUiTimeout = 0;
-        public int mInteractiveUiTimeout = 0;
-
-        private int mSoftKeyboardShowMode = 0;
-
-        public boolean mIsNavBarMagnificationAssignedToAccessibilityButton;
-        public ComponentName mServiceAssignedToAccessibilityButton;
-
-        public boolean mIsTouchExplorationEnabled;
-        public boolean mIsTextHighContrastEnabled;
-        public boolean mIsDisplayMagnificationEnabled;
-        public boolean mIsNavBarMagnificationEnabled;
-        public boolean mIsAutoclickEnabled;
-        public boolean mIsPerformGesturesEnabled;
-        public boolean mIsFilterKeyEventsEnabled;
-        public int mUserNonInteractiveUiTimeout;
-        public int mUserInteractiveUiTimeout;
-
-        private boolean mBindInstantServiceAllowed;
-
-        public UserState(int userId) {
-            mUserId = userId;
-        }
-
-        public int getClientState() {
-            int clientState = 0;
-            final boolean a11yEnabled = (mUiAutomationManager.isUiAutomationRunningLocked()
-                    || isHandlingAccessibilityEvents());
-            if (a11yEnabled) {
-                clientState |= AccessibilityManager.STATE_FLAG_ACCESSIBILITY_ENABLED;
-            }
-            // Touch exploration relies on enabled accessibility.
-            if (a11yEnabled && mIsTouchExplorationEnabled) {
-                clientState |= AccessibilityManager.STATE_FLAG_TOUCH_EXPLORATION_ENABLED;
-            }
-            if (mIsTextHighContrastEnabled) {
-                clientState |= AccessibilityManager.STATE_FLAG_HIGH_TEXT_CONTRAST_ENABLED;
-            }
-            return clientState;
-        }
-
-        public boolean isHandlingAccessibilityEvents() {
-            return !mBoundServices.isEmpty() || !mBindingServices.isEmpty();
-        }
-
-        public void onSwitchToAnotherUserLocked() {
-            // Unbind all services.
-            unbindAllServicesLocked(this);
-
-            // Clear service management state.
-            mBoundServices.clear();
-            mBindingServices.clear();
-
-            // Clear event management state.
-            mLastSentClientState = -1;
-
-            // clear UI timeout
-            mNonInteractiveUiTimeout = 0;
-            mInteractiveUiTimeout = 0;
-
-            // Clear state persisted in settings.
-            mEnabledServices.clear();
-            mTouchExplorationGrantedServices.clear();
-            mIsTouchExplorationEnabled = false;
-            mIsDisplayMagnificationEnabled = false;
-            mIsNavBarMagnificationEnabled = false;
-            mServiceAssignedToAccessibilityButton = null;
-            mIsNavBarMagnificationAssignedToAccessibilityButton = false;
-            mIsAutoclickEnabled = false;
-            mUserNonInteractiveUiTimeout = 0;
-            mUserInteractiveUiTimeout = 0;
-        }
-
-        public void addServiceLocked(AccessibilityServiceConnection serviceConnection) {
-            if (!mBoundServices.contains(serviceConnection)) {
-                serviceConnection.onAdded();
-                mBoundServices.add(serviceConnection);
-                mComponentNameToServiceMap.put(serviceConnection.mComponentName, serviceConnection);
-                scheduleNotifyClientsOfServicesStateChangeLocked(this);
-            }
-        }
-
-        /**
-         * Removes a service.
-         * There are three states to a service here: off, bound, and binding.
-         * This stops tracking the service as bound.
-         *
-         * @param serviceConnection The service.
-         */
-        public void removeServiceLocked(AccessibilityServiceConnection serviceConnection) {
-            mBoundServices.remove(serviceConnection);
-            serviceConnection.onRemoved();
-            if ((mServiceChangingSoftKeyboardMode != null)
-                    && (mServiceChangingSoftKeyboardMode.equals(
-                            serviceConnection.getServiceInfo().getComponentName()))) {
-                setSoftKeyboardModeLocked(SHOW_MODE_AUTO, null);
-            }
-            // It may be possible to bind a service twice, which confuses the map. Rebuild the map
-            // to make sure we can still reach a service
-            mComponentNameToServiceMap.clear();
-            for (int i = 0; i < mBoundServices.size(); i++) {
-                AccessibilityServiceConnection boundClient = mBoundServices.get(i);
-                mComponentNameToServiceMap.put(boundClient.mComponentName, boundClient);
-            }
-            scheduleNotifyClientsOfServicesStateChangeLocked(this);
-        }
-
-        /**
-         * Make sure a services disconnected but still 'on' state is reflected in UserState
-         * There are three states to a service here: off, bound, and binding.
-         * This drops a service from a bound state, to the binding state.
-         * The binding state describes the situation where a service is on, but not bound.
-         *
-         * @param serviceConnection The service.
-         */
-        public void serviceDisconnectedLocked(AccessibilityServiceConnection serviceConnection) {
-            removeServiceLocked(serviceConnection);
-            mBindingServices.add(serviceConnection.getComponentName());
-        }
-
-        public Set<ComponentName> getBindingServicesLocked() {
-            return mBindingServices;
-        }
-
-        /**
-         * Returns enabled service list.
-         */
-        public Set<ComponentName> getEnabledServicesLocked() {
-            return mEnabledServices;
-        }
-
-        public int getSoftKeyboardShowMode() {
-            return mSoftKeyboardShowMode;
-        }
-
-        /**
-         * Set the soft keyboard mode. This mode is a bit odd, as it spans multiple settings.
-         * The ACCESSIBILITY_SOFT_KEYBOARD_MODE setting can be checked by the rest of the system
-         * to see if it should suppress showing the IME. The SHOW_IME_WITH_HARD_KEYBOARD setting
-         * setting can be changed by the user, and prevents the system from suppressing the soft
-         * keyboard when the hard keyboard is connected. The hard keyboard setting needs to defer
-         * to the user's preference, if they have supplied one.
-         *
-         * @param newMode The new mode
-         * @param requester The service requesting the change, so we can undo it when the
-         *                  service stops. Set to null if something other than a service is forcing
-         *                  the change.
-         *
-         * @return Whether or not the soft keyboard mode equals the new mode after the call
-         */
-        public boolean setSoftKeyboardModeLocked(int newMode, @Nullable ComponentName requester) {
-            if ((newMode != SHOW_MODE_AUTO) && (newMode != SHOW_MODE_HIDDEN)
-                    && (newMode != SHOW_MODE_IGNORE_HARD_KEYBOARD))
-            {
-                Slog.w(LOG_TAG, "Invalid soft keyboard mode");
-                return false;
-            }
-            if (mSoftKeyboardShowMode == newMode) return true;
-
-            if (newMode == SHOW_MODE_IGNORE_HARD_KEYBOARD) {
-                if (hasUserOverriddenHardKeyboardSettingLocked()) {
-                    // The user has specified a default for this setting
-                    return false;
-                }
-                // Save the original value. But don't do this if the value in settings is already
-                // the new mode. That happens when we start up after a reboot, and we don't want
-                // to overwrite the value we had from when we first started controlling the setting.
-                if (getSoftKeyboardValueFromSettings() != SHOW_MODE_IGNORE_HARD_KEYBOARD) {
-                    setOriginalHardKeyboardValue(
-                            Settings.Secure.getInt(mContext.getContentResolver(),
-                                    Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD, 0) != 0);
-                }
-                putSecureIntForUser(Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD, 1, mUserId);
-            } else if (mSoftKeyboardShowMode == SHOW_MODE_IGNORE_HARD_KEYBOARD) {
-                putSecureIntForUser(Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD,
-                        getOriginalHardKeyboardValue() ? 1 : 0, mUserId);
-            }
-
-            saveSoftKeyboardValueToSettings(newMode);
-            mSoftKeyboardShowMode = newMode;
-            mServiceChangingSoftKeyboardMode = requester;
-            notifySoftKeyboardShowModeChangedLocked(mSoftKeyboardShowMode);
-            return true;
-        }
-
-        /**
-         * If the settings are inconsistent with the internal state, make the internal state
-         * match the settings.
-         */
-        public void reconcileSoftKeyboardModeWithSettingsLocked() {
-            final ContentResolver cr = mContext.getContentResolver();
-            final boolean showWithHardKeyboardSettings =
-                    Settings.Secure.getInt(cr, Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD, 0) != 0;
-            if (mSoftKeyboardShowMode == SHOW_MODE_IGNORE_HARD_KEYBOARD) {
-                if (!showWithHardKeyboardSettings) {
-                    // The user has overridden the setting. Respect that and prevent further changes
-                    // to this behavior.
-                    setSoftKeyboardModeLocked(SHOW_MODE_AUTO, null);
-                    setUserOverridesHardKeyboardSettingLocked();
-                }
-            }
-
-            // If the setting and the internal state are out of sync, set both to default
-            if (getSoftKeyboardValueFromSettings() != mSoftKeyboardShowMode)
-            {
-                Slog.e(LOG_TAG,
-                        "Show IME setting inconsistent with internal state. Overwriting");
-                setSoftKeyboardModeLocked(SHOW_MODE_AUTO, null);
-                putSecureIntForUser(Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE,
-                        SHOW_MODE_AUTO, mUserId);
-            }
-        }
-
-        private void setUserOverridesHardKeyboardSettingLocked() {
-            final int softKeyboardSetting = Settings.Secure.getInt(mContext.getContentResolver(),
-                    Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, 0);
-            putSecureIntForUser(Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE,
-                    softKeyboardSetting | SHOW_MODE_HARD_KEYBOARD_OVERRIDDEN,
-                    mUserId);
-        }
-
-        private boolean hasUserOverriddenHardKeyboardSettingLocked() {
-            final int softKeyboardSetting = Settings.Secure.getInt(mContext.getContentResolver(),
-                    Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, 0);
-            return (softKeyboardSetting & SHOW_MODE_HARD_KEYBOARD_OVERRIDDEN)
-                    != 0;
-        }
-
-        private void setOriginalHardKeyboardValue(boolean originalHardKeyboardValue) {
-            final int oldSoftKeyboardSetting = Settings.Secure.getInt(mContext.getContentResolver(),
-                    Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, 0);
-            final int newSoftKeyboardSetting = oldSoftKeyboardSetting
-                    & (~SHOW_MODE_HARD_KEYBOARD_ORIGINAL_VALUE)
-                    | ((originalHardKeyboardValue) ? SHOW_MODE_HARD_KEYBOARD_ORIGINAL_VALUE : 0);
-            putSecureIntForUser(Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE,
-                    newSoftKeyboardSetting, mUserId);
-        }
-
-        private void saveSoftKeyboardValueToSettings(int softKeyboardShowMode) {
-            final int oldSoftKeyboardSetting = Settings.Secure.getInt(mContext.getContentResolver(),
-                    Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, 0);
-            final int newSoftKeyboardSetting = oldSoftKeyboardSetting & (~SHOW_MODE_MASK)
-                    | softKeyboardShowMode;
-            putSecureIntForUser(Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE,
-                    newSoftKeyboardSetting, mUserId);
-        }
-
-        private int getSoftKeyboardValueFromSettings() {
-            return Settings.Secure.getInt(mContext.getContentResolver(),
-                    Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE,
-                    SHOW_MODE_AUTO) & SHOW_MODE_MASK;
-        }
-
-        private boolean getOriginalHardKeyboardValue() {
-            return (Settings.Secure.getInt(mContext.getContentResolver(),
-                    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 {
 
         private final Uri mTouchExplorationEnabledUri = Settings.Secure.getUriFor(
@@ -3057,7 +2670,7 @@
             synchronized (mLock) {
                 // Profiles share the accessibility state of the parent. Therefore,
                 // we are checking for changes only the parent settings.
-                UserState userState = getCurrentUserStateLocked();
+                AccessibilityUserState userState = getCurrentUserStateLocked();
 
                 if (mTouchExplorationEnabledUri.equals(uri)) {
                     if (readTouchExplorationEnabledSettingLocked(userState)) {
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
index e7f3ccc..d154060 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
@@ -35,7 +35,6 @@
 import android.util.Slog;
 import android.view.Display;
 
-import com.android.server.accessibility.AccessibilityManagerService.UserState;
 import com.android.server.wm.WindowManagerInternal;
 
 import java.lang.ref.WeakReference;
@@ -52,13 +51,13 @@
 class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnection {
     private static final String LOG_TAG = "AccessibilityServiceConnection";
     /*
-     Holding a weak reference so there isn't a loop of references. UserState keeps lists of bound
-     and binding services. These are freed on user changes, but just in case it somehow gets lost
-     the weak reference will let the memory get GCed.
+     Holding a weak reference so there isn't a loop of references. AccessibilityUserState keeps
+     lists of bound and binding services. These are freed on user changes, but just in case it
+     somehow gets lost the weak reference will let the memory get GCed.
 
      Having the reference be null when being called is a very bad sign, but we check the condition.
     */
-    final WeakReference<UserState> mUserStateWeakReference;
+    final WeakReference<AccessibilityUserState> mUserStateWeakReference;
     final Intent mIntent;
 
     private final Handler mMainHandler;
@@ -66,7 +65,7 @@
     private boolean mWasConnectedAndDied;
 
 
-    public AccessibilityServiceConnection(UserState userState, Context context,
+    AccessibilityServiceConnection(AccessibilityUserState userState, Context context,
             ComponentName componentName,
             AccessibilityServiceInfo accessibilityServiceInfo, int id, Handler mainHandler,
             Object lock, AccessibilitySecurityPolicy securityPolicy, SystemSupport systemSupport,
@@ -74,7 +73,7 @@
             SystemActionPerformer systemActionPerfomer, AccessibilityWindowManager awm) {
         super(context, componentName, accessibilityServiceInfo, id, mainHandler, lock,
                 securityPolicy, systemSupport, windowManagerInternal, systemActionPerfomer, awm);
-        mUserStateWeakReference = new WeakReference<UserState>(userState);
+        mUserStateWeakReference = new WeakReference<AccessibilityUserState>(userState);
         mIntent = new Intent().setComponent(mComponentName);
         mMainHandler = mainHandler;
         mIntent.putExtra(Intent.EXTRA_CLIENT_LABEL,
@@ -89,13 +88,13 @@
     }
 
     public void bindLocked() {
-        UserState userState = mUserStateWeakReference.get();
+        AccessibilityUserState userState = mUserStateWeakReference.get();
         if (userState == null) return;
         final long identity = Binder.clearCallingIdentity();
         try {
             int flags = Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE
                     | Context.BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS;
-            if (userState.getBindInstantServiceAllowed()) {
+            if (userState.getBindInstantServiceAllowedLocked()) {
                 flags |= Context.BIND_ALLOW_INSTANT;
             }
             if (mService == null && mContext.bindServiceAsUser(
@@ -109,7 +108,7 @@
 
     public void unbindLocked() {
         mContext.unbindService(this);
-        UserState userState = mUserStateWeakReference.get();
+        AccessibilityUserState userState = mUserStateWeakReference.get();
         if (userState == null) return;
         userState.removeServiceLocked(this);
         mSystemSupport.getMagnificationController().resetAllIfNeeded(mId);
@@ -123,7 +122,7 @@
     @Override
     public void disableSelf() {
         synchronized (mLock) {
-            UserState userState = mUserStateWeakReference.get();
+            AccessibilityUserState userState = mUserStateWeakReference.get();
             if (userState == null) return;
             if (userState.getEnabledServicesLocked().remove(mComponentName)) {
                 final long identity = Binder.clearCallingIdentity();
@@ -156,7 +155,7 @@
                 }
             }
             mServiceInterface = IAccessibilityServiceClient.Stub.asInterface(service);
-            UserState userState = mUserStateWeakReference.get();
+            AccessibilityUserState userState = mUserStateWeakReference.get();
             if (userState == null) return;
             userState.addServiceLocked(this);
             mSystemSupport.onClientChangeLocked(false);
@@ -177,7 +176,7 @@
     private void initializeService() {
         IAccessibilityServiceClient serviceInterface = null;
         synchronized (mLock) {
-            UserState userState = mUserStateWeakReference.get();
+            AccessibilityUserState userState = mUserStateWeakReference.get();
             if (userState == null) return;
             Set<ComponentName> bindingServices = userState.getBindingServicesLocked();
             if (bindingServices.contains(mComponentName) || mWasConnectedAndDied) {
@@ -240,7 +239,7 @@
             if (!hasRightsToCurrentUserLocked()) {
                 return false;
             }
-            final UserState userState = mUserStateWeakReference.get();
+            final AccessibilityUserState userState = mUserStateWeakReference.get();
             if (userState == null) return false;
             return userState.setSoftKeyboardModeLocked(showMode, mComponentName);
         }
@@ -248,8 +247,8 @@
 
     @Override
     public int getSoftKeyboardShowMode() {
-        final UserState userState = mUserStateWeakReference.get();
-        return (userState != null) ? userState.getSoftKeyboardShowMode() : 0;
+        final AccessibilityUserState userState = mUserStateWeakReference.get();
+        return (userState != null) ? userState.getSoftKeyboardShowModeLocked() : 0;
     }
 
     @Override
@@ -258,7 +257,7 @@
             if (!hasRightsToCurrentUserLocked()) {
                 return false;
             }
-            UserState userState = mUserStateWeakReference.get();
+            AccessibilityUserState userState = mUserStateWeakReference.get();
             return (userState != null) && isAccessibilityButtonAvailableLocked(userState);
         }
     }
@@ -273,7 +272,7 @@
                 return;
             }
             mWasConnectedAndDied = true;
-            UserState userState = mUserStateWeakReference.get();
+            AccessibilityUserState userState = mUserStateWeakReference.get();
             if (userState != null) {
                 userState.serviceDisconnectedLocked(this);
             }
@@ -283,7 +282,7 @@
         }
     }
 
-    public boolean isAccessibilityButtonAvailableLocked(UserState userState) {
+    public boolean isAccessibilityButtonAvailableLocked(AccessibilityUserState userState) {
         // If the service does not request the accessibility button, it isn't available
         if (!mRequestAccessibilityButton) {
             return false;
@@ -295,8 +294,8 @@
         }
 
         // If magnification is on and assigned to the accessibility button, services cannot be
-        if (userState.mIsNavBarMagnificationEnabled
-                && userState.mIsNavBarMagnificationAssignedToAccessibilityButton) {
+        if (userState.isNavBarMagnificationEnabledLocked()
+                && userState.isNavBarMagnificationAssignedToAccessibilityButtonLocked()) {
             return false;
         }
 
@@ -314,13 +313,14 @@
             return true;
         } else {
             // With more than one active service, we derive the target from the user's settings
-            if (userState.mServiceAssignedToAccessibilityButton == null) {
+            if (userState.getServiceAssignedToAccessibilityButtonLocked() == null) {
                 // If the user has not made an assignment, we treat the button as available to
                 // all services until the user interacts with the button to make an assignment
                 return true;
             } else {
                 // If an assignment was made, it defines availability
-                return mComponentName.equals(userState.mServiceAssignedToAccessibilityButton);
+                return mComponentName.equals(
+                        userState.getServiceAssignedToAccessibilityButtonLocked());
             }
         }
     }
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java
new file mode 100644
index 0000000..69f1e0e
--- /dev/null
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java
@@ -0,0 +1,573 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.accessibility;
+
+import static android.accessibilityservice.AccessibilityService.SHOW_MODE_AUTO;
+import static android.accessibilityservice.AccessibilityService.SHOW_MODE_HARD_KEYBOARD_ORIGINAL_VALUE;
+import static android.accessibilityservice.AccessibilityService.SHOW_MODE_HARD_KEYBOARD_OVERRIDDEN;
+import static android.accessibilityservice.AccessibilityService.SHOW_MODE_HIDDEN;
+import static android.accessibilityservice.AccessibilityService.SHOW_MODE_IGNORE_HARD_KEYBOARD;
+import static android.accessibilityservice.AccessibilityService.SHOW_MODE_MASK;
+
+import android.accessibilityservice.AccessibilityService.SoftKeyboardShowMode;
+import android.accessibilityservice.AccessibilityServiceInfo;
+import android.accessibilityservice.AccessibilityShortcutInfo;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.content.Context;
+import android.os.Binder;
+import android.os.RemoteCallbackList;
+import android.provider.Settings;
+import android.util.Slog;
+import android.view.accessibility.AccessibilityManager;
+import android.view.accessibility.IAccessibilityManagerClient;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Class that hold states and settings per user and share between
+ * {@link AccessibilityManagerService} and {@link AccessibilityServiceConnection}.
+ */
+class AccessibilityUserState {
+    private static final String LOG_TAG = AccessibilityUserState.class.getSimpleName();
+
+    final int mUserId;
+
+    // Non-transient state.
+
+    final RemoteCallbackList<IAccessibilityManagerClient> mUserClients = new RemoteCallbackList<>();
+
+    // Transient state.
+
+    final ArrayList<AccessibilityServiceConnection> mBoundServices = new ArrayList<>();
+
+    final Map<ComponentName, AccessibilityServiceConnection> mComponentNameToServiceMap =
+            new HashMap<>();
+
+    final List<AccessibilityServiceInfo> mInstalledServices = new ArrayList<>();
+
+    final List<AccessibilityShortcutInfo> mInstalledShortcuts = new ArrayList<>();
+
+    final Set<ComponentName> mBindingServices = new HashSet<>();
+
+    final Set<ComponentName> mEnabledServices = new HashSet<>();
+
+    final Set<ComponentName> mTouchExplorationGrantedServices = new HashSet<>();
+
+    private final ServiceInfoChangeListener mServiceInfoChangeListener;
+
+    private ComponentName mServiceAssignedToAccessibilityButton;
+
+    private ComponentName mServiceChangingSoftKeyboardMode;
+
+    private ComponentName mServiceToEnableWithShortcut;
+
+    private boolean mBindInstantServiceAllowed;
+    private boolean mIsAutoclickEnabled;
+    private boolean mIsDisplayMagnificationEnabled;
+    private boolean mIsFilterKeyEventsEnabled;
+    private boolean mIsNavBarMagnificationAssignedToAccessibilityButton;
+    private boolean mIsNavBarMagnificationEnabled;
+    private boolean mIsPerformGesturesEnabled;
+    private boolean mIsTextHighContrastEnabled;
+    private boolean mIsTouchExplorationEnabled;
+    private int mUserInteractiveUiTimeout;
+    private int mUserNonInteractiveUiTimeout;
+    private int mNonInteractiveUiTimeout = 0;
+    private int mInteractiveUiTimeout = 0;
+    private int mLastSentClientState = -1;
+
+    private Context mContext;
+
+    @SoftKeyboardShowMode
+    private int mSoftKeyboardShowMode = SHOW_MODE_AUTO;
+
+    interface ServiceInfoChangeListener {
+        void onServiceInfoChangedLocked(AccessibilityUserState userState);
+    }
+
+    AccessibilityUserState(int userId, @NonNull Context context,
+            @NonNull ServiceInfoChangeListener serviceInfoChangeListener) {
+        mUserId = userId;
+        mContext = context;
+        mServiceInfoChangeListener = serviceInfoChangeListener;
+    }
+
+    boolean isHandlingAccessibilityEventsLocked() {
+        return !mBoundServices.isEmpty() || !mBindingServices.isEmpty();
+    }
+
+    void onSwitchToAnotherUserLocked() {
+        // Unbind all services.
+        unbindAllServicesLocked();
+
+        // Clear service management state.
+        mBoundServices.clear();
+        mBindingServices.clear();
+
+        // Clear event management state.
+        mLastSentClientState = -1;
+
+        // clear UI timeout
+        mNonInteractiveUiTimeout = 0;
+        mInteractiveUiTimeout = 0;
+
+        // Clear state persisted in settings.
+        mEnabledServices.clear();
+        mTouchExplorationGrantedServices.clear();
+        mIsTouchExplorationEnabled = false;
+        mIsDisplayMagnificationEnabled = false;
+        mIsNavBarMagnificationEnabled = false;
+        mServiceAssignedToAccessibilityButton = null;
+        mIsNavBarMagnificationAssignedToAccessibilityButton = false;
+        mIsAutoclickEnabled = false;
+        mUserNonInteractiveUiTimeout = 0;
+        mUserInteractiveUiTimeout = 0;
+    }
+
+    void addServiceLocked(AccessibilityServiceConnection serviceConnection) {
+        if (!mBoundServices.contains(serviceConnection)) {
+            serviceConnection.onAdded();
+            mBoundServices.add(serviceConnection);
+            mComponentNameToServiceMap.put(serviceConnection.getComponentName(), serviceConnection);
+            mServiceInfoChangeListener.onServiceInfoChangedLocked(this);
+        }
+    }
+
+    /**
+     * Removes a service.
+     * There are three states to a service here: off, bound, and binding.
+     * This stops tracking the service as bound.
+     *
+     * @param serviceConnection The service.
+     */
+    void removeServiceLocked(AccessibilityServiceConnection serviceConnection) {
+        mBoundServices.remove(serviceConnection);
+        serviceConnection.onRemoved();
+        if ((mServiceChangingSoftKeyboardMode != null)
+                && (mServiceChangingSoftKeyboardMode.equals(
+                serviceConnection.getServiceInfo().getComponentName()))) {
+            setSoftKeyboardModeLocked(SHOW_MODE_AUTO, null);
+        }
+        // It may be possible to bind a service twice, which confuses the map. Rebuild the map
+        // to make sure we can still reach a service
+        mComponentNameToServiceMap.clear();
+        for (int i = 0; i < mBoundServices.size(); i++) {
+            AccessibilityServiceConnection boundClient = mBoundServices.get(i);
+            mComponentNameToServiceMap.put(boundClient.getComponentName(), boundClient);
+        }
+        mServiceInfoChangeListener.onServiceInfoChangedLocked(this);
+    }
+
+    /**
+     * Make sure a services disconnected but still 'on' state is reflected in AccessibilityUserState
+     * There are three states to a service here: off, bound, and binding.
+     * This drops a service from a bound state, to the binding state.
+     * The binding state describes the situation where a service is on, but not bound.
+     *
+     * @param serviceConnection The service.
+     */
+    void serviceDisconnectedLocked(AccessibilityServiceConnection serviceConnection) {
+        removeServiceLocked(serviceConnection);
+        mBindingServices.add(serviceConnection.getComponentName());
+    }
+
+    /**
+     * Set the soft keyboard mode. This mode is a bit odd, as it spans multiple settings.
+     * The ACCESSIBILITY_SOFT_KEYBOARD_MODE setting can be checked by the rest of the system
+     * to see if it should suppress showing the IME. The SHOW_IME_WITH_HARD_KEYBOARD setting
+     * setting can be changed by the user, and prevents the system from suppressing the soft
+     * keyboard when the hard keyboard is connected. The hard keyboard setting needs to defer
+     * to the user's preference, if they have supplied one.
+     *
+     * @param newMode The new mode
+     * @param requester The service requesting the change, so we can undo it when the
+     *                  service stops. Set to null if something other than a service is forcing
+     *                  the change.
+     *
+     * @return Whether or not the soft keyboard mode equals the new mode after the call
+     */
+    boolean setSoftKeyboardModeLocked(@SoftKeyboardShowMode int newMode,
+            @Nullable ComponentName requester) {
+        if ((newMode != SHOW_MODE_AUTO)
+                && (newMode != SHOW_MODE_HIDDEN)
+                && (newMode != SHOW_MODE_IGNORE_HARD_KEYBOARD)) {
+            Slog.w(LOG_TAG, "Invalid soft keyboard mode");
+            return false;
+        }
+        if (mSoftKeyboardShowMode == newMode) {
+            return true;
+        }
+
+        if (newMode == SHOW_MODE_IGNORE_HARD_KEYBOARD) {
+            if (hasUserOverriddenHardKeyboardSetting()) {
+                // The user has specified a default for this setting
+                return false;
+            }
+            // Save the original value. But don't do this if the value in settings is already
+            // the new mode. That happens when we start up after a reboot, and we don't want
+            // to overwrite the value we had from when we first started controlling the setting.
+            if (getSoftKeyboardValueFromSettings() != SHOW_MODE_IGNORE_HARD_KEYBOARD) {
+                setOriginalHardKeyboardValue(getSecureIntForUser(
+                        Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD, 0, mUserId) != 0);
+            }
+            putSecureIntForUser(Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD, 1, mUserId);
+        } else if (mSoftKeyboardShowMode == SHOW_MODE_IGNORE_HARD_KEYBOARD) {
+            putSecureIntForUser(Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD,
+                    getOriginalHardKeyboardValue() ? 1 : 0, mUserId);
+        }
+
+        saveSoftKeyboardValueToSettings(newMode);
+        mSoftKeyboardShowMode = newMode;
+        mServiceChangingSoftKeyboardMode = requester;
+        for (int i = mBoundServices.size() - 1; i >= 0; i--) {
+            final AccessibilityServiceConnection service = mBoundServices.get(i);
+            service.notifySoftKeyboardShowModeChangedLocked(mSoftKeyboardShowMode);
+        }
+        return true;
+    }
+
+    @SoftKeyboardShowMode
+    int getSoftKeyboardShowModeLocked() {
+        return mSoftKeyboardShowMode;
+    }
+
+    /**
+     * If the settings are inconsistent with the internal state, make the internal state
+     * match the settings.
+     */
+    void reconcileSoftKeyboardModeWithSettingsLocked() {
+        final boolean showWithHardKeyboardSettings =
+                getSecureIntForUser(Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD, 0, mUserId) != 0;
+        if (mSoftKeyboardShowMode == SHOW_MODE_IGNORE_HARD_KEYBOARD) {
+            if (!showWithHardKeyboardSettings) {
+                // The user has overridden the setting. Respect that and prevent further changes
+                // to this behavior.
+                setSoftKeyboardModeLocked(SHOW_MODE_AUTO, null);
+                setUserOverridesHardKeyboardSetting();
+            }
+        }
+
+        // If the setting and the internal state are out of sync, set both to default
+        if (getSoftKeyboardValueFromSettings() != mSoftKeyboardShowMode) {
+            Slog.e(LOG_TAG, "Show IME setting inconsistent with internal state. Overwriting");
+            setSoftKeyboardModeLocked(SHOW_MODE_AUTO, null);
+            putSecureIntForUser(Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE,
+                    SHOW_MODE_AUTO, mUserId);
+        }
+    }
+
+    boolean getBindInstantServiceAllowedLocked() {
+        return mBindInstantServiceAllowed;
+    }
+
+    /* Need to have a permission check on callee */
+    void setBindInstantServiceAllowedLocked(boolean allowed) {
+        mBindInstantServiceAllowed = allowed;
+    }
+
+    Set<ComponentName> getBindingServicesLocked() {
+        return mBindingServices;
+    }
+
+    /**
+     * Returns enabled service list.
+     */
+    Set<ComponentName> getEnabledServicesLocked() {
+        return mEnabledServices;
+    }
+
+    List<AccessibilityServiceConnection> getBoundServicesLocked() {
+        return mBoundServices;
+    }
+
+    int getClientStateLocked(boolean isUiAutomationRunning) {
+        int clientState = 0;
+        final boolean a11yEnabled = isUiAutomationRunning
+                || isHandlingAccessibilityEventsLocked();
+        if (a11yEnabled) {
+            clientState |= AccessibilityManager.STATE_FLAG_ACCESSIBILITY_ENABLED;
+        }
+        // Touch exploration relies on enabled accessibility.
+        if (a11yEnabled && mIsTouchExplorationEnabled) {
+            clientState |= AccessibilityManager.STATE_FLAG_TOUCH_EXPLORATION_ENABLED;
+        }
+        if (mIsTextHighContrastEnabled) {
+            clientState |= AccessibilityManager.STATE_FLAG_HIGH_TEXT_CONTRAST_ENABLED;
+        }
+        return clientState;
+    }
+
+    private void setUserOverridesHardKeyboardSetting() {
+        final int softKeyboardSetting = getSecureIntForUser(
+                Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, SHOW_MODE_AUTO, mUserId);
+        putSecureIntForUser(Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE,
+                softKeyboardSetting | SHOW_MODE_HARD_KEYBOARD_OVERRIDDEN,
+                mUserId);
+    }
+
+    private boolean hasUserOverriddenHardKeyboardSetting() {
+        final int softKeyboardSetting = getSecureIntForUser(
+                Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, SHOW_MODE_AUTO, mUserId);
+        return (softKeyboardSetting & SHOW_MODE_HARD_KEYBOARD_OVERRIDDEN)
+                != 0;
+    }
+
+    private void setOriginalHardKeyboardValue(boolean originalHardKeyboardValue) {
+        final int oldSoftKeyboardSetting = getSecureIntForUser(
+                Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, SHOW_MODE_AUTO, mUserId);
+        final int newSoftKeyboardSetting = oldSoftKeyboardSetting
+                & (~SHOW_MODE_HARD_KEYBOARD_ORIGINAL_VALUE)
+                | ((originalHardKeyboardValue) ? SHOW_MODE_HARD_KEYBOARD_ORIGINAL_VALUE : 0);
+        putSecureIntForUser(Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE,
+                newSoftKeyboardSetting, mUserId);
+    }
+
+    private void saveSoftKeyboardValueToSettings(int softKeyboardShowMode) {
+        final int oldSoftKeyboardSetting = getSecureIntForUser(
+                Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, SHOW_MODE_AUTO, mUserId);
+        final int newSoftKeyboardSetting = oldSoftKeyboardSetting & (~SHOW_MODE_MASK)
+                | softKeyboardShowMode;
+        putSecureIntForUser(Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE,
+                newSoftKeyboardSetting, mUserId);
+    }
+
+    private int getSoftKeyboardValueFromSettings() {
+        return getSecureIntForUser(
+                Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, SHOW_MODE_AUTO, mUserId)
+                & SHOW_MODE_MASK;
+    }
+
+    private boolean getOriginalHardKeyboardValue() {
+        return (getSecureIntForUser(
+                Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, SHOW_MODE_AUTO, mUserId)
+                & SHOW_MODE_HARD_KEYBOARD_ORIGINAL_VALUE) != 0;
+    }
+
+    private void unbindAllServicesLocked() {
+        final List<AccessibilityServiceConnection> services = mBoundServices;
+        for (int count = services.size(); count > 0; count--) {
+            // When the service is unbound, it disappears from the list, so there's no need to
+            // keep track of the index
+            services.get(0).unbindLocked();
+        }
+    }
+
+    private int getSecureIntForUser(String key, int def, int userId) {
+        return Settings.Secure.getIntForUser(mContext.getContentResolver(), key, def, userId);
+    }
+
+    private void putSecureIntForUser(String key, int value, int userId) {
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            Settings.Secure.putIntForUser(mContext.getContentResolver(), key, value, userId);
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
+    void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        pw.append("User state[");
+        pw.println();
+        pw.append("     attributes:{id=").append(String.valueOf(mUserId));
+        pw.append(", touchExplorationEnabled=").append(String.valueOf(mIsTouchExplorationEnabled));
+        pw.append(", displayMagnificationEnabled=").append(String.valueOf(
+                mIsDisplayMagnificationEnabled));
+        pw.append(", navBarMagnificationEnabled=").append(String.valueOf(
+                mIsNavBarMagnificationEnabled));
+        pw.append(", autoclickEnabled=").append(String.valueOf(mIsAutoclickEnabled));
+        pw.append(", nonInteractiveUiTimeout=").append(String.valueOf(mNonInteractiveUiTimeout));
+        pw.append(", interactiveUiTimeout=").append(String.valueOf(mInteractiveUiTimeout));
+        pw.append(", installedServiceCount=").append(String.valueOf(mInstalledServices.size()));
+        pw.append("}");
+        pw.println();
+        pw.append("     Bound services:{");
+        final int serviceCount = mBoundServices.size();
+        for (int j = 0; j < serviceCount; j++) {
+            if (j > 0) {
+                pw.append(", ");
+                pw.println();
+                pw.append("                     ");
+            }
+            AccessibilityServiceConnection service = mBoundServices.get(j);
+            service.dump(fd, pw, args);
+        }
+        pw.println("}");
+        pw.append("     Enabled services:{");
+        Iterator<ComponentName> it = mEnabledServices.iterator();
+        if (it.hasNext()) {
+            ComponentName componentName = it.next();
+            pw.append(componentName.toShortString());
+            while (it.hasNext()) {
+                componentName = it.next();
+                pw.append(", ");
+                pw.append(componentName.toShortString());
+            }
+        }
+        pw.println("}");
+        pw.append("     Binding services:{");
+        it = mBindingServices.iterator();
+        if (it.hasNext()) {
+            ComponentName componentName = it.next();
+            pw.append(componentName.toShortString());
+            while (it.hasNext()) {
+                componentName = it.next();
+                pw.append(", ");
+                pw.append(componentName.toShortString());
+            }
+        }
+        pw.println("}]");
+    }
+
+    public boolean isAutoclickEnabledLocked() {
+        return mIsAutoclickEnabled;
+    }
+
+    public void setAutoclickEnabledLocked(boolean enabled) {
+        mIsAutoclickEnabled = enabled;
+    }
+
+    public boolean isDisplayMagnificationEnabledLocked() {
+        return mIsDisplayMagnificationEnabled;
+    }
+
+    public void setDisplayMagnificationEnabledLocked(boolean enabled) {
+        mIsDisplayMagnificationEnabled = enabled;
+    }
+
+    public boolean isFilterKeyEventsEnabledLocked() {
+        return mIsFilterKeyEventsEnabled;
+    }
+
+    public void setFilterKeyEventsEnabledLocked(boolean enabled) {
+        mIsFilterKeyEventsEnabled = enabled;
+    }
+
+    public int getInteractiveUiTimeoutLocked() {
+        return mInteractiveUiTimeout;
+    }
+
+    public void setInteractiveUiTimeoutLocked(int timeout) {
+        mInteractiveUiTimeout = timeout;
+    }
+
+    public int getLastSentClientStateLocked() {
+        return mLastSentClientState;
+    }
+
+    public void setLastSentClientStateLocked(int state) {
+        mLastSentClientState = state;
+    }
+
+    public boolean isNavBarMagnificationAssignedToAccessibilityButtonLocked() {
+        return mIsNavBarMagnificationAssignedToAccessibilityButton;
+    }
+
+    public void setNavBarMagnificationAssignedToAccessibilityButtonLocked(boolean assigned) {
+        mIsNavBarMagnificationAssignedToAccessibilityButton = assigned;
+    }
+
+    public boolean isNavBarMagnificationEnabledLocked() {
+        return mIsNavBarMagnificationEnabled;
+    }
+
+    public void setNavBarMagnificationEnabledLocked(boolean enabled) {
+        mIsNavBarMagnificationEnabled = enabled;
+    }
+
+    public int getNonInteractiveUiTimeoutLocked() {
+        return mNonInteractiveUiTimeout;
+    }
+
+    public void setNonInteractiveUiTimeoutLocked(int timeout) {
+        mNonInteractiveUiTimeout = timeout;
+    }
+
+    public boolean isPerformGesturesEnabledLocked() {
+        return mIsPerformGesturesEnabled;
+    }
+
+    public void setPerformGesturesEnabledLocked(boolean enabled) {
+        mIsPerformGesturesEnabled = enabled;
+    }
+
+    public ComponentName getServiceAssignedToAccessibilityButtonLocked() {
+        return mServiceAssignedToAccessibilityButton;
+    }
+
+    public void setServiceAssignedToAccessibilityButtonLocked(ComponentName componentName) {
+        mServiceAssignedToAccessibilityButton = componentName;
+    }
+
+    public ComponentName getServiceChangingSoftKeyboardModeLocked() {
+        return mServiceChangingSoftKeyboardMode;
+    }
+
+    public void setServiceChangingSoftKeyboardModeLocked(
+            ComponentName serviceChangingSoftKeyboardMode) {
+        mServiceChangingSoftKeyboardMode = serviceChangingSoftKeyboardMode;
+    }
+
+    public ComponentName getServiceToEnableWithShortcutLocked() {
+        return mServiceToEnableWithShortcut;
+    }
+
+    public void setServiceToEnableWithShortcutLocked(ComponentName componentName) {
+        mServiceToEnableWithShortcut = componentName;
+    }
+
+    public boolean isTextHighContrastEnabledLocked() {
+        return mIsTextHighContrastEnabled;
+    }
+
+    public void setTextHighContrastEnabledLocked(boolean enabled) {
+        mIsTextHighContrastEnabled = enabled;
+    }
+
+    public boolean isTouchExplorationEnabledLocked() {
+        return mIsTouchExplorationEnabled;
+    }
+
+    public void setTouchExplorationEnabledLocked(boolean enabled) {
+        mIsTouchExplorationEnabled = enabled;
+    }
+
+    public int getUserInteractiveUiTimeoutLocked() {
+        return mUserInteractiveUiTimeout;
+    }
+
+    public void setUserInteractiveUiTimeoutLocked(int timeout) {
+        mUserInteractiveUiTimeout = timeout;
+    }
+
+    public int getUserNonInteractiveUiTimeoutLocked() {
+        return mUserNonInteractiveUiTimeout;
+    }
+
+    public void setUserNonInteractiveUiTimeoutLocked(int timeout) {
+        mUserNonInteractiveUiTimeout = timeout;
+    }
+}
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 b35300c..7d129ea 100644
--- a/services/autofill/java/com/android/server/autofill/ui/SaveUi.java
+++ b/services/autofill/java/com/android/server/autofill/ui/SaveUi.java
@@ -286,7 +286,7 @@
         window.addFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM
                 | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
                 | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH);
-        window.addPrivateFlags(WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS);
+        window.addPrivateFlags(WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS);
         window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN);
         window.setGravity(Gravity.BOTTOM | Gravity.CENTER);
         window.setCloseOnTouchOutside(true);
diff --git a/services/backup/backuplib/java/com/android/server/backup/TransportManager.java b/services/backup/backuplib/java/com/android/server/backup/TransportManager.java
index de48f4b..30ce4cf 100644
--- a/services/backup/backuplib/java/com/android/server/backup/TransportManager.java
+++ b/services/backup/backuplib/java/com/android/server/backup/TransportManager.java
@@ -93,8 +93,7 @@
         mTransportWhitelist = Preconditions.checkNotNull(whitelist);
         mCurrentTransportName = selectedTransport;
         mTransportStats = new TransportStats();
-        mTransportClientManager = TransportClientManager.createEncryptingClientManager(mUserId,
-                context, mTransportStats);
+        mTransportClientManager = new TransportClientManager(mUserId, context, mTransportStats);
     }
 
     @VisibleForTesting
diff --git a/services/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java
index 1d666ad..dc24cff 100644
--- a/services/core/java/android/content/pm/PackageManagerInternal.java
+++ b/services/core/java/android/content/pm/PackageManagerInternal.java
@@ -59,6 +59,8 @@
     public static final int PACKAGE_CONFIGURATOR = 9;
     public static final int PACKAGE_INCIDENT_REPORT_APPROVER = 10;
     public static final int PACKAGE_APP_PREDICTOR = 11;
+    public static final int PACKAGE_TELEPHONY = 12;
+    public static final int PACKAGE_WIFI = 13;
     @IntDef(value = {
         PACKAGE_SYSTEM,
         PACKAGE_SETUP_WIZARD,
@@ -72,6 +74,8 @@
         PACKAGE_CONFIGURATOR,
         PACKAGE_INCIDENT_REPORT_APPROVER,
         PACKAGE_APP_PREDICTOR,
+        PACKAGE_TELEPHONY,
+        PACKAGE_WIFI,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface KnownPackage {}
@@ -546,10 +550,11 @@
      */
     public abstract boolean isResolveActivityComponent(@NonNull ComponentInfo component);
 
+
     /**
-     * Returns the package name for a known package.
+     * Returns a list of package names for a known package
      */
-    public abstract @Nullable String getKnownPackageName(
+    public abstract @NonNull String[] getKnownPackageNames(
             @KnownPackage int knownPackage, int userId);
 
     /**
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index e0f60b4..0bb72cb 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -1149,7 +1149,6 @@
     private static NetworkCapabilities createDefaultNetworkCapabilitiesForUid(int uid) {
         final NetworkCapabilities netCap = new NetworkCapabilities();
         netCap.addCapability(NET_CAPABILITY_INTERNET);
-        netCap.addCapability(NET_CAPABILITY_NOT_RESTRICTED);
         netCap.removeCapability(NET_CAPABILITY_NOT_VPN);
         netCap.setSingleUid(uid);
         return netCap;
@@ -1159,7 +1158,6 @@
             int transportType, NetworkRequest.Type type) {
         final NetworkCapabilities netCap = new NetworkCapabilities();
         netCap.addCapability(NET_CAPABILITY_INTERNET);
-        netCap.addCapability(NET_CAPABILITY_NOT_RESTRICTED);
         if (transportType > -1) {
             netCap.addTransportType(transportType);
         }
diff --git a/services/core/java/com/android/server/GnssManagerService.java b/services/core/java/com/android/server/GnssManagerService.java
index b6e619e..44a8234 100644
--- a/services/core/java/com/android/server/GnssManagerService.java
+++ b/services/core/java/com/android/server/GnssManagerService.java
@@ -17,6 +17,7 @@
 package com.android.server;
 
 import android.Manifest;
+import android.annotation.NonNull;
 import android.app.ActivityManager;
 import android.app.AppOpsManager;
 import android.content.Context;
@@ -298,7 +299,8 @@
      * @param packageName name of requesting package
      * @return true if callback is successfully added, false otherwise
      */
-    public boolean addGnssBatchingCallback(IBatchedLocationCallback callback, String packageName) {
+    public boolean addGnssBatchingCallback(IBatchedLocationCallback callback, String packageName,
+            @NonNull String listenerIdentity) {
         mContext.enforceCallingPermission(
                 android.Manifest.permission.LOCATION_HARDWARE,
                 "Location Hardware permission not granted to access hardware batching");
@@ -316,7 +318,8 @@
         }
 
         CallerIdentity callerIdentity =
-                new CallerIdentity(Binder.getCallingUid(), Binder.getCallingPid(), packageName);
+                new CallerIdentity(Binder.getCallingUid(), Binder.getCallingPid(), packageName,
+                        listenerIdentity);
         synchronized (mGnssBatchingLock) {
             mGnssBatchingCallback = callback;
             mGnssBatchingDeathCallback =
@@ -494,7 +497,7 @@
     private <TListener extends IInterface> boolean addGnssDataListenerLocked(
             TListener listener,
             String packageName,
-            String listenerName,
+            @NonNull String listenerIdentifier,
             RemoteListenerHelper<TListener> gnssDataProvider,
             ArrayMap<IBinder,
                     LinkedListener<TListener>> gnssDataListeners,
@@ -513,10 +516,11 @@
         }
 
         CallerIdentity callerIdentity =
-                new CallerIdentity(Binder.getCallingUid(), Binder.getCallingPid(), packageName);
+                new CallerIdentity(Binder.getCallingUid(), Binder.getCallingPid(), packageName,
+                        listenerIdentifier);
         LinkedListener<TListener> linkedListener =
                 new LocationManagerServiceUtils.LinkedListener<>(
-                        listener, listenerName, callerIdentity, binderDeathCallback);
+                        listener, listenerIdentifier, callerIdentity, binderDeathCallback);
         IBinder binder = listener.asBinder();
         if (!linkedListener.linkToListenerDeathNotificationLocked(binder)) {
             return false;
@@ -606,7 +610,7 @@
             return addGnssDataListenerLocked(
                     listener,
                     packageName,
-                    "GnssStatusListener",
+                    "Gnss status",
                     mGnssStatusProvider,
                     mGnssStatusListeners,
                     this::unregisterGnssStatusCallback);
@@ -632,12 +636,13 @@
      * @return true if listener is successfully added, false otherwise
      */
     public boolean addGnssMeasurementsListener(
-            IGnssMeasurementsListener listener, String packageName) {
+            IGnssMeasurementsListener listener, String packageName,
+            @NonNull String listenerIdentifier) {
         synchronized (mGnssMeasurementsListeners) {
             return addGnssDataListenerLocked(
                     listener,
                     packageName,
-                    "GnssMeasurementsListener",
+                    listenerIdentifier,
                     mGnssMeasurementsProvider,
                     mGnssMeasurementsListeners,
                     this::removeGnssMeasurementsListener);
@@ -689,12 +694,13 @@
      * @return true if listener is successfully added, false otherwise
      */
     public boolean addGnssNavigationMessageListener(
-            IGnssNavigationMessageListener listener, String packageName) {
+            IGnssNavigationMessageListener listener, String packageName,
+            @NonNull String listenerIdentifier) {
         synchronized (mGnssNavigationMessageListeners) {
             return addGnssDataListenerLocked(
                     listener,
                     packageName,
-                    "GnssNavigationMessageListener",
+                    listenerIdentifier,
                     mGnssNavigationMessageProvider,
                     mGnssNavigationMessageListeners,
                     this::removeGnssNavigationMessageListener);
diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java
index 09f62ff..aa22feb 100644
--- a/services/core/java/com/android/server/LocationManagerService.java
+++ b/services/core/java/com/android/server/LocationManagerService.java
@@ -1223,8 +1223,10 @@
         PowerManager.WakeLock mWakeLock;
 
         private Receiver(ILocationListener listener, PendingIntent intent, int pid, int uid,
-                String packageName, WorkSource workSource, boolean hideFromAppOps) {
-            super(new CallerIdentity(uid, pid, packageName), "LocationListener");
+                String packageName, WorkSource workSource, boolean hideFromAppOps,
+                @NonNull String listenerIdentifier) {
+            super(new CallerIdentity(uid, pid, packageName, listenerIdentifier),
+                    "LocationListener");
             mListener = listener;
             mPendingIntent = intent;
             if (listener != null) {
@@ -1532,9 +1534,12 @@
     }
 
     @Override
-    public boolean addGnssBatchingCallback(IBatchedLocationCallback callback, String packageName) {
+    public boolean addGnssBatchingCallback(IBatchedLocationCallback callback, String packageName,
+            String listenerIdentifier) {
+        Preconditions.checkNotNull(listenerIdentifier);
+
         return mGnssManagerService == null ? false : mGnssManagerService.addGnssBatchingCallback(
-                callback, packageName);
+                callback, packageName, listenerIdentifier);
     }
 
     @Override
@@ -1696,11 +1701,12 @@
         }
     }
 
-    private boolean reportLocationAccessNoThrow(
-            int pid, int uid, String packageName, int allowedResolutionLevel) {
+    private boolean reportLocationAccessNoThrow(int pid, int uid, String packageName,
+            int allowedResolutionLevel, @Nullable String message) {
         int op = resolutionLevelToOp(allowedResolutionLevel);
         if (op >= 0) {
-            if (mAppOps.noteOpNoThrow(op, uid, packageName) != AppOpsManager.MODE_ALLOWED) {
+            if (mAppOps.noteOpNoThrow(op, uid, packageName, message)
+                    != AppOpsManager.MODE_ALLOWED) {
                 return false;
             }
         }
@@ -2133,12 +2139,13 @@
 
     @GuardedBy("mLock")
     private Receiver getReceiverLocked(ILocationListener listener, int pid, int uid,
-            String packageName, WorkSource workSource, boolean hideFromAppOps) {
+            String packageName, WorkSource workSource, boolean hideFromAppOps,
+            @NonNull String listenerIdentifier) {
         IBinder binder = listener.asBinder();
         Receiver receiver = mReceivers.get(binder);
         if (receiver == null) {
             receiver = new Receiver(listener, null, pid, uid, packageName, workSource,
-                    hideFromAppOps);
+                    hideFromAppOps, listenerIdentifier);
             if (!receiver.linkToListenerDeathNotificationLocked(
                     receiver.getListener().asBinder())) {
                 return null;
@@ -2150,11 +2157,11 @@
 
     @GuardedBy("mLock")
     private Receiver getReceiverLocked(PendingIntent intent, int pid, int uid, String packageName,
-            WorkSource workSource, boolean hideFromAppOps) {
+            WorkSource workSource, boolean hideFromAppOps, @NonNull String listenerIdentifier) {
         Receiver receiver = mReceivers.get(intent);
         if (receiver == null) {
             receiver = new Receiver(null, intent, pid, uid, packageName, workSource,
-                    hideFromAppOps);
+                    hideFromAppOps, listenerIdentifier);
             mReceivers.put(intent, receiver);
         }
         return receiver;
@@ -2216,7 +2223,9 @@
 
     @Override
     public void requestLocationUpdates(LocationRequest request, ILocationListener listener,
-            PendingIntent intent, String packageName) {
+            PendingIntent intent, String packageName, String listenerIdentifier) {
+        Preconditions.checkNotNull(listenerIdentifier);
+
         synchronized (mLock) {
             if (request == null) request = DEFAULT_LOCATION_REQUEST;
             checkPackageName(packageName);
@@ -2271,10 +2280,10 @@
                 Receiver receiver;
                 if (intent != null) {
                     receiver = getReceiverLocked(intent, pid, uid, packageName, workSource,
-                            hideFromAppOps);
+                            hideFromAppOps, listenerIdentifier);
                 } else {
                     receiver = getReceiverLocked(listener, pid, uid, packageName, workSource,
-                            hideFromAppOps);
+                            hideFromAppOps, listenerIdentifier);
                 }
                 requestLocationUpdatesLocked(sanitizedRequest, receiver, uid, packageName);
             } finally {
@@ -2343,9 +2352,9 @@
         synchronized (mLock) {
             Receiver receiver;
             if (intent != null) {
-                receiver = getReceiverLocked(intent, pid, uid, packageName, null, false);
+                receiver = getReceiverLocked(intent, pid, uid, packageName, null, false, "");
             } else {
-                receiver = getReceiverLocked(listener, pid, uid, packageName, null, false);
+                receiver = getReceiverLocked(listener, pid, uid, packageName, null, false, "");
             }
 
             long identity = Binder.clearCallingIdentity();
@@ -2464,8 +2473,8 @@
                 }
                 // Don't report location access if there is no last location to deliver.
                 if (lastLocation != null) {
-                    if (!reportLocationAccessNoThrow(
-                            pid, uid, packageName, allowedResolutionLevel)) {
+                    if (!reportLocationAccessNoThrow(pid, uid, packageName, allowedResolutionLevel,
+                            null)) {
                         if (D) {
                             Log.d(TAG, "not returning last loc for no op app: " + packageName);
                         }
@@ -2519,7 +2528,9 @@
 
     @Override
     public void requestGeofence(LocationRequest request, Geofence geofence, PendingIntent intent,
-            String packageName) {
+            String packageName, String listenerIdentifier) {
+        Preconditions.checkNotNull(listenerIdentifier);
+
         if (request == null) request = DEFAULT_LOCATION_REQUEST;
         int allowedResolutionLevel = getCallerAllowedResolutionLevel();
         checkResolutionLevelIsSufficientForGeofenceUse(allowedResolutionLevel);
@@ -2564,9 +2575,8 @@
                         mActivityManager.getPackageImportance(packageName));
             }
 
-            mGeofenceManager.addFence(sanitizedRequest, geofence, intent,
-                    allowedResolutionLevel,
-                    uid, packageName);
+            mGeofenceManager.addFence(sanitizedRequest, geofence, intent, allowedResolutionLevel,
+                    uid, packageName, listenerIdentifier);
         } finally {
             Binder.restoreCallingIdentity(identity);
         }
@@ -2613,10 +2623,13 @@
     }
 
     @Override
-    public boolean addGnssMeasurementsListener(
-            IGnssMeasurementsListener listener, String packageName) {
+    public boolean addGnssMeasurementsListener(IGnssMeasurementsListener listener,
+            String packageName, String listenerIdentifier) {
+        Preconditions.checkNotNull(listenerIdentifier);
+
         return mGnssManagerService == null ? false
-                : mGnssManagerService.addGnssMeasurementsListener(listener, packageName);
+                : mGnssManagerService.addGnssMeasurementsListener(listener, packageName,
+                        listenerIdentifier);
     }
 
     @Override
@@ -2643,10 +2656,13 @@
     }
 
     @Override
-    public boolean addGnssNavigationMessageListener(
-            IGnssNavigationMessageListener listener, String packageName) {
+    public boolean addGnssNavigationMessageListener(IGnssNavigationMessageListener listener,
+            String packageName, String listenerIdentifier) {
+        Preconditions.checkNotNull(listenerIdentifier);
+
         return mGnssManagerService == null ? false
-                : mGnssManagerService.addGnssNavigationMessageListener(listener, packageName);
+                : mGnssManagerService.addGnssNavigationMessageListener(listener, packageName,
+                        listenerIdentifier);
     }
 
     @Override
@@ -2719,12 +2735,21 @@
                     return true;
                 }
             }
-
             return false;
         }
     }
 
     @Override
+    public List<String> getProviderPackages(String providerName) {
+        mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_DEVICE_CONFIG,
+                Manifest.permission.READ_DEVICE_CONFIG + " permission required");
+        synchronized (mLock) {
+            LocationProvider provider = getLocationProviderLocked(providerName);
+            return provider == null ? Collections.emptyList() : provider.getPackagesLocked();
+        }
+    }
+
+    @Override
     public void setExtraLocationControllerPackage(String packageName) {
         mContext.enforceCallingPermission(Manifest.permission.LOCATION_HARDWARE,
                 Manifest.permission.LOCATION_HARDWARE + " permission required");
@@ -2943,7 +2968,8 @@
                             receiver.mCallerIdentity.mPid,
                             receiver.mCallerIdentity.mUid,
                             receiver.mCallerIdentity.mPackageName,
-                            receiver.mAllowedResolutionLevel)) {
+                            receiver.mAllowedResolutionLevel,
+                            "Location sent to " + receiver.mCallerIdentity.mListenerIdentifier)) {
                         if (D) {
                             Log.d(TAG, "skipping loc update for no op app: "
                                     + receiver.mCallerIdentity.mPackageName);
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 8c672a1..55cd933 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -900,7 +900,7 @@
 
         // The other observer methods are unused
         @Override
-        public void onIntentStarted(Intent intent) {
+        public void onIntentStarted(Intent intent, long timestampNs) {
         }
 
         @Override
@@ -912,7 +912,11 @@
         }
 
         @Override
-        public void onActivityLaunchFinished(byte[] finalActivity) {
+        public void onActivityLaunchFinished(byte[] finalActivity, long timestampNs) {
+        }
+
+        @Override
+        public void onReportFullyDrawn(byte[] finalActivity, long timestampNs) {
         }
     };
 
@@ -8658,7 +8662,7 @@
         lp.format = v.getBackground().getOpacity();
         lp.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
                 | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
-        lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
+        lp.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
         ((WindowManager)mContext.getSystemService(
                 Context.WINDOW_SERVICE)).addView(v, lp);
     }
diff --git a/services/core/java/com/android/server/am/AppErrorDialog.java b/services/core/java/com/android/server/am/AppErrorDialog.java
index a80a5b5..852c9b65 100644
--- a/services/core/java/com/android/server/am/AppErrorDialog.java
+++ b/services/core/java/com/android/server/am/AppErrorDialog.java
@@ -92,7 +92,7 @@
         WindowManager.LayoutParams attrs = getWindow().getAttributes();
         attrs.setTitle("Application Error: " + mProc.info.processName);
         attrs.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SYSTEM_ERROR
-                | WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
+                | WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
         getWindow().setAttributes(attrs);
         if (mProc.isPersistent()) {
             getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ERROR);
diff --git a/services/core/java/com/android/server/am/AppNotRespondingDialog.java b/services/core/java/com/android/server/am/AppNotRespondingDialog.java
index cb76e2f..65d7e01 100644
--- a/services/core/java/com/android/server/am/AppNotRespondingDialog.java
+++ b/services/core/java/com/android/server/am/AppNotRespondingDialog.java
@@ -94,7 +94,7 @@
         WindowManager.LayoutParams attrs = getWindow().getAttributes();
         attrs.setTitle("Application Not Responding: " + mProc.info.processName);
         attrs.privateFlags = WindowManager.LayoutParams.PRIVATE_FLAG_SYSTEM_ERROR |
-                WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
+                WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
         getWindow().setAttributes(attrs);
     }
 
diff --git a/services/core/java/com/android/server/am/UserSwitchingDialog.java b/services/core/java/com/android/server/am/UserSwitchingDialog.java
index 98f5557..bbf5772 100644
--- a/services/core/java/com/android/server/am/UserSwitchingDialog.java
+++ b/services/core/java/com/android/server/am/UserSwitchingDialog.java
@@ -78,7 +78,7 @@
 
         WindowManager.LayoutParams attrs = getWindow().getAttributes();
         attrs.privateFlags = WindowManager.LayoutParams.PRIVATE_FLAG_SYSTEM_ERROR |
-            WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
+            WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
         getWindow().setAttributes(attrs);
     }
 
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 6c4cc2d..2ac6eb0 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -68,6 +68,7 @@
 import android.net.Uri;
 import android.os.AsyncTask;
 import android.os.Binder;
+import android.os.Build;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
@@ -1398,6 +1399,12 @@
     }
 
     private void updatePermissionRevokedCompat(int uid, int switchCode, int mode) {
+        PackageManagerInternal packageManagerInternal = LocalServices.getService(
+                PackageManagerInternal.class);
+        if (packageManagerInternal.getUidTargetSdkVersion(uid) >= Build.VERSION_CODES.M) {
+            return;
+        }
+
         PackageManager packageManager = mContext.getPackageManager();
         String[] packageNames = packageManager.getPackagesForUid(uid);
         if (ArrayUtils.isEmpty(packageNames)) {
diff --git a/services/core/java/com/android/server/hdmi/Constants.java b/services/core/java/com/android/server/hdmi/Constants.java
index 96ba8ef..648e07a 100644
--- a/services/core/java/com/android/server/hdmi/Constants.java
+++ b/services/core/java/com/android/server/hdmi/Constants.java
@@ -394,14 +394,10 @@
     static final String PROPERTY_PREFERRED_ADDRESS_PLAYBACK = "persist.sys.hdmi.addr.playback";
     static final String PROPERTY_PREFERRED_ADDRESS_TV = "persist.sys.hdmi.addr.tv";
 
-
     // TODO(OEM): Set this to false to keep the playback device in sleep upon hotplug event.
     //            True by default.
     static final String PROPERTY_WAKE_ON_HOTPLUG = "ro.hdmi.wake_on_hotplug";
 
-    // TODO(OEM): Set this to true to enable 'Set Menu Language' feature. False by default.
-    static final String PROPERTY_SET_MENU_LANGUAGE = "ro.hdmi.set_menu_language";
-
     /**
      * Property to save the ARC port id on system audio device.
      * <p>When ARC is initiated, this port will be used to turn on ARC.
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
index 61d4d4b..211d028 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
@@ -1238,8 +1238,8 @@
         }
         // Wake up if the current device if ready to route.
         mService.wakeUp();
-        if (getLocalActivePort() == portId) {
-            HdmiLogger.debug("Not switching to the same port " + portId);
+        if ((getLocalActivePort() == portId) && (portId != Constants.CEC_SWITCH_ARC)) {
+            HdmiLogger.debug("Not switching to the same port " + portId + " except for arc");
             return;
         }
         // Switch to HOME if the current active port is not HOME yet
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
index 413e7a0..0944324 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
@@ -24,6 +24,7 @@
 import android.os.PowerManager.WakeLock;
 import android.os.SystemProperties;
 import android.provider.Settings.Global;
+import android.sysprop.HdmiProperties;
 import android.util.Slog;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -47,7 +48,7 @@
             SystemProperties.getBoolean(Constants.PROPERTY_WAKE_ON_HOTPLUG, true);
 
     private static final boolean SET_MENU_LANGUAGE =
-            SystemProperties.getBoolean(Constants.PROPERTY_SET_MENU_LANGUAGE, false);
+            HdmiProperties.set_menu_language().orElse(false);
 
     // Used to keep the device awake while it is the active source. For devices that
     // cannot wake up via CEC commands, this address the inconvenience of having to
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index f1af73a..362955d 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -199,8 +199,7 @@
             int deviceId, int sourceMask, int sw);
     private static native boolean nativeHasKeys(long ptr,
             int deviceId, int sourceMask, int[] keyCodes, boolean[] keyExists);
-    private static native void nativeRegisterInputChannel(long ptr, InputChannel inputChannel,
-            int displayId);
+    private static native void nativeRegisterInputChannel(long ptr, InputChannel inputChannel);
     private static native void nativeRegisterInputMonitor(long ptr, InputChannel inputChannel,
             int displayId, boolean isGestureMonitor);
     private static native void nativeUnregisterInputChannel(long ptr, InputChannel inputChannel);
@@ -525,7 +524,6 @@
             throw new IllegalArgumentException("displayId must >= 0.");
         }
 
-
         final long ident = Binder.clearCallingIdentity();
         try {
             InputChannel[] inputChannels = InputChannel.openInputChannelPair(inputChannelName);
@@ -533,7 +531,7 @@
             inputChannels[0].setToken(host.asBinder());
             nativeRegisterInputMonitor(mPtr, inputChannels[0], displayId,
                     true /*isGestureMonitor*/);
-            return new InputMonitor(inputChannelName, inputChannels[1], host);
+            return new InputMonitor(inputChannels[1], host);
         } finally {
             Binder.restoreCallingIdentity(ident);
         }
@@ -551,7 +549,7 @@
         }
         inputChannel.setToken(new Binder());
 
-        nativeRegisterInputChannel(mPtr, inputChannel, Display.INVALID_DISPLAY);
+        nativeRegisterInputChannel(mPtr, inputChannel);
     }
 
     /**
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index b7fcd3f..471fa72 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -3570,10 +3570,14 @@
                 return;
             }
             if (!setVisible) {
-                // Client hides the IME directly.
-                if (mCurClient != null && mCurClient.client != null) {
-                    executeOrSendMessage(mCurClient.client, mCaller.obtainMessageIO(
-                            MSG_APPLY_IME_VISIBILITY, setVisible ? 1 : 0, mCurClient));
+                if (mCurClient != null) {
+                    // IMMS only knows of focused window, not the actual IME target.
+                    // e.g. it isn't aware of any window that has both
+                    // NOT_FOCUSABLE, ALT_FOCUSABLE_IM flags set and can the IME target.
+                    // Send it to window manager to hide IME from IME target window.
+                    // TODO(b/139861270): send to mCurClient.client once IMMS is aware of
+                    // actual IME target.
+                    mWindowManagerInternal.hideIme(mCurClient.selfReportedDisplayId);
                 }
             } else {
                 // Send to window manager to show IME after IME layout finishes.
@@ -4208,7 +4212,7 @@
             // with other IME windows based on type vs. grouping based on whichever token happens
             // to get selected by the system later on.
             attrs.token = mSwitchingDialogToken;
-            attrs.privateFlags |= LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
+            attrs.privateFlags |= LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
             attrs.setTitle("Select input method");
             w.setAttributes(attrs);
             updateSystemUiLocked(mImeWindowVis, mBackDisposition);
diff --git a/services/core/java/com/android/server/integrity/engine/RuleEvaluator.java b/services/core/java/com/android/server/integrity/engine/RuleEvaluator.java
index a4bf2b0..6416505 100644
--- a/services/core/java/com/android/server/integrity/engine/RuleEvaluator.java
+++ b/services/core/java/com/android/server/integrity/engine/RuleEvaluator.java
@@ -37,6 +37,9 @@
     /**
      * Match the list of rules against an app install metadata.
      *
+     * <p>Rules must be in disjunctive normal form (DNF). A rule should contain AND'ed formulas
+     * only. All rules are OR'ed together by default.
+     *
      * @param rules              The list of rules to evaluate.
      * @param appInstallMetadata Metadata of the app to be installed, and to evaluate the rules
      *                           against.
@@ -45,7 +48,7 @@
      */
     static Rule evaluateRules(List<Rule> rules, AppInstallMetadata appInstallMetadata) {
         for (Rule rule : rules) {
-            if (isMatch(rule, appInstallMetadata)) {
+            if (isConjunctionOfFormulas(rule.getFormula()) && isMatch(rule, appInstallMetadata)) {
                 return rule;
             }
         }
@@ -99,4 +102,25 @@
 
         return false;
     }
+
+    private static boolean isConjunctionOfFormulas(Formula formula) {
+        if (formula == null) {
+            return false;
+        }
+        if (isAtomicFormula(formula)) {
+            return true;
+        }
+        OpenFormula openFormula = (OpenFormula) formula;
+        return openFormula.getConnector() == OpenFormula.Connector.AND
+                && openFormula.getFormulas().stream().allMatch(RuleEvaluator::isAtomicFormula);
+    }
+
+    private static boolean isAtomicFormula(Formula formula) {
+        if (formula instanceof AtomicFormula) {
+            return true;
+        }
+        OpenFormula openFormula = (OpenFormula) formula;
+        return openFormula.getConnector() == OpenFormula.Connector.NOT
+                && openFormula.getFormulas().get(0) instanceof AtomicFormula;
+    }
 }
diff --git a/services/core/java/com/android/server/location/CallerIdentity.java b/services/core/java/com/android/server/location/CallerIdentity.java
index da31d0b..61e5d1f 100644
--- a/services/core/java/com/android/server/location/CallerIdentity.java
+++ b/services/core/java/com/android/server/location/CallerIdentity.java
@@ -16,6 +16,8 @@
 
 package com.android.server.location;
 
+import android.annotation.NonNull;
+
 /**
  * Represents the calling process's uid, pid, and package name.
  */
@@ -23,10 +25,13 @@
     public final int mUid;
     public final int mPid;
     public final String mPackageName;
+    public final @NonNull String mListenerIdentifier;
 
-    public CallerIdentity(int uid, int pid, String packageName) {
+    public CallerIdentity(int uid, int pid, String packageName,
+            @NonNull String listenerIdentifier) {
         mUid = uid;
         mPid = pid;
         mPackageName = packageName;
+        mListenerIdentifier = listenerIdentifier;
     }
 }
diff --git a/services/core/java/com/android/server/location/GeofenceManager.java b/services/core/java/com/android/server/location/GeofenceManager.java
index a192206..895c4b3 100644
--- a/services/core/java/com/android/server/location/GeofenceManager.java
+++ b/services/core/java/com/android/server/location/GeofenceManager.java
@@ -16,6 +16,7 @@
 
 package com.android.server.location;
 
+import android.annotation.NonNull;
 import android.app.AppOpsManager;
 import android.app.PendingIntent;
 import android.content.ContentResolver;
@@ -151,14 +152,16 @@
     }
 
     public void addFence(LocationRequest request, Geofence geofence, PendingIntent intent,
-            int allowedResolutionLevel, int uid, String packageName) {
+            int allowedResolutionLevel, int uid, String packageName,
+            @NonNull String listenerIdentifier) {
         if (D) {
             Slog.d(TAG, "addFence: request=" + request + ", geofence=" + geofence
                     + ", intent=" + intent + ", uid=" + uid + ", packageName=" + packageName);
         }
 
         GeofenceState state = new GeofenceState(geofence,
-                request.getExpireAt(), allowedResolutionLevel, uid, packageName, intent);
+                request.getExpireAt(), allowedResolutionLevel, uid, packageName, listenerIdentifier,
+                intent);
         synchronized (mLock) {
             // first make sure it doesn't already exist
             for (int i = mFences.size() - 1; i >= 0; i--) {
@@ -301,7 +304,8 @@
                 int op = LocationManagerService.resolutionLevelToOp(state.mAllowedResolutionLevel);
                 if (op >= 0) {
                     if (mAppOps.noteOpNoThrow(AppOpsManager.OP_FINE_LOCATION, state.mUid,
-                            state.mPackageName) != AppOpsManager.MODE_ALLOWED) {
+                            state.mPackageName, state.mListenerIdentifier)
+                            != AppOpsManager.MODE_ALLOWED) {
                         if (D) {
                             Slog.d(TAG, "skipping geofence processing for no op app: "
                                     + state.mPackageName);
diff --git a/services/core/java/com/android/server/location/GeofenceState.java b/services/core/java/com/android/server/location/GeofenceState.java
index 3ebe20a..fe0719d 100644
--- a/services/core/java/com/android/server/location/GeofenceState.java
+++ b/services/core/java/com/android/server/location/GeofenceState.java
@@ -17,6 +17,7 @@
 
 package com.android.server.location;
 
+import android.annotation.NonNull;
 import android.app.PendingIntent;
 import android.location.Geofence;
 import android.location.Location;
@@ -38,13 +39,14 @@
     public final int mAllowedResolutionLevel;
     public final int mUid;
     public final String mPackageName;
+    public final @NonNull String mListenerIdentifier;
     public final PendingIntent mIntent;
 
     int mState;  // current state
     double mDistanceToCenter;  // current distance to center of fence
 
-    public GeofenceState(Geofence fence, long expireAt,
-            int allowedResolutionLevel, int uid, String packageName, PendingIntent intent) {
+    public GeofenceState(Geofence fence, long expireAt, int allowedResolutionLevel, int uid,
+            String packageName, @NonNull String listenerIdentifier, PendingIntent intent) {
         mState = STATE_UNKNOWN;
         mDistanceToCenter = Double.MAX_VALUE;
 
@@ -53,6 +55,7 @@
         mAllowedResolutionLevel = allowedResolutionLevel;
         mUid = uid;
         mPackageName = packageName;
+        mListenerIdentifier = listenerIdentifier;
         mIntent = intent;
 
         mLocation = new Location("");
diff --git a/services/core/java/com/android/server/location/RemoteListenerHelper.java b/services/core/java/com/android/server/location/RemoteListenerHelper.java
index aa8a25a..0929d93 100644
--- a/services/core/java/com/android/server/location/RemoteListenerHelper.java
+++ b/services/core/java/com/android/server/location/RemoteListenerHelper.java
@@ -182,7 +182,9 @@
         }
 
         return mAppOps.noteOpNoThrow(AppOpsManager.OP_FINE_LOCATION, callerIdentity.mUid,
-                callerIdentity.mPackageName) == AppOpsManager.MODE_ALLOWED;
+                callerIdentity.mPackageName,
+                "Location sent to " + callerIdentity.mListenerIdentifier)
+                == AppOpsManager.MODE_ALLOWED;
     }
 
     protected void logPermissionDisabledEventNotReported(String tag, String packageName,
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index bad484f..63ba138 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -118,6 +118,7 @@
 import com.android.internal.widget.LockPatternUtils;
 import com.android.internal.widget.LockPatternUtils.CredentialType;
 import com.android.internal.widget.LockSettingsInternal;
+import com.android.internal.widget.LockscreenCredential;
 import com.android.internal.widget.VerifyCredentialResponse;
 import com.android.server.LocalServices;
 import com.android.server.ServiceThread;
@@ -2058,7 +2059,8 @@
             @UserIdInt int userHandle) {
         synchronized (this) {
             mUserPasswordMetrics.put(userHandle,
-                    PasswordMetrics.computeForCredential(credentialType, password));
+                    PasswordMetrics.computeForCredential(
+                            LockscreenCredential.createRaw(credentialType, password)));
         }
     }
 
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java b/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java
index a5d59e3..0a8e5bd 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java
@@ -16,16 +16,12 @@
 
 package com.android.server.locksettings;
 
-import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC;
-import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_NUMERIC;
-
-import static com.android.internal.widget.LockPatternUtils.stringToPattern;
-
 import android.app.ActivityManager;
 import android.os.ShellCommand;
 
 import com.android.internal.widget.LockPatternUtils;
 import com.android.internal.widget.LockPatternUtils.RequestThrottledException;
+import com.android.internal.widget.LockscreenCredential;
 
 import java.io.PrintWriter;
 
@@ -189,31 +185,49 @@
                 mLockPatternUtils.isSyntheticPasswordEnabled()));
     }
 
+    private LockscreenCredential getOldCredential() {
+        if (mLockPatternUtils.isLockPasswordEnabled(mCurrentUserId)) {
+            final int quality = mLockPatternUtils.getKeyguardStoredPasswordQuality(mCurrentUserId);
+            if (LockPatternUtils.isQualityAlphabeticPassword(quality)) {
+                return LockscreenCredential.createPassword(mOld);
+            } else {
+                return LockscreenCredential.createPin(mOld);
+            }
+        } else if (mLockPatternUtils.isLockPatternEnabled(mCurrentUserId)) {
+            return LockscreenCredential.createPattern(LockPatternUtils.byteArrayToPattern(
+                    mOld.getBytes()));
+        } else {
+            return LockscreenCredential.createNone();
+        }
+    }
+
     private void runSetPattern() {
-        byte[] oldBytes = mOld != null ? mOld.getBytes() : null;
-        mLockPatternUtils.saveLockPattern(stringToPattern(mNew), oldBytes, mCurrentUserId);
+        mLockPatternUtils.setLockCredential(
+                LockscreenCredential.createPattern(LockPatternUtils.byteArrayToPattern(
+                        mNew.getBytes())),
+                getOldCredential(),
+                mCurrentUserId);
         getOutPrintWriter().println("Pattern set to '" + mNew + "'");
     }
 
     private void runSetPassword() {
-        byte[] newBytes = mNew != null ? mNew.getBytes() : null;
-        byte[] oldBytes = mOld != null ? mOld.getBytes() : null;
-        mLockPatternUtils.saveLockPassword(newBytes, oldBytes, PASSWORD_QUALITY_ALPHABETIC,
+        mLockPatternUtils.setLockCredential(LockscreenCredential.createPassword(mNew),
+                getOldCredential(),
                 mCurrentUserId);
         getOutPrintWriter().println("Password set to '" + mNew + "'");
     }
 
     private void runSetPin() {
-        byte[] newBytes = mNew != null ? mNew.getBytes() : null;
-        byte[] oldBytes = mOld != null ? mOld.getBytes() : null;
-        mLockPatternUtils.saveLockPassword(newBytes, oldBytes, PASSWORD_QUALITY_NUMERIC,
+        mLockPatternUtils.setLockCredential(LockscreenCredential.createPin(mNew),
+                getOldCredential(),
                 mCurrentUserId);
         getOutPrintWriter().println("Pin set to '" + mNew + "'");
     }
 
     private void runClear() {
-        byte[] oldBytes = mOld != null ? mOld.getBytes() : null;
-        mLockPatternUtils.clearLock(oldBytes, mCurrentUserId);
+        mLockPatternUtils.setLockCredential(LockscreenCredential.createNone(),
+                getOldCredential(),
+                mCurrentUserId);
         getOutPrintWriter().println("Lock credential cleared");
     }
 
@@ -238,13 +252,8 @@
             }
 
             try {
-                final boolean result;
-                if (havePassword) {
-                    byte[] passwordBytes = mOld != null ? mOld.getBytes() : null;
-                    result = mLockPatternUtils.checkPassword(passwordBytes, mCurrentUserId);
-                } else {
-                    result = mLockPatternUtils.checkPattern(stringToPattern(mOld), mCurrentUserId);
-                }
+                final boolean result = mLockPatternUtils.checkCredential(getOldCredential(),
+                        mCurrentUserId, null);
                 if (!result) {
                     if (!mLockPatternUtils.isManagedProfileWithUnifiedChallenge(mCurrentUserId)) {
                         mLockPatternUtils.reportFailedPasswordAttempt(mCurrentUserId);
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsStorage.java b/services/core/java/com/android/server/locksettings/LockSettingsStorage.java
index 5594614..f4cad63 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsStorage.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsStorage.java
@@ -95,8 +95,6 @@
 
     @VisibleForTesting
     public static class CredentialHash {
-        /** Deprecated private static final int VERSION_LEGACY = 0; */
-        private static final int VERSION_GATEKEEPER = 1;
 
         private CredentialHash(byte[] hash, @CredentialType int type) {
             if (type != LockPatternUtils.CREDENTIAL_TYPE_NONE) {
@@ -126,42 +124,6 @@
 
         byte[] hash;
         @CredentialType int type;
-
-        public byte[] toBytes() {
-            try {
-                ByteArrayOutputStream os = new ByteArrayOutputStream();
-                DataOutputStream dos = new DataOutputStream(os);
-                dos.write(VERSION_GATEKEEPER);
-                dos.write(type);
-                if (hash != null && hash.length > 0) {
-                    dos.writeInt(hash.length);
-                    dos.write(hash);
-                } else {
-                    dos.writeInt(0);
-                }
-                dos.close();
-                return os.toByteArray();
-            } catch (IOException e) {
-                throw new IllegalStateException("Fail to serialze credential hash", e);
-            }
-        }
-
-        public static CredentialHash fromBytes(byte[] bytes) {
-            try {
-                DataInputStream is = new DataInputStream(new ByteArrayInputStream(bytes));
-                /* int version = */ is.read();
-                int type = is.read();
-                int hashSize = is.readInt();
-                byte[] hash = null;
-                if (hashSize > 0) {
-                    hash = new byte[hashSize];
-                    is.readFully(hash);
-                }
-                return new CredentialHash(hash, type);
-            } catch (IOException e) {
-                throw new IllegalStateException("Fail to deserialze credential hash", e);
-            }
-        }
     }
 
     public LockSettingsStorage(Context context) {
diff --git a/services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java b/services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java
index e753a7b..9eac252 100644
--- a/services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java
+++ b/services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java
@@ -93,7 +93,7 @@
 
     public void unselectRoute(String packageName, String routeId) {
         if (mConnectionReady) {
-            mActiveConnection.unselectRotue(packageName, routeId);
+            mActiveConnection.unselectRoute(packageName, routeId);
             updateBinding();
         }
     }
@@ -105,6 +105,20 @@
         }
     }
 
+    public void requestSetVolume(MediaRoute2Info route, int volume) {
+        if (mConnectionReady) {
+            mActiveConnection.requestSetVolume(route.getId(), volume);
+            updateBinding();
+        }
+    }
+
+    public void requestUpdateVolume(MediaRoute2Info route, int delta) {
+        if (mConnectionReady) {
+            mActiveConnection.requestUpdateVolume(route.getId(), delta);
+            updateBinding();
+        }
+    }
+
     @Nullable
     public MediaRoute2ProviderInfo getProviderInfo() {
         return mProviderInfo;
@@ -324,7 +338,7 @@
             }
         }
 
-        public void unselectRotue(String packageName, String routeId) {
+        public void unselectRoute(String packageName, String routeId) {
             try {
                 mProvider.unselectRoute(packageName, routeId);
             } catch (RemoteException ex) {
@@ -340,6 +354,22 @@
             }
         }
 
+        public void requestSetVolume(String routeId, int volume) {
+            try {
+                mProvider.requestSetVolume(routeId, volume);
+            } catch (RemoteException ex) {
+                Slog.e(TAG, "Failed to deliver request to request set volume.", ex);
+            }
+        }
+
+        public void requestUpdateVolume(String routeId, int delta) {
+            try {
+                mProvider.requestUpdateVolume(routeId, delta);
+            } catch (RemoteException ex) {
+                Slog.e(TAG, "Failed to deliver request to request update volume.", ex);
+            }
+        }
+
         @Override
         public void binderDied() {
             mHandler.post(() -> onConnectionDied(Connection.this));
diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
index 668f2be..74d59ac 100644
--- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
+++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
@@ -198,6 +198,34 @@
         }
     }
 
+    public void requestSetVolume2(IMediaRouter2Client client, MediaRoute2Info route, int volume) {
+        Objects.requireNonNull(client, "client must not be null");
+        Objects.requireNonNull(route, "route must not be null");
+
+        final long token = Binder.clearCallingIdentity();
+        try {
+            synchronized (mLock) {
+                requestSetVolumeLocked(client, route, volume);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
+    public void requestUpdateVolume2(IMediaRouter2Client client, MediaRoute2Info route, int delta) {
+        Objects.requireNonNull(client, "client must not be null");
+        Objects.requireNonNull(route, "route must not be null");
+
+        final long token = Binder.clearCallingIdentity();
+        try {
+            synchronized (mLock) {
+                requestUpdateVolumeLocked(client, route, delta);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
     public void selectClientRoute2(@NonNull IMediaRouter2Manager manager,
             String packageName, @Nullable MediaRoute2Info route) {
         final long token = Binder.clearCallingIdentity();
@@ -210,6 +238,37 @@
         }
     }
 
+    public void requestSetVolume2Manager(IMediaRouter2Manager manager,
+            MediaRoute2Info route, int volume) {
+        Objects.requireNonNull(manager, "manager must not be null");
+        Objects.requireNonNull(route, "route must not be null");
+
+        final long token = Binder.clearCallingIdentity();
+        try {
+            synchronized (mLock) {
+                requestSetVolumeLocked(manager, route, volume);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
+    public void requestUpdateVolume2Manager(IMediaRouter2Manager manager,
+            MediaRoute2Info route, int delta) {
+        Objects.requireNonNull(manager, "manager must not be null");
+        Objects.requireNonNull(route, "route must not be null");
+
+        final long token = Binder.clearCallingIdentity();
+        try {
+            synchronized (mLock) {
+                requestUpdateVolumeLocked(manager, route, delta);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
+
     public void registerClient(@NonNull IMediaRouterClient client, @NonNull String packageName) {
         Objects.requireNonNull(client, "client must not be null");
 
@@ -362,6 +421,30 @@
         }
     }
 
+    private void requestSetVolumeLocked(IMediaRouter2Client client, MediaRoute2Info route,
+            int volume) {
+        final IBinder binder = client.asBinder();
+        ClientRecord clientRecord = mAllClientRecords.get(binder);
+
+        if (clientRecord != null) {
+            clientRecord.mUserRecord.mHandler.sendMessage(
+                    obtainMessage(UserHandler::requestSetVolume,
+                            clientRecord.mUserRecord.mHandler, route, volume));
+        }
+    }
+
+    private void requestUpdateVolumeLocked(IMediaRouter2Client client, MediaRoute2Info route,
+            int delta) {
+        final IBinder binder = client.asBinder();
+        ClientRecord clientRecord = mAllClientRecords.get(binder);
+
+        if (clientRecord != null) {
+            clientRecord.mUserRecord.mHandler.sendMessage(
+                    obtainMessage(UserHandler::requestUpdateVolume,
+                            clientRecord.mUserRecord.mHandler, route, delta));
+        }
+    }
+
     private void registerManagerLocked(IMediaRouter2Manager manager,
             int uid, int pid, String packageName, int userId, boolean trusted) {
         final IBinder binder = manager.asBinder();
@@ -424,6 +507,31 @@
         }
     }
 
+    private void requestSetVolumeLocked(IMediaRouter2Manager manager, MediaRoute2Info route,
+            int volume) {
+        final IBinder binder = manager.asBinder();
+        ManagerRecord managerRecord = mAllManagerRecords.get(binder);
+
+        if (managerRecord != null) {
+            managerRecord.mUserRecord.mHandler.sendMessage(
+                    obtainMessage(UserHandler::requestSetVolume,
+                            managerRecord.mUserRecord.mHandler, route, volume));
+        }
+    }
+
+    private void requestUpdateVolumeLocked(IMediaRouter2Manager manager, MediaRoute2Info route,
+            int delta) {
+        final IBinder binder = manager.asBinder();
+        ManagerRecord managerRecord = mAllManagerRecords.get(binder);
+
+        if (managerRecord != null) {
+            managerRecord.mUserRecord.mHandler.sendMessage(
+                    obtainMessage(UserHandler::requestUpdateVolume,
+                            managerRecord.mUserRecord.mHandler, route, delta));
+        }
+    }
+
+
     private void initializeUserLocked(UserRecord userRecord) {
         if (DEBUG) {
             Slog.d(TAG, userRecord + ": Initialized");
@@ -679,6 +787,20 @@
             }
         }
 
+        private void requestSetVolume(MediaRoute2Info route, int volume) {
+            final MediaRoute2ProviderProxy provider = findProvider(route.getProviderId());
+            if (provider != null) {
+                provider.requestSetVolume(route, volume);
+            }
+        }
+
+        private void requestUpdateVolume(MediaRoute2Info route, int delta) {
+            final MediaRoute2ProviderProxy provider = findProvider(route.getProviderId());
+            if (provider != null) {
+                provider.requestUpdateVolume(route, delta);
+            }
+        }
+
         private void scheduleUpdateProviderInfos() {
             if (!mProviderInfosUpdateScheduled) {
                 mProviderInfosUpdateScheduled = true;
diff --git a/services/core/java/com/android/server/media/MediaRouterService.java b/services/core/java/com/android/server/media/MediaRouterService.java
index 796a25d..afd92f6 100644
--- a/services/core/java/com/android/server/media/MediaRouterService.java
+++ b/services/core/java/com/android/server/media/MediaRouterService.java
@@ -499,6 +499,32 @@
         mService2.setControlCategories2(client, categories);
     }
 
+    // Binder call
+    @Override
+    public void requestSetVolume2(IMediaRouter2Client client, MediaRoute2Info route, int volume) {
+        mService2.requestSetVolume2(client, route, volume);
+    }
+
+    // Binder call
+    @Override
+    public void requestUpdateVolume2(IMediaRouter2Client client, MediaRoute2Info route, int delta) {
+        mService2.requestUpdateVolume2(client, route, delta);
+    }
+
+    // Binder call
+    @Override
+    public void requestSetVolume2Manager(IMediaRouter2Manager manager,
+            MediaRoute2Info route, int volume) {
+        mService2.requestSetVolume2Manager(manager, route, volume);
+    }
+
+    // Binder call
+    @Override
+    public void requestUpdateVolume2Manager(IMediaRouter2Manager manager,
+            MediaRoute2Info route, int delta) {
+        mService2.requestUpdateVolume2Manager(manager, route, delta);
+    }
+
     void restoreBluetoothA2dp() {
         try {
             boolean a2dpOn;
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 976a0c6..812ce32 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -217,7 +217,6 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
 import com.android.internal.notification.SystemNotificationChannels;
-import com.android.internal.os.SomeArgs;
 import com.android.internal.telephony.PhoneConstants;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.ConcurrentUtils;
@@ -385,6 +384,7 @@
     private static final int MSG_SUBSCRIPTION_OVERRIDE = 16;
     private static final int MSG_METERED_RESTRICTED_PACKAGES_CHANGED = 17;
     private static final int MSG_SET_NETWORK_TEMPLATE_ENABLED = 18;
+    private static final int MSG_SUBSCRIPTION_PLANS_CHANGED = 19;
 
     private static final int UID_MSG_STATE_CHANGED = 100;
     private static final int UID_MSG_GONE = 101;
@@ -1509,6 +1509,11 @@
         latch.await(5, TimeUnit.SECONDS);
     }
 
+    @VisibleForTesting
+    Handler getHandlerForTesting() {
+        return mHandler;
+    }
+
     /**
      * Update mobile policies with data cycle information from {@link CarrierConfigManager}
      * if necessary.
@@ -3064,6 +3069,34 @@
         mContext.enforceCallingOrSelfPermission(MANAGE_SUBSCRIPTION_PLANS, TAG);
     }
 
+    private void enforceSubscriptionPlanValidity(SubscriptionPlan[] plans) {
+        // nothing to check if no plans
+        if (plans.length == 0) {
+            return;
+        }
+
+        long applicableNetworkTypes = 0;
+        boolean allNetworks = false;
+        for (SubscriptionPlan plan : plans) {
+            if (plan.getNetworkTypes() == null) {
+                allNetworks = true;
+            } else {
+                if ((applicableNetworkTypes & plan.getNetworkTypesBitMask()) != 0) {
+                    throw new IllegalArgumentException(
+                            "Multiple subscription plans defined for a single network type.");
+                } else {
+                    applicableNetworkTypes |= plan.getNetworkTypesBitMask();
+                }
+            }
+        }
+
+        // ensure at least one plan applies for every network type
+        if (!allNetworks) {
+            throw new IllegalArgumentException(
+                    "No generic subscription plan that applies to all network types.");
+        }
+    }
+
     @Override
     public SubscriptionPlan[] getSubscriptionPlans(int subId, String callingPackage) {
         enforceSubscriptionPlanAccess(subId, Binder.getCallingUid(), callingPackage);
@@ -3228,6 +3261,7 @@
     @Override
     public void setSubscriptionPlans(int subId, SubscriptionPlan[] plans, String callingPackage) {
         enforceSubscriptionPlanAccess(subId, Binder.getCallingUid(), callingPackage);
+        enforceSubscriptionPlanValidity(plans);
 
         for (SubscriptionPlan plan : plans) {
             Preconditions.checkNotNull(plan);
@@ -3256,6 +3290,8 @@
             intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
             intent.putExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX, subId);
             mContext.sendBroadcast(intent, android.Manifest.permission.MANAGE_SUBSCRIPTION_PLANS);
+            mHandler.sendMessage(
+                    mHandler.obtainMessage(MSG_SUBSCRIPTION_PLANS_CHANGED, subId, 0, plans));
         } finally {
             Binder.restoreCallingIdentity(token);
         }
@@ -3282,7 +3318,7 @@
 
     @Override
     public void setSubscriptionOverride(int subId, int overrideMask, int overrideValue,
-            long networkTypeMask, long timeoutMillis, String callingPackage) {
+            long timeoutMillis, String callingPackage) {
         enforceSubscriptionPlanAccess(subId, Binder.getCallingUid(), callingPackage);
 
         // We can only override when carrier told us about plans
@@ -3300,16 +3336,11 @@
         final boolean overrideEnabled = Settings.Global.getInt(mContext.getContentResolver(),
                 NETPOLICY_OVERRIDE_ENABLED, 1) != 0;
         if (overrideEnabled || overrideValue == 0) {
-            SomeArgs args = SomeArgs.obtain();
-            args.arg1 = subId;
-            args.arg2 = overrideMask;
-            args.arg3 = overrideValue;
-            args.arg4 = networkTypeMask;
-            mHandler.sendMessage(mHandler.obtainMessage(MSG_SUBSCRIPTION_OVERRIDE, args));
+            mHandler.sendMessage(mHandler.obtainMessage(MSG_SUBSCRIPTION_OVERRIDE,
+                    overrideMask, overrideValue, subId));
             if (timeoutMillis > 0) {
-                args.arg3 = 0;
-                mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_SUBSCRIPTION_OVERRIDE, args),
-                        timeoutMillis);
+                mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_SUBSCRIPTION_OVERRIDE,
+                        overrideMask, 0, subId), timeoutMillis);
             }
         }
     }
@@ -4445,11 +4476,20 @@
     }
 
     private void dispatchSubscriptionOverride(INetworkPolicyListener listener, int subId,
-            int overrideMask, int overrideValue, long networkTypeMask) {
+            int overrideMask, int overrideValue) {
         if (listener != null) {
             try {
-                listener.onSubscriptionOverride(subId, overrideMask, overrideValue,
-                        networkTypeMask);
+                listener.onSubscriptionOverride(subId, overrideMask, overrideValue);
+            } catch (RemoteException ignored) {
+            }
+        }
+    }
+
+    private void dispatchSubscriptionPlansChanged(INetworkPolicyListener listener, int subId,
+            SubscriptionPlan[] plans) {
+        if (listener != null) {
+            try {
+                listener.onSubscriptionPlansChanged(subId, plans);
             } catch (RemoteException ignored) {
             }
         }
@@ -4550,16 +4590,13 @@
                     return true;
                 }
                 case MSG_SUBSCRIPTION_OVERRIDE: {
-                    final SomeArgs args = (SomeArgs) msg.obj;
-                    final int subId = (int) args.arg1;
-                    final int overrideMask = (int) args.arg2;
-                    final int overrideValue = (int) args.arg3;
-                    final long networkTypeMask = (long) args.arg4;
+                    final int overrideMask = msg.arg1;
+                    final int overrideValue = msg.arg2;
+                    final int subId = (int) msg.obj;
                     final int length = mListeners.beginBroadcast();
                     for (int i = 0; i < length; i++) {
                         final INetworkPolicyListener listener = mListeners.getBroadcastItem(i);
-                        dispatchSubscriptionOverride(listener, subId, overrideMask, overrideValue,
-                                networkTypeMask);
+                        dispatchSubscriptionOverride(listener, subId, overrideMask, overrideValue);
                     }
                     mListeners.finishBroadcast();
                     return true;
@@ -4576,6 +4613,17 @@
                     setNetworkTemplateEnabledInner(template, enabled);
                     return true;
                 }
+                case MSG_SUBSCRIPTION_PLANS_CHANGED: {
+                    final SubscriptionPlan[] plans = (SubscriptionPlan[]) msg.obj;
+                    final int subId = msg.arg1;
+                    final int length = mListeners.beginBroadcast();
+                    for (int i = 0; i < length; i++) {
+                        final INetworkPolicyListener listener = mListeners.getBroadcastItem(i);
+                        dispatchSubscriptionPlansChanged(listener, subId, plans);
+                    }
+                    mListeners.finishBroadcast();
+                    return true;
+                }
                 default: {
                     return false;
                 }
diff --git a/services/core/java/com/android/server/notification/NotificationDelegate.java b/services/core/java/com/android/server/notification/NotificationDelegate.java
index 61be1f5..6f0ad33 100644
--- a/services/core/java/com/android/server/notification/NotificationDelegate.java
+++ b/services/core/java/com/android/server/notification/NotificationDelegate.java
@@ -17,6 +17,7 @@
 package com.android.server.notification;
 
 import android.app.Notification;
+import android.net.Uri;
 import android.service.notification.NotificationStats;
 
 import com.android.internal.statusbar.NotificationVisibility;
@@ -49,6 +50,12 @@
     void onNotificationBubbleChanged(String key, boolean isBubble);
 
     /**
+     * Grant permission to read the specified URI to the package associated with the
+     * NotificationRecord associated with the given key.
+     */
+    void grantInlineReplyUriPermission(String key, Uri uri, int callingUid);
+
+    /**
      * Notifies that smart replies and actions have been added to the UI.
      */
     void onNotificationSmartSuggestionsAdded(String key, int smartReplyCount, int smartActionCount,
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 4457e9c..ca979f8 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -21,6 +21,7 @@
 import static android.app.Notification.FLAG_AUTOGROUP_SUMMARY;
 import static android.app.Notification.FLAG_BUBBLE;
 import static android.app.Notification.FLAG_FOREGROUND_SERVICE;
+import static android.app.Notification.FLAG_INSISTENT;
 import static android.app.Notification.FLAG_NO_CLEAR;
 import static android.app.Notification.FLAG_ONGOING_EVENT;
 import static android.app.Notification.FLAG_ONLY_ALERT_ONCE;
@@ -55,6 +56,7 @@
 import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE;
 import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.media.AudioAttributes.USAGE_NOTIFICATION_RINGTONE;
 import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_CRITICAL;
 import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_NORMAL;
 import static android.os.UserHandle.USER_NULL;
@@ -1154,6 +1156,56 @@
                 }
             }
         }
+
+        @Override
+        /**
+         * Grant permission to read the specified URI to the package specified in the
+         * NotificationRecord associated with the given key. The callingUid represents the UID of
+         * SystemUI from which this method is being called.
+         *
+         * For this to work, SystemUI must have permission to read the URI when running under the
+         * user associated with the NotificationRecord, and this grant will fail when trying
+         * to grant URI permissions across users.
+         */
+        public void grantInlineReplyUriPermission(String key, Uri uri, int callingUid) {
+            synchronized (mNotificationLock) {
+                NotificationRecord r = mNotificationsByKey.get(key);
+                if (r != null) {
+                    IBinder owner = r.permissionOwner;
+                    if (owner == null) {
+                        r.permissionOwner = mUgmInternal.newUriPermissionOwner("NOTIF:" + key);
+                        owner = r.permissionOwner;
+                    }
+                    int uid = callingUid;
+                    int userId = r.sbn.getUserId();
+                    if (userId == UserHandle.USER_ALL) {
+                        userId = USER_SYSTEM;
+                    }
+                    if (UserHandle.getUserId(uid) != userId) {
+                        try {
+                            final String[] pkgs = mPackageManager.getPackagesForUid(callingUid);
+                            if (pkgs == null) {
+                                Log.e(TAG, "Cannot grant uri permission to unknown UID: "
+                                        + callingUid);
+                            }
+                            final String pkg = pkgs[0]; // Get the SystemUI package
+                            // Find the UID for SystemUI for the correct user
+                            uid =  mPackageManager.getPackageUid(pkg, 0, userId);
+                        } catch (RemoteException re) {
+                            Log.e(TAG, "Cannot talk to package manager", re);
+                        }
+                    }
+                    grantUriPermission(owner, uri, uid, r.sbn.getPackageName(), userId);
+                } else {
+                    Log.w(TAG, "No record found for notification key:" + key);
+
+                    // TODO: figure out cancel story. I think it's: sysui needs to tell us
+                    // whenever noitifications held by a lifetimextender go away
+                    // IBinder owner = mUgmInternal.newUriPermissionOwner("InlineReply:" + key);
+                    // pass in userId and package as well as key (key for logging purposes)
+                }
+            }
+        }
     };
 
     @VisibleForTesting
@@ -1183,7 +1235,7 @@
     }
 
     @GuardedBy("mNotificationLock")
-    private void clearSoundLocked() {
+    void clearSoundLocked() {
         mSoundNotificationKey = null;
         long identity = Binder.clearCallingIdentity();
         try {
@@ -1198,7 +1250,7 @@
     }
 
     @GuardedBy("mNotificationLock")
-    private void clearVibrateLocked() {
+    void clearVibrateLocked() {
         mVibrateNotificationKey = null;
         long identity = Binder.clearCallingIdentity();
         try {
@@ -4495,13 +4547,13 @@
         if (record != null && record.getAudioAttributes() != null) {
             if ((mListenerHints & HINT_HOST_DISABLE_NOTIFICATION_EFFECTS) != 0) {
                 if (record.getAudioAttributes().getUsage()
-                        != AudioAttributes.USAGE_VOICE_COMMUNICATION) {
+                        != AudioAttributes.USAGE_NOTIFICATION_RINGTONE) {
                     return "listenerNoti";
                 }
             }
             if ((mListenerHints & HINT_HOST_DISABLE_CALL_EFFECTS) != 0) {
                 if (record.getAudioAttributes().getUsage()
-                        == AudioAttributes.USAGE_VOICE_COMMUNICATION) {
+                        == AudioAttributes.USAGE_NOTIFICATION_RINGTONE) {
                     return "listenerCall";
                 }
             }
@@ -6060,7 +6112,6 @@
                 mIsAutomotive
                         ? record.getImportance() > NotificationManager.IMPORTANCE_DEFAULT
                         : record.getImportance() >= NotificationManager.IMPORTANCE_DEFAULT;
-
         // Remember if this notification already owns the notification channels.
         boolean wasBeep = key != null && key.equals(mSoundNotificationKey);
         boolean wasBuzz = key != null && key.equals(mVibrateNotificationKey);
@@ -6076,7 +6127,6 @@
         }
 
         if (aboveThreshold && isNotificationForCurrentUser(record)) {
-
             if (mSystemReady && mAudioManager != null) {
                 Uri soundUri = record.getSound();
                 hasValidSound = soundUri != null && !Uri.EMPTY.equals(soundUri);
@@ -6091,7 +6141,6 @@
                     vibration = mFallbackVibrationPattern;
                 }
                 hasValidVibrate = vibration != null;
-
                 boolean hasAudibleAlert = hasValidSound || hasValidVibrate;
                 if (hasAudibleAlert && !shouldMuteNotificationLocked(record)) {
                     if (!sentAccessibilityEvent) {
@@ -6248,11 +6297,29 @@
             return true;
         }
 
+        // A looping ringtone, such as an incoming call is playing
+        if (isLoopingRingtoneNotification(mNotificationsByKey.get(mSoundNotificationKey))
+                || isLoopingRingtoneNotification(
+                        mNotificationsByKey.get(mVibrateNotificationKey))) {
+            return true;
+        }
+
+        return false;
+    }
+
+    @GuardedBy("mNotificationLock")
+    private boolean isLoopingRingtoneNotification(final NotificationRecord playingRecord) {
+        if (playingRecord != null) {
+            if (playingRecord.getAudioAttributes().getUsage() == USAGE_NOTIFICATION_RINGTONE
+                    && (playingRecord.getNotification().flags & FLAG_INSISTENT) != 0) {
+                return true;
+            }
+        }
         return false;
     }
 
     private boolean playSound(final NotificationRecord record, Uri soundUri) {
-        boolean looping = (record.getNotification().flags & Notification.FLAG_INSISTENT) != 0;
+        boolean looping = (record.getNotification().flags & FLAG_INSISTENT) != 0;
         // play notifications if there is no user of exclusive audio focus
         // and the stream volume is not 0 (non-zero volume implies not silenced by SILENT or
         //   VIBRATE ringer mode)
@@ -6304,7 +6371,6 @@
                     try {
                         Thread.sleep(waitMs);
                     } catch (InterruptedException e) { }
-
                     // Notifications might be canceled before it actually vibrates due to waitMs,
                     // so need to check the notification still valide for vibrate.
                     synchronized (mNotificationLock) {
@@ -7012,7 +7078,6 @@
     private void grantUriPermission(IBinder owner, Uri uri, int sourceUid, String targetPkg,
             int targetUserId) {
         if (uri == null || !ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())) return;
-
         final long ident = Binder.clearCallingIdentity();
         try {
             mUgm.grantUriPermissionFromOwner(owner, sourceUid, targetPkg,
diff --git a/services/core/java/com/android/server/os/TEST_MAPPING b/services/core/java/com/android/server/os/TEST_MAPPING
new file mode 100644
index 0000000..502f1e8
--- /dev/null
+++ b/services/core/java/com/android/server/os/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+  "presubmit": [
+    {
+      "name": "CtsUsbTests"
+    }
+  ]
+}
diff --git a/services/core/java/com/android/server/pm/ComponentResolver.java b/services/core/java/com/android/server/pm/ComponentResolver.java
index 976cdfb..b1eb7e7 100644
--- a/services/core/java/com/android/server/pm/ComponentResolver.java
+++ b/services/core/java/com/android/server/pm/ComponentResolver.java
@@ -50,6 +50,7 @@
 import android.util.Slog;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.ArrayUtils;
 import com.android.server.IntentResolver;
 
 import java.io.PrintWriter;
@@ -386,8 +387,11 @@
             addProvidersLocked(pkg, chatty);
             addServicesLocked(pkg, chatty);
         }
-        final String setupWizardPackage = sPackageManagerInternal.getKnownPackageName(
-                PACKAGE_SETUP_WIZARD, UserHandle.USER_SYSTEM);
+        // expect single setupwizard package
+        final String setupWizardPackage = ArrayUtils.firstOrNull(
+                sPackageManagerInternal.getKnownPackageNames(
+                        PACKAGE_SETUP_WIZARD, UserHandle.USER_SYSTEM));
+
         for (int i = newIntents.size() - 1; i >= 0; --i) {
             final PackageParser.ActivityIntentInfo intentInfo = newIntents.get(i);
             final PackageParser.Package disabledPkg = sPackageManagerInternal
@@ -421,8 +425,11 @@
         final List<ActivityIntentInfo> protectedFilters = mProtectedFilters;
         mProtectedFilters = null;
 
-        final String setupWizardPackage = sPackageManagerInternal.getKnownPackageName(
-                PACKAGE_SETUP_WIZARD, UserHandle.USER_SYSTEM);
+        // expect single setupwizard package
+        final String setupWizardPackage = ArrayUtils.firstOrNull(
+                sPackageManagerInternal.getKnownPackageNames(
+                        PACKAGE_SETUP_WIZARD, UserHandle.USER_SYSTEM));
+
         if (DEBUG_FILTERS && setupWizardPackage == null) {
             Slog.i(TAG, "No setup wizard;"
                     + " All protected intents capped to priority 0");
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 67d0f62..982ecbb 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -974,124 +974,9 @@
         @Override public final boolean hasFeature(String feature) {
             return PackageManagerService.this.hasSystemFeature(feature, 0);
         }
-
-        final List<PackageParser.Package> getStaticOverlayPackages(
-                Collection<PackageParser.Package> allPackages, String targetPackageName) {
-            if ("android".equals(targetPackageName)) {
-                // Static RROs targeting to "android", ie framework-res.apk, are already applied by
-                // native AssetManager.
-                return null;
-            }
-
-            List<PackageParser.Package> overlayPackages = null;
-            for (PackageParser.Package p : allPackages) {
-                if (targetPackageName.equals(p.mOverlayTarget) && p.mOverlayIsStatic) {
-                    if (overlayPackages == null) {
-                        overlayPackages = new ArrayList<>();
-                    }
-                    overlayPackages.add(p);
-                }
-            }
-            if (overlayPackages != null) {
-                Comparator<PackageParser.Package> cmp =
-                        Comparator.comparingInt(p -> p.mOverlayPriority);
-                overlayPackages.sort(cmp);
-            }
-            return overlayPackages;
-        }
-
-        final String[] getStaticOverlayPaths(List<PackageParser.Package> overlayPackages,
-                String targetPath) {
-            if (overlayPackages == null || overlayPackages.isEmpty()) {
-                return null;
-            }
-            List<String> overlayPathList = null;
-            for (PackageParser.Package overlayPackage : overlayPackages) {
-                if (targetPath == null) {
-                    if (overlayPathList == null) {
-                        overlayPathList = new ArrayList<>();
-                    }
-                    overlayPathList.add(overlayPackage.baseCodePath);
-                    continue;
-                }
-
-                try {
-                    // Creates idmaps for system to parse correctly the Android manifest of the
-                    // target package.
-                    //
-                    // OverlayManagerService will update each of them with a correct gid from its
-                    // target package app id.
-                    mInstaller.idmap(targetPath, overlayPackage.baseCodePath,
-                            UserHandle.getSharedAppGid(
-                                    UserHandle.getUserGid(UserHandle.USER_SYSTEM)));
-                    if (overlayPathList == null) {
-                        overlayPathList = new ArrayList<>();
-                    }
-                    overlayPathList.add(overlayPackage.baseCodePath);
-                } catch (InstallerException e) {
-                    Slog.e(TAG, "Failed to generate idmap for " + targetPath + " and " +
-                            overlayPackage.baseCodePath);
-                }
-            }
-            return overlayPathList == null ? null : overlayPathList.toArray(new String[0]);
-        }
-
-        String[] getStaticOverlayPaths(String targetPackageName, String targetPath) {
-            List<PackageParser.Package> overlayPackages;
-            synchronized (mInstallLock) {
-                synchronized (mLock) {
-                    overlayPackages = getStaticOverlayPackages(
-                            mPackages.values(), targetPackageName);
-                }
-                // It is safe to keep overlayPackages without holding mPackages because static overlay
-                // packages can't be uninstalled or disabled.
-                return getStaticOverlayPaths(overlayPackages, targetPath);
-            }
-        }
-
-        @Override public final String[] getOverlayApks(String targetPackageName) {
-            return getStaticOverlayPaths(targetPackageName, null);
-        }
-
-        @Override public final String[] getOverlayPaths(String targetPackageName,
-                String targetPath) {
-            return getStaticOverlayPaths(targetPackageName, targetPath);
-        }
-    }
-
-    class ParallelPackageParserCallback extends PackageParserCallback {
-        List<PackageParser.Package> mOverlayPackages = null;
-
-        void findStaticOverlayPackages() {
-            synchronized (mLock) {
-                for (PackageParser.Package p : mPackages.values()) {
-                    if (p.mOverlayIsStatic) {
-                        if (mOverlayPackages == null) {
-                            mOverlayPackages = new ArrayList<>();
-                        }
-                        mOverlayPackages.add(p);
-                    }
-                }
-            }
-        }
-
-        @Override
-        synchronized String[] getStaticOverlayPaths(String targetPackageName, String targetPath) {
-            // We can trust mOverlayPackages without holding mPackages because package uninstall
-            // can't happen while running parallel parsing.
-            // And we can call mInstaller inside getStaticOverlayPaths without holding mInstallLock
-            // because mInstallLock is held before running parallel parsing.
-            // Moreover holding mPackages or mInstallLock on each parsing thread causes dead-lock.
-            return mOverlayPackages == null ? null :
-                    getStaticOverlayPaths(
-                            getStaticOverlayPackages(mOverlayPackages, targetPackageName),
-                            targetPath);
-        }
     }
 
     final PackageParser.Callback mPackageParserCallback = new PackageParserCallback();
-    final ParallelPackageParserCallback mParallelPackageParserCallback =
-            new ParallelPackageParserCallback();
 
     // Currently known shared libraries.
     final ArrayMap<String, LongSparseArray<SharedLibraryInfo>> mSharedLibraries = new ArrayMap<>();
@@ -1603,6 +1488,8 @@
     final @Nullable String mConfiguratorPackage;
     final @Nullable String mAppPredictionServicePackage;
     final @Nullable String mIncidentReportApproverPackage;
+    final @Nullable String[] mTelephonyPackages;
+    final @Nullable String mWifiPackage;
     final @NonNull String mServicesSystemSharedLibraryPackageName;
     final @NonNull String mSharedSystemSharedLibraryPackageName;
 
@@ -2806,8 +2693,6 @@
                         systemScanFlags | partition.scanFlag, 0);
             }
 
-            mParallelPackageParserCallback.findStaticOverlayPackages();
-
             scanDirTracedLI(frameworkDir, systemParseFlags,
                     systemScanFlags | SCAN_NO_DEX | SCAN_AS_PRIVILEGED, 0);
             if (!mPackages.containsKey("android")) {
@@ -3059,6 +2944,8 @@
                     mContext.getString(R.string.config_deviceConfiguratorPackageName);
             mAppPredictionServicePackage = getAppPredictionServicePackageName();
             mIncidentReportApproverPackage = getIncidentReportApproverPackageName();
+            mTelephonyPackages = getTelephonyPackageNames();
+            mWifiPackage = mContext.getString(R.string.config_wifiPackage);
 
             // Now that we know all of the shared libraries, update all clients to have
             // the correct library paths.
@@ -8467,7 +8354,7 @@
         }
         try (ParallelPackageParser parallelPackageParser = new ParallelPackageParser(
                 mSeparateProcesses, mOnlyCore, mMetrics, mCacheDir,
-                mParallelPackageParserCallback)) {
+                mPackageParserCallback)) {
             // Submit files for parsing in parallel
             int fileCount = 0;
             for (File file : files) {
@@ -10683,6 +10570,50 @@
         return changedAbiCodePath;
     }
 
+    /**
+     * Sets the enabled state of components configured through {@link SystemConfig}.
+     * This modifies the {@link PackageSetting} object.
+     **/
+    static void configurePackageComponents(PackageParser.Package pkg) {
+        final ArrayMap<String, Boolean> componentsEnabledStates = SystemConfig.getInstance()
+                .getComponentsEnabledStates(pkg.packageName);
+        if (componentsEnabledStates == null) {
+            return;
+        }
+
+        for (int i = pkg.activities.size() - 1; i >= 0; i--) {
+            final PackageParser.Activity component = pkg.activities.get(i);
+            final Boolean enabled = componentsEnabledStates.get(component.className);
+            if (enabled != null) {
+                component.info.enabled = enabled;
+            }
+        }
+
+        for (int i = pkg.receivers.size() - 1; i >= 0; i--) {
+            final PackageParser.Activity component = pkg.receivers.get(i);
+            final Boolean enabled = componentsEnabledStates.get(component.className);
+            if (enabled != null) {
+                component.info.enabled = enabled;
+            }
+        }
+
+        for (int i = pkg.providers.size() - 1; i >= 0; i--) {
+            final PackageParser.Provider component = pkg.providers.get(i);
+            final Boolean enabled = componentsEnabledStates.get(component.className);
+            if (enabled != null) {
+                component.info.enabled = enabled;
+            }
+        }
+
+        for (int i = pkg.services.size() - 1; i >= 0; i--) {
+            final PackageParser.Service component = pkg.services.get(i);
+            final Boolean enabled = componentsEnabledStates.get(component.className);
+            if (enabled != null) {
+                component.info.enabled = enabled;
+            }
+        }
+    }
+
 
     /**
      * Just scans the package without any side effects.
@@ -10850,6 +10781,10 @@
             pkg.applicationInfo.initForUser(UserHandle.USER_SYSTEM);
         }
 
+        if (pkg.isSystem()) {
+            configurePackageComponents(pkg);
+        }
+
         final String cpuAbiOverride = deriveAbiOverride(pkg.cpuAbiOverride, pkgSetting);
 
         if ((scanFlags & SCAN_NEW_INSTALL) == 0) {
@@ -16065,10 +16000,6 @@
      * will be used to scan and reconcile the package.
      */
     private static class PrepareResult {
-        public final int installReason;
-        public final String volumeUuid;
-        public final String installerPackageName;
-        public final UserHandle user;
         public final boolean replace;
         public final int scanFlags;
         public final int parseFlags;
@@ -16077,24 +16008,16 @@
         public final PackageParser.Package packageToScan;
         public final boolean clearCodeCache;
         public final boolean system;
-        /* The original package name if it was changed during an update, otherwise {@code null}. */
-        @Nullable
-        public final String renamedPackage;
         public final PackageFreezer freezer;
         public final PackageSetting originalPs;
         public final PackageSetting disabledPs;
         public final PackageSetting[] childPackageSettings;
 
-        private PrepareResult(int installReason, String volumeUuid,
-                String installerPackageName, UserHandle user, boolean replace, int scanFlags,
+        private PrepareResult(boolean replace, int scanFlags,
                 int parseFlags, PackageParser.Package existingPackage,
                 PackageParser.Package packageToScan, boolean clearCodeCache, boolean system,
-                String renamedPackage, PackageFreezer freezer, PackageSetting originalPs,
+                PackageFreezer freezer, PackageSetting originalPs,
                 PackageSetting disabledPs, PackageSetting[] childPackageSettings) {
-            this.installReason = installReason;
-            this.volumeUuid = volumeUuid;
-            this.installerPackageName = installerPackageName;
-            this.user = user;
             this.replace = replace;
             this.scanFlags = scanFlags;
             this.parseFlags = parseFlags;
@@ -16102,7 +16025,6 @@
             this.packageToScan = packageToScan;
             this.clearCodeCache = clearCodeCache;
             this.system = system;
-            this.renamedPackage = renamedPackage;
             this.freezer = freezer;
             this.originalPs = originalPs;
             this.disabledPs = disabledPs;
@@ -16142,8 +16064,6 @@
     private PrepareResult preparePackageLI(InstallArgs args, PackageInstalledInfo res)
             throws PrepareFailure {
         final int installFlags = args.installFlags;
-        final String installerPackageName = args.installerPackageName;
-        final String volumeUuid = args.volumeUuid;
         final File tmpPackageFile = new File(args.getCodePath());
         final boolean onExternal = args.volumeUuid != null;
         final boolean instantApp = ((installFlags & PackageManager.INSTALL_INSTANT_APP) != 0);
@@ -16568,14 +16488,12 @@
             final PackageParser.Package existingPackage;
             String renamedPackage = null;
             boolean sysPkg = false;
-            String targetVolumeUuid = volumeUuid;
             int targetScanFlags = scanFlags;
             int targetParseFlags = parseFlags;
             final PackageSetting ps;
             final PackageSetting disabledPs;
             final PackageSetting[] childPackages;
             if (replace) {
-                targetVolumeUuid = null;
                 if (pkg.applicationInfo.isStaticSharedLibrary()) {
                     // Static libs have a synthetic package name containing the version
                     // and cannot be updated as an update would get a new package name,
@@ -16827,9 +16745,8 @@
             // we're passing the freezer back to be closed in a later phase of install
             shouldCloseFreezerBeforeReturn = false;
 
-            return new PrepareResult(args.installReason, targetVolumeUuid, installerPackageName,
-                    args.user, replace, targetScanFlags, targetParseFlags, existingPackage, pkg,
-                    replace /* clearCodeCache */, sysPkg, renamedPackage, freezer,
+            return new PrepareResult(replace, targetScanFlags, targetParseFlags,
+                    existingPackage, pkg, replace /* clearCodeCache */, sysPkg, freezer,
                     ps, disabledPs, childPackages);
         } finally {
             if (shouldCloseFreezerBeforeReturn) {
@@ -19798,6 +19715,16 @@
     }
 
     @Override
+    public String[] getTelephonyPackageNames() {
+        String names = mContext.getString(R.string.config_telephonyPackages);
+        String[] telephonyPackageNames = null;
+        if (!TextUtils.isEmpty(names)) {
+            telephonyPackageNames = names.trim().split(",");
+        }
+        return telephonyPackageNames;
+    }
+
+    @Override
     public void setApplicationEnabledSetting(String appPackageName,
             int newState, int flags, int userId, String callingPackage) {
         if (!mUserManager.exists(userId)) return;
@@ -22902,34 +22829,38 @@
         }
 
         @Override
-        public String getKnownPackageName(int knownPackage, int userId) {
+        public @NonNull String[] getKnownPackageNames(int knownPackage, int userId) {
             switch(knownPackage) {
                 case PackageManagerInternal.PACKAGE_BROWSER:
-                    return mPermissionManager.getDefaultBrowser(userId);
+                    return new String[]{mPermissionManager.getDefaultBrowser(userId)};
                 case PackageManagerInternal.PACKAGE_INSTALLER:
-                    return mRequiredInstallerPackage;
+                    return new String[]{mRequiredInstallerPackage};
                 case PackageManagerInternal.PACKAGE_SETUP_WIZARD:
-                    return mSetupWizardPackage;
+                    return new String[]{mSetupWizardPackage};
                 case PackageManagerInternal.PACKAGE_SYSTEM:
-                    return "android";
+                    return new String[]{"android"};
                 case PackageManagerInternal.PACKAGE_VERIFIER:
-                    return mRequiredVerifierPackage;
+                    return new String[]{mRequiredVerifierPackage};
                 case PackageManagerInternal.PACKAGE_SYSTEM_TEXT_CLASSIFIER:
-                    return mSystemTextClassifierPackage;
+                    return new String[]{mSystemTextClassifierPackage};
                 case PackageManagerInternal.PACKAGE_PERMISSION_CONTROLLER:
-                    return mRequiredPermissionControllerPackage;
+                    return new String[]{mRequiredPermissionControllerPackage};
                 case PackageManagerInternal.PACKAGE_WELLBEING:
-                    return mWellbeingPackage;
+                    return new String[]{mWellbeingPackage};
                 case PackageManagerInternal.PACKAGE_DOCUMENTER:
-                    return mDocumenterPackage;
+                    return new String[]{mDocumenterPackage};
                 case PackageManagerInternal.PACKAGE_CONFIGURATOR:
-                    return mConfiguratorPackage;
+                    return new String[]{mConfiguratorPackage};
                 case PackageManagerInternal.PACKAGE_INCIDENT_REPORT_APPROVER:
-                    return mIncidentReportApproverPackage;
+                    return new String[]{mIncidentReportApproverPackage};
                 case PackageManagerInternal.PACKAGE_APP_PREDICTOR:
-                    return mAppPredictionServicePackage;
+                    return new String[]{mAppPredictionServicePackage};
+                case PackageManagerInternal.PACKAGE_TELEPHONY:
+                    return mTelephonyPackages;
+                case PackageManagerInternal.PACKAGE_WIFI:
+                    return new String[]{mWifiPackage};
             }
-            return null;
+            return ArrayUtils.emptyArray(String.class);
         }
 
         @Override
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 1c1c947..52a7c6e 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -468,7 +468,7 @@
      * @param pckg
      */
     private int displayPackageFilePath(String pckg, int userId) throws RemoteException {
-        PackageInfo info = mInterface.getPackageInfo(pckg, 0, userId);
+        PackageInfo info = mInterface.getPackageInfo(pckg, PackageManager.MATCH_APEX, userId);
         if (info != null && info.applicationInfo != null) {
             final PrintWriter pw = getOutPrintWriter();
             pw.print("package:");
diff --git a/services/core/java/com/android/server/pm/permission/BasePermission.java b/services/core/java/com/android/server/pm/permission/BasePermission.java
index 6d22faa..037912a 100644
--- a/services/core/java/com/android/server/pm/permission/BasePermission.java
+++ b/services/core/java/com/android/server/pm/permission/BasePermission.java
@@ -276,6 +276,12 @@
     public boolean isAppPredictor() {
         return (protectionLevel & PermissionInfo.PROTECTION_FLAG_APP_PREDICTOR) != 0;
     }
+    public boolean isTelephony() {
+        return (protectionLevel & PermissionInfo.PROTECTION_FLAG_TELEPHONY) != 0;
+    }
+    public boolean isWifi() {
+        return (protectionLevel & PermissionInfo.PROTECTION_FLAG_WIFI) != 0;
+    }
 
     public void transfer(@NonNull String origPackageName, @NonNull String newPackageName) {
         if (!origPackageName.equals(sourcePackageName)) {
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 793cdd2..f247037 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -437,17 +437,20 @@
 
         // Installer
         grantSystemFixedPermissionsToSystemPackage(
-                getKnownPackage(PackageManagerInternal.PACKAGE_INSTALLER, userId),
+                ArrayUtils.firstOrNull(getKnownPackages(
+                        PackageManagerInternal.PACKAGE_INSTALLER, userId)),
                 userId, STORAGE_PERMISSIONS);
 
         // Verifier
-        final String verifier = getKnownPackage(PackageManagerInternal.PACKAGE_VERIFIER, userId);
+        final String verifier = ArrayUtils.firstOrNull(getKnownPackages(
+                PackageManagerInternal.PACKAGE_VERIFIER, userId));
         grantSystemFixedPermissionsToSystemPackage(verifier, userId, STORAGE_PERMISSIONS);
         grantPermissionsToSystemPackage(verifier, userId, PHONE_PERMISSIONS, SMS_PERMISSIONS);
 
         // SetupWizard
         grantPermissionsToSystemPackage(
-                getKnownPackage(PackageManagerInternal.PACKAGE_SETUP_WIZARD, userId), userId,
+                ArrayUtils.firstOrNull(getKnownPackages(
+                        PackageManagerInternal.PACKAGE_SETUP_WIZARD, userId)), userId,
                 PHONE_PERMISSIONS, CONTACTS_PERMISSIONS, ALWAYS_LOCATION_PERMISSIONS,
                 CAMERA_PERMISSIONS);
 
@@ -596,7 +599,8 @@
                 userId, CONTACTS_PERMISSIONS, CALENDAR_PERMISSIONS);
 
         // Browser
-        String browserPackage = getKnownPackage(PackageManagerInternal.PACKAGE_BROWSER, userId);
+        String browserPackage = ArrayUtils.firstOrNull(getKnownPackages(
+                PackageManagerInternal.PACKAGE_BROWSER, userId));
         if (browserPackage == null) {
             browserPackage = getDefaultSystemHandlerActivityPackageForCategory(
                     Intent.CATEGORY_APP_BROWSER, userId);
@@ -761,8 +765,8 @@
         }
     }
 
-    private String getKnownPackage(int knownPkgId, int userId) {
-        return mServiceInternal.getKnownPackageName(knownPkgId, userId);
+    private @NonNull String[] getKnownPackages(int knownPkgId, int userId) {
+        return mServiceInternal.getKnownPackageNames(knownPkgId, userId);
     }
 
     private void grantDefaultPermissionsToDefaultSystemDialerApp(
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 89908f0..3f45b0b 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -3078,8 +3078,9 @@
                 }
             }
         }
-        final String systemPackageName = mPackageManagerInt.getKnownPackageName(
-                PackageManagerInternal.PACKAGE_SYSTEM, UserHandle.USER_SYSTEM);
+        // expect single system package
+        String systemPackageName = ArrayUtils.firstOrNull(mPackageManagerInt.getKnownPackageNames(
+                PackageManagerInternal.PACKAGE_SYSTEM, UserHandle.USER_SYSTEM));
         final PackageParser.Package systemPackage =
                 mPackageManagerInt.getPackage(systemPackageName);
 
@@ -3195,18 +3196,19 @@
             //                  need a separate flag anymore. Hence we need to check which
             //                  permissions are needed by the permission controller
             if (!allowed && bp.isInstaller()
-                    && (pkg.packageName.equals(mPackageManagerInt.getKnownPackageName(
-                            PackageManagerInternal.PACKAGE_INSTALLER, UserHandle.USER_SYSTEM))
-                    || pkg.packageName.equals(mPackageManagerInt.getKnownPackageName(
+                    && ArrayUtils.contains(mPackageManagerInt.getKnownPackageNames(
+                            PackageManagerInternal.PACKAGE_INSTALLER, UserHandle.USER_SYSTEM),
+                    pkg.packageName) || ArrayUtils.contains(mPackageManagerInt.getKnownPackageNames(
                             PackageManagerInternal.PACKAGE_PERMISSION_CONTROLLER,
-                            UserHandle.USER_SYSTEM)))) {
+                    UserHandle.USER_SYSTEM), pkg.packageName)) {
                 // If this permission is to be granted to the system installer and
                 // this app is an installer, then it gets the permission.
                 allowed = true;
             }
             if (!allowed && bp.isVerifier()
-                    && pkg.packageName.equals(mPackageManagerInt.getKnownPackageName(
-                            PackageManagerInternal.PACKAGE_VERIFIER, UserHandle.USER_SYSTEM))) {
+                    && ArrayUtils.contains(mPackageManagerInt.getKnownPackageNames(
+                            PackageManagerInternal.PACKAGE_VERIFIER, UserHandle.USER_SYSTEM),
+                    pkg.packageName)) {
                 // If this permission is to be granted to the system verifier and
                 // this app is a verifier, then it gets the permission.
                 allowed = true;
@@ -3222,53 +3224,71 @@
                 allowed = origPermissions.hasInstallPermission(perm);
             }
             if (!allowed && bp.isSetup()
-                    && pkg.packageName.equals(mPackageManagerInt.getKnownPackageName(
-                            PackageManagerInternal.PACKAGE_SETUP_WIZARD, UserHandle.USER_SYSTEM))) {
+                    && ArrayUtils.contains(mPackageManagerInt.getKnownPackageNames(
+                            PackageManagerInternal.PACKAGE_SETUP_WIZARD, UserHandle.USER_SYSTEM),
+                    pkg.packageName)) {
                 // If this permission is to be granted to the system setup wizard and
                 // this app is a setup wizard, then it gets the permission.
                 allowed = true;
             }
             if (!allowed && bp.isSystemTextClassifier()
-                    && pkg.packageName.equals(mPackageManagerInt.getKnownPackageName(
+                    && ArrayUtils.contains(mPackageManagerInt.getKnownPackageNames(
                             PackageManagerInternal.PACKAGE_SYSTEM_TEXT_CLASSIFIER,
-                            UserHandle.USER_SYSTEM))) {
+                    UserHandle.USER_SYSTEM), pkg.packageName)) {
                 // Special permissions for the system default text classifier.
                 allowed = true;
             }
             if (!allowed && bp.isConfigurator()
-                    && pkg.packageName.equals(mPackageManagerInt.getKnownPackageName(
-                    PackageManagerInternal.PACKAGE_CONFIGURATOR,
-                    UserHandle.USER_SYSTEM))) {
+                    && ArrayUtils.contains(mPackageManagerInt.getKnownPackageNames(
+                            PackageManagerInternal.PACKAGE_CONFIGURATOR,
+                    UserHandle.USER_SYSTEM), pkg.packageName)) {
                 // Special permissions for the device configurator.
                 allowed = true;
             }
             if (!allowed && bp.isWellbeing()
-                    && pkg.packageName.equals(mPackageManagerInt.getKnownPackageName(
-                    PackageManagerInternal.PACKAGE_WELLBEING, UserHandle.USER_SYSTEM))) {
+                    && ArrayUtils.contains(mPackageManagerInt.getKnownPackageNames(
+                            PackageManagerInternal.PACKAGE_WELLBEING, UserHandle.USER_SYSTEM),
+                    pkg.packageName)) {
                 // Special permission granted only to the OEM specified wellbeing app
                 allowed = true;
             }
             if (!allowed && bp.isDocumenter()
-                    && pkg.packageName.equals(mPackageManagerInt.getKnownPackageName(
-                            PackageManagerInternal.PACKAGE_DOCUMENTER, UserHandle.USER_SYSTEM))) {
+                    && ArrayUtils.contains(mPackageManagerInt.getKnownPackageNames(
+                            PackageManagerInternal.PACKAGE_DOCUMENTER, UserHandle.USER_SYSTEM),
+                    pkg.packageName)) {
                 // If this permission is to be granted to the documenter and
                 // this app is the documenter, then it gets the permission.
                 allowed = true;
             }
             if (!allowed && bp.isIncidentReportApprover()
-                    && pkg.packageName.equals(mPackageManagerInt.getKnownPackageName(
+                    && ArrayUtils.contains(mPackageManagerInt.getKnownPackageNames(
                             PackageManagerInternal.PACKAGE_INCIDENT_REPORT_APPROVER,
-                            UserHandle.USER_SYSTEM))) {
+                    UserHandle.USER_SYSTEM), pkg.packageName)) {
                 // If this permission is to be granted to the incident report approver and
                 // this app is the incident report approver, then it gets the permission.
                 allowed = true;
             }
             if (!allowed && bp.isAppPredictor()
-                    && pkg.packageName.equals(mPackageManagerInt.getKnownPackageName(
-                        PackageManagerInternal.PACKAGE_APP_PREDICTOR, UserHandle.USER_SYSTEM))) {
+                    && ArrayUtils.contains(mPackageManagerInt.getKnownPackageNames(
+                            PackageManagerInternal.PACKAGE_APP_PREDICTOR, UserHandle.USER_SYSTEM),
+                    pkg.packageName)) {
                 // Special permissions for the system app predictor.
                 allowed = true;
             }
+            if (!allowed && bp.isTelephony()
+                    && ArrayUtils.contains(mPackageManagerInt.getKnownPackageNames(
+                        PackageManagerInternal.PACKAGE_TELEPHONY, UserHandle.USER_SYSTEM),
+                    pkg.packageName)) {
+                // Special permissions for the system telephony apps.
+                allowed = true;
+            }
+            if (!allowed && bp.isWifi()
+                    && ArrayUtils.contains(mPackageManagerInt.getKnownPackageNames(
+                        PackageManagerInternal.PACKAGE_WIFI, UserHandle.USER_SYSTEM),
+                    pkg.packageName)) {
+                // Special permissions for the system wifi.
+                allowed = true;
+            }
         }
         return allowed;
     }
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index c851cc6..2593c38 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -47,8 +47,8 @@
 import static android.view.WindowManager.LayoutParams.LAST_SYSTEM_WINDOW;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD;
-import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_SYSTEM_ERROR;
+import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
 import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
@@ -2189,10 +2189,10 @@
             default:
                 // These are the windows that by default are shown only to the user that created
                 // them. If this needs to be overridden, set
-                // {@link WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS} in
+                // {@link WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS} in
                 // {@link WindowManager.LayoutParams}. Note that permission
                 // {@link android.Manifest.permission.INTERNAL_SYSTEM_WINDOW} is required as well.
-                if ((attrs.privateFlags & PRIVATE_FLAG_SHOW_FOR_ALL_USERS) == 0) {
+                if ((attrs.privateFlags & SYSTEM_FLAG_SHOW_FOR_ALL_USERS) == 0) {
                     return true;
                 }
                 break;
@@ -2446,7 +2446,7 @@
                     com.android.internal.R.styleable.Window_windowAnimationStyle, 0);
             params.privateFlags |=
                     WindowManager.LayoutParams.PRIVATE_FLAG_FAKE_HARDWARE_ACCELERATED;
-            params.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
+            params.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
 
             if (!compatInfo.supportsScreen()) {
                 params.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW;
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index 3439d38..65bb2342 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -29,6 +29,7 @@
 import android.hardware.biometrics.IBiometricServiceReceiverInternal;
 import android.hardware.display.DisplayManager;
 import android.hardware.display.DisplayManager.DisplayListener;
+import android.net.Uri;
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.Handler;
@@ -1334,6 +1335,18 @@
     }
 
     @Override
+    public void grantInlineReplyUriPermission(String key, Uri uri) {
+        enforceStatusBarService();
+        int callingUid = Binder.getCallingUid();
+        long identity = Binder.clearCallingIdentity();
+        try {
+            mNotificationDelegate.grantInlineReplyUriPermission(key, uri, callingUid);
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
+    @Override
     public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
             String[] args, ShellCallback callback, ResultReceiver resultReceiver) {
         (new StatusBarShellCommand(this, mContext)).exec(
diff --git a/services/core/java/com/android/server/wm/ActivityDisplay.java b/services/core/java/com/android/server/wm/ActivityDisplay.java
index 9d9a37c..673366f 100644
--- a/services/core/java/com/android/server/wm/ActivityDisplay.java
+++ b/services/core/java/com/android/server/wm/ActivityDisplay.java
@@ -1430,9 +1430,8 @@
                 continue;
             }
 
-            final ArrayList<ActivityRecord> activities = task.mActivities;
-            for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
-                final ActivityRecord r = activities.get(activityNdx);
+            for (int activityNdx = task.getChildCount() - 1; activityNdx >= 0; --activityNdx) {
+                final ActivityRecord r = task.getChildAt(activityNdx);
                 if (r.isActivityTypeHome()
                         && ((userId == UserHandle.USER_ALL) || (r.mUserId == userId))) {
                     return r;
diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLaunchObserver.java b/services/core/java/com/android/server/wm/ActivityMetricsLaunchObserver.java
index eff0f75..c6b17e2 100644
--- a/services/core/java/com/android/server/wm/ActivityMetricsLaunchObserver.java
+++ b/services/core/java/com/android/server/wm/ActivityMetricsLaunchObserver.java
@@ -39,8 +39,13 @@
  * 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.
+ * It must then transition to either {@code CANCELLED} with {@link #onActivityLaunchCancelled},
+ * which is a terminal state or into {@code FINISHED} with {@link #onActivityLaunchFinished}.
+ *
+ * The {@code FINISHED} with {@link #onActivityLaunchFinished} then may transition to
+ * {@code FULLY_DRAWN} with {@link #onReportFullyDrawn}, which is a terminal state.
+ * Note this transition may not happen if the reportFullyDrawn event is not receivied,
+ * in which case {@code FINISHED} is terminal.
  *
  * Note that the {@code ActivityRecordProto} 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
@@ -51,15 +56,15 @@
  * 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 ║
- *        ╚════════════════╝     ╚═══════════════════════════╝
+ *        ┌⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯┐     ┌⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯┐     ┌--------------------------┐
+ *    ╴╴▶  INTENT_STARTED    ──▶      ACTIVITY_LAUNCHED        ──▶   ACTIVITY_LAUNCH_FINISHED
+ *        └⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯┘     └⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯┘     └--------------------------┘
+ *          :                      :                                 :
+ *          :                      :                                 :
+ *          ▼                      ▼                                 ▼
+ *        ╔════════════════╗     ╔═══════════════════════════╗     ╔═══════════════════════════╗
+ *        ║ INTENT_FAILED  ║     ║ ACTIVITY_LAUNCH_CANCELLED ║     ║    REPORT_FULLY_DRAWN     ║
+ *        ╚════════════════╝     ╚═══════════════════════════╝     ╚═══════════════════════════╝
  * </pre>
  */
 public interface ActivityMetricsLaunchObserver {
@@ -111,7 +116,7 @@
      * Multiple calls to this method cannot occur without first terminating the current
      * launch sequence.
      */
-    public void onIntentStarted(@NonNull Intent intent);
+    public void onIntentStarted(@NonNull Intent intent, long timestampNanos);
 
     /**
      * Notifies the observer that the current launch sequence has failed to launch an activity.
@@ -177,6 +182,9 @@
      * 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
+     * @param timestampNanos the timestamp of ActivityLaunchFinished event in nanoseconds.
+     *        To compute the TotalTime duration, deduct the timestamp {@link #onIntentStarted}
+     *        from {@code timestampNanos}.
      *
      * Multiple calls to this method cannot occur without first terminating the current
      * launch sequence.
@@ -186,5 +194,22 @@
      *          and only the latest activity that was top-most during first-frame drawn
      *          is reported here.
      */
-    public void onActivityLaunchFinished(@NonNull @ActivityRecordProto byte[] finalActivity);
+    public void onActivityLaunchFinished(@NonNull @ActivityRecordProto byte[] finalActivity,
+                                         long timestampNanos);
+
+    /**
+     * Notifies the observer that the application self-reported itself as being fully drawn.
+     *
+     * @param activity the activity that triggers the ReportFullyDrawn event.
+     * @param timestampNanos the timestamp of ReportFullyDrawn event in nanoseconds.
+     *        To compute the duration, deduct the deduct the timestamp {@link #onIntentStarted}
+     *        from {@code timestampNanos}.
+     *
+     * @apiNote The behavior of ReportFullyDrawn mostly depends on the app.
+     *          It is used as an accurate estimate of meanfully app startup time.
+     *          This event may be missing for many apps.
+     */
+    public void onReportFullyDrawn(@NonNull @ActivityRecordProto byte[] activity,
+        long timestampNanos);
+
 }
diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
index a0a2967..e6c6b12e 100644
--- a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
+++ b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
@@ -92,6 +92,7 @@
 import com.android.internal.os.BackgroundThread;
 import com.android.internal.os.SomeArgs;
 import com.android.server.LocalServices;
+import java.util.concurrent.TimeUnit;
 
 /**
  * Listens to activity launches, transitions, visibility changes and window drawn callbacks to
@@ -132,8 +133,8 @@
 
     // 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 long mCurrentTransitionStartTimeNs = INVALID_START_TIME;
+    private long mLastTransitionStartTimeNs = INVALID_START_TIME;
 
     private int mCurrentTransitionDeviceUptime;
     private int mCurrentTransitionDelayMs;
@@ -326,12 +327,12 @@
                                       intent));
         }
 
-        if (mCurrentTransitionStartTime == INVALID_START_TIME) {
+        if (mCurrentTransitionStartTimeNs == INVALID_START_TIME) {
 
-            mCurrentTransitionStartTime = SystemClock.uptimeMillis();
-            mLastTransitionStartTime = mCurrentTransitionStartTime;
+            mCurrentTransitionStartTimeNs = SystemClock.elapsedRealtimeNanos();
+            mLastTransitionStartTimeNs = mCurrentTransitionStartTimeNs;
 
-            launchObserverNotifyIntentStarted(intent);
+            launchObserverNotifyIntentStarted(intent, mCurrentTransitionStartTimeNs);
         }
     }
 
@@ -382,14 +383,15 @@
                 ? launchedActivity.getWindowingMode()
                 : WINDOWING_MODE_UNDEFINED;
         final WindowingModeTransitionInfo info = mWindowingModeTransitionInfo.get(windowingMode);
-        if (mCurrentTransitionStartTime == INVALID_START_TIME) {
+        if (mCurrentTransitionStartTimeNs == INVALID_START_TIME) {
             // No transition is active ignore this launch.
             return;
         }
 
         if (launchedActivity != null && launchedActivity.mDrawn) {
             // Launched activity is already visible. We cannot measure windows drawn delay.
-            reset(true /* abort */, info, "launched activity already visible");
+            reset(true /* abort */, info, "launched activity already visible",
+                0L /* timestampNs */);
             return;
         }
 
@@ -407,7 +409,8 @@
         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");
+            reset(true /* abort */, info, "failed to launch or not a process switch",
+                0L /* timestampNs */);
             return;
         } else if (otherWindowModesLaunching) {
             // Don't log this windowing mode but continue with the other windowing modes.
@@ -441,19 +444,20 @@
      * Notifies the tracker that all windows of the app have been drawn.
      */
     WindowingModeTransitionInfoSnapshot notifyWindowsDrawn(@WindowingMode int windowingMode,
-                                                           long timestamp) {
+                                                           long timestampNs) {
         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.windowsDrawnDelayMs = calculateDelay(timestampNs);
         info.loggedWindowsDrawn = true;
         final WindowingModeTransitionInfoSnapshot infoSnapshot =
                 new WindowingModeTransitionInfoSnapshot(info);
         if (allWindowsDrawn() && mLoggedTransitionStarting) {
-            reset(false /* abort */, info, "notifyWindowsDrawn - all windows drawn");
+            reset(false /* abort */, info, "notifyWindowsDrawn - all windows drawn",
+                timestampNs /* timestampNs */);
         }
         return infoSnapshot;
     }
@@ -476,7 +480,7 @@
      * @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) {
+    void notifyTransitionStarting(SparseIntArray windowingModeToReason, long timestampNs) {
         if (!isAnyTransitionActive() || mLoggedTransitionStarting) {
             // Ignore calls to this made after a reset and prior to notifyActivityLaunching.
 
@@ -484,7 +488,7 @@
             return;
         }
         if (DEBUG_METRICS) Slog.i(TAG, "notifyTransitionStarting");
-        mCurrentTransitionDelayMs = calculateDelay(timestamp);
+        mCurrentTransitionDelayMs = calculateDelay(timestampNs);
         mLoggedTransitionStarting = true;
 
         WindowingModeTransitionInfo foundInfo = null;
@@ -501,7 +505,8 @@
         if (allWindowsDrawn()) {
             // abort metrics collection if we cannot find a matching transition.
             final boolean abortMetrics = foundInfo == null;
-            reset(abortMetrics, foundInfo, "notifyTransitionStarting - all windows drawn");
+            reset(abortMetrics, foundInfo, "notifyTransitionStarting - all windows drawn",
+                timestampNs /* timestampNs */);
         }
     }
 
@@ -527,8 +532,8 @@
     }
 
     private boolean hasVisibleNonFinishingActivity(TaskRecord t) {
-        for (int i = t.mActivities.size() - 1; i >= 0; --i) {
-            final ActivityRecord r = t.mActivities.get(i);
+        for (int i = t.getChildCount() - 1; i >= 0; --i) {
+            final ActivityRecord r = t.getChildAt(i);
             if (r.visible && !r.finishing) {
                 return true;
             }
@@ -567,7 +572,8 @@
             logAppTransitionCancel(info);
             mWindowingModeTransitionInfo.remove(r.getWindowingMode());
             if (mWindowingModeTransitionInfo.size() == 0) {
-                reset(true /* abort */, info, "notifyVisibilityChanged to invisible");
+                reset(true /* abort */, info, "notifyVisibilityChanged to invisible",
+                    0L /* timestampNs */);
             }
         }
     }
@@ -598,12 +604,16 @@
     }
 
     private boolean isAnyTransitionActive() {
-        return mCurrentTransitionStartTime != INVALID_START_TIME
+        return mCurrentTransitionStartTimeNs != 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);
+    private void reset(boolean abort, WindowingModeTransitionInfo info, String cause,
+        long timestampNs) {
+        if (DEBUG_METRICS) {
+            Slog.i(TAG,
+                "reset abort=" + abort + ",cause=" + cause + ",timestamp=" + timestampNs);
+        }
         if (!abort && isAnyTransitionActive()) {
             logAppTransitionMultiEvents();
         }
@@ -615,13 +625,13 @@
             if (abort) {
                 launchObserverNotifyActivityLaunchCancelled(info);
             } else {
-                launchObserverNotifyActivityLaunchFinished(info);
+                launchObserverNotifyActivityLaunchFinished(info, timestampNs);
             }
         } else {
             launchObserverNotifyIntentFailed();
         }
 
-        mCurrentTransitionStartTime = INVALID_START_TIME;
+        mCurrentTransitionStartTimeNs = INVALID_START_TIME;
         mCurrentTransitionDelayMs = INVALID_DELAY;
         mLoggedTransitionStarting = false;
         mWindowingModeTransitionInfo.clear();
@@ -629,12 +639,14 @@
 
     private int calculateCurrentDelay() {
         // Shouldn't take more than 25 days to launch an app, so int is fine here.
-        return (int) (SystemClock.uptimeMillis() - mCurrentTransitionStartTime);
+        return (int) TimeUnit.NANOSECONDS
+            .toMillis(SystemClock.elapsedRealtimeNanos() - mCurrentTransitionStartTimeNs);
     }
 
-    private int calculateDelay(long timestamp) {
+    private int calculateDelay(long timestampNs) {
         // Shouldn't take more than 25 days to launch an app, so int is fine here.
-        return (int) (timestamp - mCurrentTransitionStartTime);
+        return (int) TimeUnit.NANOSECONDS.toMillis(timestampNs -
+            mCurrentTransitionStartTimeNs);
     }
 
     private void logAppTransitionCancel(WindowingModeTransitionInfo info) {
@@ -679,7 +691,7 @@
             // 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);
+                     new WindowingModeTransitionInfoSnapshot(info);
             final int currentTransitionDeviceUptime = mCurrentTransitionDeviceUptime;
             final int currentTransitionDelayMs = mCurrentTransitionDelayMs;
             BackgroundThread.getHandler().post(() -> logAppTransition(
@@ -811,7 +823,9 @@
         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;
+        long currentTimestampNs = SystemClock.elapsedRealtimeNanos();
+        long startupTimeMs =
+            TimeUnit.NANOSECONDS.toMillis(currentTimestampNs - mLastTransitionStartTimeNs);
         builder.addTaggedData(APP_TRANSITION_REPORTED_DRAWN_MS, startupTimeMs);
         builder.setType(restoredFromBundle
                 ? TYPE_TRANSITION_REPORTED_DRAWN_WITH_BUNDLE
@@ -837,6 +851,10 @@
         final WindowingModeTransitionInfoSnapshot infoSnapshot =
                 new WindowingModeTransitionInfoSnapshot(info, r, (int) startupTimeMs);
         BackgroundThread.getHandler().post(() -> logAppFullyDrawn(infoSnapshot));
+
+        // Notify reportFullyDrawn event.
+        launchObserverNotifyReportFullyDrawn(r, currentTimestampNs);
+
         return infoSnapshot;
     }
 
@@ -1006,12 +1024,12 @@
     }
 
     /** Notify the {@link ActivityMetricsLaunchObserver} that a new launch sequence has begun. */
-    private void launchObserverNotifyIntentStarted(Intent intent) {
+    private void launchObserverNotifyIntentStarted(Intent intent, long timestampNs) {
         Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
                 "MetricsLogger:launchObserverNotifyIntentStarted");
 
         // Beginning a launch is timing sensitive and so should be observed as soon as possible.
-        mLaunchObserver.onIntentStarted(intent);
+        mLaunchObserver.onIntentStarted(intent, timestampNs);
 
         Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
     }
@@ -1049,6 +1067,16 @@
     }
 
     /**
+     * Notifies the {@link ActivityMetricsLaunchObserver} the reportFullDrawn event.
+     */
+    private void launchObserverNotifyReportFullyDrawn(ActivityRecord r, long timestampNs) {
+        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
+            "MetricsLogger:launchObserverNotifyReportFullyDrawn");
+        mLaunchObserver.onReportFullyDrawn(convertActivityRecordToProto(r), timestampNs);
+        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+    }
+
+    /**
      * Notify the {@link ActivityMetricsLaunchObserver} that the current launch sequence is
      * cancelled.
      */
@@ -1068,12 +1096,14 @@
      * Notify the {@link ActivityMetricsLaunchObserver} that the current launch sequence's activity
      * has fully finished (successfully).
      */
-    private void launchObserverNotifyActivityLaunchFinished(WindowingModeTransitionInfo info) {
+    private void launchObserverNotifyActivityLaunchFinished(WindowingModeTransitionInfo info,
+        long timestampNs) {
         Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
                 "MetricsLogger:launchObserverNotifyActivityLaunchFinished");
 
-        mLaunchObserver.onActivityLaunchFinished(
-                convertActivityRecordToProto(info.launchedActivity));
+        mLaunchObserver
+            .onActivityLaunchFinished(convertActivityRecordToProto(info.launchedActivity),
+                timestampNs);
 
         Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
     }
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 4f52d9d..76a551f 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -144,10 +144,10 @@
 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.wm.ActivityTaskManagerService.getInputDispatchingTimeoutLocked;
 import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_ADD_REMOVE;
 import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_ORIENTATION;
 import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_STARTING_WINDOW;
-import static com.android.server.wm.ActivityTaskManagerService.getInputDispatchingTimeoutLocked;
 import static com.android.server.wm.TaskPersister.DEBUG;
 import static com.android.server.wm.TaskPersister.IMAGE_EXTENSION;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
@@ -318,6 +318,7 @@
     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
+    Intent mLastNewIntent;  // the last new intent we delivered to client
     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
@@ -1253,7 +1254,7 @@
 
     boolean setOccludesParent(boolean occludesParent) {
         final boolean changed = super.setOccludesParent(occludesParent);
-        if (changed) {
+        if (changed && task != null) {
             if (!occludesParent) {
                 getActivityStack().convertActivityToTranslucent(this);
             }
@@ -1577,12 +1578,12 @@
                     task.mTaskId, shortComponentName, reason);
             final ArrayList<ActivityRecord> activities = task.mActivities;
             final int index = activities.indexOf(this);
-            if (index < (activities.size() - 1)) {
+            if (index < (task.getChildCount() - 1)) {
                 if ((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.
-                    final ActivityRecord next = activities.get(index + 1);
+                    final ActivityRecord next = task.getChildAt(index + 1);
                     next.intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
                 }
             }
@@ -2788,12 +2789,12 @@
         if (positionInTask == -1) {
             throw new IllegalStateException("Activity not found in its task");
         }
-        if (positionInTask == task.mActivities.size() - 1) {
+        if (positionInTask == task.getChildCount() - 1) {
             // It's the topmost activity in the task - should become resumed 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);
+        final ActivityRecord activityAbove = task.getChildAt(positionInTask + 1);
         if (activityAbove.finishing && results == null) {
             // We will only allow making active if activity above wasn't launched for result.
             // Otherwise it will cause this activity to resume before getting result.
@@ -2844,11 +2845,14 @@
         }
         idle = false;
         results = null;
+        if (newIntents != null && newIntents.size() > 0) {
+            mLastNewIntent = newIntents.get(newIntents.size() - 1);
+        }
         newIntents = null;
         stopped = false;
 
         if (isActivityTypeHome()) {
-            mStackSupervisor.updateHomeProcess(task.mActivities.get(0).app);
+            mStackSupervisor.updateHomeProcess(task.getChildAt(0).app);
         }
 
         if (nowVisible) {
@@ -3102,7 +3106,7 @@
 
     void reportFullyDrawnLocked(boolean restoredFromBundle) {
         final WindowingModeTransitionInfoSnapshot info = mStackSupervisor
-                .getActivityMetricsLogger().logAppTransitionReportedDrawn(this, restoredFromBundle);
+            .getActivityMetricsLogger().logAppTransitionReportedDrawn(this, restoredFromBundle);
         if (info != null) {
             mStackSupervisor.reportActivityLaunchedLocked(false /* timeout */, this,
                     info.windowsFullyDrawnDelayMs, info.getLaunchState());
@@ -3110,13 +3114,13 @@
     }
 
     /** Called when the windows associated app window container are drawn. */
-    void onWindowsDrawn(boolean drawn, long timestamp) {
+    void onWindowsDrawn(boolean drawn, long timestampNs) {
         mDrawn = drawn;
         if (!drawn) {
             return;
         }
         final WindowingModeTransitionInfoSnapshot info = mStackSupervisor
-                .getActivityMetricsLogger().notifyWindowsDrawn(getWindowingMode(), timestamp);
+            .getActivityMetricsLogger().notifyWindowsDrawn(getWindowingMode(), timestampNs);
         final int windowsDrawnDelayMs = info != null ? info.windowsDrawnDelayMs : INVALID_DELAY;
         final @LaunchState int launchState = info != null ? info.getLaunchState() : -1;
         mStackSupervisor.reportActivityLaunchedLocked(false /* timeout */, this,
@@ -3542,7 +3546,7 @@
             super.resolveOverrideConfiguration(newParentConfiguration);
             // If the activity has override bounds, the relative configuration (e.g. screen size,
             // layout) needs to be resolved according to the bounds.
-            if (!matchParentBounds()) {
+            if (task != null && !matchParentBounds()) {
                 task.computeConfigResourceOverrides(getResolvedOverrideConfiguration(),
                         newParentConfiguration);
             }
@@ -4198,12 +4202,19 @@
             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.
+        // Restrict task snapshot starting window to launcher start, or is same as the last
+        // delivered intent, 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)) {
+            if (intent == null || ActivityRecord.isMainIntent(intent)) {
+                continue;
+            }
+
+            final boolean sameIntent = mLastNewIntent != null ? mLastNewIntent.filterEquals(intent)
+                    : this.intent.filterEquals(intent);
+            if (!sameIntent || intent.getExtras() != null) {
                 return false;
             }
         }
diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java
index 5959254..41c1e4e 100644
--- a/services/core/java/com/android/server/wm/ActivityStack.java
+++ b/services/core/java/com/android/server/wm/ActivityStack.java
@@ -33,7 +33,7 @@
 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.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
+import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
 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;
@@ -488,7 +488,7 @@
     int numActivities() {
         int count = 0;
         for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
-            count += mTaskHistory.get(taskNdx).mActivities.size();
+            count += mTaskHistory.get(taskNdx).getChildCount();
         }
         return count;
     }
@@ -1077,9 +1077,8 @@
     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);
+            for (int activityNdx = task.getChildCount() - 1; activityNdx >= 0; --activityNdx) {
+                final ActivityRecord r = task.getChildAt(activityNdx);
                 if (!r.finishing && !r.mTaskOverlay) {
                     return r;
                 }
@@ -1091,9 +1090,8 @@
     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);
+            for (int activityNdx = task.getChildCount() - 1; activityNdx >= 0; --activityNdx) {
+                final ActivityRecord r = task.getChildAt(activityNdx);
                 if (!r.finishing && !r.delayedResume && r != notTop && r.okToShowLocked()) {
                     return r;
                 }
@@ -1117,9 +1115,8 @@
             if (task.mTaskId == taskId) {
                 continue;
             }
-            ArrayList<ActivityRecord> activities = task.mActivities;
-            for (int i = activities.size() - 1; i >= 0; --i) {
-                final ActivityRecord r = activities.get(i);
+            for (int activityNdx = task.getChildCount() - 1; activityNdx >= 0; --activityNdx) {
+                final ActivityRecord r = task.getChildAt(activityNdx);
                 // Note: the taskId check depends on real taskId fields being non-zero
                 if (!r.finishing && (token != r.appToken) && r.okToShowLocked()) {
                     return r;
@@ -1431,10 +1428,8 @@
 
         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);
+            for (int activityNdx = task.getChildCount() - 1; activityNdx >= 0; --activityNdx) {
+                final ActivityRecord r = task.getChildAt(activityNdx);
                 if (!r.okToShowLocked()) {
                     continue;
                 }
@@ -1500,9 +1495,10 @@
     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);
+            final TaskRecord task = mTaskHistory.get(taskNdx);
+            for (int activityNdx = task.getChildCount() - 1; activityNdx >= 0; --activityNdx) {
+                final ActivityRecord r = task.getChildAt(activityNdx);
+                r.setSleeping(false);
             }
         }
         if (mPausingActivity != null) {
@@ -1516,9 +1512,9 @@
         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);
+            final TaskRecord task = mTaskHistory.get(taskNdx);
+            for (int activityNdx = task.getChildCount() - 1; activityNdx >= 0; --activityNdx) {
+                final ActivityRecord ar = task.getChildAt(activityNdx);
 
                 if ((userId == ar.mUserId) && packageName.equals(ar.packageName)) {
                     ar.updateApplicationInfo(aInfo);
@@ -1594,9 +1590,9 @@
         // 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);
+            final TaskRecord task = mTaskHistory.get(taskNdx);
+            for (int activityNdx = task.getChildCount() - 1; activityNdx >= 0; --activityNdx) {
+                final ActivityRecord r = task.getChildAt(activityNdx);
                 if (r.isState(STARTED, STOPPING, STOPPED, PAUSED, PAUSING)) {
                     r.setSleeping(true);
                 }
@@ -1880,9 +1876,8 @@
         }
         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);
+            for (int activityNdx = task.getChildCount() - 1; activityNdx >= 0; --activityNdx) {
+                final ActivityRecord r = task.getChildAt(activityNdx);
 
                 if (r.finishing) {
                     // We don't factor in finishing activities when determining translucency since
@@ -2113,9 +2108,8 @@
                     && top != null && !top.mLaunchTaskBehind;
             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);
+                for (int activityNdx = task.getChildCount() - 1; activityNdx >= 0; --activityNdx) {
+                    final ActivityRecord r = task.getChildAt(activityNdx);
                     final boolean isTop = r == top;
                     if (aboveTop && !isTop) {
                         continue;
@@ -2363,9 +2357,8 @@
     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);
+            for (int activityNdx = task.getChildCount() - 1; activityNdx >= 0; --activityNdx) {
+                final ActivityRecord r = task.getChildAt(activityNdx);
                 if ( r.appTimeTracker != except) {
                     r.appTimeTracker = null;
                 }
@@ -2423,9 +2416,9 @@
         }
 
         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);
+            final TaskRecord task = mTaskHistory.get(taskNdx);
+            for (int activityNdx = task.getChildCount() - 1; activityNdx >= 0; --activityNdx) {
+                final ActivityRecord r = task.getChildAt(activityNdx);
                 if (aboveTop) {
                     if (r == topActivity) {
                         aboveTop = false;
@@ -3210,14 +3203,13 @@
 
         // 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 numActivities = task.getChildCount();
         int lastActivityNdx = task.findRootIndex(true /* effectiveRoot */);
         if (lastActivityNdx == -1) {
             lastActivityNdx = 0;
         }
         for (int i = numActivities - 1; i > lastActivityNdx; --i) {
-            ActivityRecord target = activities.get(i);
+            ActivityRecord target = task.getChildAt(i);
             // TODO: Why is this needed? Looks like we're breaking the loop before we reach the root
             if (target.isRootOfTask()) break;
 
@@ -3257,7 +3249,7 @@
                 final TaskRecord targetTask;
                 final ActivityRecord bottom =
                         !mTaskHistory.isEmpty() && !mTaskHistory.get(0).mActivities.isEmpty() ?
-                                mTaskHistory.get(0).mActivities.get(0) : null;
+                                mTaskHistory.get(0).getChildAt(0) : null;
                 if (bottom != null && target.taskAffinity.equals(bottom.getTaskRecord().affinity)) {
                     // If the activity currently at the bottom has the
                     // same task affinity as the one we are moving,
@@ -3279,7 +3271,7 @@
                 boolean noOptions = canMoveOptions;
                 final int start = replyChainEnd < 0 ? i : replyChainEnd;
                 for (int srcPos = start; srcPos >= i; --srcPos) {
-                    final ActivityRecord p = activities.get(srcPos);
+                    final ActivityRecord p = task.getChildAt(srcPos);
                     if (p.finishing) {
                         continue;
                     }
@@ -3312,7 +3304,7 @@
                     // 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;
+                    end = task.getChildCount() - 1;
                 } else if (replyChainEnd < 0) {
                     end = i;
                 } else {
@@ -3320,7 +3312,7 @@
                 }
                 boolean noOptions = canMoveOptions;
                 for (int srcPos = i; srcPos <= end; srcPos++) {
-                    ActivityRecord p = activities.get(srcPos);
+                    ActivityRecord p = task.getChildAt(srcPos);
                     if (p.finishing) {
                         continue;
                     }
@@ -3389,8 +3381,7 @@
         int replyChainEnd = -1;
         final String taskAffinity = task.affinity;
 
-        final ArrayList<ActivityRecord> activities = affinityTask.mActivities;
-        final int numActivities = activities.size();
+        final int numActivities = task.getChildCount();
 
         // Do not operate on or below the effective root Activity.
         int lastActivityNdx = affinityTask.findRootIndex(true /* effectiveRoot */);
@@ -3398,7 +3389,7 @@
             lastActivityNdx = 0;
         }
         for (int i = numActivities - 1; i > lastActivityNdx; --i) {
-            ActivityRecord target = activities.get(i);
+            ActivityRecord target = task.getChildAt(i);
             // TODO: Why is this needed? Looks like we're breaking the loop before we reach the root
             if (target.isRootOfTask()) break;
 
@@ -3435,7 +3426,7 @@
                     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);
+                        final ActivityRecord p = task.getChildAt(srcPos);
                         if (p.finishing) {
                             continue;
                         }
@@ -3443,7 +3434,7 @@
                     }
                 } else {
                     if (taskInsertionPoint < 0) {
-                        taskInsertionPoint = task.mActivities.size();
+                        taskInsertionPoint = task.getChildCount();
 
                     }
 
@@ -3452,7 +3443,7 @@
                             "Reparenting from task=" + affinityTask + ":" + start + "-" + i
                             + " to task=" + task + ":" + taskInsertionPoint);
                     for (int srcPos = start; srcPos >= i; --srcPos) {
-                        final ActivityRecord p = activities.get(srcPos);
+                        final ActivityRecord p = task.getChildAt(srcPos);
                         p.reparent(task, taskInsertionPoint, "resetAffinityTaskIfNeededLocked");
 
                         if (DEBUG_ADD_REMOVE) Slog.i(TAG_ADD_REMOVE,
@@ -3589,9 +3580,9 @@
     /** Finish all activities that were started for result from the specified activity. */
     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);
+            final TaskRecord task = mTaskHistory.get(taskNdx);
+            for (int activityNdx = task.getChildCount() - 1; activityNdx >= 0; --activityNdx) {
+                final ActivityRecord r = task.getChildAt(activityNdx);
                 if (r.resultTo == self && r.requestCode == requestCode) {
                     if ((r.resultWho == null && resultWho == null) ||
                         (r.resultWho != null && r.resultWho.equals(resultWho))) {
@@ -3637,11 +3628,11 @@
                 if (taskNdx < 0) {
                     break;
                 }
-                activityNdx = mTaskHistory.get(taskNdx).mActivities.size() - 1;
+                activityNdx = mTaskHistory.get(taskNdx).getChildCount() - 1;
             } while (activityNdx < 0);
         }
         if (activityNdx >= 0) {
-            r = mTaskHistory.get(taskNdx).mActivities.get(activityNdx);
+            r = mTaskHistory.get(taskNdx).getChildAt(activityNdx);
             if (r.isState(STARTED, RESUMED, PAUSING, PAUSED)) {
                 if (!r.isActivityTypeHome() || mService.mHomeProcess != r.app) {
                     Slog.w(TAG, "  Force finishing activity "
@@ -3659,8 +3650,8 @@
         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);
+                for (int activityNdx = tr.getChildCount() - 1; activityNdx >= 0; --activityNdx) {
+                    ActivityRecord r = tr.getChildAt(activityNdx);
                     if (!r.finishing) {
                         r.finishIfPossible("finish-voice", false /* oomAdj */);
                         didOne = true;
@@ -3668,8 +3659,8 @@
                 }
             } 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);
+                for (int activityNdx = tr.getChildCount() - 1; activityNdx >= 0; --activityNdx) {
+                    ActivityRecord r = tr.getChildAt(activityNdx);
                     if (r.voiceSession != null && r.voiceSession.asBinder() == sessionBinder) {
                         // Inform of cancellation
                         r.clearVoiceSessionLocked();
@@ -3695,9 +3686,9 @@
     void finishAllActivitiesImmediately() {
         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);
+            final TaskRecord task = mTaskHistory.get(taskNdx);
+            for (int activityNdx = task.getChildCount() - 1; activityNdx >= 0; --activityNdx) {
+                final ActivityRecord r = task.getChildAt(activityNdx);
                 noActivitiesInStack = false;
                 Slog.d(TAG, "finishAllActivitiesImmediatelyLocked: finishing " + r);
                 r.destroyIfPossible("finishAllActivitiesImmediatelyLocked");
@@ -3765,12 +3756,12 @@
             return false;
         }
         int finishTo = start - 1;
-        ActivityRecord parent = finishTo < 0 ? null : activities.get(finishTo);
+        ActivityRecord parent = finishTo < 0 ? null : task.getChildAt(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);
+                ActivityRecord r = task.getChildAt(i);
                 if (r.info.packageName.equals(dest.getPackageName()) &&
                         r.info.name.equals(dest.getClassName())) {
                     finishTo = i;
@@ -3931,9 +3922,9 @@
         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);
+            final TaskRecord task = mTaskHistory.get(taskNdx);
+            for (int activityNdx = task.getChildCount() - 1; activityNdx >= 0; --activityNdx) {
+                final ActivityRecord r = task.getChildAt(activityNdx);
                 if (r.finishing) {
                     continue;
                 }
@@ -3978,15 +3969,14 @@
             }
             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);
+            for (int actNdx = 0; actNdx < task.getChildCount(); actNdx++) {
+                final ActivityRecord activity = task.getChildAt(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);
                     activity.destroyImmediately(true /* removeFromApp */, reason);
-                    if (activities.get(actNdx) != activity) {
+                    if (task.getChildAt(actNdx) != activity) {
                         // Was removed from list, back up so we don't miss the next one.
                         actNdx--;
                     }
@@ -4170,8 +4160,8 @@
 
         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;
+            for (int i = tr.getChildCount() - 1; i >= 0; i--) {
+                tr.getChildAt(i).appTimeTracker = timeTracker;
             }
         }
 
@@ -4352,7 +4342,7 @@
             return;
         }
 
-        Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "stack.resize_" + mStackId);
+        Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "stack.resize_" + mStackId);
         mService.deferWindowLayout();
         try {
             // Update override configurations of all tasks in the stack.
@@ -4378,7 +4368,7 @@
             }
         } finally {
             mService.continueWindowLayout();
-            Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+            Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
         }
     }
 
@@ -4425,9 +4415,9 @@
 
     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);
+            final TaskRecord task = mTaskHistory.get(taskNdx);
+            for (int activityNdx = task.getChildCount() - 1; activityNdx >= 0; --activityNdx) {
+                final ActivityRecord r = task.getChildAt(activityNdx);
                 if (r.appToken == token) {
                     return true;
                 }
@@ -4447,9 +4437,9 @@
 
     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);
+            final TaskRecord task = mTaskHistory.get(taskNdx);
+            for (int activityNdx = task.getChildCount() - 1; activityNdx >= 0; --activityNdx) {
+                final ActivityRecord r = task.getChildAt(activityNdx);
                 if ((r.info.flags&ActivityInfo.FLAG_FINISH_ON_CLOSE_SYSTEM_DIALOGS) != 0) {
                     r.finishIfPossible("close-sys", true /* oomAdj */);
                 }
@@ -4556,10 +4546,10 @@
         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;
+            final TaskRecord task = mTaskHistory.get(top);
+            int activityTop = task.getChildCount() - 1;
             if (activityTop >= 0) {
-                activities.get(activityTop).finishIfPossible("unhandled-back", true /* oomAdj */);
+                task.getChildAt(activityTop).finishIfPossible("unhandled-back", true /* oomAdj */);
             }
         }
     }
@@ -4585,9 +4575,9 @@
 
     void handleAppCrash(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);
+            final TaskRecord task = mTaskHistory.get(taskNdx);
+            for (int activityNdx = task.getChildCount() - 1; activityNdx >= 0; --activityNdx) {
+                final ActivityRecord r = task.getChildAt(activityNdx);
                 if (r.app == app) {
                     Slog.w(TAG, "  Force finishing activity "
                             + r.intent.getComponent().flattenToShortString());
@@ -4705,9 +4695,9 @@
         // 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);
+            final TaskRecord task = mTaskHistory.get(taskNdx);
+            for (int activityNdx = task.getChildCount() - 1; activityNdx >= 0; --activityNdx) {
+                final ActivityRecord a = task.getChildAt(activityNdx);
                 if (a.info.packageName.equals(packageName)) {
                     a.forceNewConfig = true;
                     if (starting != null && a == starting && a.visible) {
diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
index f1284d81..7753f57 100644
--- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
@@ -44,7 +44,7 @@
 import static android.os.PowerManager.PARTIAL_WAKE_LOCK;
 import static android.os.Process.INVALID_UID;
 import static android.os.Process.SYSTEM_UID;
-import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
+import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.Display.INVALID_DISPLAY;
 import static android.view.Display.TYPE_VIRTUAL;
@@ -690,7 +690,7 @@
     ResolveInfo resolveIntent(Intent intent, String resolvedType, int userId, int flags,
             int filterCallingUid) {
         try {
-            Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "resolveIntent");
+            Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "resolveIntent");
             int modifiedFlags = flags
                     | PackageManager.MATCH_DEFAULT_ONLY | ActivityManagerService.STOCK_PM_FLAGS;
             if (intent.isWebIntent()
@@ -711,7 +711,7 @@
                 Binder.restoreCallingIdentity(token);
             }
         } finally {
-            Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+            Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
         }
     }
 
@@ -824,7 +824,7 @@
                         System.identityHashCode(r), task.mTaskId, r.shortComponentName);
                 if (r.isActivityTypeHome()) {
                     // Home process is the root process of the task.
-                    updateHomeProcess(task.mActivities.get(0).app);
+                    updateHomeProcess(task.getChildAt(0).app);
                 }
                 mService.getPackageManagerInternalLocked().notifyPackageUse(
                         r.intent.getComponent().getPackageName(), NOTIFY_PACKAGE_USE_ACTIVITY);
@@ -1629,7 +1629,7 @@
             mPendingTempOtherTaskInsetBounds = copyOrNull(tempOtherTaskInsetBounds);
         }
 
-        Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "am.resizeDockedStack");
+        Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "resizeDockedStack");
         mService.deferWindowLayout();
         try {
             // Don't allow re-entry while resizing. E.g. due to docked stack detaching.
@@ -1695,7 +1695,7 @@
         } finally {
             mAllowDockedStackResize = true;
             mService.continueWindowLayout();
-            Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+            Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
         }
     }
 
@@ -1717,7 +1717,7 @@
             return;
         }
 
-        Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "am.resizePinnedStack");
+        Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "resizePinnedStack");
         mService.deferWindowLayout();
         try {
             Rect insetBounds = null;
@@ -1739,7 +1739,7 @@
                     !DEFER_RESUME);
         } finally {
             mService.continueWindowLayout();
-            Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+            Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
         }
     }
 
@@ -1899,9 +1899,9 @@
         task.createTask(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).setTask(task);
+        for (int activityNdx = task.getChildCount() - 1; activityNdx >= 0; --activityNdx) {
+            final ActivityRecord r = task.getChildAt(activityNdx);
+            r.setTask(task);
         }
         return true;
     }
@@ -2501,8 +2501,8 @@
             return;
         }
 
-        for (int i = task.mActivities.size() - 1; i >= 0; i--) {
-            final ActivityRecord r = task.mActivities.get(i);
+        for (int i = task.getChildCount() - 1; i >= 0; i--) {
+            final ActivityRecord r = task.getChildAt(i);
             if (r.attachedToProcess()) {
                 mMultiWindowModeChangedActivities.add(r);
             }
@@ -2524,8 +2524,8 @@
     }
 
     void scheduleUpdatePictureInPictureModeIfNeeded(TaskRecord task, Rect targetStackBounds) {
-        for (int i = task.mActivities.size() - 1; i >= 0; i--) {
-            final ActivityRecord r = task.mActivities.get(i);
+        for (int i = task.getChildCount() - 1; i >= 0; i--) {
+            final ActivityRecord r = task.getChildAt(i);
             if (r.attachedToProcess()) {
                 mPipModeChangedActivities.add(r);
                 // If we are scheduling pip change, then remove this activity from multi-window
@@ -2543,8 +2543,8 @@
 
     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);
+        for (int i = task.getChildCount() - 1; i >= 0; i--) {
+            final ActivityRecord r = task.getChildAt(i);
             if (r.attachedToProcess()) {
                 r.updatePictureInPictureMode(targetStackBounds, forceUpdate);
             }
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 9397893..1a80006 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -770,13 +770,13 @@
         boolean restrictedBgActivity = false;
         if (!abort) {
             try {
-                Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
+                Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER,
                         "shouldAbortBackgroundActivityStart");
                 restrictedBgActivity = shouldAbortBackgroundActivityStart(callingUid,
                         callingPid, callingPackage, realCallingUid, realCallingPid, callerApp,
                         originatingPendingIntent, allowBackgroundActivityStart, intent);
             } finally {
-                Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+                Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
             }
         }
 
@@ -1401,41 +1401,12 @@
         final ActivityStack startedActivityStack;
         try {
             mService.deferWindowLayout();
-            result = startActivityUnchecked(r, sourceRecord, voiceSession, voiceInteractor,
+            Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "startActivityInner");
+            result = startActivityInner(r, sourceRecord, voiceSession, voiceInteractor,
                     startFlags, doResume, options, inTask, outActivity, restrictedBgActivity);
         } finally {
-            final ActivityStack currentStack = r.getActivityStack();
-            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()) {
-                        mRootActivityContainer.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.getActivityStack();
-                if (stack != null) {
-                    mStartActivity.finishIfPossible("startActivity", true /* oomAdj */);
-                }
-
-                // Stack should also be detached from display and be removed if it's empty.
-                if (startedActivityStack != null && startedActivityStack.isAttached()
-                        && startedActivityStack.numActivities() == 0
-                        && !startedActivityStack.isActivityTypeHome()) {
-                    startedActivityStack.remove();
-                }
-            }
+            Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
+            startedActivityStack = handleStartResult(r, result);
             mService.continueWindowLayout();
         }
 
@@ -1445,6 +1416,49 @@
     }
 
     /**
+     * If the start result is success, ensure that the configuration of the started activity matches
+     * the current display. Otherwise clean up unassociated containers to avoid leakage.
+     *
+     * @return the stack where the successful started activity resides.
+     */
+    private @Nullable ActivityStack handleStartResult(@NonNull ActivityRecord started, int result) {
+        final ActivityStack currentStack = started.getActivityStack();
+        ActivityStack 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()) {
+                    mRootActivityContainer.ensureVisibilityAndConfig(
+                            currentTop, currentTop.getDisplayId(),
+                            true /* markFrozenIfConfigChanged */, false /* deferResume */);
+                }
+            }
+            return startedActivityStack;
+        }
+
+        // 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.getActivityStack();
+        if (stack != null) {
+            mStartActivity.finishIfPossible("startActivity", true /* oomAdj */);
+        }
+
+        // Stack should also be detached from display and be removed if it's empty.
+        if (startedActivityStack != null && startedActivityStack.isAttached()
+                && startedActivityStack.numActivities() == 0
+                && !startedActivityStack.isActivityTypeHome()) {
+            startedActivityStack.remove();
+            startedActivityStack = null;
+        }
+        return startedActivityStack;
+    }
+
+    /**
      * Return true if background activity is really aborted.
      *
      * TODO(b/131748165): Refactor the logic so we don't need to call this method everywhere.
@@ -1469,7 +1483,7 @@
     }
 
     // Note: This method should only be called from {@link startActivity}.
-    private int startActivityUnchecked(final ActivityRecord r, ActivityRecord sourceRecord,
+    private int startActivityInner(final ActivityRecord r, ActivityRecord sourceRecord,
             IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
             int startFlags, boolean doResume, ActivityOptions options, TaskRecord inTask,
             ActivityRecord[] outActivity, boolean restrictedBgActivity) {
@@ -1592,7 +1606,7 @@
                 // accordingly.
                 if (mTargetStack.isFocusable()
                         && !mRootActivityContainer.isTopDisplayFocusedStack(mTargetStack)) {
-                    mTargetStack.moveToFront("startActivityUnchecked");
+                    mTargetStack.moveToFront("startActivityInner");
                 }
                 mRootActivityContainer.resumeFocusedStacksTopActivities(
                         mTargetStack, mStartActivity, mOptions);
@@ -1873,7 +1887,7 @@
                     mTargetStack = computeStackFocus(mSourceRecord, false /* newTask */,
                             mLaunchFlags, mOptions);
                     mTargetStack.addTask(targetTask,
-                            !mLaunchTaskBehind /* toTop */, "startActivityUnchecked");
+                            !mLaunchTaskBehind /* toTop */, "complyActivityFlags");
                 }
             }
         } else if ((mLaunchFlags & FLAG_ACTIVITY_CLEAR_TOP) == 0 && !mAddingToTask
@@ -2430,7 +2444,7 @@
         if (mStartActivity.getTaskRecord() == null || mStartActivity.getTaskRecord() == parent) {
             parent.addActivityToTop(mStartActivity);
         } else {
-            mStartActivity.reparent(parent, parent.mActivities.size() /* top */, reason);
+            mStartActivity.reparent(parent, parent.getChildCount() /* top */, reason);
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
index ab35652..0488a3b 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
@@ -159,10 +159,10 @@
      *
      * @param reasons A map from windowing mode to a reason integer why the transition was started,
      *                which must be one of the APP_TRANSITION_* values.
-     * @param timestamp The time at which the app transition started in
-     *                  {@link SystemClock#uptimeMillis()} timebase.
+     * @param timestampNs The time at which the app transition started in
+     *                  {@link SystemClock#elapsedRealtimeNs()} ()} timebase.
      */
-    public abstract void notifyAppTransitionStarting(SparseIntArray reasons, long timestamp);
+    public abstract void notifyAppTransitionStarting(SparseIntArray reasons, long timestampNs);
 
     /**
      * Callback for window manager to let activity manager know that the app transition was
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 9b9dc88..20113a6 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -52,7 +52,7 @@
 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.os.Trace.TRACE_TAG_WINDOW_MANAGER;
 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;
@@ -1606,6 +1606,7 @@
             }
 
             final long origId = Binder.clearCallingIdentity();
+            Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "finishActivity");
             try {
                 boolean res;
                 final boolean finishWithRootActivity =
@@ -1633,6 +1634,7 @@
                 }
                 return res;
             } finally {
+                Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
                 Binder.restoreCallingIdentity(origId);
             }
         }
@@ -1667,6 +1669,7 @@
         try {
             WindowProcessController proc = null;
             synchronized (mGlobalLock) {
+                Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "activityIdle");
                 ActivityStack stack = ActivityRecord.getStackLocked(token);
                 if (stack == null) {
                     return;
@@ -1681,6 +1684,7 @@
                 }
             }
         } finally {
+            Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
             Binder.restoreCallingIdentity(origId);
         }
     }
@@ -1707,10 +1711,12 @@
     public final void activityPaused(IBinder token) {
         final long origId = Binder.clearCallingIdentity();
         synchronized (mGlobalLock) {
+            Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "activityPaused");
             ActivityStack stack = ActivityRecord.getStackLocked(token);
             if (stack != null) {
                 stack.activityPausedLocked(token, false);
             }
+            Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
         }
         Binder.restoreCallingIdentity(origId);
     }
@@ -1731,6 +1737,7 @@
         int restartingUid = 0;
         final ActivityRecord r;
         synchronized (mGlobalLock) {
+            Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "activityStopped");
             r = ActivityRecord.isInStackLocked(token);
             if (r != null) {
                 if (r.attachedToProcess()
@@ -1742,6 +1749,7 @@
                 }
                 r.activityStoppedLocked(icicle, persistentState, description);
             }
+            Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
         }
 
         if (restartingName != null) {
@@ -1763,12 +1771,14 @@
         if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "ACTIVITY DESTROYED: " + token);
         synchronized (mGlobalLock) {
             final long origId = Binder.clearCallingIdentity();
+            Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "activityDestroyed");
             try {
                 final ActivityRecord activity = ActivityRecord.forTokenLocked(token);
                 if (activity != null) {
                     activity.destroyed("activityDestroyed");
                 }
             } finally {
+                Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
                 Binder.restoreCallingIdentity(origId);
             }
         }
@@ -1981,7 +1991,7 @@
                 final TaskRecord task = r.getTaskRecord();
                 int index = task.mActivities.lastIndexOf(r);
                 if (index > 0) {
-                    ActivityRecord under = task.mActivities.get(index - 1);
+                    ActivityRecord under = task.getChildAt(index - 1);
                     under.returningOptions = safeOptions != null ? safeOptions.getOptions(r) : null;
                 }
                 return r.setOccludesParent(false);
@@ -5506,8 +5516,8 @@
     void startProcessAsync(ActivityRecord activity, boolean knownToBeDead, boolean isTop,
             String hostingType) {
         try {
-            if (Trace.isTagEnabled(TRACE_TAG_ACTIVITY_MANAGER)) {
-                Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "dispatchingStartProcess:"
+            if (Trace.isTagEnabled(TRACE_TAG_WINDOW_MANAGER)) {
+                Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "dispatchingStartProcess:"
                         + activity.processName);
             }
             // Post message to start process to avoid possible deadlock of calling into AMS with the
@@ -5517,7 +5527,7 @@
                     isTop, hostingType, activity.intent.getComponent());
             mH.sendMessage(m);
         } finally {
-            Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+            Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
         }
     }
 
@@ -5670,11 +5680,11 @@
 
     private void updateResumedAppTrace(@Nullable ActivityRecord resumed) {
         if (mTracedResumedActivity != null) {
-            Trace.asyncTraceEnd(TRACE_TAG_ACTIVITY_MANAGER,
+            Trace.asyncTraceEnd(TRACE_TAG_WINDOW_MANAGER,
                     constructResumedTraceName(mTracedResumedActivity.packageName), 0);
         }
         if (resumed != null) {
-            Trace.asyncTraceBegin(TRACE_TAG_ACTIVITY_MANAGER,
+            Trace.asyncTraceBegin(TRACE_TAG_WINDOW_MANAGER,
                     constructResumedTraceName(resumed.packageName), 0);
         }
         mTracedResumedActivity = resumed;
@@ -6002,10 +6012,10 @@
 
         @Override
         public void notifyAppTransitionStarting(SparseIntArray reasons,
-                long timestamp) {
+                long timestampNs) {
             synchronized (mGlobalLock) {
                 mStackSupervisor.getActivityMetricsLogger().notifyTransitionStarting(
-                        reasons, timestamp);
+                        reasons, timestampNs);
             }
         }
 
@@ -6773,7 +6783,14 @@
         @Override
         public boolean attachApplication(WindowProcessController wpc) throws RemoteException {
             synchronized (mGlobalLockWithoutBoost) {
-                return mRootActivityContainer.attachApplication(wpc);
+                if (Trace.isTagEnabled(TRACE_TAG_WINDOW_MANAGER)) {
+                    Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "attachApplication:" + wpc.mName);
+                }
+                try {
+                    return mRootActivityContainer.attachApplication(wpc);
+                } finally {
+                    Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
+                }
             }
         }
 
diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java
index 894dfd4..0c07e15 100644
--- a/services/core/java/com/android/server/wm/AppTransitionController.java
+++ b/services/core/java/com/android/server/wm/AppTransitionController.java
@@ -209,7 +209,7 @@
         mDisplayContent.computeImeTarget(true /* updateImeTarget */);
 
         mService.mAtmInternal.notifyAppTransitionStarting(mTempTransitionReasons.clone(),
-                SystemClock.uptimeMillis());
+            SystemClock.elapsedRealtimeNanos());
 
         if (transit == TRANSIT_SHOW_SINGLE_TASK_DISPLAY) {
             mService.mAnimator.addAfterPrepareSurfacesRunnable(() -> {
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index a261341..e56fdd2 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -512,7 +512,7 @@
         if (DEBUG_VISIBILITY) Slog.v(TAG, "VIS " + this + ": interesting="
                 + numInteresting + " visible=" + numVisible);
         if (nowDrawn != reportedDrawn) {
-            onWindowsDrawn(nowDrawn, SystemClock.uptimeMillis());
+            onWindowsDrawn(nowDrawn, SystemClock.elapsedRealtimeNanos());
             reportedDrawn = nowDrawn;
         }
         if (nowVisible != reportedVisible) {
@@ -1420,7 +1420,7 @@
         mReparenting = true;
 
         getParent().removeChild(this);
-        task.addChild(this, position);
+        task.addChild((ActivityRecord) this, position);
 
         mReparenting = false;
 
@@ -2283,7 +2283,7 @@
         }
     }
 
-    private final Runnable mAddStartingWindow = new Runnable() {
+    private class AddStartingWindow implements Runnable {
 
         @Override
         public void run() {
@@ -2343,7 +2343,9 @@
                         AppWindowToken.this);
             }
         }
-    };
+    }
+
+    private final AddStartingWindow mAddStartingWindow = new AddStartingWindow();
 
     private int getStartingWindowType(boolean newTask, boolean taskSwitch, boolean processRunning,
             boolean allowTaskSnapshot, boolean activityCreated, boolean fromRecents,
diff --git a/services/core/java/com/android/server/wm/ConfigurationContainer.java b/services/core/java/com/android/server/wm/ConfigurationContainer.java
index 8afbbdf..300ee1d 100644
--- a/services/core/java/com/android/server/wm/ConfigurationContainer.java
+++ b/services/core/java/com/android/server/wm/ConfigurationContainer.java
@@ -626,6 +626,10 @@
         return mFullConfiguration.windowConfiguration.isAlwaysOnTop();
     }
 
+    boolean hasChild() {
+        return getChildCount() > 0;
+    }
+
     abstract protected int getChildCount();
 
     abstract protected E getChildAt(int index);
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 150ae79..60e9819 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -196,6 +196,11 @@
     // Nav bar is never forced opaque.
     private static final int NAV_BAR_FORCE_TRANSPARENT = 2;
 
+    /** Don't apply window animation (see {@link #selectAnimation}). */
+    static final int ANIMATION_NONE = -1;
+    /** Use the transit animation in style resource (see {@link #selectAnimation}). */
+    static final int ANIMATION_STYLEABLE = 0;
+
     /**
      * These are the system UI flags that, when changing, can cause the layout
      * of the screen to change.
@@ -1036,9 +1041,9 @@
     }
 
     /**
-     * Control the animation to run when a window's state changes.  Return a
-     * non-0 number to force the animation to a specific resource ID, or 0
-     * to use the default animation.
+     * Control the animation to run when a window's state changes.  Return a positive number to
+     * force the animation to a specific resource ID, {@link #ANIMATION_STYLEABLE} to use the
+     * style resource defining the animation, or {@link #ANIMATION_NONE} for no animation.
      *
      * @param win The window that is changing.
      * @param transit What is happening to the window:
@@ -1047,9 +1052,9 @@
      *                {@link com.android.server.policy.WindowManagerPolicy#TRANSIT_SHOW}, or
      *                {@link com.android.server.policy.WindowManagerPolicy#TRANSIT_HIDE}.
      *
-     * @return Resource ID of the actual animation to use, or 0 for none.
+     * @return Resource ID of the actual animation to use, or {@link #ANIMATION_NONE} for none.
      */
-    public int selectAnimationLw(WindowState win, int transit) {
+    int selectAnimation(WindowState win, int transit) {
         if (DEBUG_ANIM) Slog.i(TAG, "selectAnimation in " + win
                 + ": transit=" + transit);
         if (win == mStatusBar) {
@@ -1057,7 +1062,7 @@
             final boolean expanded = win.getAttrs().height == MATCH_PARENT
                     && win.getAttrs().width == MATCH_PARENT;
             if (isKeyguard || expanded) {
-                return -1;
+                return ANIMATION_NONE;
             }
             if (transit == TRANSIT_EXIT
                     || transit == TRANSIT_HIDE) {
@@ -1068,7 +1073,7 @@
             }
         } else if (win == mNavigationBar) {
             if (win.getAttrs().windowAnimations != 0) {
-                return 0;
+                return ANIMATION_STYLEABLE;
             }
             // This can be on either the bottom or the right or the left.
             if (mNavigationBarPosition == NAV_BAR_BOTTOM) {
@@ -1101,7 +1106,7 @@
                 }
             }
         } else if (win.getAttrs().type == TYPE_DOCK_DIVIDER) {
-            return selectDockedDividerAnimationLw(win, transit);
+            return selectDockedDividerAnimation(win, transit);
         }
 
         if (transit == TRANSIT_PREVIEW_DONE) {
@@ -1115,13 +1120,13 @@
             // is shown.  We don't want an animation on the dream, because
             // we need it shown immediately with the keyguard animating away
             // to reveal it.
-            return -1;
+            return ANIMATION_NONE;
         }
 
-        return 0;
+        return ANIMATION_STYLEABLE;
     }
 
-    private int selectDockedDividerAnimationLw(WindowState win, int transit) {
+    private int selectDockedDividerAnimation(WindowState win, int transit) {
         int insets = mDisplayContent.getDockedDividerController().getContentInsets();
 
         // If the divider is behind the navigation bar, don't animate.
@@ -1140,14 +1145,14 @@
                 || frame.bottom + insets >= win.getDisplayFrameLw().bottom);
         final boolean offscreen = offscreenLandscape || offscreenPortrait;
         if (behindNavBar || offscreen) {
-            return 0;
+            return ANIMATION_STYLEABLE;
         }
         if (transit == TRANSIT_ENTER || transit == TRANSIT_SHOW) {
             return R.anim.fade_in;
         } else if (transit == TRANSIT_EXIT) {
             return R.anim.fade_out;
         } else {
-            return 0;
+            return ANIMATION_STYLEABLE;
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
index 7e085f6..bc95481 100644
--- a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
@@ -25,7 +25,7 @@
  */
 class ImeInsetsSourceProvider extends InsetsSourceProvider {
 
-    private WindowState mCurImeTarget;
+    private WindowState mImeTargetFromIme;
     private Runnable mShowImeRunner;
     private boolean mIsImeLayoutDrawn;
 
@@ -40,8 +40,8 @@
     void onPostLayout() {
         super.onPostLayout();
 
-        if (mCurImeTarget != null
-                && mCurImeTarget == mDisplayContent.mInputMethodTarget
+        if (mImeTargetFromIme != null
+                && isImeTargetFromDisplayContentAndImeSame()
                 && mWin != null
                 && mWin.isDrawnLw()
                 && !mWin.mGivenInsetsPending) {
@@ -64,18 +64,33 @@
     /**
      * Called from {@link WindowManagerInternal#showImePostLayout} when {@link InputMethodService}
      * requests to show IME on {@param imeTarget}.
-     * @param imeTarget imeTarget on which IME is displayed.
+     * @param imeTarget imeTarget on which IME request is coming from.
      */
     void scheduleShowImePostLayout(WindowState imeTarget) {
-        mCurImeTarget = imeTarget;
+        mImeTargetFromIme = imeTarget;
         mShowImeRunner = () -> {
             // Target should still be the same.
-            if (mCurImeTarget == mDisplayContent.mInputMethodTarget) {
+            if (isImeTargetFromDisplayContentAndImeSame()) {
                 mDisplayContent.mInputMethodTarget.showInsets(
                         WindowInsets.Type.ime(), true /* fromIme */);
             }
-            mCurImeTarget = null;
+            mImeTargetFromIme = null;
         };
     }
 
+    private boolean isImeTargetFromDisplayContentAndImeSame() {
+        // IMMS#mLastImeTargetWindow always considers focused window as
+        // IME target, however DisplayContent#computeImeTarget() can compute
+        // a different IME target.
+        // Refer to WindowManagerService#applyImeVisibility(token, false).
+        // If IMMS's imeTarget is child of DisplayContent's imeTarget and child window
+        // is above the parent, we will consider it as the same target for now.
+        // TODO(b/139861270): Remove the child & sublayer check once IMMS is aware of
+        //  actual IME target.
+        return mImeTargetFromIme == mDisplayContent.mInputMethodTarget
+                || (mDisplayContent.mInputMethodTarget.getParentWindow() == mImeTargetFromIme
+                        && mDisplayContent.mInputMethodTarget.mSubLayer
+                                > mImeTargetFromIme.mSubLayer);
+    }
+
 }
diff --git a/services/core/java/com/android/server/wm/ImmersiveModeConfirmation.java b/services/core/java/com/android/server/wm/ImmersiveModeConfirmation.java
index d774dc3..82ac6b8 100644
--- a/services/core/java/com/android/server/wm/ImmersiveModeConfirmation.java
+++ b/services/core/java/com/android/server/wm/ImmersiveModeConfirmation.java
@@ -189,7 +189,7 @@
                         | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED
                         | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL,
                 PixelFormat.TRANSLUCENT);
-        lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
+        lp.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
         lp.setTitle("ImmersiveModeConfirmation");
         lp.windowAnimations = com.android.internal.R.style.Animation_ImmersiveModeConfirmation;
         lp.token = getWindowToken();
diff --git a/services/core/java/com/android/server/wm/InputManagerCallback.java b/services/core/java/com/android/server/wm/InputManagerCallback.java
index eb9b3e9..9973e11 100644
--- a/services/core/java/com/android/server/wm/InputManagerCallback.java
+++ b/services/core/java/com/android/server/wm/InputManagerCallback.java
@@ -266,7 +266,8 @@
 
         final IWindow focusedWindow = mFocusedWindow.get();
         if (focusedWindow != null) {
-            if (focusedWindow.asBinder() == newFocusedWindow.asBinder()) {
+            if (newFocusedWindow != null
+                    && newFocusedWindow.asBinder() == focusedWindow.asBinder()) {
                 Slog.w(TAG, "notifyFocusChanged called with unchanged mFocusedWindow="
                         + focusedWindow);
                 return false;
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index dc9a598..584c6e1 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -112,7 +112,7 @@
         }
     }
 
-    private final Runnable mUpdateInputWindows = new Runnable() {
+    private class UpdateInputWindows implements Runnable {
         @Override
         public void run() {
             synchronized (mService.mGlobalLock) {
@@ -148,7 +148,9 @@
                 mUpdateInputForAllWindowsConsumer.updateInputWindows(inDrag);
             }
         }
-    };
+    }
+
+    private final UpdateInputWindows mUpdateInputWindows = new UpdateInputWindows();
 
     public InputMonitor(WindowManagerService service, int displayId) {
         mService = service;
diff --git a/services/core/java/com/android/server/wm/InsetsControlTarget.java b/services/core/java/com/android/server/wm/InsetsControlTarget.java
index b7184a5..22ba82a 100644
--- a/services/core/java/com/android/server/wm/InsetsControlTarget.java
+++ b/services/core/java/com/android/server/wm/InsetsControlTarget.java
@@ -33,4 +33,13 @@
      */
     default void showInsets(@InsetType int types, boolean fromIme) {
     }
+
+    /**
+     * Instructs the control target to hide inset sources.
+     *
+     * @param types to specify which types of insets source window should be hidden.
+     * @param fromIme {@code true} if IME hide request originated from {@link InputMethodService}.
+     */
+    default void hideInsets(@InsetType int types, boolean fromIme) {
+    }
 }
diff --git a/services/core/java/com/android/server/wm/KeyguardController.java b/services/core/java/com/android/server/wm/KeyguardController.java
index 2b5eb3a..52cc422 100644
--- a/services/core/java/com/android/server/wm/KeyguardController.java
+++ b/services/core/java/com/android/server/wm/KeyguardController.java
@@ -16,7 +16,7 @@
 
 package com.android.server.wm;
 
-import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
+import static android.os.Trace.TRACE_TAG_WINDOW_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_SUBTLE_ANIMATION;
@@ -180,7 +180,7 @@
         if (!mKeyguardShowing) {
             return;
         }
-        Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "keyguardGoingAway");
+        Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "keyguardGoingAway");
         mService.deferWindowLayout();
         try {
             setKeyguardGoingAway(true);
@@ -202,11 +202,8 @@
                     true /* taskSwitch */);
             mWindowManager.executeAppTransition();
         } finally {
-            Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "keyguardGoingAway: surfaceLayout");
             mService.continueWindowLayout();
-            Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
-
-            Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+            Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/LaunchObserverRegistryImpl.java b/services/core/java/com/android/server/wm/LaunchObserverRegistryImpl.java
index 93e2d8d..362ed3c 100644
--- a/services/core/java/com/android/server/wm/LaunchObserverRegistryImpl.java
+++ b/services/core/java/com/android/server/wm/LaunchObserverRegistryImpl.java
@@ -70,9 +70,12 @@
     }
 
     @Override
-    public void onIntentStarted(Intent intent) {
+    public void onIntentStarted(Intent intent, long timestampNs) {
         mHandler.sendMessage(PooledLambda.obtainMessage(
-                LaunchObserverRegistryImpl::handleOnIntentStarted, this, intent));
+                LaunchObserverRegistryImpl::handleOnIntentStarted,
+                this,
+                intent,
+                timestampNs));
     }
 
     @Override
@@ -99,9 +102,22 @@
 
     @Override
     public void onActivityLaunchFinished(
-        @ActivityRecordProto byte[] activity) {
+        @ActivityRecordProto byte[] activity,
+        long timestampNs) {
         mHandler.sendMessage(PooledLambda.obtainMessage(
-                LaunchObserverRegistryImpl::handleOnActivityLaunchFinished, this, activity));
+            LaunchObserverRegistryImpl::handleOnActivityLaunchFinished,
+            this,
+            activity,
+            timestampNs));
+    }
+
+    @Override
+    public void onReportFullyDrawn(@ActivityRecordProto byte[] activity, long timestampNs) {
+        mHandler.sendMessage(PooledLambda.obtainMessage(
+            LaunchObserverRegistryImpl::handleOnReportFullyDrawn,
+            this,
+            activity,
+            timestampNs));
     }
 
     // Use PooledLambda.obtainMessage to invoke below methods. Every method reference must be
@@ -116,11 +132,11 @@
         mList.remove(observer);
     }
 
-    private void handleOnIntentStarted(Intent intent) {
+    private void handleOnIntentStarted(Intent intent, long timestampNs) {
         // Traverse start-to-end to meet the registerLaunchObserver multi-cast order guarantee.
         for (int i = 0; i < mList.size(); i++) {
              ActivityMetricsLaunchObserver o = mList.get(i);
-             o.onIntentStarted(intent);
+             o.onIntentStarted(intent, timestampNs);
         }
     }
 
@@ -152,11 +168,20 @@
     }
 
     private void handleOnActivityLaunchFinished(
-            @ActivityRecordProto byte[] activity) {
+            @ActivityRecordProto byte[] activity, long timestampNs) {
         // Traverse start-to-end to meet the registerLaunchObserver multi-cast order guarantee.
         for (int i = 0; i < mList.size(); i++) {
             ActivityMetricsLaunchObserver o = mList.get(i);
-            o.onActivityLaunchFinished(activity);
+            o.onActivityLaunchFinished(activity, timestampNs);
+        }
+    }
+
+    private void handleOnReportFullyDrawn(
+            @ActivityRecordProto byte[] activity, long timestampNs) {
+        // Traverse start-to-end to meet the registerLaunchObserver multi-cast order guarantee.
+        for (int i = 0; i < mList.size(); i++) {
+            ActivityMetricsLaunchObserver o = mList.get(i);
+            o.onReportFullyDrawn(activity, timestampNs);
         }
     }
 }
diff --git a/services/core/java/com/android/server/wm/RecentsAnimation.java b/services/core/java/com/android/server/wm/RecentsAnimation.java
index 062cdc5..12579e6 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimation.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimation.java
@@ -22,7 +22,7 @@
 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.os.Trace.TRACE_TAG_WINDOW_MANAGER;
 import static android.view.WindowManager.TRANSIT_NONE;
 
 import static com.android.server.wm.ActivityStackSupervisor.PRESERVE_WINDOWS;
@@ -158,7 +158,7 @@
 
     void startRecentsActivity(IRecentsAnimationRunner recentsAnimationRunner) {
         ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS, "startRecentsActivity(): intent=%s", mTargetIntent);
-        Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "RecentsAnimation#startRecentsActivity");
+        Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "RecentsAnimation#startRecentsActivity");
 
         // TODO(multi-display) currently only support recents animation in default display.
         final DisplayContent dc =
@@ -263,7 +263,7 @@
             throw e;
         } finally {
             mService.continueWindowLayout();
-            Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+            Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
         }
     }
 
@@ -297,7 +297,7 @@
             }
 
             mWindowManager.inSurfaceTransaction(() -> {
-                Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER,
+                Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER,
                         "RecentsAnimation#onAnimationFinished_inSurfaceTransaction");
                 mService.deferWindowLayout();
                 try {
@@ -394,7 +394,7 @@
                     throw e;
                 } finally {
                     mService.continueWindowLayout();
-                    Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+                    Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
                 }
             });
         }
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
index bd27905..d606e5d 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimationController.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -98,8 +98,10 @@
     private final ArrayList<WallpaperAnimationAdapter> mPendingWallpaperAnimations =
             new ArrayList<>();
     private final int mDisplayId;
-    private final Runnable mFailsafeRunnable = () ->
-            cancelAnimation(REORDER_MOVE_TO_ORIGINAL_POSITION, "failSafeRunnable");
+    private boolean mWillFinishToHome = false;
+    private final Runnable mFailsafeRunnable = () -> cancelAnimation(
+            mWillFinishToHome ? REORDER_MOVE_TO_TOP : REORDER_MOVE_TO_ORIGINAL_POSITION,
+            "failSafeRunnable");
 
     // The recents component app token that is shown behind the visibile tasks
     private AppWindowToken mTargetAppToken;
@@ -326,6 +328,13 @@
                 }
             }
         }
+
+        @Override
+        public void setWillFinishToHome(boolean willFinishToHome) {
+            synchronized (mService.getWindowManagerLock()) {
+                mWillFinishToHome = willFinishToHome;
+            }
+        }
     };
 
     /**
@@ -494,7 +503,8 @@
         }
         final SparseIntArray reasons = new SparseIntArray();
         reasons.put(WINDOWING_MODE_FULLSCREEN, APP_TRANSITION_RECENTS_ANIM);
-        mService.mAtmInternal.notifyAppTransitionStarting(reasons, SystemClock.uptimeMillis());
+        mService.mAtmInternal
+            .notifyAppTransitionStarting(reasons, SystemClock.elapsedRealtimeNanos());
     }
 
     private RemoteAnimationTarget[] createAppAnimations() {
diff --git a/services/core/java/com/android/server/wm/RootActivityContainer.java b/services/core/java/com/android/server/wm/RootActivityContainer.java
index d78d517..60833c3 100644
--- a/services/core/java/com/android/server/wm/RootActivityContainer.java
+++ b/services/core/java/com/android/server/wm/RootActivityContainer.java
@@ -983,7 +983,7 @@
             stack.resize(task.getRequestedOverrideBounds(), null /* tempTaskBounds */,
                     null /* tempTaskInsetBounds */, !PRESERVE_WINDOWS, !DEFER_RESUME);
 
-            if (task.mActivities.size() == 1) {
+            if (task.getChildCount() == 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);
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index a181c18..f0717ca 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -58,7 +58,7 @@
 import java.io.PrintWriter;
 import java.util.function.Consumer;
 
-class Task extends WindowContainer<AppWindowToken> implements ConfigurationContainerListener{
+class Task extends WindowContainer<ActivityRecord> implements ConfigurationContainerListener{
     static final String TAG = TAG_WITH_CLASS_NAME ? "Task" : TAG_WM;
 
     // TODO: Track parent marks like this in WindowContainer.
@@ -121,8 +121,11 @@
     // TODO: Remove after unification.
     @Override
     public void onConfigurationChanged(Configuration newParentConfig) {
-        // Only forward configuration changes in cases where children won't get it from TaskRecord.
-        onConfigurationChanged(newParentConfig, mTaskRecord == null /*forwardToChildren*/);
+        // Forward configuration changes in cases
+        // - children won't get it from TaskRecord
+        // - it's a pinned task
+        onConfigurationChanged(newParentConfig,
+                (mTaskRecord == null) || inPinnedWindowingMode() /*forwardToChildren*/);
     }
 
     Task(int taskId, TaskStack stack, int userId, WindowManagerService service, int resizeMode,
@@ -170,14 +173,14 @@
     }
 
     @Override
-    void addChild(AppWindowToken wtoken, int position) {
+    void addChild(ActivityRecord child, int position) {
         position = getAdjustedAddPosition(position);
-        super.addChild(wtoken, position);
+        super.addChild(child, position);
         mDeferRemoval = false;
     }
 
     @Override
-    void positionChildAt(int position, AppWindowToken child, boolean includingParents) {
+    void positionChildAt(int position, ActivityRecord child, boolean includingParents) {
         position = getAdjustedAddPosition(position);
         super.positionChildAt(position, child, includingParents);
         mDeferRemoval = false;
@@ -279,13 +282,13 @@
     }
 
     @Override
-    void removeChild(AppWindowToken token) {
-        if (!mChildren.contains(token)) {
+    void removeChild(ActivityRecord child) {
+        if (!mChildren.contains(child)) {
             Slog.e(TAG, "removeChild: token=" + this + " not found.");
             return;
         }
 
-        super.removeChild(token);
+        super.removeChild(child);
 
         if (mChildren.isEmpty()) {
             EventLog.writeEvent(WM_TASK_REMOVED, mTaskId, "removeAppToken: last token");
@@ -674,18 +677,18 @@
         return null;
     }
 
-    void positionChildAtTop(AppWindowToken aToken) {
-        positionChildAt(aToken, POSITION_TOP);
+    void positionChildAtTop(ActivityRecord child) {
+        positionChildAt(child, POSITION_TOP);
     }
 
-    void positionChildAt(AppWindowToken aToken, int position) {
-        if (aToken == null) {
+    void positionChildAt(ActivityRecord child, int position) {
+        if (child == null) {
             Slog.w(TAG_WM,
                     "Attempted to position of non-existing app");
             return;
         }
 
-        positionChildAt(position, aToken, false /* includeParents */);
+        positionChildAt(position, child, false /* includeParents */);
     }
 
     void forceWindowsScaleable(boolean force) {
diff --git a/services/core/java/com/android/server/wm/TaskRecord.java b/services/core/java/com/android/server/wm/TaskRecord.java
index 75333c7..299b32c 100644
--- a/services/core/java/com/android/server/wm/TaskRecord.java
+++ b/services/core/java/com/android/server/wm/TaskRecord.java
@@ -48,7 +48,7 @@
 import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
 import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
 import static android.content.res.Configuration.ORIENTATION_UNDEFINED;
-import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
+import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
 import static android.provider.Settings.Secure.USER_SETUP_COMPLETE;
 import static android.view.Display.DEFAULT_DISPLAY;
 
@@ -447,7 +447,7 @@
     }
 
     void cleanUpResourcesForDestroy() {
-        if (!mActivities.isEmpty()) {
+        if (hasChild()) {
             return;
         }
 
@@ -553,7 +553,7 @@
             // 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_" + mTaskId);
+            Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "resizeTask_" + mTaskId);
 
             boolean updatedConfig = false;
             mTmpConfig.setTo(getResolvedOverrideConfiguration());
@@ -587,7 +587,7 @@
 
             saveLaunchingStateIfNeeded();
 
-            Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+            Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
             return kept;
         } finally {
             mAtmService.continueWindowLayout();
@@ -1095,7 +1095,7 @@
             // There are no non-finishing activities in the task.
             return null;
         }
-        return mActivities.get(rootActivityIndex);
+        return getChildAt(rootActivityIndex);
     }
 
     ActivityRecord getTopActivity() {
@@ -1103,8 +1103,8 @@
     }
 
     ActivityRecord getTopActivity(boolean includeOverlays) {
-        for (int i = mActivities.size() - 1; i >= 0; --i) {
-            final ActivityRecord r = mActivities.get(i);
+        for (int i = getChildCount() - 1; i >= 0; --i) {
+            final ActivityRecord r = getChildAt(i);
             if (r.finishing || (!includeOverlays && r.mTaskOverlay)) {
                 continue;
             }
@@ -1115,8 +1115,8 @@
 
     ActivityRecord topRunningActivityLocked() {
         if (mStack != null) {
-            for (int activityNdx = mActivities.size() - 1; activityNdx >= 0; --activityNdx) {
-                ActivityRecord r = mActivities.get(activityNdx);
+            for (int activityNdx = getChildCount() - 1; activityNdx >= 0; --activityNdx) {
+                ActivityRecord r = getChildAt(activityNdx);
                 if (!r.finishing && r.okToShowLocked()) {
                     return r;
                 }
@@ -1126,8 +1126,8 @@
     }
 
     boolean isVisible() {
-        for (int i = mActivities.size() - 1; i >= 0; --i) {
-            final ActivityRecord r = mActivities.get(i);
+        for (int i = getChildCount() - 1; i >= 0; --i) {
+            final ActivityRecord r = getChildAt(i);
             if (r.visible) {
                 return true;
             }
@@ -1139,8 +1139,8 @@
      * Return true if any activities in this task belongs to input uid.
      */
     boolean containsAppUid(int uid) {
-        for (int i = mActivities.size() - 1; i >= 0; --i) {
-            final ActivityRecord r = mActivities.get(i);
+        for (int i = getChildCount() - 1; i >= 0; --i) {
+            final ActivityRecord r = getChildAt(i);
             if (r.getUid() == uid) {
                 return true;
             }
@@ -1150,8 +1150,8 @@
 
     void getAllRunningVisibleActivitiesLocked(ArrayList<ActivityRecord> outActivities) {
         if (mStack != null) {
-            for (int activityNdx = mActivities.size() - 1; activityNdx >= 0; --activityNdx) {
-                ActivityRecord r = mActivities.get(activityNdx);
+            for (int activityNdx = getChildCount() - 1; activityNdx >= 0; --activityNdx) {
+                ActivityRecord r = getChildAt(activityNdx);
                 if (!r.finishing && r.okToShowLocked() && r.visibleIgnoringKeyguard) {
                     outActivities.add(r);
                 }
@@ -1161,8 +1161,8 @@
 
     ActivityRecord topRunningActivityWithStartingWindowLocked() {
         if (mStack != null) {
-            for (int activityNdx = mActivities.size() - 1; activityNdx >= 0; --activityNdx) {
-                ActivityRecord r = mActivities.get(activityNdx);
+            for (int activityNdx = getChildCount() - 1; activityNdx >= 0; --activityNdx) {
+                ActivityRecord r = getChildAt(activityNdx);
                 if (r.mStartingWindowState != STARTING_WINDOW_SHOWN
                         || r.finishing || !r.okToShowLocked()) {
                     continue;
@@ -1179,8 +1179,8 @@
      */
     void getNumRunningActivities(TaskActivitiesReport reportOut) {
         reportOut.reset();
-        for (int i = mActivities.size() - 1; i >= 0; --i) {
-            final ActivityRecord r = mActivities.get(i);
+        for (int i = getChildCount() - 1; i >= 0; --i) {
+            final ActivityRecord r = getChildAt(i);
             if (r.finishing) {
                 continue;
             }
@@ -1227,17 +1227,17 @@
     }
 
     void addActivityToTop(ActivityRecord r) {
-        addActivityAtIndex(mActivities.size(), r);
+        addActivityAtIndex(getChildCount(), r);
     }
 
     @Override
     /*@WindowConfiguration.ActivityType*/
     public int getActivityType() {
         final int applicationType = super.getActivityType();
-        if (applicationType != ACTIVITY_TYPE_UNDEFINED || mActivities.isEmpty()) {
+        if (applicationType != ACTIVITY_TYPE_UNDEFINED || !hasChild()) {
             return applicationType;
         }
-        return mActivities.get(0).getActivityType();
+        return getChildAt(0).getActivityType();
     }
 
     /**
@@ -1259,7 +1259,7 @@
             numFullscreen++;
         }
         // Only set this based on the first activity
-        if (mActivities.isEmpty()) {
+        if (!hasChild()) {
             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
@@ -1279,10 +1279,10 @@
             r.setActivityType(getActivityType());
         }
 
-        final int size = mActivities.size();
+        final int size = getChildCount();
 
         if (index == size && size > 0) {
-            final ActivityRecord top = mActivities.get(size - 1);
+            final ActivityRecord top = getChildAt(size - 1);
             if (top.mTaskOverlay) {
                 // Place below the task overlay activity since the overlay activity should always
                 // be on top.
@@ -1341,7 +1341,7 @@
             mAtmService.getTaskChangeNotificationController().notifyTaskStackChanged();
         }
 
-        if (mActivities.isEmpty()) {
+        if (!hasChild()) {
             return !mReuseTask;
         }
         updateEffectiveIntent();
@@ -1355,8 +1355,8 @@
      */
     boolean onlyHasTaskOverlayActivities(boolean excludeFinishing) {
         int count = 0;
-        for (int i = mActivities.size() - 1; i >= 0; i--) {
-            final ActivityRecord r = mActivities.get(i);
+        for (int i = getChildCount() - 1; i >= 0; i--) {
+            final ActivityRecord r = getChildAt(i);
             if (excludeFinishing && r.finishing) {
                 continue;
             }
@@ -1372,7 +1372,7 @@
         // 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);
+        return autoRemoveRecents || (!hasChild() && !hasBeenVisible);
     }
 
     /**
@@ -1380,9 +1380,9 @@
      * task starting at a specified index.
      */
     final void performClearTaskAtIndexLocked(int activityNdx, String reason) {
-        int numActivities = mActivities.size();
+        int numActivities = getChildCount();
         for ( ; activityNdx < numActivities; ++activityNdx) {
-            final ActivityRecord r = mActivities.get(activityNdx);
+            final ActivityRecord r = getChildAt(activityNdx);
             if (r.finishing) {
                 continue;
             }
@@ -1429,9 +1429,9 @@
      * or null if none was found.
      */
     final ActivityRecord performClearTaskLocked(ActivityRecord newR, int launchFlags) {
-        int numActivities = mActivities.size();
+        int numActivities = getChildCount();
         for (int activityNdx = numActivities - 1; activityNdx >= 0; --activityNdx) {
-            ActivityRecord r = mActivities.get(activityNdx);
+            ActivityRecord r = getChildAt(activityNdx);
             if (r.finishing) {
                 continue;
             }
@@ -1440,7 +1440,7 @@
                 final ActivityRecord ret = r;
 
                 for (++activityNdx; activityNdx < numActivities; ++activityNdx) {
-                    r = mActivities.get(activityNdx);
+                    r = getChildAt(activityNdx);
                     if (r.finishing) {
                         continue;
                     }
@@ -1591,8 +1591,8 @@
      */
     final ActivityRecord findActivityInHistoryLocked(ActivityRecord r) {
         final ComponentName realActivity = r.mActivityComponent;
-        for (int activityNdx = mActivities.size() - 1; activityNdx >= 0; --activityNdx) {
-            ActivityRecord candidate = mActivities.get(activityNdx);
+        for (int activityNdx = getChildCount() - 1; activityNdx >= 0; --activityNdx) {
+            ActivityRecord candidate = getChildAt(activityNdx);
             if (candidate.finishing) {
                 continue;
             }
@@ -1609,12 +1609,12 @@
         // Traverse upwards looking for any break between main task activities and
         // utility activities.
         int activityNdx;
-        final int numActivities = mActivities.size();
+        final int numActivities = getChildCount();
         final boolean relinquish = numActivities != 0 &&
-                (mActivities.get(0).info.flags & FLAG_RELINQUISH_TASK_IDENTITY) != 0;
+                (getChildAt(0).info.flags & FLAG_RELINQUISH_TASK_IDENTITY) != 0;
         for (activityNdx = Math.min(numActivities, 1); activityNdx < numActivities;
                 ++activityNdx) {
-            final ActivityRecord r = mActivities.get(activityNdx);
+            final ActivityRecord r = getChildAt(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.
@@ -1642,7 +1642,7 @@
             boolean navigationBarContrastWhenTransparent = false;
             boolean topActivity = true;
             for (--activityNdx; activityNdx >= 0; --activityNdx) {
-                final ActivityRecord r = mActivities.get(activityNdx);
+                final ActivityRecord r = getChildAt(activityNdx);
                 if (r.mTaskOverlay) {
                     continue;
                 }
@@ -1697,9 +1697,9 @@
      */
     int findRootIndex(boolean effectiveRoot) {
         int effectiveNdx = -1;
-        final int topActivityNdx = mActivities.size() - 1;
+        final int topActivityNdx = getChildCount() - 1;
         for (int activityNdx = 0; activityNdx <= topActivityNdx; ++activityNdx) {
-            final ActivityRecord r = mActivities.get(activityNdx);
+            final ActivityRecord r = getChildAt(activityNdx);
             if (r.finishing) {
                 continue;
             }
@@ -1720,7 +1720,7 @@
             // But we still want to update the intent, so let's use the bottom activity.
             effectiveRootIndex = 0;
         }
-        final ActivityRecord r = mActivities.get(effectiveRootIndex);
+        final ActivityRecord r = getChildAt(effectiveRootIndex);
         setIntent(r);
 
         // Update the task description when the activities change
@@ -2289,8 +2289,8 @@
     }
 
     void addStartingWindowsForVisibleActivities(boolean taskSwitch) {
-        for (int activityNdx = mActivities.size() - 1; activityNdx >= 0; --activityNdx) {
-            final ActivityRecord r = mActivities.get(activityNdx);
+        for (int activityNdx = getChildCount() - 1; activityNdx >= 0; --activityNdx) {
+            final ActivityRecord r = getChildAt(activityNdx);
             if (r.visible) {
                 r.showStartingWindow(null /* prev */, false /* newTask */, taskSwitch);
             }
@@ -2462,7 +2462,7 @@
             sb.append(" StackId=");
             sb.append(getStackId());
             sb.append(" sz=");
-            sb.append(mActivities.size());
+            sb.append(getChildCount());
             sb.append('}');
             return sb.toString();
         }
@@ -2495,8 +2495,8 @@
         final long token = proto.start(fieldId);
         super.writeToProto(proto, CONFIGURATION_CONTAINER, logLevel);
         proto.write(ID, mTaskId);
-        for (int i = mActivities.size() - 1; i >= 0; i--) {
-            ActivityRecord activity = mActivities.get(i);
+        for (int i = getChildCount() - 1; i >= 0; i--) {
+            ActivityRecord activity = getChildAt(i);
             activity.writeToProto(proto, ACTIVITIES);
         }
         proto.write(STACK_ID, mStack.mStackId);
@@ -2607,10 +2607,9 @@
             out.endTag(null, TAG_INTENT);
         }
 
-        final ArrayList<ActivityRecord> activities = mActivities;
-        final int numActivities = activities.size();
+        final int numActivities = getChildCount();
         for (int activityNdx = 0; activityNdx < numActivities; ++activityNdx) {
-            final ActivityRecord r = activities.get(activityNdx);
+            final ActivityRecord r = getChildAt(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) &&
diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java
index f4b7672..0cb4826 100644
--- a/services/core/java/com/android/server/wm/WindowManagerInternal.java
+++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java
@@ -514,6 +514,13 @@
     public abstract void showImePostLayout(IBinder imeTargetWindowToken);
 
     /**
+     * Hide IME using imeTargetWindow when requested.
+     *
+     * @param displayId on which IME is shown
+     */
+    public abstract void hideIme(int displayId);
+
+    /**
      * Tell window manager about a package that should not be running with high refresh rate
      * setting until removeNonHighRefreshRatePackage is called for the same package.
      *
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 0287270..c485280 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -236,6 +236,7 @@
 import android.view.SurfaceSession;
 import android.view.View;
 import android.view.WindowContentFrameStats;
+import android.view.WindowInsets;
 import android.view.WindowManager;
 import android.view.WindowManager.LayoutParams;
 import android.view.WindowManager.RemoveContentMode;
@@ -7312,6 +7313,16 @@
         }
 
         @Override
+        public void hideIme(int displayId) {
+            synchronized (mGlobalLock) {
+                final DisplayContent dc = mRoot.getDisplayContent(displayId);
+                if (dc != null && dc.mInputMethodTarget != null) {
+                    dc.mInputMethodTarget.hideInsets(WindowInsets.Type.ime(), true /* fromIme */);
+                }
+            }
+        }
+
+        @Override
         public boolean isUidAllowedOnDisplay(int displayId, int uid) {
             if (displayId == Display.DEFAULT_DISPLAY) {
                 return true;
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 40bb059..7ff9b70 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -3377,6 +3377,15 @@
         }
     }
 
+    @Override
+    public void hideInsets(@InsetType int types, boolean fromIme) {
+        try {
+            mClient.hideInsets(types, fromIme);
+        } catch (RemoteException e) {
+            Slog.w(TAG, "Failed to deliver showInsets", e);
+        }
+    }
+
     Rect getBackdropFrame(Rect frame) {
         // When the task is docked, we send fullscreen sized backDropFrame as soon as resizing
         // start even if we haven't received the relayout window, so that the client requests
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 1328273..eac372f 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -1348,13 +1348,16 @@
         // frozen, there is no reason to animate and it can cause strange
         // artifacts when we unfreeze the display if some different animation
         // is running.
-        Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "WSA#applyAnimationLocked");
         if (mWin.mToken.okToAnimate()) {
-            int anim = mWin.getDisplayContent().getDisplayPolicy().selectAnimationLw(mWin, transit);
+            int anim = mWin.getDisplayContent().getDisplayPolicy().selectAnimation(mWin, transit);
             int attr = -1;
             Animation a = null;
-            if (anim != 0) {
-                a = anim != -1 ? AnimationUtils.loadAnimation(mContext, anim) : null;
+            if (anim != DisplayPolicy.ANIMATION_STYLEABLE) {
+                if (anim != DisplayPolicy.ANIMATION_NONE) {
+                    Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "WSA#loadAnimation");
+                    a = AnimationUtils.loadAnimation(mContext, anim);
+                    Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
+                }
             } else {
                 switch (transit) {
                     case WindowManagerPolicy.TRANSIT_ENTER:
@@ -1384,7 +1387,9 @@
                     + " isEntrance=" + isEntrance + " Callers " + Debug.getCallers(3));
             if (a != null) {
                 if (DEBUG_ANIM) logWithStack(TAG, "Loaded animation " + a + " for " + this);
+                Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "WSA#startAnimation");
                 mWin.startAnimation(a);
+                Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
                 mAnimationIsEntrance = isEntrance;
             }
         } else {
@@ -1395,7 +1400,6 @@
             mWin.getDisplayContent().adjustForImeIfNeeded();
         }
 
-        Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
         return mWin.isAnimating();
     }
 
diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
index 56f6d4b..0cfdebc 100644
--- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
+++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
@@ -23,7 +23,6 @@
 import static com.android.server.wm.WindowManagerService.LAYOUT_REPEAT_THRESHOLD;
 
 import android.os.Debug;
-import android.os.Trace;
 import android.util.Slog;
 
 import java.io.PrintWriter;
@@ -52,15 +51,19 @@
     /** The number of layout requests when deferring. */
     private int mDeferredRequests;
 
-    private final Runnable mPerformSurfacePlacement;
-
-    public WindowSurfacePlacer(WindowManagerService service) {
-        mService = service;
-        mPerformSurfacePlacement = () -> {
+    private class Traverser implements Runnable {
+        @Override
+        public void run() {
             synchronized (mService.mGlobalLock) {
                 performSurfacePlacement();
             }
-        };
+        }
+    }
+
+    private final Traverser mPerformSurfacePlacement = new Traverser();
+
+    WindowSurfacePlacer(WindowManagerService service) {
+        mService = service;
     }
 
     /**
@@ -152,7 +155,6 @@
             return;
         }
 
-        Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "wmLayout");
         mInLayout = true;
 
         boolean recoveringMemory = false;
@@ -198,8 +200,6 @@
             mInLayout = false;
             Slog.wtf(TAG, "Unhandled exception while laying out windows", e);
         }
-
-        Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
     }
 
     void debugLayoutRepeats(final String msg, int pendingLayoutChanges) {
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index fb2fdab..dd2629d 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -201,8 +201,7 @@
 
     void setDisplayViewports(JNIEnv* env, jobjectArray viewportObjArray);
 
-    status_t registerInputChannel(JNIEnv* env, const sp<InputChannel>& inputChannel,
-            int32_t displayId);
+    status_t registerInputChannel(JNIEnv* env, const sp<InputChannel>& inputChannel);
     status_t registerInputMonitor(JNIEnv* env, const sp<InputChannel>& inputChannel,
             int32_t displayId, bool isGestureMonitor);
     status_t unregisterInputChannel(JNIEnv* env, const sp<InputChannel>& inputChannel);
@@ -435,10 +434,9 @@
 }
 
 status_t NativeInputManager::registerInputChannel(JNIEnv* /* env */,
-        const sp<InputChannel>& inputChannel, int32_t displayId) {
+        const sp<InputChannel>& inputChannel) {
     ATRACE_CALL();
-    return mInputManager->getDispatcher()->registerInputChannel(
-            inputChannel, displayId);
+    return mInputManager->getDispatcher()->registerInputChannel(inputChannel);
 }
 
 status_t NativeInputManager::registerInputMonitor(JNIEnv* /* env */,
@@ -1405,7 +1403,7 @@
 }
 
 static void nativeRegisterInputChannel(JNIEnv* env, jclass /* clazz */,
-        jlong ptr, jobject inputChannelObj, jint displayId) {
+        jlong ptr, jobject inputChannelObj) {
     NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
 
     sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env,
@@ -1415,7 +1413,7 @@
         return;
     }
 
-    status_t status = im->registerInputChannel(env, inputChannel, displayId);
+    status_t status = im->registerInputChannel(env, inputChannel);
 
     if (status) {
         std::string message;
@@ -1757,7 +1755,7 @@
     { "nativeHasKeys", "(JII[I[Z)Z",
             (void*) nativeHasKeys },
     { "nativeRegisterInputChannel",
-            "(JLandroid/view/InputChannel;I)V",
+            "(JLandroid/view/InputChannel;)V",
             (void*) nativeRegisterInputChannel },
     { "nativeRegisterInputMonitor",
             "(JLandroid/view/InputChannel;IZ)V",
diff --git a/services/core/jni/com_android_server_security_VerityUtils.cpp b/services/core/jni/com_android_server_security_VerityUtils.cpp
index 9ceb770..906b568 100644
--- a/services/core/jni/com_android_server_security_VerityUtils.cpp
+++ b/services/core/jni/com_android_server_security_VerityUtils.cpp
@@ -17,6 +17,8 @@
 #define LOG_TAG "VerityUtils"
 
 #include <nativehelper/JNIHelp.h>
+#include <nativehelper/ScopedPrimitiveArray.h>
+#include <nativehelper/ScopedUtfChars.h>
 #include "jni.h"
 #include <utils/Log.h>
 
@@ -72,11 +74,17 @@
 namespace {
 
 int enableFsverity(JNIEnv* env, jobject /* clazz */, jstring filePath, jbyteArray signature) {
-    const char* path = env->GetStringUTFChars(filePath, nullptr);
-    ::android::base::unique_fd rfd(open(path, O_RDONLY | O_CLOEXEC));
-    env->ReleaseStringUTFChars(filePath, path);
+    ScopedUtfChars path(env, filePath);
+    if (path.c_str() == nullptr) {
+        return EINVAL;
+    }
+    ::android::base::unique_fd rfd(open(path.c_str(), O_RDONLY | O_CLOEXEC));
     if (rfd.get() < 0) {
-      return errno;
+        return errno;
+    }
+    ScopedByteArrayRO signature_bytes(env, signature);
+    if (signature_bytes.get() == nullptr) {
+        return EINVAL;
     }
 
     fsverity_enable_arg arg = {};
@@ -85,11 +93,11 @@
     arg.block_size = 4096;
     arg.salt_size = 0;
     arg.salt_ptr = reinterpret_cast<uintptr_t>(nullptr);
-    arg.sig_size = env->GetArrayLength(signature);
-    arg.sig_ptr = reinterpret_cast<uintptr_t>(signature);
+    arg.sig_size = signature_bytes.size();
+    arg.sig_ptr = reinterpret_cast<uintptr_t>(signature_bytes.get());
 
     if (ioctl(rfd.get(), FS_IOC_ENABLE_VERITY, &arg) < 0) {
-      return errno;
+        return errno;
     }
     return 0;
 }
@@ -101,14 +109,16 @@
     fsverity_digest *data = reinterpret_cast<fsverity_digest *>(&bytes);
     data->digest_size = kSha256Bytes;  // the only input/output parameter
 
-    const char* path = env->GetStringUTFChars(filePath, nullptr);
-    ::android::base::unique_fd rfd(open(path, O_RDONLY | O_CLOEXEC));
-    env->ReleaseStringUTFChars(filePath, path);
+    ScopedUtfChars path(env, filePath);
+    if (path.c_str() == nullptr) {
+        return EINVAL;
+    }
+    ::android::base::unique_fd rfd(open(path.c_str(), O_RDONLY | O_CLOEXEC));
     if (rfd.get() < 0) {
-      return errno;
+        return errno;
     }
     if (ioctl(rfd.get(), FS_IOC_MEASURE_VERITY, data) < 0) {
-      return errno;
+        return errno;
     }
     return 0;
 }
diff --git a/services/devicepolicy/Android.bp b/services/devicepolicy/Android.bp
index 47790ce..91c05a8 100644
--- a/services/devicepolicy/Android.bp
+++ b/services/devicepolicy/Android.bp
@@ -5,4 +5,13 @@
     libs: [
         "services.core",
     ],
+
+    plugins: [
+        "compat-changeid-annotation-processor",
+    ],
 }
+
+platform_compat_config {
+    name: "services-devicepolicy-platform-compat-config",
+    src: ":services.devicepolicy",
+}
\ No newline at end of file
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 9569ac8..0ae205a 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -137,6 +137,8 @@
 import android.app.backup.IBackupManager;
 import android.app.trust.TrustManager;
 import android.app.usage.UsageStatsManagerInternal;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledAfter;
 import android.content.ActivityNotFoundException;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
@@ -235,6 +237,7 @@
 import com.android.internal.R;
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.compat.IPlatformCompat;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
 import com.android.internal.notification.SystemNotificationChannels;
@@ -251,6 +254,7 @@
 import com.android.internal.util.XmlUtils;
 import com.android.internal.widget.LockPatternUtils;
 import com.android.internal.widget.LockSettingsInternal;
+import com.android.internal.widget.LockscreenCredential;
 import com.android.server.LocalServices;
 import com.android.server.LockGuard;
 import com.android.server.SystemServerInitThreadPool;
@@ -494,6 +498,22 @@
     private static final String LOG_TAG_PROFILE_OWNER = "profile-owner";
     private static final String LOG_TAG_DEVICE_OWNER = "device-owner";
 
+    /**
+     * For admin apps targeting R+, throw when the app sets password requirement
+     * that is not taken into account at given quality. For example when quality is set
+     * to {@link DevicePolicyManager#PASSWORD_QUALITY_UNSPECIFIED}, it doesn't make sense to
+     * require certain password length. If the intent is to require a password of certain length
+     * having at least NUMERIC quality, the admin should first call
+     * {@link #setPasswordQuality(ComponentName, int, boolean)} and only then call
+     * {@link #setPasswordMinimumLength(ComponentName, int, boolean)}.
+     *
+     * <p>Conversely when an admin app targeting R+ lowers password quality, those
+     * requirements that stop making sense are reset to default values.
+     */
+    @ChangeId
+    @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.Q)
+    private static final long ADMIN_APP_PASSWORD_COMPLEXITY = 123562444L;
+
     final Context mContext;
     final Injector mInjector;
     final IPackageManager mIPackageManager;
@@ -506,6 +526,7 @@
     private final LockSettingsInternal mLockSettingsInternal;
     private final DeviceAdminServiceController mDeviceAdminServiceController;
     private final OverlayPackagesProvider mOverlayPackagesProvider;
+    private final IPlatformCompat mIPlatformCompat;
 
     private final DevicePolicyCacheImpl mPolicyCache = new DevicePolicyCacheImpl();
     private final DeviceStateCacheImpl mStateCache = new DeviceStateCacheImpl();
@@ -1984,6 +2005,11 @@
             return LocalServices.getService(LockSettingsInternal.class);
         }
 
+        IPlatformCompat getIPlatformCompat() {
+            return IPlatformCompat.Stub.asInterface(
+                    ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE));
+        }
+
         boolean hasUserSetupCompleted(DevicePolicyData userData) {
             return userData.mUserSetupComplete;
         }
@@ -2222,6 +2248,7 @@
         mUsageStatsManagerInternal = Preconditions.checkNotNull(
                 injector.getUsageStatsManagerInternal());
         mIPackageManager = Preconditions.checkNotNull(injector.getIPackageManager());
+        mIPlatformCompat = Preconditions.checkNotNull(injector.getIPlatformCompat());
         mIPermissionManager = Preconditions.checkNotNull(injector.getIPermissionManager());
         mTelephonyManager = Preconditions.checkNotNull(injector.getTelephonyManager());
 
@@ -4156,12 +4183,22 @@
                 .write();
     }
 
+    private boolean passwordQualityInvocationOrderCheckEnabled(String packageName, int userId) {
+        try {
+            return mIPlatformCompat.isChangeEnabledByPackageName(ADMIN_APP_PASSWORD_COMPLEXITY,
+                    packageName);
+        } catch (RemoteException e) {
+            Log.e(LOG_TAG, "Failed to get a response from PLATFORM_COMPAT_SERVICE", e);
+        }
+        return getTargetSdk(packageName, userId) > Build.VERSION_CODES.Q;
+    }
+
     /**
      * For admins targeting R+ reset various password constraints to default values when quality is
      * set to a value that makes those constraints that have no effect.
      */
     private void resetInactivePasswordRequirementsIfRPlus(int userId, ActiveAdmin admin) {
-        if (getTargetSdk(admin.info.getPackageName(), userId) > Build.VERSION_CODES.Q) {
+        if (passwordQualityInvocationOrderCheckEnabled(admin.info.getPackageName(), userId)) {
             final PasswordMetrics metrics = admin.minimumPasswordMetrics;
             if (metrics.quality < PASSWORD_QUALITY_NUMERIC) {
                 metrics.length = ActiveAdmin.DEF_MINIMUM_PASSWORD_LENGTH;
@@ -4326,7 +4363,8 @@
     private void ensureMinimumQuality(
             int userId, ActiveAdmin admin, int minimumQuality, String operation) {
         if (admin.minimumPasswordMetrics.quality < minimumQuality
-                && getTargetSdk(admin.info.getPackageName(), userId) > Build.VERSION_CODES.Q) {
+                && passwordQualityInvocationOrderCheckEnabled(admin.info.getPackageName(),
+                userId)) {
             throw new IllegalStateException(String.format(
                     "password quality should be at least %d for %s", minimumQuality, operation));
         }
@@ -5222,28 +5260,20 @@
         // back in to the service.
         final long ident = mInjector.binderClearCallingIdentity();
         final boolean result;
+        final LockscreenCredential newCredential =
+                LockscreenCredential.createPasswordOrNone(password);
         try {
             if (token == null) {
                 // This is the legacy reset password for DPM. Here we want to be able to override
                 // the old device password without necessarily knowing it.
-                if (!TextUtils.isEmpty(password)) {
-                    mLockPatternUtils.saveLockPassword(password.getBytes(), null, quality,
-                            userHandle, /*allowUntrustedChange */true);
-                } else {
-                    mLockPatternUtils.clearLock(null, userHandle,
-                            /*allowUntrustedChange */ true);
-                }
+                mLockPatternUtils.setLockCredential(
+                        newCredential,
+                        LockscreenCredential.createNone(),
+                        userHandle, /*allowUntrustedChange */true);
                 result = true;
             } else {
-                if (!TextUtils.isEmpty(password)) {
-                    result = mLockPatternUtils.setLockCredentialWithToken(password.getBytes(),
-                            LockPatternUtils.CREDENTIAL_TYPE_PASSWORD,
-                            quality, tokenHandle, token, userHandle);
-                } else {
-                    result = mLockPatternUtils.setLockCredentialWithToken(null,
-                            LockPatternUtils.CREDENTIAL_TYPE_NONE,
-                            quality, tokenHandle, token, userHandle);
-                }
+                result = mLockPatternUtils.setLockCredentialWithToken(newCredential, tokenHandle,
+                        token, userHandle);
             }
             boolean requireEntry = (flags & DevicePolicyManager.RESET_PASSWORD_REQUIRE_ENTRY) != 0;
             if (requireEntry) {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/RemoteBugreportUtils.java b/services/devicepolicy/java/com/android/server/devicepolicy/RemoteBugreportUtils.java
index 0838fbc..7cfbcc8 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/RemoteBugreportUtils.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/RemoteBugreportUtils.java
@@ -51,7 +51,7 @@
     static final long REMOTE_BUGREPORT_TIMEOUT_MILLIS = 10 * DateUtils.MINUTE_IN_MILLIS;
 
     static final String CTL_STOP = "ctl.stop";
-    static final String REMOTE_BUGREPORT_SERVICE = "bugreportremote";
+    static final String REMOTE_BUGREPORT_SERVICE = "bugreportd";
 
     static final String BUGREPORT_MIMETYPE = "application/vnd.android.bugreport";
 
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 4cf98d3..88859a7 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -921,7 +921,6 @@
                 false);
         boolean disableCameraService = SystemProperties.getBoolean("config.disable_cameraservice",
                 false);
-        boolean disableSlices = SystemProperties.getBoolean("config.disable_slices", false);
         boolean enableLeftyService = SystemProperties.getBoolean("config.enable_lefty", false);
 
         boolean isEmulator = SystemProperties.get("ro.kernel.qemu").equals("1");
@@ -1856,7 +1855,7 @@
             t.traceEnd();
         }
 
-        if (!disableSlices) {
+        if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_SLICES_DISABLED)) {
             t.traceBegin("StartSliceManagerService");
             mSystemServiceManager.startService(SLICE_MANAGER_SERVICE_CLASS);
             t.traceEnd();
diff --git a/services/print/java/com/android/server/print/TEST_MAPPING b/services/print/java/com/android/server/print/TEST_MAPPING
new file mode 100644
index 0000000..4fa8822
--- /dev/null
+++ b/services/print/java/com/android/server/print/TEST_MAPPING
@@ -0,0 +1,12 @@
+{
+  "presubmit": [
+    {
+      "name": "CtsPrintTestCases",
+      "options": [
+        {
+          "include-annotation": "android.platform.test.annotations.Presubmit"
+        }
+      ]
+    }
+  ]
+}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java
index edf82ee..597d337 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java
@@ -41,12 +41,14 @@
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.UserHandle;
+import android.testing.DexmakerShareClassLoaderRule;
 import android.view.Display;
 
 import com.android.server.wm.WindowManagerInternal;
 
 import org.junit.After;
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
@@ -65,9 +67,14 @@
             "com.android.server.accessibility", "AccessibilityServiceConnectionTest");
     static final int SERVICE_ID = 42;
 
+    // Mock package-private AccessibilityUserState class
+    @Rule
+    public final DexmakerShareClassLoaderRule mDexmakerShareClassLoaderRule =
+            new DexmakerShareClassLoaderRule();
+
     AccessibilityServiceConnection mConnection;
 
-    @Mock AccessibilityManagerService.UserState mMockUserState;
+    @Mock AccessibilityUserState mMockUserState;
     @Mock Context mMockContext;
     @Mock AccessibilityServiceInfo mMockServiceInfo;
     @Mock ResolveInfo mMockResolveInfo;
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java
new file mode 100644
index 0000000..9180054
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java
@@ -0,0 +1,297 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.accessibility;
+
+import static android.accessibilityservice.AccessibilityService.SHOW_MODE_AUTO;
+import static android.accessibilityservice.AccessibilityService.SHOW_MODE_HARD_KEYBOARD_ORIGINAL_VALUE;
+import static android.accessibilityservice.AccessibilityService.SHOW_MODE_HARD_KEYBOARD_OVERRIDDEN;
+import static android.accessibilityservice.AccessibilityService.SHOW_MODE_HIDDEN;
+import static android.accessibilityservice.AccessibilityService.SHOW_MODE_IGNORE_HARD_KEYBOARD;
+import static android.view.accessibility.AccessibilityManager.STATE_FLAG_ACCESSIBILITY_ENABLED;
+import static android.view.accessibility.AccessibilityManager.STATE_FLAG_HIGH_TEXT_CONTRAST_ENABLED;
+import static android.view.accessibility.AccessibilityManager.STATE_FLAG_TOUCH_EXPLORATION_ENABLED;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertNull;
+import static junit.framework.Assert.assertTrue;
+
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.accessibilityservice.AccessibilityServiceInfo;
+import android.content.ComponentName;
+import android.content.Context;
+import android.provider.Settings;
+import android.test.mock.MockContentResolver;
+import android.testing.DexmakerShareClassLoaderRule;
+
+import com.android.internal.util.test.FakeSettingsProvider;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/** Tests for AccessibilityUserState */
+public class AccessibilityUserStateTest {
+
+    private static final ComponentName COMPONENT_NAME =
+            new ComponentName("com.android.server.accessibility", "AccessibilityUserStateTest");
+
+    // Values of setting key SHOW_IME_WITH_HARD_KEYBOARD
+    private static final int STATE_HIDE_IME = 0;
+    private static final int STATE_SHOW_IME = 1;
+
+    private static final int USER_ID = 42;
+
+    // Mock package-private class AccessibilityServiceConnection
+    @Rule public final DexmakerShareClassLoaderRule mDexmakerShareClassLoaderRule =
+            new DexmakerShareClassLoaderRule();
+
+    @Mock private AccessibilityServiceInfo mMockServiceInfo;
+
+    @Mock private AccessibilityServiceConnection mMockConnection;
+
+    @Mock private AccessibilityUserState.ServiceInfoChangeListener mMockListener;
+
+    @Mock private Context mContext;
+
+    private MockContentResolver mMockResolver;
+
+    private AccessibilityUserState mUserState;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        FakeSettingsProvider.clearSettingsProvider();
+        mMockResolver = new MockContentResolver();
+        mMockResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
+        when(mContext.getContentResolver()).thenReturn(mMockResolver);
+        when(mMockServiceInfo.getComponentName()).thenReturn(COMPONENT_NAME);
+        when(mMockConnection.getServiceInfo()).thenReturn(mMockServiceInfo);
+
+        mUserState = new AccessibilityUserState(USER_ID, mContext, mMockListener);
+    }
+
+    @After
+    public void tearDown() {
+        FakeSettingsProvider.clearSettingsProvider();
+    }
+
+    @Test
+    public void onSwitchToAnotherUser_userStateClearedNonDefaultValues() {
+        mUserState.getBoundServicesLocked().add(mMockConnection);
+        mUserState.getBindingServicesLocked().add(COMPONENT_NAME);
+        mUserState.setLastSentClientStateLocked(
+                STATE_FLAG_ACCESSIBILITY_ENABLED
+                        | STATE_FLAG_TOUCH_EXPLORATION_ENABLED
+                        | STATE_FLAG_HIGH_TEXT_CONTRAST_ENABLED);
+        mUserState.setNonInteractiveUiTimeoutLocked(30);
+        mUserState.setInteractiveUiTimeoutLocked(30);
+        mUserState.mEnabledServices.add(COMPONENT_NAME);
+        mUserState.mTouchExplorationGrantedServices.add(COMPONENT_NAME);
+        mUserState.setTouchExplorationEnabledLocked(true);
+        mUserState.setDisplayMagnificationEnabledLocked(true);
+        mUserState.setNavBarMagnificationEnabledLocked(true);
+        mUserState.setServiceAssignedToAccessibilityButtonLocked(COMPONENT_NAME);
+        mUserState.setNavBarMagnificationAssignedToAccessibilityButtonLocked(true);
+        mUserState.setAutoclickEnabledLocked(true);
+        mUserState.setUserNonInteractiveUiTimeoutLocked(30);
+        mUserState.setUserInteractiveUiTimeoutLocked(30);
+
+        mUserState.onSwitchToAnotherUserLocked();
+
+        verify(mMockConnection).unbindLocked();
+        assertTrue(mUserState.getBoundServicesLocked().isEmpty());
+        assertTrue(mUserState.getBindingServicesLocked().isEmpty());
+        assertEquals(-1, mUserState.getLastSentClientStateLocked());
+        assertEquals(0, mUserState.getNonInteractiveUiTimeoutLocked());
+        assertEquals(0, mUserState.getInteractiveUiTimeoutLocked());
+        assertTrue(mUserState.mEnabledServices.isEmpty());
+        assertTrue(mUserState.mTouchExplorationGrantedServices.isEmpty());
+        assertFalse(mUserState.isTouchExplorationEnabledLocked());
+        assertFalse(mUserState.isDisplayMagnificationEnabledLocked());
+        assertFalse(mUserState.isNavBarMagnificationEnabledLocked());
+        assertNull(mUserState.getServiceAssignedToAccessibilityButtonLocked());
+        assertFalse(mUserState.isNavBarMagnificationAssignedToAccessibilityButtonLocked());
+        assertFalse(mUserState.isAutoclickEnabledLocked());
+        assertEquals(0, mUserState.getUserNonInteractiveUiTimeoutLocked());
+        assertEquals(0, mUserState.getUserInteractiveUiTimeoutLocked());
+    }
+
+    @Test
+    public void addService_connectionAlreadyAdded_notAddAgain() {
+        mUserState.getBoundServicesLocked().add(mMockConnection);
+
+        mUserState.addServiceLocked(mMockConnection);
+
+        verify(mMockConnection, never()).onAdded();
+    }
+
+    @Test
+    public void addService_connectionNotYetAddedToBoundService_addAndNotifyServices() {
+        when(mMockConnection.getComponentName()).thenReturn(COMPONENT_NAME);
+
+        mUserState.addServiceLocked(mMockConnection);
+
+        verify(mMockConnection).onAdded();
+        assertTrue(mUserState.getBoundServicesLocked().contains(mMockConnection));
+        assertEquals(mMockConnection, mUserState.mComponentNameToServiceMap.get(COMPONENT_NAME));
+        verify(mMockListener).onServiceInfoChangedLocked(eq(mUserState));
+    }
+
+    @Test
+    public void reconcileSoftKeyboardMode_whenStateNotMatchSettings_setBothDefault() {
+        // When soft kb show mode is hidden in settings and is auto in state.
+        putSecureIntForUser(Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE,
+                SHOW_MODE_HIDDEN, USER_ID);
+
+        mUserState.reconcileSoftKeyboardModeWithSettingsLocked();
+
+        assertEquals(SHOW_MODE_AUTO, mUserState.getSoftKeyboardShowModeLocked());
+        assertEquals(SHOW_MODE_AUTO, getSecureIntForUser(
+                Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, USER_ID));
+        assertNull(mUserState.getServiceChangingSoftKeyboardModeLocked());
+    }
+
+    @Test
+    public void
+            reconcileSoftKeyboardMode_stateIgnoreHardKb_settingsShowImeHardKb_setAutoOverride() {
+        // When show mode is ignore hard kb without original hard kb value
+        // and show ime with hard kb is hide
+        putSecureIntForUser(Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE,
+                SHOW_MODE_IGNORE_HARD_KEYBOARD, USER_ID);
+        mUserState.setSoftKeyboardModeLocked(SHOW_MODE_IGNORE_HARD_KEYBOARD, COMPONENT_NAME);
+        putSecureIntForUser(Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD,
+                STATE_HIDE_IME, USER_ID);
+
+        mUserState.reconcileSoftKeyboardModeWithSettingsLocked();
+
+        assertEquals(SHOW_MODE_AUTO | SHOW_MODE_HARD_KEYBOARD_OVERRIDDEN,
+                getSecureIntForUser(Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, USER_ID));
+        assertNull(mUserState.getServiceChangingSoftKeyboardModeLocked());
+    }
+
+    @Test
+    public void removeService_serviceChangingSoftKeyboardMode_removeAndSetSoftKbModeAuto() {
+        mUserState.setServiceChangingSoftKeyboardModeLocked(COMPONENT_NAME);
+        mUserState.mComponentNameToServiceMap.put(COMPONENT_NAME, mMockConnection);
+        mUserState.setSoftKeyboardModeLocked(SHOW_MODE_HIDDEN, COMPONENT_NAME);
+
+        mUserState.removeServiceLocked(mMockConnection);
+
+        assertFalse(mUserState.getBoundServicesLocked().contains(mMockConnection));
+        verify(mMockConnection).onRemoved();
+        assertEquals(SHOW_MODE_AUTO, mUserState.getSoftKeyboardShowModeLocked());
+        assertNull(mUserState.mComponentNameToServiceMap.get(COMPONENT_NAME));
+        verify(mMockListener).onServiceInfoChangedLocked(eq(mUserState));
+    }
+
+    @Test
+    public void serviceDisconnected_removeServiceAndAddToBinding() {
+        when(mMockConnection.getComponentName()).thenReturn(COMPONENT_NAME);
+        mUserState.addServiceLocked(mMockConnection);
+
+        mUserState.serviceDisconnectedLocked(mMockConnection);
+
+        assertFalse(mUserState.getBoundServicesLocked().contains(mMockConnection));
+        assertTrue(mUserState.getBindingServicesLocked().contains(COMPONENT_NAME));
+    }
+
+    @Test
+    public void setSoftKeyboardMode_withInvalidShowMode_shouldKeepDefaultAuto() {
+        final int invalidShowMode = SHOW_MODE_HIDDEN | SHOW_MODE_HARD_KEYBOARD_ORIGINAL_VALUE;
+
+        assertFalse(mUserState.setSoftKeyboardModeLocked(invalidShowMode, null));
+
+        assertEquals(SHOW_MODE_AUTO, mUserState.getSoftKeyboardShowModeLocked());
+    }
+
+    @Test
+    public void setSoftKeyboardMode_newModeSameWithCurrentState_returnTrue() {
+        when(mMockConnection.getComponentName()).thenReturn(COMPONENT_NAME);
+        mUserState.addServiceLocked(mMockConnection);
+
+        assertTrue(mUserState.setSoftKeyboardModeLocked(SHOW_MODE_AUTO, null));
+    }
+
+    @Test
+    public void setSoftKeyboardMode_withIgnoreHardKb_whenHardKbOverridden_returnFalseAdNoChange() {
+        putSecureIntForUser(Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE,
+                SHOW_MODE_AUTO | SHOW_MODE_HARD_KEYBOARD_OVERRIDDEN, USER_ID);
+
+        assertFalse(mUserState.setSoftKeyboardModeLocked(SHOW_MODE_IGNORE_HARD_KEYBOARD, null));
+
+        assertEquals(SHOW_MODE_AUTO, mUserState.getSoftKeyboardShowModeLocked());
+    }
+
+    @Test
+    public void
+            setSoftKeyboardMode_withIgnoreHardKb_whenShowImeWithHardKb_setOriginalHardKbValue() {
+        putSecureIntForUser(Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD, STATE_SHOW_IME, USER_ID);
+
+        assertTrue(mUserState.setSoftKeyboardModeLocked(SHOW_MODE_IGNORE_HARD_KEYBOARD, null));
+
+        assertEquals(SHOW_MODE_IGNORE_HARD_KEYBOARD | SHOW_MODE_HARD_KEYBOARD_ORIGINAL_VALUE,
+                getSecureIntForUser(Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, USER_ID));
+    }
+
+    @Test
+    public void setSoftKeyboardMode_whenCurrentIgnoreHardKb_shouldSetShowImeWithHardKbValue() {
+        mUserState.setSoftKeyboardModeLocked(SHOW_MODE_IGNORE_HARD_KEYBOARD, COMPONENT_NAME);
+        putSecureIntForUser(Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD, STATE_HIDE_IME, USER_ID);
+        putSecureIntForUser(Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE,
+                SHOW_MODE_IGNORE_HARD_KEYBOARD | SHOW_MODE_HARD_KEYBOARD_ORIGINAL_VALUE, USER_ID);
+
+        assertTrue(mUserState.setSoftKeyboardModeLocked(SHOW_MODE_AUTO, null));
+
+        assertEquals(STATE_SHOW_IME, getSecureIntForUser(
+                Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD, USER_ID));
+    }
+
+    @Test
+    public void setSoftKeyboardMode_withRequester_shouldUpdateInternalStateAndSettingsAsIs() {
+        assertTrue(mUserState.setSoftKeyboardModeLocked(SHOW_MODE_HIDDEN, COMPONENT_NAME));
+
+        assertEquals(SHOW_MODE_HIDDEN, mUserState.getSoftKeyboardShowModeLocked());
+        assertEquals(SHOW_MODE_HIDDEN, getSecureIntForUser(
+                Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, USER_ID));
+        assertEquals(COMPONENT_NAME, mUserState.getServiceChangingSoftKeyboardModeLocked());
+    }
+
+    @Test
+    public void setSoftKeyboardMode_shouldNotifyBoundService() {
+        mUserState.addServiceLocked(mMockConnection);
+
+        assertTrue(mUserState.setSoftKeyboardModeLocked(SHOW_MODE_HIDDEN, COMPONENT_NAME));
+
+        verify(mMockConnection).notifySoftKeyboardShowModeChangedLocked(eq(SHOW_MODE_HIDDEN));
+    }
+
+    private int getSecureIntForUser(String key, int userId) {
+        return Settings.Secure.getIntForUser(mMockResolver, key, -1, userId);
+    }
+
+    private void putSecureIntForUser(String key, int value, int userId) {
+        Settings.Secure.putIntForUser(mMockResolver, key, value, userId);
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/UiAutomationManagerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/UiAutomationManagerTest.java
index deb6f71..8da927d 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/UiAutomationManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/UiAutomationManagerTest.java
@@ -56,7 +56,6 @@
 
     MessageCapturingHandler mMessageCapturingHandler;
 
-    @Mock AccessibilityManagerService.UserState mMockUserState;
     @Mock Context mMockContext;
     @Mock AccessibilityServiceInfo mMockServiceInfo;
     @Mock ResolveInfo mMockResolveInfo;
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index 9a1fd9c..c3ef832 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -93,7 +93,7 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.internal.R;
-import com.android.internal.widget.LockPatternUtils;
+import com.android.internal.widget.LockscreenCredential;
 import com.android.server.LocalServices;
 import com.android.server.SystemService;
 import com.android.server.devicepolicy.DevicePolicyManagerService.RestrictionsListener;
@@ -4266,9 +4266,9 @@
         assertTrue(dpm.isResetPasswordTokenActive(admin1));
 
         // test reset password with token
-        when(getServices().lockPatternUtils.setLockCredentialWithToken(eq(password.getBytes()),
-                eq(LockPatternUtils.CREDENTIAL_TYPE_PASSWORD),
-                eq(DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC), eq(handle), eq(token),
+        when(getServices().lockPatternUtils.setLockCredentialWithToken(
+                eq(LockscreenCredential.createPassword(password)),
+                eq(handle), eq(token),
                 eq(UserHandle.USER_SYSTEM)))
                 .thenReturn(true);
         assertTrue(dpm.resetPasswordWithToken(admin1, password, token, 0));
diff --git a/services/tests/servicestests/src/com/android/server/integrity/engine/RuleEvaluatorTest.java b/services/tests/servicestests/src/com/android/server/integrity/engine/RuleEvaluatorTest.java
index 1fcd0ef..baf1ed0 100644
--- a/services/tests/servicestests/src/com/android/server/integrity/engine/RuleEvaluatorTest.java
+++ b/services/tests/servicestests/src/com/android/server/integrity/engine/RuleEvaluatorTest.java
@@ -127,4 +127,57 @@
 
         assertEquals(rule1, matchedRule);
     }
+
+    @Test
+    public void testMatchRules_validForm() {
+        OpenFormula openFormula = new OpenFormula(OpenFormula.Connector.AND, Arrays.asList(
+                new AtomicFormula(AtomicFormula.Key.PACKAGE_NAME, AtomicFormula.Operator.EQ,
+                        PACKAGE_NAME_1),
+                new AtomicFormula(AtomicFormula.Key.APP_CERTIFICATE,
+                        AtomicFormula.Operator.EQ,
+                        APP_CERTIFICATE)));
+        Rule rule = new Rule(
+                openFormula, Rule.Effect.DENY);
+
+        Rule matchedRule = RuleEvaluator.evaluateRules(Collections.singletonList(rule),
+                APP_INSTALL_METADATA);
+
+        assertEquals(rule, matchedRule);
+    }
+
+    @Test
+    public void testMatchRules_ruleNotInDNF() {
+        OpenFormula openFormula = new OpenFormula(OpenFormula.Connector.OR, Arrays.asList(
+                new AtomicFormula(AtomicFormula.Key.PACKAGE_NAME, AtomicFormula.Operator.EQ,
+                        PACKAGE_NAME_1),
+                new AtomicFormula(AtomicFormula.Key.APP_CERTIFICATE,
+                        AtomicFormula.Operator.EQ,
+                        APP_CERTIFICATE)));
+        Rule rule = new Rule(
+                openFormula, Rule.Effect.DENY);
+
+        Rule matchedRule = RuleEvaluator.evaluateRules(Collections.singletonList(rule),
+                APP_INSTALL_METADATA);
+
+        assertEquals(Rule.EMPTY, matchedRule);
+    }
+
+    @Test
+    public void testMatchRules_openFormulaWithNot() {
+        OpenFormula openSubFormula = new OpenFormula(OpenFormula.Connector.AND, Arrays.asList(
+                new AtomicFormula(AtomicFormula.Key.PACKAGE_NAME, AtomicFormula.Operator.EQ,
+                        PACKAGE_NAME_2),
+                new AtomicFormula(AtomicFormula.Key.APP_CERTIFICATE,
+                        AtomicFormula.Operator.EQ,
+                        APP_CERTIFICATE)));
+        OpenFormula openFormula = new OpenFormula(OpenFormula.Connector.NOT,
+                Collections.singletonList(openSubFormula));
+        Rule rule = new Rule(
+                openFormula, Rule.Effect.DENY);
+
+        Rule matchedRule = RuleEvaluator.evaluateRules(Collections.singletonList(rule),
+                APP_INSTALL_METADATA);
+
+        assertEquals(Rule.EMPTY, matchedRule);
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java b/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java
index f9ac022..537287d 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java
@@ -35,7 +35,9 @@
 import android.content.pm.PackageManager;
 import android.content.pm.UserInfo;
 import android.hardware.authsecret.V1_0.IAuthSecret;
+import android.hardware.face.Face;
 import android.hardware.face.FaceManager;
+import android.hardware.fingerprint.Fingerprint;
 import android.hardware.fingerprint.FingerprintManager;
 import android.os.FileUtils;
 import android.os.IProgressListener;
@@ -249,11 +251,32 @@
         when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)).thenReturn(true);
         when(mFingerprintManager.isHardwareDetected()).thenReturn(true);
         when(mFingerprintManager.hasEnrolledFingerprints(userId)).thenReturn(true);
+        doAnswer(new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                Fingerprint fp = (Fingerprint) invocation.getArguments()[0];
+                FingerprintManager.RemovalCallback callback =
+                        (FingerprintManager.RemovalCallback) invocation.getArguments()[2];
+                callback.onRemovalSucceeded(fp, 0);
+                return null;
+            }
+        }).when(mFingerprintManager).remove(any(), eq(userId), any());
+
 
         // Hardware must be detected and templates must be enrolled
         when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FACE)).thenReturn(true);
         when(mFaceManager.isHardwareDetected()).thenReturn(true);
         when(mFaceManager.hasEnrolledTemplates(userId)).thenReturn(true);
+        doAnswer(new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                Face face = (Face) invocation.getArguments()[0];
+                FaceManager.RemovalCallback callback =
+                        (FaceManager.RemovalCallback) invocation.getArguments()[2];
+                callback.onRemovalSucceeded(face, 0);
+                return null;
+            }
+        }).when(mFaceManager).remove(any(), eq(userId), any());
     }
 
     @Override
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsShellCommandTest.java b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsShellCommandTest.java
index c00d33b..b60111e 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsShellCommandTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsShellCommandTest.java
@@ -19,10 +19,9 @@
 import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC;
 import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_NUMERIC;
 
-import static com.android.internal.widget.LockPatternUtils.stringToPattern;
-
 import static junit.framework.Assert.assertEquals;
 
+import static org.mockito.Matchers.anyBoolean;
 import static org.mockito.Matchers.anyInt;
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.never;
@@ -48,6 +47,8 @@
 import androidx.test.runner.AndroidJUnit4;
 
 import com.android.internal.widget.LockPatternUtils;
+import com.android.internal.widget.LockPatternView;
+import com.android.internal.widget.LockscreenCredential;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -55,6 +56,8 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
+import java.util.List;
+
 /**
  * Test class for {@link LockSettingsShellCommand}.
  *
@@ -87,24 +90,30 @@
     public void testWrongPassword() throws Exception {
         when(mLockPatternUtils.isLockPatternEnabled(mUserId)).thenReturn(false);
         when(mLockPatternUtils.isLockPasswordEnabled(mUserId)).thenReturn(true);
-        when(mLockPatternUtils.checkPassword("1234".getBytes(), mUserId)).thenReturn(false);
+        when(mLockPatternUtils.checkCredential(
+                LockscreenCredential.createPassword("1234"), mUserId, null)).thenReturn(false);
         assertEquals(-1, mCommand.exec(mBinder, in, out, err,
                 new String[] { "set-pin", "--old", "1234" },
                 mShellCallback, mResultReceiver));
-        verify(mLockPatternUtils, never()).saveLockPassword(any(byte[].class), any(byte[].class),
-                anyInt(), anyInt());
+        verify(mLockPatternUtils, never()).setLockCredential(any(), any(),
+                anyInt(), anyBoolean());
     }
 
     @Test
     public void testChangePin() throws Exception {
         when(mLockPatternUtils.isLockPatternEnabled(mUserId)).thenReturn(false);
         when(mLockPatternUtils.isLockPasswordEnabled(mUserId)).thenReturn(true);
-        when(mLockPatternUtils.checkPassword("1234".getBytes(), mUserId)).thenReturn(true);
+        when(mLockPatternUtils.getKeyguardStoredPasswordQuality(mUserId)).thenReturn(
+                PASSWORD_QUALITY_NUMERIC);
+        when(mLockPatternUtils.checkCredential(
+                LockscreenCredential.createPin("1234"), mUserId, null)).thenReturn(true);
         assertEquals(0, mCommand.exec(new Binder(), in, out, err,
                 new String[] { "set-pin", "--old", "1234", "4321" },
                 mShellCallback, mResultReceiver));
-        verify(mLockPatternUtils).saveLockPassword("4321".getBytes(), "1234".getBytes(),
-                PASSWORD_QUALITY_NUMERIC, mUserId);
+        verify(mLockPatternUtils).setLockCredential(
+                LockscreenCredential.createPin("4321"),
+                LockscreenCredential.createPin("1234"),
+                mUserId);
     }
 
     @Test
@@ -121,12 +130,17 @@
     public void testChangePassword() throws Exception {
         when(mLockPatternUtils.isLockPatternEnabled(mUserId)).thenReturn(false);
         when(mLockPatternUtils.isLockPasswordEnabled(mUserId)).thenReturn(true);
-        when(mLockPatternUtils.checkPassword("1234".getBytes(), mUserId)).thenReturn(true);
+        when(mLockPatternUtils.getKeyguardStoredPasswordQuality(mUserId)).thenReturn(
+                PASSWORD_QUALITY_ALPHABETIC);
+        when(mLockPatternUtils.checkCredential(
+                LockscreenCredential.createPassword("1234"), mUserId, null)).thenReturn(true);
         assertEquals(0,  mCommand.exec(new Binder(), in, out, err,
                 new String[] { "set-password", "--old", "1234", "4321" },
                 mShellCallback, mResultReceiver));
-        verify(mLockPatternUtils).saveLockPassword("4321".getBytes(), "1234".getBytes(),
-                PASSWORD_QUALITY_ALPHABETIC, mUserId);
+        verify(mLockPatternUtils).setLockCredential(
+                LockscreenCredential.createPassword("4321"),
+                LockscreenCredential.createPassword("1234"),
+                mUserId);
     }
 
     @Test
@@ -143,11 +157,15 @@
     public void testChangePattern() throws Exception {
         when(mLockPatternUtils.isLockPatternEnabled(mUserId)).thenReturn(true);
         when(mLockPatternUtils.isLockPasswordEnabled(mUserId)).thenReturn(false);
-        when(mLockPatternUtils.checkPattern(stringToPattern("1234"), mUserId)).thenReturn(true);
+        when(mLockPatternUtils.checkCredential(
+                LockscreenCredential.createPattern(stringToPattern("1234")),
+                mUserId, null)).thenReturn(true);
         assertEquals(0, mCommand.exec(new Binder(), in, out, err,
                 new String[] { "set-pattern", "--old", "1234", "4321" },
                 mShellCallback, mResultReceiver));
-        verify(mLockPatternUtils).saveLockPattern(stringToPattern("4321"), "1234".getBytes(),
+        verify(mLockPatternUtils).setLockCredential(
+                LockscreenCredential.createPattern(stringToPattern("4321")),
+                LockscreenCredential.createPattern(stringToPattern("1234")),
                 mUserId);
     }
 
@@ -165,10 +183,19 @@
     public void testClear() throws Exception {
         when(mLockPatternUtils.isLockPatternEnabled(mUserId)).thenReturn(true);
         when(mLockPatternUtils.isLockPasswordEnabled(mUserId)).thenReturn(false);
-        when(mLockPatternUtils.checkPattern(stringToPattern("1234"), mUserId)).thenReturn(true);
+        when(mLockPatternUtils.checkCredential(
+                LockscreenCredential.createPattern(stringToPattern("1234")),
+                mUserId, null)).thenReturn(true);
         assertEquals(0, mCommand.exec(new Binder(), in, out, err,
                 new String[] { "clear", "--old", "1234" },
                 mShellCallback, mResultReceiver));
-        verify(mLockPatternUtils).clearLock("1234".getBytes(), mUserId);
+        verify(mLockPatternUtils).setLockCredential(
+                LockscreenCredential.createNone(),
+                LockscreenCredential.createPattern(stringToPattern("1234")),
+                mUserId);
+    }
+
+    private List<LockPatternView.Cell> stringToPattern(String str) {
+        return LockPatternUtils.byteArrayToPattern(str.getBytes());
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTests.java b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTests.java
index 2a169b7..cb51897 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTests.java
@@ -434,35 +434,6 @@
         assertEquals(2, PersistentData.TYPE_SP_WEAVER);
     }
 
-    public void testCredentialHash_serializeUnserialize() {
-        byte[] serialized = CredentialHash.create(
-                PAYLOAD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD).toBytes();
-        CredentialHash deserialized = CredentialHash.fromBytes(serialized);
-
-        assertEquals(LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, deserialized.type);
-        assertArrayEquals(PAYLOAD, deserialized.hash);
-    }
-
-    public void testCredentialHash_unserialize_versionGatekeeper() {
-        // This test ensures that we can read serialized VERSION_GATEKEEPER CredentialHashes
-        // even if we change the wire format in the future.
-        byte[] serialized = new byte[] {
-                1, /* VERSION_GATEKEEPER */
-                2, /* CREDENTIAL_TYPE_PASSWORD */
-                0, 0, 0, 5, /* hash length */
-                1, 2, -1, -2, 33, /* hash */
-        };
-        CredentialHash deserialized = CredentialHash.fromBytes(serialized);
-
-        assertEquals(LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, deserialized.type);
-        assertArrayEquals(PAYLOAD, deserialized.hash);
-
-        // Make sure the constants we use on the wire do not change.
-        assertEquals(-1, LockPatternUtils.CREDENTIAL_TYPE_NONE);
-        assertEquals(1, LockPatternUtils.CREDENTIAL_TYPE_PATTERN);
-        assertEquals(2, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD);
-    }
-
     private static void assertArrayEquals(byte[] expected, byte[] actual) {
         if (!Arrays.equals(expected, actual)) {
             fail("expected:<" + Arrays.toString(expected) +
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java b/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java
index 0776589..42ca42a 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java
@@ -39,6 +39,7 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.internal.widget.LockPatternUtils;
+import com.android.internal.widget.LockscreenCredential;
 import com.android.internal.widget.VerifyCredentialResponse;
 import com.android.server.locksettings.SyntheticPasswordManager.AuthenticationResult;
 import com.android.server.locksettings.SyntheticPasswordManager.AuthenticationToken;
@@ -364,7 +365,7 @@
         // Verify DPM gets notified about new device lock
         flushHandlerTasks();
         final PasswordMetrics metric = PasswordMetrics.computeForCredential(
-                LockPatternUtils.CREDENTIAL_TYPE_PATTERN, pattern);
+                LockscreenCredential.createPattern(LockPatternUtils.byteArrayToPattern(pattern)));
         assertEquals(metric, mService.getUserPasswordMetrics(PRIMARY_USER_ID));
         verify(mDevicePolicyManager).reportPasswordChanged(PRIMARY_USER_ID);
 
@@ -512,7 +513,7 @@
         assertFalse(mService.havePattern(PRIMARY_USER_ID));
     }
 
-    public void testgetHashFactorPrimaryUser() throws RemoteException {
+    public void testGetHashFactorPrimaryUser() throws RemoteException {
         final byte[] password = "password".getBytes();
         mService.setLockCredential(password, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null,
                 PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID, false);
@@ -527,7 +528,7 @@
         assertArrayEquals(hashFactor, newHashFactor);
     }
 
-    public void testgetHashFactorManagedProfileUnifiedChallenge() throws RemoteException {
+    public void testGetHashFactorManagedProfileUnifiedChallenge() throws RemoteException {
         final byte[] pattern = "1236".getBytes();
         mService.setLockCredential(pattern, LockPatternUtils.CREDENTIAL_TYPE_PATTERN,
                 null, PASSWORD_QUALITY_SOMETHING, PRIMARY_USER_ID, false);
@@ -535,7 +536,7 @@
         assertNotNull(mService.getHashFactor(null, MANAGED_PROFILE_USER_ID));
     }
 
-    public void testgetHashFactorManagedProfileSeparateChallenge() throws RemoteException {
+    public void testGetHashFactorManagedProfileSeparateChallenge() throws RemoteException {
         final byte[] primaryPassword = "primary".getBytes();
         final byte[] profilePassword = "profile".getBytes();
         mService.setLockCredential(primaryPassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null,
diff --git a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
index ba12b73..8a48904 100644
--- a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
@@ -113,6 +113,7 @@
 import android.net.NetworkTemplate;
 import android.net.StringNetworkSpecifier;
 import android.os.Binder;
+import android.os.Handler;
 import android.os.INetworkManagementService;
 import android.os.PersistableBundle;
 import android.os.PowerManagerInternal;
@@ -1117,7 +1118,7 @@
         // Define simple data plan
         final SubscriptionPlan plan = buildMonthlyDataPlan(
                 ZonedDateTime.parse("2015-11-01T00:00:00.00Z"), DataUnit.MEGABYTES.toBytes(1800));
-        mService.setSubscriptionPlans(TEST_SUB_ID, new SubscriptionPlan[] { plan },
+        setSubscriptionPlans(TEST_SUB_ID, new SubscriptionPlan[] { plan },
                 mServiceContext.getOpPackageName());
 
         // We're 20% through the month (6 days)
@@ -1241,7 +1242,7 @@
         // Define simple data plan which gives us effectively 60MB/day
         final SubscriptionPlan plan = buildMonthlyDataPlan(
                 ZonedDateTime.parse("2015-11-01T00:00:00.00Z"), DataUnit.MEGABYTES.toBytes(1800));
-        mService.setSubscriptionPlans(TEST_SUB_ID, new SubscriptionPlan[] { plan },
+        setSubscriptionPlans(TEST_SUB_ID, new SubscriptionPlan[] { plan },
                 mServiceContext.getOpPackageName());
 
         // We're 20% through the month (6 days)
@@ -1457,6 +1458,8 @@
         when(mConnManager.getAllNetworkState()).thenReturn(new NetworkState[0]);
         when(mSubscriptionManager.getActiveSubscriptionIdList()).thenReturn(new int[]{FAKE_SUB_ID});
         when(mTelephonyManager.getSubscriberId(FAKE_SUB_ID)).thenReturn(FAKE_SUBSCRIBER_ID);
+        when(mTelephonyManager.createForSubscriptionId(FAKE_SUB_ID))
+                .thenReturn(mock(TelephonyManager.class));
         PersistableBundle bundle = CarrierConfigManager.getDefaultConfig();
         when(mCarrierConfigManager.getConfigForSubId(FAKE_SUB_ID)).thenReturn(bundle);
         setNetworkPolicies(buildDefaultFakeMobilePolicy());
@@ -1468,6 +1471,8 @@
         when(mConnManager.getAllNetworkState()).thenReturn(new NetworkState[0]);
         when(mSubscriptionManager.getActiveSubscriptionIdList()).thenReturn(new int[]{FAKE_SUB_ID});
         when(mTelephonyManager.getSubscriberId(FAKE_SUB_ID)).thenReturn(FAKE_SUBSCRIBER_ID);
+        when(mTelephonyManager.createForSubscriptionId(FAKE_SUB_ID))
+                .thenReturn(mock(TelephonyManager.class));
         when(mCarrierConfigManager.getConfigForSubId(FAKE_SUB_ID)).thenReturn(null);
         setNetworkPolicies(buildDefaultFakeMobilePolicy());
         // smoke test to make sure no errors are raised
@@ -1653,7 +1658,7 @@
             final SubscriptionPlan plan = buildMonthlyDataPlan(
                     ZonedDateTime.parse("2015-11-01T00:00:00.00Z"),
                     DataUnit.MEGABYTES.toBytes(1800));
-            mService.setSubscriptionPlans(TEST_SUB_ID, new SubscriptionPlan[]{plan},
+            setSubscriptionPlans(TEST_SUB_ID, new SubscriptionPlan[]{plan},
                     mServiceContext.getOpPackageName());
 
             reset(mTelephonyManager, mNetworkManager, mNotifManager);
@@ -1674,7 +1679,7 @@
             final SubscriptionPlan plan = buildMonthlyDataPlan(
                     ZonedDateTime.parse("2015-11-01T00:00:00.00Z"),
                     DataUnit.MEGABYTES.toBytes(100));
-            mService.setSubscriptionPlans(TEST_SUB_ID, new SubscriptionPlan[]{plan},
+            setSubscriptionPlans(TEST_SUB_ID, new SubscriptionPlan[]{plan},
                     mServiceContext.getOpPackageName());
 
             reset(mTelephonyManager, mNetworkManager, mNotifManager);
@@ -1690,7 +1695,7 @@
         {
             final SubscriptionPlan plan = buildMonthlyDataPlan(
                     ZonedDateTime.parse("2015-11-01T00:00:00.00Z"), BYTES_UNLIMITED);
-            mService.setSubscriptionPlans(TEST_SUB_ID, new SubscriptionPlan[]{plan},
+            setSubscriptionPlans(TEST_SUB_ID, new SubscriptionPlan[]{plan},
                     mServiceContext.getOpPackageName());
 
             reset(mTelephonyManager, mNetworkManager, mNotifManager);
@@ -1707,7 +1712,7 @@
         {
             final SubscriptionPlan plan = buildMonthlyDataPlan(
                     ZonedDateTime.parse("2015-11-01T00:00:00.00Z"), BYTES_UNLIMITED);
-            mService.setSubscriptionPlans(TEST_SUB_ID, new SubscriptionPlan[]{plan},
+            setSubscriptionPlans(TEST_SUB_ID, new SubscriptionPlan[]{plan},
                     mServiceContext.getOpPackageName());
 
             reset(mTelephonyManager, mNetworkManager, mNotifManager);
@@ -1923,6 +1928,8 @@
         when(mSubscriptionManager.getActiveSubscriptionIdList()).thenReturn(
                 new int[] { TEST_SUB_ID });
         when(mTelephonyManager.getSubscriberId(TEST_SUB_ID)).thenReturn(TEST_IMSI);
+        when(mTelephonyManager.createForSubscriptionId(TEST_SUB_ID))
+                .thenReturn(mock(TelephonyManager.class));
         doNothing().when(mTelephonyManager).setPolicyDataEnabled(anyBoolean(), anyInt());
         expectNetworkState(false /* roaming */);
     }
@@ -2049,6 +2056,23 @@
 
     private FutureIntent mRestrictBackgroundChanged;
 
+    private void postMsgAndWaitForCompletion() throws InterruptedException {
+        final Handler handler = mService.getHandlerForTesting();
+        final CountDownLatch latch = new CountDownLatch(1);
+        mService.getHandlerForTesting().post(latch::countDown);
+        if (!latch.await(5, TimeUnit.SECONDS)) {
+            fail("Timed out waiting for the test msg to be handled");
+        }
+    }
+
+    private void setSubscriptionPlans(int subId, SubscriptionPlan[] plans, String callingPackage)
+            throws InterruptedException {
+        mService.setSubscriptionPlans(subId, plans, callingPackage);
+        // setSubscriptionPlans() triggers async events, wait for those to be completed before
+        // moving forward as they could interfere with the tests later.
+        postMsgAndWaitForCompletion();
+    }
+
     private void setRestrictBackground(boolean flag) throws Exception {
         mService.setRestrictBackground(flag);
         // Sanity check.
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java b/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java
index 8c3373f..3ae5674 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java
@@ -22,6 +22,7 @@
 import static android.app.NotificationManager.IMPORTANCE_LOW;
 import static android.app.NotificationManager.IMPORTANCE_MIN;
 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_LIGHTS;
+import static android.media.AudioAttributes.USAGE_NOTIFICATION_RINGTONE;
 
 import static junit.framework.Assert.assertFalse;
 import static junit.framework.Assert.assertNull;
@@ -105,6 +106,7 @@
     private int mUid = 1000;
     private int mPid = 2000;
     private android.os.UserHandle mUser = UserHandle.of(ActivityManager.getCurrentUser());
+    private NotificationChannel mChannel;
 
     private VibrateRepeatMatcher mVibrateOnceMatcher = new VibrateRepeatMatcher(-1);
     private VibrateRepeatMatcher mVibrateLoopMatcher = new VibrateRepeatMatcher(0);
@@ -158,6 +160,8 @@
         mService.mScreenOn = false;
         mService.mInCallStateOffHook = false;
         mService.mNotificationPulseEnabled = true;
+
+        mChannel = new NotificationChannel("test", "test", IMPORTANCE_HIGH);
     }
 
     //
@@ -174,13 +178,18 @@
                 true /* noisy */, false /* buzzy*/, false /* lights */);
     }
 
+    private NotificationRecord getBeepyOtherNotification() {
+        return getNotificationRecord(mOtherId, false /* insistent */, false /* once */,
+                true /* noisy */, false /* buzzy*/, false /* lights */);
+    }
+
     private NotificationRecord getBeepyOnceNotification() {
         return getNotificationRecord(mId, false /* insistent */, true /* once */,
                 true /* noisy */, false /* buzzy*/, false /* lights */);
     }
 
     private NotificationRecord getQuietNotification() {
-        return getNotificationRecord(mId, false /* insistent */, false /* once */,
+        return getNotificationRecord(mId, false /* insistent */, true /* once */,
                 false /* noisy */, false /* buzzy*/, false /* lights */);
     }
 
@@ -214,6 +223,11 @@
                 false /* noisy */, true /* buzzy*/, false /* lights */);
     }
 
+    private NotificationRecord getBuzzyOtherNotification() {
+        return getNotificationRecord(mOtherId, false /* insistent */, false /* once */,
+                false /* noisy */, true /* buzzy*/, false /* lights */);
+    }
+
     private NotificationRecord getBuzzyOnceNotification() {
         return getNotificationRecord(mId, false /* insistent */, true /* once */,
                 false /* noisy */, true /* buzzy*/, false /* lights */);
@@ -239,22 +253,34 @@
                 false /* noisy */, false /* buzzy*/, true /* lights */);
     }
 
-    private NotificationRecord getCallRecord(int id, boolean insistent) {
-        return getNotificationRecord(id, false, false /* once */, true /* noisy */,
-                false /* buzzy */, false /* lights */, false /* default vib */,
-                false /* default sound */, false /* default lights */, "",
-                Notification.GROUP_ALERT_ALL, false);
+    private NotificationRecord getCallRecord(int id, NotificationChannel channel, boolean looping) {
+        final Builder builder = new Builder(getContext())
+                .setContentTitle("foo")
+                .setSmallIcon(android.R.drawable.sym_def_app_icon)
+                .setPriority(Notification.PRIORITY_HIGH);
+        Notification n = builder.build();
+        if (looping) {
+            n.flags |= Notification.FLAG_INSISTENT;
+        }
+        StatusBarNotification sbn = new StatusBarNotification(mPkg, mPkg, id, mTag, mUid,
+                mPid, n, mUser, null, System.currentTimeMillis());
+        NotificationRecord r = new NotificationRecord(getContext(), sbn, channel);
+        mService.addNotification(r);
+
+        return r;
     }
 
     private NotificationRecord getNotificationRecord(int id, boolean insistent, boolean once,
             boolean noisy, boolean buzzy, boolean lights) {
-        return getNotificationRecord(id, insistent, once, noisy, buzzy, lights, true, true, true,
-                null, Notification.GROUP_ALERT_ALL, false);
+        return getNotificationRecord(id, insistent, once, noisy, buzzy, lights, buzzy, noisy,
+                lights, null, Notification.GROUP_ALERT_ALL, false);
     }
 
-    private NotificationRecord getLeanbackNotificationRecord(int id, boolean insistent, boolean once,
+    private NotificationRecord getLeanbackNotificationRecord(int id, boolean insistent,
+            boolean once,
             boolean noisy, boolean buzzy, boolean lights) {
-        return getNotificationRecord(id, insistent, once, noisy, buzzy, lights, true, true, true,
+        return getNotificationRecord(id, insistent, once, noisy, buzzy, lights, true, true,
+                true,
                 null, Notification.GROUP_ALERT_ALL, true);
     }
 
@@ -265,16 +291,16 @@
 
     private NotificationRecord getLightsNotificationRecord(String groupKey,
             int groupAlertBehavior) {
-        return getNotificationRecord(mId, false, false, false, false, true /*lights*/, true, true,
-                true, groupKey, groupAlertBehavior, false);
+        return getNotificationRecord(mId, false, false, false, false, true /*lights*/, true,
+                true, true, groupKey, groupAlertBehavior, false);
     }
 
-    private NotificationRecord getNotificationRecord(int id, boolean insistent, boolean once,
+    private NotificationRecord getNotificationRecord(int id,
+            boolean insistent, boolean once,
             boolean noisy, boolean buzzy, boolean lights, boolean defaultVibration,
             boolean defaultSound, boolean defaultLights, String groupKey, int groupAlertBehavior,
             boolean isLeanback) {
-        NotificationChannel channel =
-                new NotificationChannel("test", "test", IMPORTANCE_HIGH);
+
         final Builder builder = new Builder(getContext())
                 .setContentTitle("foo")
                 .setSmallIcon(android.R.drawable.sym_def_app_icon)
@@ -285,31 +311,37 @@
         if (noisy) {
             if (defaultSound) {
                 defaults |= Notification.DEFAULT_SOUND;
-                channel.setSound(Settings.System.DEFAULT_NOTIFICATION_URI,
+                mChannel.setSound(Settings.System.DEFAULT_NOTIFICATION_URI,
                         Notification.AUDIO_ATTRIBUTES_DEFAULT);
             } else {
                 builder.setSound(CUSTOM_SOUND);
-                channel.setSound(CUSTOM_SOUND, CUSTOM_ATTRIBUTES);
+                mChannel.setSound(CUSTOM_SOUND, CUSTOM_ATTRIBUTES);
             }
         } else {
-            channel.setSound(null, null);
+            mChannel.setSound(null, null);
         }
         if (buzzy) {
             if (defaultVibration) {
                 defaults |= Notification.DEFAULT_VIBRATE;
             } else {
                 builder.setVibrate(CUSTOM_VIBRATION);
-                channel.setVibrationPattern(CUSTOM_VIBRATION);
+                mChannel.setVibrationPattern(CUSTOM_VIBRATION);
             }
-            channel.enableVibration(true);
+            mChannel.enableVibration(true);
+        } else {
+            mChannel.setVibrationPattern(null);
+            mChannel.enableVibration(false);
         }
+
         if (lights) {
             if (defaultLights) {
                 defaults |= Notification.DEFAULT_LIGHTS;
             } else {
                 builder.setLights(CUSTOM_LIGHT_COLOR, CUSTOM_LIGHT_ON, CUSTOM_LIGHT_OFF);
             }
-            channel.enableLights(true);
+            mChannel.enableLights(true);
+        } else {
+            mChannel.enableLights(false);
         }
         builder.setDefaults(defaults);
 
@@ -329,7 +361,7 @@
 
         StatusBarNotification sbn = new StatusBarNotification(mPkg, mPkg, id, mTag, mUid,
                 mPid, n, mUser, null, System.currentTimeMillis());
-        NotificationRecord r = new NotificationRecord(context, sbn, channel);
+        NotificationRecord r = new NotificationRecord(context, sbn, mChannel);
         mService.addNotification(r);
         return r;
     }
@@ -339,18 +371,19 @@
     //
 
     private void verifyNeverBeep() throws RemoteException {
-        verify(mRingtonePlayer, never()).playAsync((Uri) anyObject(), (UserHandle) anyObject(),
-                anyBoolean(), (AudioAttributes) anyObject());
+        verify(mRingtonePlayer, never()).playAsync(any(), any(), anyBoolean(), any());
     }
 
-    private void verifyBeep() throws RemoteException {
-        verify(mRingtonePlayer, times(1)).playAsync((Uri) anyObject(), (UserHandle) anyObject(),
-                eq(true), (AudioAttributes) anyObject());
+    private void verifyBeepUnlooped() throws RemoteException  {
+        verify(mRingtonePlayer, times(1)).playAsync(any(), any(), eq(false), any());
     }
 
-    private void verifyBeepLooped() throws RemoteException {
-        verify(mRingtonePlayer, times(1)).playAsync((Uri) anyObject(), (UserHandle) anyObject(),
-                eq(false), (AudioAttributes) anyObject());
+    private void verifyBeepLooped() throws RemoteException  {
+        verify(mRingtonePlayer, times(1)).playAsync(any(), any(), eq(true), any());
+    }
+
+    private void verifyBeep(int times)  throws RemoteException  {
+        verify(mRingtonePlayer, times(times)).playAsync(any(), any(), anyBoolean(), any());
     }
 
     private void verifyNeverStopAudio() throws RemoteException {
@@ -362,24 +395,31 @@
     }
 
     private void verifyNeverVibrate() {
-        verify(mVibrator, never()).vibrate(anyInt(), anyString(), (VibrationEffect) anyObject(),
-                anyString(), (AudioAttributes) anyObject());
+        verify(mVibrator, never()).vibrate(anyInt(), anyString(), any(), anyString(), any());
     }
 
     private void verifyVibrate() {
         verify(mVibrator, times(1)).vibrate(anyInt(), anyString(), argThat(mVibrateOnceMatcher),
-                anyString(), (AudioAttributes) anyObject());
+                anyString(), any());
+    }
+
+    private void verifyVibrate(int times) {
+        verify(mVibrator, times(times)).vibrate(anyInt(), anyString(), any(), anyString(), any());
     }
 
     private void verifyVibrateLooped() {
         verify(mVibrator, times(1)).vibrate(anyInt(), anyString(), argThat(mVibrateLoopMatcher),
-                anyString(), (AudioAttributes) anyObject());
+                anyString(), any());
     }
 
     private void verifyDelayedVibrateLooped() {
         verify(mVibrator, timeout(MAX_VIBRATION_DELAY).times(1)).vibrate(anyInt(), anyString(),
-                argThat(mVibrateLoopMatcher), anyString(),
-                (AudioAttributes) anyObject());
+                argThat(mVibrateLoopMatcher), anyString(), any());
+    }
+
+    private void verifyDelayedVibrate() {
+        verify(mVibrator, timeout(MAX_VIBRATION_DELAY).times(1)).vibrate(anyInt(), anyString(),
+                argThat(mVibrateOnceMatcher), anyString(), any());
     }
 
     private void verifyStopVibrate() {
@@ -398,11 +438,6 @@
         verify(mLight, times(1)).setFlashing(anyInt(), anyInt(), anyInt(), anyInt());
     }
 
-    private void verifyCustomLights() {
-        verify(mLight, times(1)).setFlashing(
-                eq(CUSTOM_LIGHT_COLOR), anyInt(), eq(CUSTOM_LIGHT_ON), eq(CUSTOM_LIGHT_OFF));
-    }
-
     //
     // Tests
     //
@@ -425,7 +460,7 @@
 
         mService.buzzBeepBlinkLocked(r);
 
-        verifyBeepLooped();
+        verifyBeepUnlooped();
         verifyNeverVibrate();
         verify(mAccessibilityService, times(1)).sendAccessibilityEvent(any(), anyInt());
         assertTrue(r.isInterruptive());
@@ -438,7 +473,7 @@
 
         mService.buzzBeepBlinkLocked(r);
 
-        verifyBeep();
+        verifyBeepLooped();
         assertTrue(r.isInterruptive());
         assertNotEquals(-1, r.getLastAudiblyAlertedMs());
     }
@@ -492,7 +527,7 @@
 
         mService.buzzBeepBlinkLocked(r);
 
-        verifyBeepLooped();
+        verifyBeepUnlooped();
         assertTrue(r.isInterruptive());
     }
 
@@ -533,7 +568,7 @@
         // update should beep
         r.isUpdate = true;
         mService.buzzBeepBlinkLocked(r);
-        verifyBeepLooped();
+        verifyBeepUnlooped();
         verify(mAccessibilityService, times(2)).sendAccessibilityEvent(any(), anyInt());
         assertTrue(r.isInterruptive());
         assertNotEquals(-1, r.getLastAudiblyAlertedMs());
@@ -723,7 +758,7 @@
         mService.buzzBeepBlinkLocked(r);
 
         verifyNeverVibrate();
-        verifyBeepLooped();
+        verifyBeepUnlooped();
         assertTrue(r.isInterruptive());
         assertNotEquals(-1, r.getLastAudiblyAlertedMs());
     }
@@ -821,7 +856,7 @@
 
         mService.buzzBeepBlinkLocked(summary);
 
-        verifyBeepLooped();
+        verifyBeepUnlooped();
         // summaries are never interruptive for notification counts
         assertFalse(summary.isInterruptive());
         assertNotEquals(-1, summary.getLastAudiblyAlertedMs());
@@ -833,7 +868,7 @@
 
         mService.buzzBeepBlinkLocked(nonGroup);
 
-        verifyBeepLooped();
+        verifyBeepUnlooped();
         assertTrue(nonGroup.isInterruptive());
         assertNotEquals(-1, nonGroup.getLastAudiblyAlertedMs());
     }
@@ -856,7 +891,7 @@
 
         mService.buzzBeepBlinkLocked(child);
 
-        verifyBeepLooped();
+        verifyBeepUnlooped();
         assertTrue(child.isInterruptive());
         assertNotEquals(-1, child.getLastAudiblyAlertedMs());
     }
@@ -867,7 +902,7 @@
 
         mService.buzzBeepBlinkLocked(nonGroup);
 
-        verifyBeepLooped();
+        verifyBeepUnlooped();
         assertTrue(nonGroup.isInterruptive());
         assertNotEquals(-1, nonGroup.getLastAudiblyAlertedMs());
     }
@@ -878,7 +913,7 @@
 
         mService.buzzBeepBlinkLocked(group);
 
-        verifyBeepLooped();
+        verifyBeepUnlooped();
         assertTrue(group.isInterruptive());
         assertNotEquals(-1, group.getLastAudiblyAlertedMs());
     }
@@ -1293,7 +1328,11 @@
 
     @Test
     public void testListenerHintCall() throws Exception {
-        NotificationRecord r = getCallRecord(1, true);
+        NotificationChannel ringtoneChannel =
+                new NotificationChannel("ringtone", "", IMPORTANCE_HIGH);
+        ringtoneChannel.setSound(Settings.System.DEFAULT_RINGTONE_URI,
+                new AudioAttributes.Builder().setUsage(USAGE_NOTIFICATION_RINGTONE).build());
+        NotificationRecord r = getCallRecord(1, ringtoneChannel, true);
 
         mService.setHints(NotificationListenerService.HINT_HOST_DISABLE_CALL_EFFECTS);
 
@@ -1310,7 +1349,7 @@
 
         mService.buzzBeepBlinkLocked(r);
 
-        verifyBeepLooped();
+        verifyBeepUnlooped();
     }
 
     @Test
@@ -1326,7 +1365,11 @@
 
     @Test
     public void testListenerHintBoth() throws Exception {
-        NotificationRecord r = getCallRecord(1, true);
+        NotificationChannel ringtoneChannel =
+                new NotificationChannel("ringtone", "", IMPORTANCE_HIGH);
+        ringtoneChannel.setSound(Settings.System.DEFAULT_RINGTONE_URI,
+                new AudioAttributes.Builder().setUsage(USAGE_NOTIFICATION_RINGTONE).build());
+        NotificationRecord r = getCallRecord(1, ringtoneChannel, true);
         NotificationRecord s = getBeepyNotification();
 
         mService.setHints(NotificationListenerService.HINT_HOST_DISABLE_NOTIFICATION_EFFECTS
@@ -1340,7 +1383,11 @@
 
     @Test
     public void testListenerHintNotification_callSound() throws Exception {
-        NotificationRecord r = getCallRecord(1, true);
+        NotificationChannel ringtoneChannel =
+                new NotificationChannel("ringtone", "", IMPORTANCE_HIGH);
+        ringtoneChannel.setSound(Settings.System.DEFAULT_RINGTONE_URI,
+                new AudioAttributes.Builder().setUsage(USAGE_NOTIFICATION_RINGTONE).build());
+        NotificationRecord r = getCallRecord(1, ringtoneChannel, true);
 
         mService.setHints(NotificationListenerService.HINT_HOST_DISABLE_NOTIFICATION_EFFECTS);
 
@@ -1349,6 +1396,167 @@
         verifyBeepLooped();
     }
 
+    @Test
+    public void testCannotInterruptRingtoneInsistentBeep() throws Exception {
+        NotificationChannel ringtoneChannel =
+                new NotificationChannel("ringtone", "", IMPORTANCE_HIGH);
+        ringtoneChannel.setSound(Settings.System.DEFAULT_RINGTONE_URI,
+                new AudioAttributes.Builder().setUsage(USAGE_NOTIFICATION_RINGTONE).build());
+        NotificationRecord ringtoneNotification = getCallRecord(1, ringtoneChannel, true);
+        mService.addNotification(ringtoneNotification);
+
+        mService.buzzBeepBlinkLocked(ringtoneNotification);
+        verifyBeepLooped();
+
+        NotificationRecord interrupter = getBeepyOtherNotification();
+        assertTrue(mService.shouldMuteNotificationLocked(interrupter));
+        mService.buzzBeepBlinkLocked(interrupter);
+
+        verifyBeep(1);
+
+        assertFalse(interrupter.isInterruptive());
+        assertEquals(-1, interrupter.getLastAudiblyAlertedMs());
+    }
+
+    @Test
+    public void testCannotInterruptRingtoneInsistentBuzz() {
+        NotificationChannel ringtoneChannel =
+                new NotificationChannel("ringtone", "", IMPORTANCE_HIGH);
+        ringtoneChannel.setSound(Uri.EMPTY,
+                new AudioAttributes.Builder().setUsage(USAGE_NOTIFICATION_RINGTONE).build());
+        ringtoneChannel.enableVibration(true);
+        NotificationRecord ringtoneNotification = getCallRecord(1, ringtoneChannel, true);
+        assertFalse(mService.shouldMuteNotificationLocked(ringtoneNotification));
+
+        mService.buzzBeepBlinkLocked(ringtoneNotification);
+        verifyVibrateLooped();
+
+        NotificationRecord interrupter = getBuzzyOtherNotification();
+        assertTrue(mService.shouldMuteNotificationLocked(interrupter));
+        mService.buzzBeepBlinkLocked(interrupter);
+
+        verifyVibrate(1);
+
+        assertFalse(interrupter.isInterruptive());
+        assertEquals(-1, interrupter.getLastAudiblyAlertedMs());
+    }
+
+    @Test
+    public void testCanInterruptRingtoneNonInsistentBeep() throws Exception {
+        NotificationChannel ringtoneChannel =
+                new NotificationChannel("ringtone", "", IMPORTANCE_HIGH);
+        ringtoneChannel.setSound(Settings.System.DEFAULT_RINGTONE_URI,
+                new AudioAttributes.Builder().setUsage(USAGE_NOTIFICATION_RINGTONE).build());
+        NotificationRecord ringtoneNotification = getCallRecord(1, ringtoneChannel, false);
+
+        mService.buzzBeepBlinkLocked(ringtoneNotification);
+        verifyBeepUnlooped();
+
+        NotificationRecord interrupter = getBeepyOtherNotification();
+        mService.buzzBeepBlinkLocked(interrupter);
+
+        verifyBeep(2);
+
+        assertTrue(interrupter.isInterruptive());
+    }
+
+    @Test
+    public void testCanInterruptRingtoneNonInsistentBuzz() {
+        NotificationChannel ringtoneChannel =
+                new NotificationChannel("ringtone", "", IMPORTANCE_HIGH);
+        ringtoneChannel.setSound(null,
+                new AudioAttributes.Builder().setUsage(USAGE_NOTIFICATION_RINGTONE).build());
+        ringtoneChannel.enableVibration(true);
+        NotificationRecord ringtoneNotification = getCallRecord(1, ringtoneChannel, false);
+
+        mService.buzzBeepBlinkLocked(ringtoneNotification);
+        verifyVibrate();
+
+        NotificationRecord interrupter = getBuzzyOtherNotification();
+        mService.buzzBeepBlinkLocked(interrupter);
+
+        verifyVibrate(2);
+
+        assertTrue(interrupter.isInterruptive());
+    }
+
+    @Test
+    public void testRingtoneInsistentBeep_doesNotBlockFutureSoundsOnceStopped() throws Exception {
+        NotificationChannel ringtoneChannel =
+                new NotificationChannel("ringtone", "", IMPORTANCE_HIGH);
+        ringtoneChannel.setSound(Settings.System.DEFAULT_RINGTONE_URI,
+                new AudioAttributes.Builder().setUsage(USAGE_NOTIFICATION_RINGTONE).build());
+        NotificationRecord ringtoneNotification = getCallRecord(1, ringtoneChannel, true);
+
+        mService.buzzBeepBlinkLocked(ringtoneNotification);
+        verifyBeepLooped();
+
+        mService.clearSoundLocked();
+
+        NotificationRecord interrupter = getBeepyOtherNotification();
+        mService.buzzBeepBlinkLocked(interrupter);
+
+        verifyBeep(2);
+
+        assertTrue(interrupter.isInterruptive());
+    }
+
+    @Test
+    public void testRingtoneInsistentBuzz_doesNotBlockFutureSoundsOnceStopped() {
+        NotificationChannel ringtoneChannel =
+                new NotificationChannel("ringtone", "", IMPORTANCE_HIGH);
+        ringtoneChannel.setSound(null,
+                new AudioAttributes.Builder().setUsage(USAGE_NOTIFICATION_RINGTONE).build());
+        ringtoneChannel.enableVibration(true);
+        NotificationRecord ringtoneNotification = getCallRecord(1, ringtoneChannel, true);
+
+        mService.buzzBeepBlinkLocked(ringtoneNotification);
+        verifyVibrateLooped();
+
+        mService.clearVibrateLocked();
+
+        NotificationRecord interrupter = getBuzzyOtherNotification();
+        mService.buzzBeepBlinkLocked(interrupter);
+
+        verifyVibrate(2);
+
+        assertTrue(interrupter.isInterruptive());
+    }
+
+    @Test
+    public void testCanInterruptNonRingtoneInsistentBeep() throws Exception {
+        NotificationChannel fakeRingtoneChannel =
+                new NotificationChannel("ringtone", "", IMPORTANCE_HIGH);
+        NotificationRecord ringtoneNotification = getCallRecord(1, fakeRingtoneChannel, true);
+
+        mService.buzzBeepBlinkLocked(ringtoneNotification);
+        verifyBeepLooped();
+
+        NotificationRecord interrupter = getBeepyOtherNotification();
+        mService.buzzBeepBlinkLocked(interrupter);
+
+        verifyBeep(2);
+
+        assertTrue(interrupter.isInterruptive());
+    }
+
+    @Test
+    public void testCanInterruptNonRingtoneInsistentBuzz() throws Exception {
+        NotificationChannel fakeRingtoneChannel =
+                new NotificationChannel("ringtone", "", IMPORTANCE_HIGH);
+        fakeRingtoneChannel.enableVibration(true);
+        NotificationRecord ringtoneNotification = getCallRecord(1, fakeRingtoneChannel, true);
+
+        mService.buzzBeepBlinkLocked(ringtoneNotification);
+
+        NotificationRecord interrupter = getBuzzyOtherNotification();
+        mService.buzzBeepBlinkLocked(interrupter);
+
+        verifyVibrate(2);
+
+        assertTrue(interrupter.isInterruptive());
+    }
+
     static class VibrateRepeatMatcher implements ArgumentMatcher<VibrationEffect> {
         private final int mRepeatIndex;
 
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 4ea2fc0..cd0f4f1 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -537,6 +537,18 @@
         return new NotificationRecord(mContext, sbn, channel);
     }
 
+    private NotificationRecord generateNotificationRecord(NotificationChannel channel, int userId) {
+        if (channel == null) {
+            channel = mTestNotificationChannel;
+        }
+        Notification.Builder nb = new Notification.Builder(mContext, channel.getId())
+                .setContentTitle("foo")
+                .setSmallIcon(android.R.drawable.sym_def_app_icon);
+        StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 1, "tag", mUid, 0,
+                nb.build(), new UserHandle(userId), null, 0);
+        return new NotificationRecord(mContext, sbn, channel);
+    }
+
     private Map<String, Answer> getSignalExtractorSideEffects() {
         Map<String, Answer> answers = new ArrayMap<>();
 
@@ -5451,6 +5463,112 @@
     }
 
     @Test
+    public void testGrantInlineReplyUriPermission_recordExists() throws Exception {
+        NotificationRecord nr = generateNotificationRecord(mTestNotificationChannel, 0);
+        mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag",
+                nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId());
+        waitForIdle();
+
+        // A notification exists for the given record
+        StatusBarNotification[] notifsBefore = mBinderService.getActiveNotifications(PKG);
+        assertEquals(1, notifsBefore.length);
+
+        reset(mPackageManager);
+
+        Uri uri = ContentUris.withAppendedId(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, 1);
+
+        mService.mNotificationDelegate.grantInlineReplyUriPermission(
+                nr.getKey(), uri, nr.sbn.getUid());
+
+        // Grant permission called for the UID of SystemUI under the target user ID
+        verify(mUgm, times(1)).grantUriPermissionFromOwner(any(),
+                eq(nr.sbn.getUid()), eq(nr.sbn.getPackageName()), eq(uri), anyInt(), anyInt(),
+                eq(nr.sbn.getUserId()));
+    }
+
+    @Test
+    public void testGrantInlineReplyUriPermission_userAll() throws Exception {
+        // generate a NotificationRecord for USER_ALL to make sure it's converted into USER_SYSTEM
+        NotificationRecord nr =
+                generateNotificationRecord(mTestNotificationChannel, UserHandle.USER_ALL);
+        mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag",
+                nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId());
+        waitForIdle();
+
+        // A notification exists for the given record
+        StatusBarNotification[] notifsBefore = mBinderService.getActiveNotifications(PKG);
+        assertEquals(1, notifsBefore.length);
+
+        reset(mPackageManager);
+
+        Uri uri = ContentUris.withAppendedId(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, 1);
+
+        mService.mNotificationDelegate.grantInlineReplyUriPermission(
+                nr.getKey(), uri, nr.sbn.getUid());
+
+        // Target user for the grant is USER_ALL instead of USER_SYSTEM
+        verify(mUgm, times(1)).grantUriPermissionFromOwner(any(),
+                eq(nr.sbn.getUid()), eq(nr.sbn.getPackageName()), eq(uri), anyInt(), anyInt(),
+                eq(UserHandle.USER_SYSTEM));
+    }
+
+    @Test
+    public void testGrantInlineReplyUriPermission_acrossUsers() throws Exception {
+        // generate a NotificationRecord for USER_ALL to make sure it's converted into USER_SYSTEM
+        int otherUserId = 11;
+        NotificationRecord nr =
+                generateNotificationRecord(mTestNotificationChannel, otherUserId);
+        mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag",
+                nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId());
+        waitForIdle();
+
+        // A notification exists for the given record
+        StatusBarNotification[] notifsBefore = mBinderService.getActiveNotifications(PKG);
+        assertEquals(1, notifsBefore.length);
+
+        reset(mPackageManager);
+
+        Uri uri = ContentUris.withAppendedId(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, 1);
+
+        int uid = 0; // sysui on primary user
+        int otherUserUid = (otherUserId * 100000) + 1; // SystemUI as a different user
+        String sysuiPackage = "sysui";
+        final String[] sysuiPackages = new String[] { sysuiPackage };
+        when(mPackageManager.getPackagesForUid(uid)).thenReturn(sysuiPackages);
+
+        // Make sure to mock call for USER_SYSTEM and not USER_ALL, since it's been replaced by the
+        // time this is called
+        when(mPackageManager.getPackageUid(sysuiPackage, 0, otherUserId))
+                .thenReturn(otherUserUid);
+
+        mService.mNotificationDelegate.grantInlineReplyUriPermission(nr.getKey(), uri, uid);
+
+        // Target user for the grant is USER_ALL instead of USER_SYSTEM
+        verify(mUgm, times(1)).grantUriPermissionFromOwner(any(),
+                eq(otherUserUid), eq(nr.sbn.getPackageName()), eq(uri), anyInt(), anyInt(),
+                eq(otherUserId));
+    }
+
+    @Test
+    public void testGrantInlineReplyUriPermission_noRecordExists() throws Exception {
+        NotificationRecord nr = generateNotificationRecord(mTestNotificationChannel);
+        waitForIdle();
+
+        // No notifications exist for the given record
+        StatusBarNotification[] notifsBefore = mBinderService.getActiveNotifications(PKG);
+        assertEquals(0, notifsBefore.length);
+
+        Uri uri = ContentUris.withAppendedId(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, 1);
+        int uid = 0; // sysui on primary user
+
+        mService.mNotificationDelegate.grantInlineReplyUriPermission(nr.getKey(), uri, uid);
+
+        // Grant permission not called if no record exists for the given key
+        verify(mUgm, times(0)).grantUriPermissionFromOwner(any(), anyInt(), any(),
+                eq(uri), anyInt(), anyInt(), anyInt());
+    }
+
+    @Test
     public void testNotificationBubbles_disabled_lowRamDevice() throws Exception {
         // Bubbles are allowed!
         setUpPrefsForBubbles(true /* global */, true /* app */, true /* channel */);
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
index aaaa7a5..2836e69 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
@@ -28,6 +28,7 @@
 import static com.google.common.truth.Truth.assertWithMessage;
 
 import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.ArgumentMatchers.argThat;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.timeout;
@@ -128,7 +129,7 @@
 
         mActivityMetricsLogger.notifyActivityLaunching(intent);
 
-        verifyAsync(mLaunchObserver).onIntentStarted(eq(intent));
+        verifyAsync(mLaunchObserver).onIntentStarted(eq(intent), anyLong());
         verifyNoMoreInteractions(mLaunchObserver);
     }
 
@@ -163,12 +164,12 @@
        testOnActivityLaunched();
 
        mActivityMetricsLogger.notifyTransitionStarting(new SparseIntArray(),
-               SystemClock.uptimeMillis());
+               SystemClock.elapsedRealtimeNanos());
 
        mActivityMetricsLogger.notifyWindowsDrawn(mActivityRecord.getWindowingMode(),
-               SystemClock.uptimeMillis());
+               SystemClock.elapsedRealtimeNanos());
 
-       verifyAsync(mLaunchObserver).onActivityLaunchFinished(eqProto(mActivityRecord));
+       verifyAsync(mLaunchObserver).onActivityLaunchFinished(eqProto(mActivityRecord), anyLong());
        verifyNoMoreInteractions(mLaunchObserver);
     }
 
@@ -186,6 +187,16 @@
     }
 
     @Test
+    public void testOnReportFullyDrawn() throws Exception {
+        testOnActivityLaunched();
+
+        mActivityMetricsLogger.logAppTransitionReportedDrawn(mActivityRecord, false);
+
+        verifyAsync(mLaunchObserver).onReportFullyDrawn(eqProto(mActivityRecord), anyLong());
+        verifyNoMoreInteractions(mLaunchObserver);
+    }
+
+    @Test
     public void testOnActivityLaunchedTrampoline() throws Exception {
         testOnIntentStarted();
 
@@ -206,12 +217,13 @@
        testOnActivityLaunchedTrampoline();
 
        mActivityMetricsLogger.notifyTransitionStarting(new SparseIntArray(),
-               SystemClock.uptimeMillis());
+               SystemClock.elapsedRealtimeNanos());
 
        mActivityMetricsLogger.notifyWindowsDrawn(mActivityRecordTrampoline.getWindowingMode(),
-               SystemClock.uptimeMillis());
+               SystemClock.elapsedRealtimeNanos());
 
-       verifyAsync(mLaunchObserver).onActivityLaunchFinished(eqProto(mActivityRecordTrampoline));
+       verifyAsync(mLaunchObserver).onActivityLaunchFinished(eqProto(mActivityRecordTrampoline),
+                                                             anyLong());
        verifyNoMoreInteractions(mLaunchObserver);
     }
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
index c2a05c24..2835c1f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
@@ -891,7 +891,7 @@
         activity.app = null;
         overlayActivity.app = null;
 
-        assertEquals(2, mTask.mActivities.size());
+        assertEquals(2, mTask.getChildCount());
 
         mStack.finishDisabledPackageActivitiesLocked(activity.packageName,
                 null  /* filterByClasses */, true /* doit */, true /* evenPersistent */,
@@ -900,7 +900,7 @@
         // Although the overlay activity is in another package, the non-overlay activities are
         // removed from the task. Since the overlay activity should be removed as well, the task
         // should be empty.
-        assertThat(mTask.mActivities).isEmpty();
+        assertFalse(mTask.hasChild());
         assertThat(mStack.getAllTasks()).isEmpty();
     }
 
@@ -918,11 +918,11 @@
         // second activity will be immediately removed as it has no state.
         secondActivity.setSavedState(null /* savedState */);
 
-        assertEquals(2, mTask.mActivities.size());
+        assertEquals(2, mTask.getChildCount());
 
         mStack.handleAppDiedLocked(secondActivity.app);
 
-        assertThat(mTask.mActivities).isEmpty();
+        assertFalse(mTask.hasChild());
         assertThat(mStack.getAllTasks()).isEmpty();
     }
 
@@ -936,7 +936,7 @@
 
         mStack.handleAppDiedLocked(activity.app);
 
-        assertEquals(1, mTask.mActivities.size());
+        assertEquals(1, mTask.getChildCount());
         assertEquals(1, mStack.getAllTasks().size());
     }
 
@@ -950,7 +950,7 @@
 
         mStack.handleAppDiedLocked(activity.app);
 
-        assertThat(mTask.mActivities).isEmpty();
+        assertFalse(mTask.hasChild());
         assertThat(mStack.getAllTasks()).isEmpty();
     }
 
@@ -964,7 +964,7 @@
 
         mStack.handleAppDiedLocked(activity.app);
 
-        assertEquals(1, mTask.mActivities.size());
+        assertEquals(1, mTask.getChildCount());
         assertEquals(1, mStack.getAllTasks().size());
     }
 
@@ -978,7 +978,7 @@
 
         mStack.handleAppDiedLocked(activity.app);
 
-        assertThat(mTask.mActivities).isEmpty();
+        assertFalse(mTask.hasChild());
         assertThat(mStack.getAllTasks()).isEmpty();
     }
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
index 77fbdcf..d43fe63 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
@@ -215,23 +215,6 @@
             return this;
         }
 
-        static Pair<Intent, ActivityInfo> createIntentAndActivityInfo() {
-            // TODO: Look into consolidating with dup. code in build() method below.
-            final int id = sCurrentActivityId++;
-            final ComponentName component = ComponentName.createRelative(
-                    DEFAULT_COMPONENT_PACKAGE_NAME, DEFAULT_COMPONENT_CLASS_NAME + id);
-
-            final Intent intent = new Intent();
-            intent.setComponent(component);
-
-            final ActivityInfo aInfo = new ActivityInfo();
-            aInfo.applicationInfo = new ApplicationInfo();
-            aInfo.applicationInfo.packageName = component.getPackageName();
-            aInfo.applicationInfo.targetSdkVersion = Build.VERSION_CODES.CUR_DEVELOPMENT;
-            aInfo.packageName = component.getPackageName();
-            return new Pair<>(intent, aInfo);
-        }
-
         ActivityRecord build() {
             if (mComponent == null) {
                 final int id = sCurrentActivityId++;
@@ -249,6 +232,7 @@
             intent.setComponent(mComponent);
             final ActivityInfo aInfo = new ActivityInfo();
             aInfo.applicationInfo = new ApplicationInfo();
+            aInfo.applicationInfo.targetSdkVersion = Build.VERSION_CODES.CUR_DEVELOPMENT;
             aInfo.applicationInfo.packageName = mComponent.getPackageName();
             aInfo.applicationInfo.uid = mUid;
             aInfo.packageName = mComponent.getPackageName();
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java
index 650a911..b174251 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java
@@ -57,7 +57,7 @@
 
     private TaskStack mStack;
     private Task mTask;
-    private AppWindowToken mToken;
+    private ActivityRecord mToken;
 
     public void setUpOnDisplay(DisplayContent dc) {
         mStack = createTaskStackOnDisplay(WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD, dc);
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
index 605d520..14939cc 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
@@ -101,10 +101,10 @@
     @Test
     @FlakyTest(bugId = 131005232)
     public void testTransitWithinTask() {
-        final AppWindowToken opening = createAppWindowToken(mDisplayContent,
+        final ActivityRecord opening = createAppWindowToken(mDisplayContent,
                 WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD);
         opening.setOccludesParent(false);
-        final AppWindowToken closing = createAppWindowToken(mDisplayContent,
+        final ActivityRecord closing = createAppWindowToken(mDisplayContent,
                 WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD);
         closing.setOccludesParent(false);
         final Task task = opening.getTask();
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
index 72d9bd0..9d53676 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
@@ -159,7 +159,7 @@
 
         final TaskStack stack1 = createTaskStackOnDisplay(dc1);
         final Task task1 = createTaskInStack(stack1, 0 /* userId */);
-        final AppWindowToken token1 =
+        final ActivityRecord token1 =
                 WindowTestUtils.createTestAppWindowToken(dc1);
         task1.addChild(token1, 0);
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java
index 2661735..b4c978f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java
@@ -77,7 +77,7 @@
 
     TaskStack mStack;
     Task mTask;
-    AppWindowToken mToken;
+    ActivityRecord mToken;
 
     private final String mPackageName = getInstrumentation().getTargetContext().getPackageName();
 
@@ -410,7 +410,7 @@
     }
 
     private AppWindowToken createTestAppWindowTokenForGivenTask(Task task) {
-        final AppWindowToken appToken =
+        final ActivityRecord appToken =
                 WindowTestUtils.createTestAppWindowToken(mDisplayContent);
         task.addChild(appToken, 0);
         waitUntilHandlersIdle();
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index 2ba3cbd..f12c349 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -241,7 +241,7 @@
         assertEquals(dc, stack.getDisplayContent());
 
         final Task task = createTaskInStack(stack, 0 /* userId */);
-        final AppWindowToken token = WindowTestUtils.createTestAppWindowToken(dc);
+        final ActivityRecord token = WindowTestUtils.createTestAppWindowToken(dc);
         task.addChild(token, 0);
         assertEquals(dc, task.getDisplayContent());
         assertEquals(dc, token.getDisplayContent());
@@ -313,7 +313,7 @@
         // Add stack with activity.
         final TaskStack stack0 = createTaskStackOnDisplay(dc0);
         final Task task0 = createTaskInStack(stack0, 0 /* userId */);
-        final AppWindowToken token =
+        final ActivityRecord token =
                 WindowTestUtils.createTestAppWindowToken(dc0);
         task0.addChild(token, 0);
         dc0.configureDisplayPolicy();
@@ -321,7 +321,7 @@
 
         final TaskStack stack1 = createTaskStackOnDisplay(dc1);
         final Task task1 = createTaskInStack(stack1, 0 /* userId */);
-        final AppWindowToken token1 =
+        final ActivityRecord token1 =
                 WindowTestUtils.createTestAppWindowToken(dc0);
         task1.addChild(token1, 0);
         dc1.configureDisplayPolicy();
@@ -682,16 +682,15 @@
         // is appWin & null on the other display.
         mDisplayContent.setInputMethodWindowLocked(mImeWindow);
         newDisplay.setInputMethodWindowLocked(null);
-        assertTrue("appWin should be IME target window",
-                appWin.equals(mDisplayContent.mInputMethodTarget));
+        assertEquals("appWin should be IME target window",
+                appWin, mDisplayContent.mInputMethodTarget);
         assertNull("newDisplay Ime target: ", newDisplay.mInputMethodTarget);
 
         // Switch input method window on new display & make sure the input method target also
         // switched as expected.
         newDisplay.setInputMethodWindowLocked(mImeWindow);
         mDisplayContent.setInputMethodWindowLocked(null);
-        assertTrue("appWin1 should be IME target window",
-                appWin1.equals(newDisplay.mInputMethodTarget));
+        assertEquals("appWin1 should be IME target window", appWin1, newDisplay.mInputMethodTarget);
         assertNull("default display Ime target: ", mDisplayContent.mInputMethodTarget);
     }
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java
index 304df22..452e06f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java
@@ -96,7 +96,7 @@
      * Creates a window state which can be used as a drop target.
      */
     private WindowState createDropTargetWindow(String name, int ownerId) {
-        final AppWindowToken token = WindowTestUtils.createTestAppWindowToken(
+        final ActivityRecord token = WindowTestUtils.createTestAppWindowToken(
                 mDisplayContent);
         final TaskStack stack = createTaskStackOnDisplay(
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, mDisplayContent);
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskStackContainersTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskStackContainersTests.java
index 92ddb35..eef680b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskStackContainersTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskStackContainersTests.java
@@ -54,7 +54,7 @@
         // Stack should contain visible app window to be considered visible.
         final Task pinnedTask = createTaskInStack(mPinnedStack, 0 /* userId */);
         assertFalse(mPinnedStack.isVisible());
-        final AppWindowToken pinnedApp =
+        final ActivityRecord pinnedApp =
                 WindowTestUtils.createTestAppWindowToken(mDisplayContent);
         pinnedTask.addChild(pinnedApp, 0 /* addPos */);
         assertTrue(mPinnedStack.isVisible());
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskStackTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskStackTests.java
index 2eb6ea4..d045073 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskStackTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskStackTests.java
@@ -68,13 +68,13 @@
     public void testClosingAppDifferentStackOrientation() {
         final TaskStack stack = createTaskStackOnDisplay(mDisplayContent);
         final Task task1 = createTaskInStack(stack, 0 /* userId */);
-        AppWindowToken appWindowToken1 =
+        ActivityRecord appWindowToken1 =
                 WindowTestUtils.createTestAppWindowToken(mDisplayContent);
         task1.addChild(appWindowToken1, 0);
         appWindowToken1.setOrientation(SCREEN_ORIENTATION_LANDSCAPE);
 
         final Task task2 = createTaskInStack(stack, 1 /* userId */);
-        AppWindowToken appWindowToken2 =
+        ActivityRecord appWindowToken2 =
                 WindowTestUtils.createTestAppWindowToken(mDisplayContent);
         task2.addChild(appWindowToken2, 0);
         appWindowToken2.setOrientation(SCREEN_ORIENTATION_PORTRAIT);
@@ -88,13 +88,13 @@
     public void testMoveTaskToBackDifferentStackOrientation() {
         final TaskStack stack = createTaskStackOnDisplay(mDisplayContent);
         final Task task1 = createTaskInStack(stack, 0 /* userId */);
-        AppWindowToken appWindowToken1 =
+        ActivityRecord appWindowToken1 =
                 WindowTestUtils.createTestAppWindowToken(mDisplayContent);
         task1.addChild(appWindowToken1, 0);
         appWindowToken1.setOrientation(SCREEN_ORIENTATION_LANDSCAPE);
 
         final Task task2 = createTaskInStack(stack, 1 /* userId */);
-        AppWindowToken appWindowToken2 =
+        ActivityRecord appWindowToken2 =
                 WindowTestUtils.createTestAppWindowToken(mDisplayContent);
         task2.addChild(appWindowToken2, 0);
         appWindowToken2.setOrientation(SCREEN_ORIENTATION_PORTRAIT);
diff --git a/services/tests/wmtests/src/com/android/server/wm/TestIWindow.java b/services/tests/wmtests/src/com/android/server/wm/TestIWindow.java
index 09e5027..6a94137 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TestIWindow.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TestIWindow.java
@@ -115,4 +115,8 @@
     @Override
     public void showInsets(int types, boolean fromIme) throws RemoteException {
     }
+
+    @Override
+    public void hideInsets(int types, boolean fromIme) throws RemoteException {
+    }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java
index c627c19..f44c969 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java
@@ -19,17 +19,11 @@
 import static android.app.AppOpsManager.OP_NONE;
 import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
 
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
-import static com.android.server.wm.ActivityTestsBase.ActivityBuilder.createIntentAndActivityInfo;
 import static com.android.server.wm.WindowContainer.POSITION_TOP;
 
 import android.app.ActivityManager;
-import android.content.Intent;
-import android.content.pm.ActivityInfo;
 import android.os.IBinder;
-import android.util.Pair;
 import android.view.IWindow;
 import android.view.WindowManager;
 
@@ -51,23 +45,22 @@
     }
 
     /** Creates an {@link AppWindowToken} and adds it to the specified {@link Task}. */
-    static AppWindowToken createAppWindowTokenInTask(DisplayContent dc, Task task) {
-        final AppWindowToken newToken = createTestAppWindowToken(dc);
+    static ActivityRecord createAppWindowTokenInTask(DisplayContent dc, Task task) {
+        final ActivityRecord newToken = createTestAppWindowToken(dc);
         task.addChild(newToken, POSITION_TOP);
         return newToken;
     }
 
-    static AppWindowToken createTestAppWindowToken(DisplayContent dc) {
+    static ActivityRecord createTestAppWindowToken(DisplayContent dc) {
         synchronized (dc.mWmService.mGlobalLock) {
-            Pair<Intent, ActivityInfo> pair = createIntentAndActivityInfo();
-            final AppWindowToken token = new AppWindowToken(dc.mWmService,
-                    dc.mWmService.mAtmService, new ActivityRecord.Token(pair.first), pair.second,
-                    null, pair.first, dc);
-            token.setOccludesParent(true);
-            token.setHidden(false);
-            token.hiddenRequested = false;
-            spyOn(token);
-            return token;
+            final ActivityRecord r =
+                    new ActivityTestsBase.ActivityBuilder(dc.mWmService.mAtmService)
+                            .build();
+            r.onDisplayChanged(dc);
+            r.setOccludesParent(true);
+            r.setHidden(false);
+            r.hiddenRequested = false;
+            return r;
         }
     }
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
index 4c4b21e..1fce46c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -204,15 +204,15 @@
         }
     }
 
-    AppWindowToken createAppWindowToken(DisplayContent dc, int windowingMode, int activityType) {
+    ActivityRecord createAppWindowToken(DisplayContent dc, int windowingMode, int activityType) {
         return createTestAppWindowToken(dc, windowingMode, activityType);
     }
 
-    AppWindowToken createTestAppWindowToken(DisplayContent dc, int
+    ActivityRecord createTestAppWindowToken(DisplayContent dc, int
             windowingMode, int activityType) {
         final TaskStack stack = createTaskStackOnDisplay(windowingMode, activityType, dc);
         final Task task = createTaskInStack(stack, 0 /* userId */);
-        final AppWindowToken appWindowToken =
+        final ActivityRecord appWindowToken =
                 WindowTestUtils.createTestAppWindowToken(dc);
         task.addChild(appWindowToken, 0);
         return appWindowToken;
@@ -244,7 +244,7 @@
 
     WindowState createAppWindow(Task task, int type, String name) {
         synchronized (mWm.mGlobalLock) {
-            final AppWindowToken token = WindowTestUtils.createTestAppWindowToken(mDisplayContent);
+            final ActivityRecord token = WindowTestUtils.createTestAppWindowToken(mDisplayContent);
             task.addChild(token, 0);
             return createWindow(null, type, token, name);
         }
diff --git a/startop/apps/test/Android.bp b/startop/apps/test/Android.bp
index a806320..2ff26b8 100644
--- a/startop/apps/test/Android.bp
+++ b/startop/apps/test/Android.bp
@@ -17,12 +17,14 @@
 android_app {
     name: "startop_test_app",
     srcs: [
+        "src/ComplexLayoutInflationActivity.java",
         "src/CPUIntensive.java",
         "src/EmptyActivity.java",
-        "src/LayoutInflationActivity.java",
-        "src/ComplexLayoutInflationActivity.java",
         "src/FrameLayoutInflationActivity.java",
+        "src/LayoutInflationActivity.java",
+        "src/NonInteractiveSystemServerBenchmarkActivity.java",
         "src/SystemServerBenchmarkActivity.java",
+        "src/SystemServerBenchmarks.java",
         "src/TextViewInflationActivity.java",
     ],
     sdk_version: "26", // Android O (8.0) and higher
diff --git a/startop/apps/test/AndroidManifest.xml b/startop/apps/test/AndroidManifest.xml
index 15785d4..a5ac348 100644
--- a/startop/apps/test/AndroidManifest.xml
+++ b/startop/apps/test/AndroidManifest.xml
@@ -84,6 +84,13 @@
             </intent-filter>
         </activity>
 
+        <activity
+            android:label="Non-interactive SystemServer Benchmark"
+            android:name=".NonInteractiveSystemServerBenchmarkActivity"
+            android:exported="true" />
+
     </application>
 
+    <uses-permission android:name="android.permission.WAKE_LOCK" />
+
 </manifest>
diff --git a/startop/apps/test/README.md b/startop/apps/test/README.md
index dadc66a..949dff7 100644
--- a/startop/apps/test/README.md
+++ b/startop/apps/test/README.md
@@ -24,3 +24,14 @@
 inflation.
 
     adb shell am start -n com.android.startop.test/.ComplexLayoutInflationActivity
+
+## NonInteractiveSystemServerBenchmark
+
+This activity is for running microbenchmarks from the command line. Run as follows:
+
+   adb shell am start -W -n com.android.startop.test .NonInteractiveSystemServerBenchmarkActivity
+
+It takes awhile (and there's currently no automated way to make sure it's done),
+but when it finishes, you can get the results like this:
+
+    adb shell cat /sdcard/Android/data/com.android.startop.test/files/benchmark.csv
diff --git a/startop/apps/test/src/NonInteractiveSystemServerBenchmarkActivity.java b/startop/apps/test/src/NonInteractiveSystemServerBenchmarkActivity.java
new file mode 100644
index 0000000..a2dc2cf
--- /dev/null
+++ b/startop/apps/test/src/NonInteractiveSystemServerBenchmarkActivity.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.startop.test;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.PrintStream;
+import java.util.ArrayList;
+
+import android.app.Activity;
+import android.app.ActivityManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.GridLayout;
+import android.widget.TextView;
+
+public class NonInteractiveSystemServerBenchmarkActivity extends Activity {
+    ArrayList<CharSequence> benchmarkNames = new ArrayList();
+    ArrayList<Runnable> benchmarkThunks = new ArrayList();
+
+    PrintStream out;
+
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        SystemServerBenchmarks.initializeBenchmarks(this, (name, thunk) -> {
+            benchmarkNames.add(name);
+            benchmarkThunks.add(thunk);
+        });
+
+        try {
+            out = new PrintStream(new File(getExternalFilesDir(null), "benchmark.csv"));
+        } catch (FileNotFoundException e) {
+            throw new RuntimeException(e);
+        }
+        out.println("Name,Mean,Stdev");
+        runBenchmarks(0);
+    }
+
+    void runBenchmarks(int i) {
+        if (i < benchmarkNames.size()) {
+            SystemServerBenchmarks.runBenchmarkInBackground(benchmarkThunks.get(i),
+                    (mean, stdev) -> {
+                        out.printf("%s,%.0f,%.0f\n", benchmarkNames.get(i), mean, stdev);
+                        runBenchmarks(i + 1);
+                    });
+        }
+    }
+}
diff --git a/startop/apps/test/src/SystemServerBenchmarkActivity.java b/startop/apps/test/src/SystemServerBenchmarkActivity.java
index c8d9fde..75ea69b 100644
--- a/startop/apps/test/src/SystemServerBenchmarkActivity.java
+++ b/startop/apps/test/src/SystemServerBenchmarkActivity.java
@@ -31,13 +31,25 @@
 import android.widget.GridLayout;
 import android.widget.TextView;
 
+public class SystemServerBenchmarkActivity extends Activity implements BenchmarkRunner {
+    private GridLayout benchmarkList;
 
-class Benchmark {
-    // Time limit to run benchmarks in seconds
-    public static final int TIME_LIMIT = 5;
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.system_server_benchmark_page);
 
-    public Benchmark(ViewGroup parent, CharSequence name, Runnable thunk) {
-        Context context = parent.getContext();
+        benchmarkList = findViewById(R.id.benchmark_list);
+
+        SystemServerBenchmarks.initializeBenchmarks(this, this);
+    }
+
+    /**
+     * Adds a benchmark to the set to run.
+     *
+     * @param name A short name that shows up in the UI or benchmark results
+     */
+    public void addBenchmark(CharSequence name, Runnable thunk) {
+        Context context = benchmarkList.getContext();
         Button button = new Button(context);
         TextView mean = new TextView(context);
         TextView stdev = new TextView(context);
@@ -50,165 +62,14 @@
             mean.setText("Running...");
             stdev.setText("");
 
-            new AsyncTask() {
-                double resultMean = 0;
-                double resultStdev = 0;
-
-                @Override
-                protected Object doInBackground(Object... _args) {
-                    long startTime = System.nanoTime();
-                    int count = 0;
-
-                    // Run benchmark
-                    while (true) {
-                        long elapsed = -System.nanoTime();
-                        thunk.run();
-                        elapsed += System.nanoTime();
-
-                        count++;
-                        double elapsedVariance = (double) elapsed - resultMean;
-                        resultMean += elapsedVariance / count;
-                        resultStdev += elapsedVariance * ((double) elapsed - resultMean);
-
-                        if (System.nanoTime() - startTime > TIME_LIMIT * 1e9) {
-                            break;
-                        }
-                    }
-                    resultStdev = Math.sqrt(resultStdev / (count - 1));
-
-                    return null;
-                }
-
-                @Override
-                protected void onPostExecute(Object _result) {
-                    mean.setText(String.format("%.3f", resultMean / 1e6));
-                    stdev.setText(String.format("%.3f", resultStdev / 1e6));
-                }
-            }.execute(new Object());
-        });
-
-        parent.addView(button);
-        parent.addView(mean);
-        parent.addView(stdev);
-    }
-}
-
-public class SystemServerBenchmarkActivity extends Activity {
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        setContentView(R.layout.system_server_benchmark_page);
-
-        GridLayout benchmarkList = findViewById(R.id.benchmark_list);
-
-        new Benchmark(benchmarkList, "Empty", () -> {
-        });
-
-        new Benchmark(benchmarkList, "CPU Intensive (1 thread)", () -> {
-            CPUIntensive.doSomeWork(1);
-        });
-
-        new Benchmark(benchmarkList, "CPU Intensive (2 thread)", () -> {
-            CPUIntensive.doSomeWork(2);
-        });
-
-        new Benchmark(benchmarkList, "CPU Intensive (4 thread)", () -> {
-            CPUIntensive.doSomeWork(4);
-        });
-
-        new Benchmark(benchmarkList, "CPU Intensive (8 thread)", () -> {
-            CPUIntensive.doSomeWork(8);
-        });
-
-        PackageManager pm = getPackageManager();
-        new Benchmark(benchmarkList, "getInstalledApplications", () -> {
-            pm.getInstalledApplications(PackageManager.MATCH_SYSTEM_ONLY);
-        });
-
-        new Benchmark(benchmarkList, "getInstalledPackages", () -> {
-            pm.getInstalledPackages(PackageManager.GET_ACTIVITIES);
-        });
-
-        new Benchmark(benchmarkList, "getPackageInfo", () -> {
-            try {
-                pm.getPackageInfo("com.android.startop.test", 0);
-            } catch (NameNotFoundException e) {
-                throw new RuntimeException(e);
-            }
-        });
-
-        new Benchmark(benchmarkList, "getApplicationInfo", () -> {
-            try {
-                pm.getApplicationInfo("com.android.startop.test", 0);
-            } catch (NameNotFoundException e) {
-                throw new RuntimeException(e);
-            }
-        });
-
-        try {
-            ApplicationInfo app = pm.getApplicationInfo("com.android.startop.test", 0);
-            new Benchmark(benchmarkList, "getResourcesForApplication", () -> {
-                try {
-                    pm.getResourcesForApplication(app);
-                } catch (NameNotFoundException e) {
-                    throw new RuntimeException(e);
-                }
+            SystemServerBenchmarks.runBenchmarkInBackground(thunk, (resultMean, resultStdev) -> {
+                mean.setText(String.format("%.3f", resultMean / 1e6));
+                stdev.setText(String.format("%.3f", resultStdev / 1e6));
             });
-
-            new Benchmark(benchmarkList, "getPackagesForUid", () -> {
-                pm.getPackagesForUid(app.uid);
-            });
-        } catch (NameNotFoundException e) {
-            throw new RuntimeException(e);
-        }
-
-        ComponentName component = new ComponentName(this, this.getClass());
-        new Benchmark(benchmarkList, "getActivityInfo", () -> {
-            try {
-                pm.getActivityInfo(component, PackageManager.GET_META_DATA);
-            } catch (NameNotFoundException e) {
-                throw new RuntimeException(e);
-            }
         });
 
-        new Benchmark(benchmarkList, "getLaunchIntentForPackage", () -> {
-            pm.getLaunchIntentForPackage("com.android.startop.test");
-        });
-
-        new Benchmark(benchmarkList, "getPackageUid", () -> {
-            try {
-                pm.getPackageUid("com.android.startop.test", 0);
-            } catch (NameNotFoundException e) {
-                throw new RuntimeException(e);
-            }
-        });
-
-        new Benchmark(benchmarkList, "checkPermission", () -> {
-            // Check for the first permission I could find.
-            pm.checkPermission("android.permission.SEND_SMS", "com.android.startop.test");
-        });
-
-        new Benchmark(benchmarkList, "checkSignatures", () -> {
-            // Compare with settings, since settings is on both AOSP and Master builds
-            pm.checkSignatures("com.android.settings", "com.android.startop.test");
-        });
-
-        Intent intent = new Intent(Intent.ACTION_BOOT_COMPLETED);
-        new Benchmark(benchmarkList, "queryBroadcastReceivers", () -> {
-            pm.queryBroadcastReceivers(intent, 0);
-        });
-
-        new Benchmark(benchmarkList, "hasSystemFeature", () -> {
-            pm.hasSystemFeature(PackageManager.FEATURE_CAMERA);
-        });
-
-        new Benchmark(benchmarkList, "resolveService", () -> {
-            pm.resolveService(intent, 0);
-        });
-
-        ActivityManager am = (ActivityManager)getSystemService(Context.ACTIVITY_SERVICE);
-        new Benchmark(benchmarkList, "getRunningAppProcesses", () -> {
-            am.getRunningAppProcesses();
-        });
-
+        benchmarkList.addView(button);
+        benchmarkList.addView(mean);
+        benchmarkList.addView(stdev);
     }
 }
diff --git a/startop/apps/test/src/SystemServerBenchmarks.java b/startop/apps/test/src/SystemServerBenchmarks.java
new file mode 100644
index 0000000..126c2c8
--- /dev/null
+++ b/startop/apps/test/src/SystemServerBenchmarks.java
@@ -0,0 +1,222 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.startop.test;
+
+import android.app.Activity;
+import android.app.ActivityManager;
+import android.app.PendingIntent;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.os.AsyncTask;
+import android.os.PowerManager;
+
+/**
+ * An interface for running benchmarks and collecting results. Used so we can have both an
+ * interactive runner and a non-interactive runner.
+ */
+interface BenchmarkRunner {
+    void addBenchmark(CharSequence name, Runnable thunk);
+}
+
+interface ResultListener {
+    /**
+     * Called when a benchmark result is ready
+     *
+     * @param mean  The average iteration time in nanoseconds
+     * @param stdev The standard deviation of iteration times in nanoseconds
+     */
+    void onResult(double mean, double stdev);
+}
+
+class SystemServerBenchmarks {
+    // Time limit to run benchmarks in seconds
+    public static final int TIME_LIMIT = 5;
+
+    static void initializeBenchmarks(Activity parent, BenchmarkRunner benchmarks) {
+        benchmarks.addBenchmark("Empty", () -> {
+        });
+
+        benchmarks.addBenchmark("CPU Intensive (1 thread)", () -> {
+            CPUIntensive.doSomeWork(1);
+        });
+
+        benchmarks.addBenchmark("CPU Intensive (2 thread)", () -> {
+            CPUIntensive.doSomeWork(2);
+        });
+
+        benchmarks.addBenchmark("CPU Intensive (4 thread)", () -> {
+            CPUIntensive.doSomeWork(4);
+        });
+
+        benchmarks.addBenchmark("CPU Intensive (8 thread)", () -> {
+            CPUIntensive.doSomeWork(8);
+        });
+
+        PackageManager pm = parent.getPackageManager();
+        benchmarks.addBenchmark("getInstalledApplications", () -> {
+            pm.getInstalledApplications(PackageManager.MATCH_SYSTEM_ONLY);
+        });
+
+        benchmarks.addBenchmark("getInstalledPackages", () -> {
+            pm.getInstalledPackages(PackageManager.GET_ACTIVITIES);
+        });
+
+        benchmarks.addBenchmark("getPackageInfo", () -> {
+            try {
+                pm.getPackageInfo("com.android.startop.test", 0);
+            } catch (NameNotFoundException e) {
+                throw new RuntimeException(e);
+            }
+        });
+
+        benchmarks.addBenchmark("getApplicationInfo", () -> {
+            try {
+                pm.getApplicationInfo("com.android.startop.test", 0);
+            } catch (NameNotFoundException e) {
+                throw new RuntimeException(e);
+            }
+        });
+
+        try {
+            ApplicationInfo app = pm.getApplicationInfo("com.android.startop.test", 0);
+            benchmarks.addBenchmark("getResourcesForApplication", () -> {
+                try {
+                    pm.getResourcesForApplication(app);
+                } catch (NameNotFoundException e) {
+                    throw new RuntimeException(e);
+                }
+            });
+
+            benchmarks.addBenchmark("getPackagesForUid", () -> {
+                pm.getPackagesForUid(app.uid);
+            });
+        } catch (NameNotFoundException e) {
+            throw new RuntimeException(e);
+        }
+
+        ComponentName component = new ComponentName(parent, parent.getClass());
+        benchmarks.addBenchmark("getActivityInfo", () -> {
+            try {
+                pm.getActivityInfo(component, PackageManager.GET_META_DATA);
+            } catch (NameNotFoundException e) {
+                throw new RuntimeException(e);
+            }
+        });
+
+        benchmarks.addBenchmark("getLaunchIntentForPackage", () -> {
+            pm.getLaunchIntentForPackage("com.android.startop.test");
+        });
+
+        benchmarks.addBenchmark("getPackageUid", () -> {
+            try {
+                pm.getPackageUid("com.android.startop.test", 0);
+            } catch (NameNotFoundException e) {
+                throw new RuntimeException(e);
+            }
+        });
+
+        benchmarks.addBenchmark("checkPermission", () -> {
+            // Check for the first permission I could find.
+            pm.checkPermission("android.permission.SEND_SMS", "com.android.startop.test");
+        });
+
+        benchmarks.addBenchmark("checkSignatures", () -> {
+            // Compare with settings, since settings is on both AOSP and Master builds
+            pm.checkSignatures("com.android.settings", "com.android.startop.test");
+        });
+
+        Intent intent = new Intent(Intent.ACTION_BOOT_COMPLETED);
+        benchmarks.addBenchmark("queryBroadcastReceivers", () -> {
+            pm.queryBroadcastReceivers(intent, 0);
+        });
+
+        benchmarks.addBenchmark("hasSystemFeature", () -> {
+            pm.hasSystemFeature(PackageManager.FEATURE_CAMERA);
+        });
+
+        benchmarks.addBenchmark("resolveService", () -> {
+            pm.resolveService(intent, 0);
+        });
+
+        ActivityManager am = (ActivityManager) parent.getSystemService(Context.ACTIVITY_SERVICE);
+        benchmarks.addBenchmark("getRunningAppProcesses", () -> {
+            am.getRunningAppProcesses();
+        });
+
+        // We use PendingIntent.getCreatorPackage, since
+        // getPackageIntentForSender is not public to us, but getCreatorPackage
+        // is just a thin wrapper around it.
+        PendingIntent pi = PendingIntent.getActivity(parent, 0, new Intent(), 0);
+        benchmarks.addBenchmark("getPackageIntentForSender", () -> {
+            pi.getCreatorPackage();
+        });
+
+        PowerManager pwr = (PowerManager) parent.getSystemService(Context.POWER_SERVICE);
+        PowerManager.WakeLock wl = pwr.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "benchmark tag");
+        benchmarks.addBenchmark("WakeLock Acquire/Release", () -> {
+            wl.acquire();
+            wl.release();
+        });
+    }
+
+    /**
+     * A helper method for benchark runners to actually run the benchmark and gather stats
+     *
+     * @param thunk    The code whose performance we want to measure
+     * @param reporter What to do with the results
+     */
+    static void runBenchmarkInBackground(Runnable thunk, ResultListener reporter) {
+        new AsyncTask() {
+            double resultMean = 0;
+            double resultStdev = 0;
+
+            @Override
+            protected Object doInBackground(Object... _args) {
+                long startTime = System.nanoTime();
+                int count = 0;
+
+                // Run benchmark
+                while (true) {
+                    long elapsed = -System.nanoTime();
+                    thunk.run();
+                    elapsed += System.nanoTime();
+
+                    count++;
+                    double elapsedVariance = (double) elapsed - resultMean;
+                    resultMean += elapsedVariance / count;
+                    resultStdev += elapsedVariance * ((double) elapsed - resultMean);
+
+                    if (System.nanoTime() - startTime > TIME_LIMIT * 1e9) {
+                        break;
+                    }
+                }
+                resultStdev = Math.sqrt(resultStdev / (count - 1));
+
+                return null;
+            }
+
+            @Override
+            protected void onPostExecute(Object _result) {
+                reporter.onResult(resultMean, resultStdev);
+            }
+        }.execute(new Object());
+    }
+}
diff --git a/startop/iorap/src/com/google/android/startop/iorap/AppLaunchEvent.java b/startop/iorap/src/com/google/android/startop/iorap/AppLaunchEvent.java
index acf9946..cf120cf 100644
--- a/startop/iorap/src/com/google/android/startop/iorap/AppLaunchEvent.java
+++ b/startop/iorap/src/com/google/android/startop/iorap/AppLaunchEvent.java
@@ -86,10 +86,14 @@
     public static final class IntentStarted extends AppLaunchEvent {
         @NonNull
         public final Intent intent;
+        public final long timestampNs;
 
-        public IntentStarted(@SequenceId long sequenceId, Intent intent) {
+        public IntentStarted(@SequenceId long sequenceId,
+                             Intent intent,
+                             long timestampNs) {
             super(sequenceId);
             this.intent = intent;
+            this.timestampNs = timestampNs;
 
             Objects.requireNonNull(intent, "intent");
         }
@@ -98,14 +102,16 @@
         public boolean equals(Object other) {
             if (other instanceof IntentStarted) {
                 return intent.equals(((IntentStarted)other).intent) &&
-                        super.equals(other);
+                       timestampNs == ((IntentStarted)other).timestampNs &&
+                       super.equals(other);
             }
             return false;
         }
 
         @Override
         protected String toStringBody() {
-            return ", intent=" + intent.toString();
+            return ", intent=" + intent.toString() +
+                   " , timestampNs=" + Long.toString(timestampNs);
         }
 
 
@@ -113,11 +119,13 @@
         protected void writeToParcelImpl(Parcel p, int flags) {
             super.writeToParcelImpl(p, flags);
             IntentProtoParcelable.write(p, intent, flags);
+            p.writeLong(timestampNs);
         }
 
         IntentStarted(Parcel p) {
             super(p);
             intent = IntentProtoParcelable.create(p);
+            timestampNs = p.readLong();
         }
     }
 
@@ -216,18 +224,39 @@
     }
 
     public static final class ActivityLaunchFinished extends BaseWithActivityRecordData {
+        public final long timestampNs;
+
         public ActivityLaunchFinished(@SequenceId long sequenceId,
-                @NonNull @ActivityRecordProto byte[] snapshot) {
+                @NonNull @ActivityRecordProto byte[] snapshot,
+                long timestampNs) {
             super(sequenceId, snapshot);
+            this.timestampNs = timestampNs;
         }
 
         @Override
         public boolean equals(Object other) {
             if (other instanceof ActivityLaunched) {
-                return super.equals(other);
+                return timestampNs == ((ActivityLaunchFinished)other).timestampNs &&
+                       super.equals(other);
             }
             return false;
         }
+
+        @Override
+        protected String toStringBody() {
+            return ", timestampNs=" + Long.toString(timestampNs);
+        }
+
+        @Override
+        protected void writeToParcelImpl(Parcel p, int flags) {
+           super.writeToParcelImpl(p, flags);
+           p.writeLong(timestampNs);
+        }
+
+        ActivityLaunchFinished(Parcel p) {
+            super(p);
+            timestampNs = p.readLong();
+        }
     }
 
      public static class ActivityLaunchCancelled extends AppLaunchEvent {
@@ -275,6 +304,42 @@
         }
     }
 
+    public static final class ReportFullyDrawn extends BaseWithActivityRecordData {
+        public final long timestampNs;
+
+        public ReportFullyDrawn(@SequenceId long sequenceId,
+                @NonNull @ActivityRecordProto byte[] snapshot,
+                long timestampNs) {
+            super(sequenceId, snapshot);
+            this.timestampNs = timestampNs;
+        }
+
+        @Override
+        public boolean equals(Object other) {
+            if (other instanceof ReportFullyDrawn) {
+                return timestampNs == ((ReportFullyDrawn)other).timestampNs &&
+                        super.equals(other);
+            }
+            return false;
+        }
+
+        @Override
+        protected String toStringBody() {
+            return ", timestampNs=" + Long.toString(timestampNs);
+        }
+
+        @Override
+        protected void writeToParcelImpl(Parcel p, int flags) {
+           super.writeToParcelImpl(p, flags);
+           p.writeLong(timestampNs);
+        }
+
+        ReportFullyDrawn(Parcel p) {
+            super(p);
+            timestampNs = p.readLong();
+        }
+    }
+
     @Override
     public @ContentsFlags int describeContents() { return 0; }
 
@@ -348,6 +413,7 @@
             ActivityLaunched.class,
             ActivityLaunchFinished.class,
             ActivityLaunchCancelled.class,
+            ReportFullyDrawn.class,
     };
 
     public static class ActivityRecordProtoParcelable {
diff --git a/startop/iorap/src/com/google/android/startop/iorap/IorapForwardingService.java b/startop/iorap/src/com/google/android/startop/iorap/IorapForwardingService.java
index 902da4c..f753548 100644
--- a/startop/iorap/src/com/google/android/startop/iorap/IorapForwardingService.java
+++ b/startop/iorap/src/com/google/android/startop/iorap/IorapForwardingService.java
@@ -315,19 +315,19 @@
         // All callbacks occur on the same background thread. Don't synchronize explicitly.
 
         @Override
-        public void onIntentStarted(@NonNull Intent intent) {
+        public void onIntentStarted(@NonNull Intent intent, long timestampNs) {
             // #onIntentStarted [is the only transition that] initiates a new launch sequence.
             ++mSequenceId;
 
             if (DEBUG) {
-                Log.v(TAG, String.format("AppLaunchObserver#onIntentStarted(%d, %s)",
-                        mSequenceId, intent));
+                Log.v(TAG, String.format("AppLaunchObserver#onIntentStarted(%d, %s, %d)",
+                        mSequenceId, intent, timestampNs));
             }
 
             invokeRemote(mIorapRemote,
                 (IIorap remote) ->
                     remote.onAppLaunchEvent(RequestId.nextValueForSequence(),
-                        new AppLaunchEvent.IntentStarted(mSequenceId, intent))
+                        new AppLaunchEvent.IntentStarted(mSequenceId, intent, timestampNs))
             );
         }
 
@@ -374,16 +374,34 @@
         }
 
         @Override
-        public void onActivityLaunchFinished(@NonNull @ActivityRecordProto byte[] activity) {
+        public void onActivityLaunchFinished(@NonNull @ActivityRecordProto byte[] activity,
+            long timestampNs) {
             if (DEBUG) {
-                Log.v(TAG, String.format("AppLaunchObserver#onActivityLaunchFinished(%d, %s)",
-                        mSequenceId, activity));
+                Log.v(TAG, String.format("AppLaunchObserver#onActivityLaunchFinished(%d, %s, %d)",
+                        mSequenceId, activity, timestampNs));
             }
 
             invokeRemote(mIorapRemote,
                 (IIorap remote) ->
                     remote.onAppLaunchEvent(RequestId.nextValueForSequence(),
-                        new AppLaunchEvent.ActivityLaunchFinished(mSequenceId, activity))
+                        new AppLaunchEvent.ActivityLaunchFinished(mSequenceId,
+                            activity,
+                            timestampNs))
+            );
+        }
+
+        @Override
+        public void onReportFullyDrawn(@NonNull @ActivityRecordProto byte[] activity,
+            long timestampNs) {
+            if (DEBUG) {
+                Log.v(TAG, String.format("AppLaunchObserver#onReportFullyDrawn(%d, %s, %d)",
+                        mSequenceId, activity, timestampNs));
+            }
+
+            invokeRemote(mIorapRemote,
+                (IIorap remote) ->
+                    remote.onAppLaunchEvent(RequestId.nextValueForSequence(),
+                        new AppLaunchEvent.ReportFullyDrawn(mSequenceId, activity, timestampNs))
             );
         }
     }
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 1d5c18d..9e60afc 100755
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -2817,7 +2817,7 @@
             "ping_test_before_data_switch_bool";
 
     /**
-     * Controls time in milli seconds until DcTracker reevaluates 5G connection state.
+     * Controls time in milliseconds until DcTracker reevaluates 5G connection state.
      * @hide
      */
     public static final String KEY_5G_WATCHDOG_TIME_MS_LONG =
@@ -3199,6 +3199,13 @@
     public static final String KEY_CARRIER_CERTIFICATE_STRING_ARRAY =
             "carrier_certificate_string_array";
 
+    /**
+     * DisconnectCause array to play busy tone. Value should be array of
+     * {@link android.telephony.DisconnectCause}.
+     */
+    public static final String KEY_DISCONNECT_CAUSE_PLAY_BUSYTONE_INT_ARRAY =
+            "disconnect_cause_play_busytone_int_array";
+
     /** The default value for every variable. */
     private final static PersistableBundle sDefaults;
 
@@ -3628,6 +3635,8 @@
         sDefaults.putBoolean(KEY_SUPPORT_WPS_OVER_IMS_BOOL, true);
         sDefaults.putAll(Ims.getDefaults());
         sDefaults.putStringArray(KEY_CARRIER_CERTIFICATE_STRING_ARRAY, null);
+        sDefaults.putIntArray(KEY_DISCONNECT_CAUSE_PLAY_BUSYTONE_INT_ARRAY,
+                new int[] {4 /* BUSY */});
     }
 
     /**
diff --git a/telephony/java/android/telephony/CellBroadcastService.java b/telephony/java/android/telephony/CellBroadcastService.java
index d5e447e..46eb9df 100644
--- a/telephony/java/android/telephony/CellBroadcastService.java
+++ b/telephony/java/android/telephony/CellBroadcastService.java
@@ -21,6 +21,7 @@
 import android.app.Service;
 import android.content.Intent;
 import android.os.IBinder;
+import android.telephony.cdma.CdmaSmsCbProgramData;
 
 /**
  * A service which exposes the cell broadcast handling module to the system.
@@ -69,9 +70,11 @@
     /**
      * Handle a CDMA cell broadcast SMS message forwarded from the system.
      * @param slotIndex the index of the slot which received the message
-     * @param message the SMS PDU
+     * @param bearerData the CDMA SMS bearer data
+     * @param serviceCategory the CDMA SCPT service category
      */
-    public abstract void onCdmaCellBroadcastSms(int slotIndex, byte[] message);
+    public abstract void onCdmaCellBroadcastSms(int slotIndex, byte[] bearerData,
+            @CdmaSmsCbProgramData.Category int serviceCategory);
 
     /**
      * If overriding this method, call through to the super method for any unknown actions.
@@ -102,11 +105,14 @@
         /**
          * Handle a CDMA cell broadcast SMS.
          * @param slotIndex the index of the slot which received the broadcast
-         * @param message the SMS message PDU
+         * @param bearerData the CDMA SMS bearer data
+         * @param serviceCategory the CDMA SCPT service category
          */
         @Override
-        public void handleCdmaCellBroadcastSms(int slotIndex, byte[] message) {
-            CellBroadcastService.this.onCdmaCellBroadcastSms(slotIndex, message);
+        public void handleCdmaCellBroadcastSms(int slotIndex, byte[] bearerData,
+                int serviceCategory) {
+            CellBroadcastService.this.onCdmaCellBroadcastSms(slotIndex, bearerData,
+                    serviceCategory);
         }
     }
 }
diff --git a/telephony/java/android/telephony/CellSignalStrengthWcdma.java b/telephony/java/android/telephony/CellSignalStrengthWcdma.java
index cdf01da..4dc54f0 100644
--- a/telephony/java/android/telephony/CellSignalStrengthWcdma.java
+++ b/telephony/java/android/telephony/CellSignalStrengthWcdma.java
@@ -245,9 +245,12 @@
     }
 
     /**
-     * Get the Ec/No as dB
+     * Get the Ec/No (Energy per chip over the noise spectral density) as dB.
      *
-     * @hide
+     * Reference: TS 25.133 Section 9.1.2.3
+     *
+     * @return the Ec/No of the measured cell in the range [-24, 1] or
+     * {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable
      */
     public int getEcNo() {
         return mEcNo;
diff --git a/telephony/java/android/telephony/ICellBroadcastService.aidl b/telephony/java/android/telephony/ICellBroadcastService.aidl
index eff64a2..bcd6cc5 100644
--- a/telephony/java/android/telephony/ICellBroadcastService.aidl
+++ b/telephony/java/android/telephony/ICellBroadcastService.aidl
@@ -28,5 +28,5 @@
     oneway void handleGsmCellBroadcastSms(int slotId, in byte[] message);
 
     /** @see android.telephony.CellBroadcastService#onCdmaCellBroadcastSms */
-    oneway void handleCdmaCellBroadcastSms(int slotId, in byte[] message);
+    oneway void handleCdmaCellBroadcastSms(int slotId, in byte[] bearerData, int serviceCategory);
 }
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index e288f25..40d057f 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -53,7 +53,6 @@
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.ServiceManager;
-import android.telephony.Annotation.NetworkType;
 import android.telephony.euicc.EuiccManager;
 import android.telephony.ims.ImsMmTelManager;
 import android.util.DisplayMetrics;
@@ -2420,8 +2419,12 @@
      * @param plans the list of plans. The first plan is always the primary and
      *            most important plan. Any additional plans are secondary and
      *            may not be displayed or used by decision making logic.
+     *            The list of all plans must meet the requirements defined in
+     *            {@link SubscriptionPlan.Builder#setNetworkTypes(int[])}.
      * @throws SecurityException if the caller doesn't meet the requirements
      *             outlined above.
+     * @throws IllegalArgumentException if plans don't meet the requirements
+     *             mentioned above.
      */
     public void setSubscriptionPlans(int subId, @NonNull List<SubscriptionPlan> plans) {
         try {
@@ -2466,51 +2469,10 @@
      */
     public void setSubscriptionOverrideUnmetered(int subId, boolean overrideUnmetered,
             @DurationMillisLong long timeoutMillis) {
-        setSubscriptionOverrideUnmetered(subId, null, overrideUnmetered, timeoutMillis);
-    }
-
-    /**
-     * Temporarily override the billing relationship between a carrier and
-     * a specific subscriber to be considered unmetered for the given network
-     * types. This will be reflected to apps via
-     * {@link NetworkCapabilities#NET_CAPABILITY_NOT_METERED}.
-     * This method is only accessible to the following narrow set of apps:
-     * <ul>
-     * <li>The carrier app for this subscriberId, as determined by
-     * {@link TelephonyManager#hasCarrierPrivileges()}.
-     * <li>The carrier app explicitly delegated access through
-     * {@link CarrierConfigManager#KEY_CONFIG_PLANS_PACKAGE_OVERRIDE_STRING}.
-     * </ul>
-     *
-     * @param subId the subscriber this override applies to.
-     * @param networkTypes all network types to set an override for. A null
-     *            network type means to apply the override to all network types.
-     *            Any unspecified network types will default to metered.
-     * @param overrideUnmetered set if the billing relationship should be
-     *            considered unmetered.
-     * @param timeoutMillis the timeout after which the requested override will
-     *            be automatically cleared, or {@code 0} to leave in the
-     *            requested state until explicitly cleared, or the next reboot,
-     *            whichever happens first.
-     * @throws SecurityException if the caller doesn't meet the requirements
-     *            outlined above.
-     * {@hide}
-     */
-    public void setSubscriptionOverrideUnmetered(int subId,
-            @Nullable @NetworkType int[] networkTypes, boolean overrideUnmetered,
-            @DurationMillisLong long timeoutMillis) {
         try {
-            long networkTypeMask = 0;
-            if (networkTypes != null) {
-                for (int networkType : networkTypes) {
-                    networkTypeMask |= TelephonyManager.getBitMaskForNetworkType(networkType);
-                }
-            } else {
-                networkTypeMask = ~0;
-            }
             final int overrideValue = overrideUnmetered ? OVERRIDE_UNMETERED : 0;
             getNetworkPolicy().setSubscriptionOverride(subId, OVERRIDE_UNMETERED, overrideValue,
-                    networkTypeMask, timeoutMillis, mContext.getOpPackageName());
+                    timeoutMillis, mContext.getOpPackageName());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -2542,52 +2504,10 @@
      */
     public void setSubscriptionOverrideCongested(int subId, boolean overrideCongested,
             @DurationMillisLong long timeoutMillis) {
-        setSubscriptionOverrideCongested(subId, null, overrideCongested, timeoutMillis);
-    }
-
-    /**
-     * Temporarily override the billing relationship plan between a carrier and
-     * a specific subscriber to be considered congested. This will cause the
-     * device to delay certain network requests when possible, such as developer
-     * jobs that are willing to run in a flexible time window.
-     * <p>
-     * This method is only accessible to the following narrow set of apps:
-     * <ul>
-     * <li>The carrier app for this subscriberId, as determined by
-     * {@link TelephonyManager#hasCarrierPrivileges()}.
-     * <li>The carrier app explicitly delegated access through
-     * {@link CarrierConfigManager#KEY_CONFIG_PLANS_PACKAGE_OVERRIDE_STRING}.
-     * </ul>
-     *
-     * @param subId the subscriber this override applies to.
-     * @param networkTypes all network types to set an override for. A null
-     *            network type means to apply the override to all network types.
-     *            Any unspecified network types will default to not congested.
-     * @param overrideCongested set if the subscription should be considered
-     *            congested.
-     * @param timeoutMillis the timeout after which the requested override will
-     *            be automatically cleared, or {@code 0} to leave in the
-     *            requested state until explicitly cleared, or the next reboot,
-     *            whichever happens first.
-     * @throws SecurityException if the caller doesn't meet the requirements
-     *             outlined above.
-     * @hide
-     */
-    public void setSubscriptionOverrideCongested(int subId,
-            @Nullable @NetworkType int[] networkTypes, boolean overrideCongested,
-            @DurationMillisLong long timeoutMillis) {
         try {
-            long networkTypeMask = 0;
-            if (networkTypes != null) {
-                for (int networkType : networkTypes) {
-                    networkTypeMask |= TelephonyManager.getBitMaskForNetworkType(networkType);
-                }
-            } else {
-                networkTypeMask = ~0;
-            }
             final int overrideValue = overrideCongested ? OVERRIDE_CONGESTED : 0;
             getNetworkPolicy().setSubscriptionOverride(subId, OVERRIDE_CONGESTED, overrideValue,
-                    networkTypeMask, timeoutMillis, mContext.getOpPackageName());
+                    timeoutMillis, mContext.getOpPackageName());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
diff --git a/telephony/java/android/telephony/SubscriptionPlan.java b/telephony/java/android/telephony/SubscriptionPlan.java
index ec2050f..e24eb26 100644
--- a/telephony/java/android/telephony/SubscriptionPlan.java
+++ b/telephony/java/android/telephony/SubscriptionPlan.java
@@ -24,6 +24,7 @@
 import android.annotation.SystemApi;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.telephony.Annotation.NetworkType;
 import android.util.Range;
 import android.util.RecurrenceRule;
 
@@ -33,6 +34,7 @@
 import java.lang.annotation.RetentionPolicy;
 import java.time.Period;
 import java.time.ZonedDateTime;
+import java.util.Arrays;
 import java.util.Iterator;
 import java.util.Objects;
 
@@ -80,6 +82,8 @@
     private int dataLimitBehavior = LIMIT_BEHAVIOR_UNKNOWN;
     private long dataUsageBytes = BYTES_UNKNOWN;
     private long dataUsageTime = TIME_UNKNOWN;
+    private @NetworkType int[] networkTypes;
+    private long networkTypesBitMask;
 
     private SubscriptionPlan(RecurrenceRule cycleRule) {
         this.cycleRule = Preconditions.checkNotNull(cycleRule);
@@ -93,6 +97,7 @@
         dataLimitBehavior = source.readInt();
         dataUsageBytes = source.readLong();
         dataUsageTime = source.readLong();
+        networkTypes = source.createIntArray();
     }
 
     @Override
@@ -109,6 +114,7 @@
         dest.writeInt(dataLimitBehavior);
         dest.writeLong(dataUsageBytes);
         dest.writeLong(dataUsageTime);
+        dest.writeIntArray(networkTypes);
     }
 
     @Override
@@ -121,13 +127,14 @@
                 .append(" dataLimitBehavior=").append(dataLimitBehavior)
                 .append(" dataUsageBytes=").append(dataUsageBytes)
                 .append(" dataUsageTime=").append(dataUsageTime)
+                .append(" networkTypes=").append(Arrays.toString(networkTypes))
                 .append("}").toString();
     }
 
     @Override
     public int hashCode() {
         return Objects.hash(cycleRule, title, summary, dataLimitBytes, dataLimitBehavior,
-                dataUsageBytes, dataUsageTime);
+                dataUsageBytes, dataUsageTime, Arrays.hashCode(networkTypes));
     }
 
     @Override
@@ -140,7 +147,8 @@
                     && dataLimitBytes == other.dataLimitBytes
                     && dataLimitBehavior == other.dataLimitBehavior
                     && dataUsageBytes == other.dataUsageBytes
-                    && dataUsageTime == other.dataUsageTime;
+                    && dataUsageTime == other.dataUsageTime
+                    && Arrays.equals(networkTypes, other.networkTypes);
         }
         return false;
     }
@@ -204,6 +212,32 @@
     }
 
     /**
+     * Return an array containing all {@link NetworkType}s this SubscriptionPlan applies to.
+     * A null array means this SubscriptionPlan applies to all network types.
+     */
+    public @Nullable @NetworkType int[] getNetworkTypes() {
+        return networkTypes;
+    }
+
+    /**
+     * Return the networkTypes array converted to a {@link TelephonyManager.NetworkTypeBitMask}
+     * @hide
+     */
+    public long getNetworkTypesBitMask() {
+        // calculate bitmask the first time and save for future calls
+        if (networkTypesBitMask == 0) {
+            if (networkTypes == null) {
+                networkTypesBitMask = ~0;
+            } else {
+                for (int networkType : networkTypes) {
+                    networkTypesBitMask |= TelephonyManager.getBitMaskForNetworkType(networkType);
+                }
+            }
+        }
+        return networkTypesBitMask;
+    }
+
+    /**
      * Return an iterator that will return all valid data usage cycles based on
      * any recurrence rules. The iterator starts from the currently active cycle
      * and walks backwards through time.
@@ -335,5 +369,24 @@
             plan.dataUsageTime = dataUsageTime;
             return this;
         }
+
+        /**
+         * Set the network types this SubscriptionPlan applies to.
+         * The developer must supply at least one plan that applies to all network types (default),
+         * and all additional plans may not include a particular network type more than once.
+         * Plan selection will prefer plans that have specific network types defined
+         * over plans that apply to all network types.
+         *
+         * @param networkTypes a set of all {@link NetworkType}s that apply to this plan.
+         *            A null value or empty array means the plan applies to all network types.
+         */
+        public @NonNull Builder setNetworkTypes(@Nullable @NetworkType int[] networkTypes) {
+            if (networkTypes == null || networkTypes.length == 0) {
+                plan.networkTypes = null;
+            } else {
+                plan.networkTypes = networkTypes;
+            }
+            return this;
+        }
     }
 }
diff --git a/telephony/java/android/telephony/ims/ImsReasonInfo.java b/telephony/java/android/telephony/ims/ImsReasonInfo.java
index 1e0d9a78..10251d7 100644
--- a/telephony/java/android/telephony/ims/ImsReasonInfo.java
+++ b/telephony/java/android/telephony/ims/ImsReasonInfo.java
@@ -885,6 +885,12 @@
      */
     public static final int CODE_WFC_SERVICE_NOT_AVAILABLE_IN_THIS_LOCATION = 1623;
 
+    /**
+     * The dialed RTT call should be retried without RTT
+     * @hide
+     */
+    public static final int CODE_RETRY_ON_IMS_WITHOUT_RTT = 3001;
+
     /*
      * OEM specific error codes. To be used by OEMs when they don't want to reveal error code which
      * would be replaced by ERROR_UNSPECIFIED.
@@ -1065,6 +1071,7 @@
             CODE_REJECT_VT_AVPF_NOT_ALLOWED,
             CODE_REJECT_ONGOING_ENCRYPTED_CALL,
             CODE_REJECT_ONGOING_CS_CALL,
+            CODE_RETRY_ON_IMS_WITHOUT_RTT,
             CODE_OEM_CAUSE_1,
             CODE_OEM_CAUSE_2,
             CODE_OEM_CAUSE_3,
diff --git a/telephony/java/com/android/internal/telephony/DctConstants.java b/telephony/java/com/android/internal/telephony/DctConstants.java
index 668a6af..9e786ce 100644
--- a/telephony/java/com/android/internal/telephony/DctConstants.java
+++ b/telephony/java/com/android/internal/telephony/DctConstants.java
@@ -111,7 +111,7 @@
     public static final int EVENT_DATA_SERVICE_BINDING_CHANGED = BASE + 49;
     public static final int EVENT_DEVICE_PROVISIONED_CHANGE = BASE + 50;
     public static final int EVENT_DATA_ENABLED_OVERRIDE_RULES_CHANGED = BASE + 51;
-    public static final int EVENT_5G_NETWORK_CHANGED = BASE + 52;
+    public static final int EVENT_SERVICE_STATE_CHANGED = BASE + 52;
     public static final int EVENT_5G_TIMER_HYSTERESIS = BASE + 53;
     public static final int EVENT_5G_TIMER_WATCHDOG = BASE + 54;
 
diff --git a/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java b/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java
index 4654437..b357fa4 100644
--- a/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java
+++ b/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java
@@ -900,6 +900,20 @@
     }
 
     /**
+     * @return the bearer data byte array
+     */
+    public byte[] getEnvelopeBearerData() {
+        return mEnvelope.bearerData;
+    }
+
+    /**
+     * @return the 16-bit CDMA SCPT service category
+     */
+    public @CdmaSmsCbProgramData.Category int getEnvelopeServiceCategory() {
+        return mEnvelope.serviceCategory;
+    }
+
+    /**
      * {@inheritDoc}
      */
     @Override
diff --git a/telephony/java/com/android/internal/telephony/cdma/sms/SmsEnvelope.java b/telephony/java/com/android/internal/telephony/cdma/sms/SmsEnvelope.java
index 2787b24..c924ab3 100644
--- a/telephony/java/com/android/internal/telephony/cdma/sms/SmsEnvelope.java
+++ b/telephony/java/com/android/internal/telephony/cdma/sms/SmsEnvelope.java
@@ -57,17 +57,17 @@
 
     // CMAS alert service category assignments, see 3GPP2 C.R1001 table 9.3.3-1
     public static final int SERVICE_CATEGORY_CMAS_PRESIDENTIAL_LEVEL_ALERT  =
-            CdmaSmsCbProgramData.CATEGORY_CMAS_PRESIDENTIAL_LEVEL_ALERT;
+            CdmaSmsCbProgramData.CATEGORY_CMAS_PRESIDENTIAL_LEVEL_ALERT;  // = 4096
     public static final int SERVICE_CATEGORY_CMAS_EXTREME_THREAT            =
-            CdmaSmsCbProgramData.CATEGORY_CMAS_EXTREME_THREAT;
+            CdmaSmsCbProgramData.CATEGORY_CMAS_EXTREME_THREAT;            // = 4097
     public static final int SERVICE_CATEGORY_CMAS_SEVERE_THREAT             =
-            CdmaSmsCbProgramData.CATEGORY_CMAS_SEVERE_THREAT;
+            CdmaSmsCbProgramData.CATEGORY_CMAS_SEVERE_THREAT;             // = 4098
     public static final int SERVICE_CATEGORY_CMAS_CHILD_ABDUCTION_EMERGENCY =
-            CdmaSmsCbProgramData.CATEGORY_CMAS_CHILD_ABDUCTION_EMERGENCY;
+            CdmaSmsCbProgramData.CATEGORY_CMAS_CHILD_ABDUCTION_EMERGENCY; // = 4099
     public static final int SERVICE_CATEGORY_CMAS_TEST_MESSAGE              =
-            CdmaSmsCbProgramData.CATEGORY_CMAS_TEST_MESSAGE;
+            CdmaSmsCbProgramData.CATEGORY_CMAS_TEST_MESSAGE;              // = 4100
     public static final int SERVICE_CATEGORY_CMAS_LAST_RESERVED_VALUE       =
-            CdmaSmsCbProgramData.CATEGORY_CMAS_LAST_RESERVED_VALUE;
+            CdmaSmsCbProgramData.CATEGORY_CMAS_LAST_RESERVED_VALUE;       // = 4351
 
     /**
      * Provides the type of a SMS message like point to point, broadcast or acknowledge
diff --git a/test-mock/src/android/test/mock/MockContext.java b/test-mock/src/android/test/mock/MockContext.java
index fcd4701..5053cee 100644
--- a/test-mock/src/android/test/mock/MockContext.java
+++ b/test-mock/src/android/test/mock/MockContext.java
@@ -758,7 +758,7 @@
 
     /** {@hide} */
     @Override
-    public Context createContextAsUser(UserHandle user) {
+    public Context createContextAsUser(UserHandle user, @CreatePackageOptions int flags) {
         throw new UnsupportedOperationException();
     }
 
diff --git a/tests/ApkVerityTest/block_device_writer/block_device_writer.cpp b/tests/ApkVerityTest/block_device_writer/block_device_writer.cpp
index b0c7251..02dfd73 100644
--- a/tests/ApkVerityTest/block_device_writer/block_device_writer.cpp
+++ b/tests/ApkVerityTest/block_device_writer/block_device_writer.cpp
@@ -42,6 +42,42 @@
 //  https://www.kernel.org/doc/Documentation/filesystems/fiemap.txt
 //  https://git.kernel.org/pub/scm/fs/xfs/xfsprogs-dev.git/tree/io/fiemap.c
 
+#ifndef F2FS_IOC_SET_PIN_FILE
+#ifndef F2FS_IOCTL_MAGIC
+#define F2FS_IOCTL_MAGIC 0xf5
+#endif
+#define F2FS_IOC_SET_PIN_FILE _IOW(F2FS_IOCTL_MAGIC, 13, __u32)
+#define F2FS_IOC_GET_PIN_FILE _IOR(F2FS_IOCTL_MAGIC, 14, __u32)
+#endif
+
+struct Args {
+  const char* block_device;
+  const char* file_name;
+  uint64_t byte_offset;
+  bool use_f2fs_pinning;
+};
+
+class ScopedF2fsFilePinning {
+ public:
+  explicit ScopedF2fsFilePinning(const char* file_path) {
+    fd_.reset(TEMP_FAILURE_RETRY(open(file_path, O_WRONLY | O_CLOEXEC, 0)));
+    if (fd_.get() == -1) {
+      perror("Failed to open");
+      return;
+    }
+    __u32 set = 1;
+    ioctl(fd_.get(), F2FS_IOC_SET_PIN_FILE, &set);
+  }
+
+  ~ScopedF2fsFilePinning() {
+    __u32 set = 0;
+    ioctl(fd_.get(), F2FS_IOC_SET_PIN_FILE, &set);
+  }
+
+ private:
+  android::base::unique_fd fd_;
+};
+
 ssize_t get_logical_block_size(const char* block_device) {
   android::base::unique_fd fd(open(block_device, O_RDONLY));
   if (fd.get() < 0) {
@@ -138,28 +174,51 @@
   return 0;
 }
 
-int main(int argc, const char** argv) {
-  if (argc != 4) {
+std::unique_ptr<Args> parse_args(int argc, const char** argv) {
+  if (argc != 4 && argc != 5) {
     fprintf(stderr,
-            "Usage: %s block_dev filename byte_offset\n"
+            "Usage: %s [--use-f2fs-pinning] block_dev filename byte_offset\n"
             "\n"
             "This program bypasses filesystem and damages the specified byte\n"
             "at the physical position on <block_dev> corresponding to the\n"
             "logical byte location in <filename>.\n",
             argv[0]);
+    return nullptr;
+  }
+
+  auto args = std::make_unique<Args>();
+  const char** arg = &argv[1];
+  args->use_f2fs_pinning = strcmp(*arg, "--use-f2fs-pinning") == 0;
+  if (args->use_f2fs_pinning) {
+    ++arg;
+  }
+  args->block_device = *(arg++);
+  args->file_name = *(arg++);
+  args->byte_offset = strtoull(*arg, nullptr, 10);
+  if (args->byte_offset == ULLONG_MAX) {
+    perror("Invalid byte offset");
+    return nullptr;
+  }
+  return args;
+}
+
+int main(int argc, const char** argv) {
+  std::unique_ptr<Args> args = parse_args(argc, argv);
+  if (args == nullptr) {
     return -1;
   }
 
-  const char* block_device = argv[1];
-  const char* file_name = argv[2];
-  uint64_t byte_offset = strtoull(argv[3], nullptr, 10);
-
-  ssize_t block_size = get_logical_block_size(block_device);
+  ssize_t block_size = get_logical_block_size(args->block_device);
   if (block_size < 0) {
     return -1;
   }
 
-  int64_t physical_offset_signed = get_physical_offset(file_name, byte_offset);
+  std::unique_ptr<ScopedF2fsFilePinning> pinned_file;
+  if (args->use_f2fs_pinning) {
+    pinned_file = std::make_unique<ScopedF2fsFilePinning>(args->file_name);
+  }
+
+  int64_t physical_offset_signed = get_physical_offset(args->file_name, args->byte_offset);
   if (physical_offset_signed < 0) {
     return -1;
   }
@@ -172,7 +231,7 @@
   std::unique_ptr<char> buf(static_cast<char*>(
       aligned_alloc(block_size /* alignment */, block_size /* size */)));
 
-  if (read_block_from_device(block_device, physical_block_offset, block_size,
+  if (read_block_from_device(args->block_device, physical_block_offset, block_size,
                              buf.get()) < 0) {
     return -1;
   }
@@ -180,7 +239,7 @@
   printf("before: %hhx\n", *p);
   *p ^= 0xff;
   printf("after: %hhx\n", *p);
-  if (write_block_to_device(block_device, physical_block_offset, block_size,
+  if (write_block_to_device(args->block_device, physical_block_offset, block_size,
                             buf.get()) < 0) {
     return -1;
   }
diff --git a/tests/ApkVerityTest/src/com/android/apkverity/ApkVerityTest.java b/tests/ApkVerityTest/src/com/android/apkverity/ApkVerityTest.java
index 761c5ce..2445a6a 100644
--- a/tests/ApkVerityTest/src/com/android/apkverity/ApkVerityTest.java
+++ b/tests/ApkVerityTest/src/com/android/apkverity/ApkVerityTest.java
@@ -38,6 +38,7 @@
 import org.junit.runner.RunWith;
 
 import java.io.FileNotFoundException;
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashSet;
 
@@ -440,8 +441,15 @@
             throws DeviceNotAvailableException {
         assertTrue(path.startsWith("/data/"));
         ITestDevice.MountPointInfo mountPoint = mDevice.getMountPointInfo("/data");
-        expectRemoteCommandToSucceed(String.join(" ", DAMAGING_EXECUTABLE,
-                    mountPoint.filesystem, path, Long.toString(offsetOfTargetingByte)));
+        ArrayList<String> args = new ArrayList<>();
+        args.add(DAMAGING_EXECUTABLE);
+        if ("f2fs".equals(mountPoint.type)) {
+            args.add("--use-f2fs-pinning");
+        }
+        args.add(mountPoint.filesystem);
+        args.add(path);
+        args.add(Long.toString(offsetOfTargetingByte));
+        expectRemoteCommandToSucceed(String.join(" ", args));
     }
 
     private String getApkPath(String packageName) throws DeviceNotAvailableException {
diff --git a/tests/Codegen/runTest.sh b/tests/Codegen/runTest.sh
index 0e90dea..929f122 100755
--- a/tests/Codegen/runTest.sh
+++ b/tests/Codegen/runTest.sh
@@ -17,11 +17,13 @@
         header_and_eval codegen $ANDROID_BUILD_TOP/frameworks/base/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassBase.java && \
         header_and_eval codegen $ANDROID_BUILD_TOP/frameworks/base/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassChild.java && \
         header_and_eval codegen $ANDROID_BUILD_TOP/frameworks/base/tests/Codegen/src/com/android/codegentest/StaleDataclassDetectorFalsePositivesTest.java && \
-        cd $ANDROID_BUILD_TOP &&
-        header_and_eval mmma -j16 frameworks/base/tests/Codegen && \
-        header_and_eval adb install -r -t "$(find $ANDROID_TARGET_OUT_TESTCASES -name 'CodegenTests.apk')" && \
-        # header_and_eval adb shell am set-debug-app -w com.android.codegentest && \
-        header_and_eval adb shell am instrument -w -e package com.android.codegentest com.android.codegentest/androidx.test.runner.AndroidJUnitRunner
+        (
+            cd $ANDROID_BUILD_TOP &&
+            header_and_eval mmma -j16 frameworks/base/tests/Codegen && \
+            header_and_eval adb install -r -t "$(find $ANDROID_TARGET_OUT_TESTCASES -name 'CodegenTests.apk')" && \
+            # header_and_eval adb shell am set-debug-app -w com.android.codegentest && \
+            header_and_eval adb shell am instrument -w -e package com.android.codegentest com.android.codegentest/androidx.test.runner.AndroidJUnitRunner
+        )
 
         exitCode=$?
 
diff --git a/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassBase.java b/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassBase.java
index 10eba6a..3515053 100644
--- a/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassBase.java
+++ b/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassBase.java
@@ -32,13 +32,17 @@
 
 
 
-    // Code below generated by codegen v1.0.7.
+    // Code below generated by codegen v1.0.8.
     //
     // DO NOT MODIFY!
     // CHECKSTYLE:OFF Generated code
     //
     // To regenerate run:
     // $ codegen $ANDROID_BUILD_TOP/frameworks/base/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassBase.java
+    //
+    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+    //   Settings > Editor > Code Style > Formatter Control
+    //@formatter:off
 
 
     @DataClass.Generated.Member
@@ -94,8 +98,8 @@
     };
 
     @DataClass.Generated(
-            time = 1570576455287L,
-            codegenVersion = "1.0.7",
+            time = 1570828332402L,
+            codegenVersion = "1.0.8",
             sourceFile = "frameworks/base/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassBase.java",
             inputSignatures = "private  int mBaseData\nclass HierrarchicalDataClassBase extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genConstructor=false, genSetters=true)")
     @Deprecated
diff --git a/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassChild.java b/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassChild.java
index 1085a6a..c867409 100644
--- a/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassChild.java
+++ b/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassChild.java
@@ -46,13 +46,17 @@
 
 
 
-    // Code below generated by codegen v1.0.7.
+    // Code below generated by codegen v1.0.8.
     //
     // DO NOT MODIFY!
     // CHECKSTYLE:OFF Generated code
     //
     // To regenerate run:
     // $ codegen $ANDROID_BUILD_TOP/frameworks/base/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassChild.java
+    //
+    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+    //   Settings > Editor > Code Style > Formatter Control
+    //@formatter:off
 
 
     @DataClass.Generated.Member
@@ -116,8 +120,8 @@
     };
 
     @DataClass.Generated(
-            time = 1570576456245L,
-            codegenVersion = "1.0.7",
+            time = 1570828333399L,
+            codegenVersion = "1.0.8",
             sourceFile = "frameworks/base/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassChild.java",
             inputSignatures = "private @android.annotation.NonNull java.lang.String mChildData\nclass HierrarchicalDataClassChild extends com.android.codegentest.HierrarchicalDataClassBase implements []\n@com.android.internal.util.DataClass(genParcelable=true, genConstructor=false, genSetters=true)")
     @Deprecated
diff --git a/tests/Codegen/src/com/android/codegentest/ParcelAllTheThingsDataClass.java b/tests/Codegen/src/com/android/codegentest/ParcelAllTheThingsDataClass.java
index 75ef963..8d097a0 100644
--- a/tests/Codegen/src/com/android/codegentest/ParcelAllTheThingsDataClass.java
+++ b/tests/Codegen/src/com/android/codegentest/ParcelAllTheThingsDataClass.java
@@ -16,6 +16,7 @@
 package com.android.codegentest;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.util.SparseArray;
@@ -46,15 +47,22 @@
     @NonNull SparseArray<SampleWithCustomBuilder> mSparseArray = null;
     @NonNull SparseIntArray mSparseIntArray = null;
 
+    @SuppressWarnings({"WeakerAccess"})
+    @Nullable Boolean mNullableBoolean = null;
 
 
-    // Code below generated by codegen v1.0.7.
+
+    // Code below generated by codegen v1.0.8.
     //
     // DO NOT MODIFY!
     // CHECKSTYLE:OFF Generated code
     //
     // To regenerate run:
     // $ codegen $ANDROID_BUILD_TOP/frameworks/base/tests/Codegen/src/com/android/codegentest/ParcelAllTheThingsDataClass.java
+    //
+    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+    //   Settings > Editor > Code Style > Formatter Control
+    //@formatter:off
 
 
     @DataClass.Generated.Member
@@ -65,7 +73,8 @@
             @NonNull Map<String,SampleWithCustomBuilder> map,
             @NonNull Map<String,String> stringMap,
             @NonNull SparseArray<SampleWithCustomBuilder> sparseArray,
-            @NonNull SparseIntArray sparseIntArray) {
+            @NonNull SparseIntArray sparseIntArray,
+            @SuppressWarnings({ "WeakerAccess" }) @Nullable Boolean nullableBoolean) {
         this.mStringArray = stringArray;
         AnnotationValidations.validate(
                 NonNull.class, null, mStringArray);
@@ -87,6 +96,7 @@
         this.mSparseIntArray = sparseIntArray;
         AnnotationValidations.validate(
                 NonNull.class, null, mSparseIntArray);
+        this.mNullableBoolean = nullableBoolean;
 
         // onConstructed(); // You can define this method to get a callback
     }
@@ -126,6 +136,11 @@
         return mSparseIntArray;
     }
 
+    @DataClass.Generated.Member
+    public @SuppressWarnings({ "WeakerAccess" }) @Nullable Boolean getNullableBoolean() {
+        return mNullableBoolean;
+    }
+
     @Override
     @DataClass.Generated.Member
     public String toString() {
@@ -139,7 +154,8 @@
                 "map = " + mMap + ", " +
                 "stringMap = " + mStringMap + ", " +
                 "sparseArray = " + mSparseArray + ", " +
-                "sparseIntArray = " + mSparseIntArray +
+                "sparseIntArray = " + mSparseIntArray + ", " +
+                "nullableBoolean = " + mNullableBoolean +
         " }";
     }
 
@@ -149,6 +165,9 @@
         // You can override field parcelling by defining methods like:
         // void parcelFieldName(Parcel dest, int flags) { ... }
 
+        int flg = 0;
+        if (mNullableBoolean != null) flg |= 0x80;
+        dest.writeInt(flg);
         dest.writeStringArray(mStringArray);
         dest.writeIntArray(mIntArray);
         dest.writeStringList(mStringList);
@@ -156,6 +175,7 @@
         dest.writeMap(mStringMap);
         dest.writeSparseArray(mSparseArray);
         dest.writeSparseIntArray(mSparseIntArray);
+        if (mNullableBoolean != null) dest.writeBoolean(mNullableBoolean);
     }
 
     @Override
@@ -169,6 +189,7 @@
         // You can override field unparcelling by defining methods like:
         // static FieldType unparcelFieldName(Parcel in) { ... }
 
+        int flg = in.readInt();
         String[] stringArray = in.createStringArray();
         int[] intArray = in.createIntArray();
         List<String> stringList = new java.util.ArrayList<>();
@@ -179,6 +200,7 @@
         in.readMap(stringMap, String.class.getClassLoader());
         SparseArray<SampleWithCustomBuilder> sparseArray = (SparseArray) in.readSparseArray(SampleWithCustomBuilder.class.getClassLoader());
         SparseIntArray sparseIntArray = (SparseIntArray) in.readSparseIntArray();
+        Boolean nullableBoolean = (flg & 0x80) == 0 ? null : (Boolean) in.readBoolean();
 
         this.mStringArray = stringArray;
         AnnotationValidations.validate(
@@ -201,6 +223,7 @@
         this.mSparseIntArray = sparseIntArray;
         AnnotationValidations.validate(
                 NonNull.class, null, mSparseIntArray);
+        this.mNullableBoolean = nullableBoolean;
 
         // onConstructed(); // You can define this method to get a callback
     }
@@ -233,6 +256,7 @@
         private @NonNull Map<String,String> mStringMap;
         private @NonNull SparseArray<SampleWithCustomBuilder> mSparseArray;
         private @NonNull SparseIntArray mSparseIntArray;
+        private @SuppressWarnings({ "WeakerAccess" }) @Nullable Boolean mNullableBoolean;
 
         private long mBuilderFieldsSet = 0L;
 
@@ -328,10 +352,18 @@
             return this;
         }
 
+        @DataClass.Generated.Member
+        public @NonNull Builder setNullableBoolean(@SuppressWarnings({ "WeakerAccess" }) @Nullable Boolean value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x80;
+            mNullableBoolean = value;
+            return this;
+        }
+
         /** Builds the instance. This builder should not be touched after calling this! */
         public ParcelAllTheThingsDataClass build() {
             checkNotUsed();
-            mBuilderFieldsSet |= 0x80; // Mark builder used
+            mBuilderFieldsSet |= 0x100; // Mark builder used
 
             if ((mBuilderFieldsSet & 0x1) == 0) {
                 mStringArray = null;
@@ -354,6 +386,9 @@
             if ((mBuilderFieldsSet & 0x40) == 0) {
                 mSparseIntArray = null;
             }
+            if ((mBuilderFieldsSet & 0x80) == 0) {
+                mNullableBoolean = null;
+            }
             ParcelAllTheThingsDataClass o = new ParcelAllTheThingsDataClass(
                     mStringArray,
                     mIntArray,
@@ -361,12 +396,13 @@
                     mMap,
                     mStringMap,
                     mSparseArray,
-                    mSparseIntArray);
+                    mSparseIntArray,
+                    mNullableBoolean);
             return o;
         }
 
         private void checkNotUsed() {
-            if ((mBuilderFieldsSet & 0x80) != 0) {
+            if ((mBuilderFieldsSet & 0x100) != 0) {
                 throw new IllegalStateException(
                         "This Builder should not be reused. Use a new Builder instance instead");
             }
@@ -374,10 +410,10 @@
     }
 
     @DataClass.Generated(
-            time = 1570576454326L,
-            codegenVersion = "1.0.7",
+            time = 1570828331396L,
+            codegenVersion = "1.0.8",
             sourceFile = "frameworks/base/tests/Codegen/src/com/android/codegentest/ParcelAllTheThingsDataClass.java",
-            inputSignatures = " @android.annotation.NonNull java.lang.String[] mStringArray\n @android.annotation.NonNull int[] mIntArray\n @android.annotation.NonNull java.util.List<java.lang.String> mStringList\n @android.annotation.NonNull java.util.Map<java.lang.String,com.android.codegentest.SampleWithCustomBuilder> mMap\n @android.annotation.NonNull java.util.Map<java.lang.String,java.lang.String> mStringMap\n @android.annotation.NonNull android.util.SparseArray<com.android.codegentest.SampleWithCustomBuilder> mSparseArray\n @android.annotation.NonNull android.util.SparseIntArray mSparseIntArray\nclass ParcelAllTheThingsDataClass extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genBuilder=true, genAidl=false, genToString=true)")
+            inputSignatures = " @android.annotation.NonNull java.lang.String[] mStringArray\n @android.annotation.NonNull int[] mIntArray\n @android.annotation.NonNull java.util.List<java.lang.String> mStringList\n @android.annotation.NonNull java.util.Map<java.lang.String,com.android.codegentest.SampleWithCustomBuilder> mMap\n @android.annotation.NonNull java.util.Map<java.lang.String,java.lang.String> mStringMap\n @android.annotation.NonNull android.util.SparseArray<com.android.codegentest.SampleWithCustomBuilder> mSparseArray\n @android.annotation.NonNull android.util.SparseIntArray mSparseIntArray\n @java.lang.SuppressWarnings({\"WeakerAccess\"}) @android.annotation.Nullable java.lang.Boolean mNullableBoolean\nclass ParcelAllTheThingsDataClass extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genBuilder=true, genAidl=false, genToString=true)")
     @Deprecated
     private void __metadata() {}
 
diff --git a/tests/Codegen/src/com/android/codegentest/SampleDataClass.java b/tests/Codegen/src/com/android/codegentest/SampleDataClass.java
index 14010a9..d014d6d 100644
--- a/tests/Codegen/src/com/android/codegentest/SampleDataClass.java
+++ b/tests/Codegen/src/com/android/codegentest/SampleDataClass.java
@@ -342,13 +342,17 @@
 
 
 
-    // Code below generated by codegen v1.0.7.
+    // Code below generated by codegen v1.0.8.
     //
     // DO NOT MODIFY!
     // CHECKSTYLE:OFF Generated code
     //
     // To regenerate run:
     // $ codegen $ANDROID_BUILD_TOP/frameworks/base/tests/Codegen/src/com/android/codegentest/SampleDataClass.java
+    //
+    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+    //   Settings > Editor > Code Style > Formatter Control
+    //@formatter:off
 
 
     @IntDef(prefix = "STATE_", value = {
@@ -1868,8 +1872,8 @@
     }
 
     @DataClass.Generated(
-            time = 1570576452225L,
-            codegenVersion = "1.0.7",
+            time = 1570828329319L,
+            codegenVersion = "1.0.8",
             sourceFile = "frameworks/base/tests/Codegen/src/com/android/codegentest/SampleDataClass.java",
             inputSignatures = "public static final  java.lang.String STATE_NAME_UNDEFINED\npublic static final  java.lang.String STATE_NAME_ON\npublic static final  java.lang.String STATE_NAME_OFF\npublic static final  int STATE_UNDEFINED\npublic static final  int STATE_ON\npublic static final  int STATE_OFF\npublic static final @com.android.codegentest.SampleDataClass.RequestFlags int FLAG_MANUAL_REQUEST\npublic static final @com.android.codegentest.SampleDataClass.RequestFlags int FLAG_COMPATIBILITY_MODE_REQUEST\npublic static final @com.android.codegentest.SampleDataClass.RequestFlags int FLAG_AUGMENTED_REQUEST\nprivate  int mNum\nprivate  int mNum2\nprivate  int mNum4\nprivate @android.annotation.Nullable java.lang.String mName\nprivate @android.annotation.NonNull java.lang.String mName2\nprivate @android.annotation.NonNull java.lang.String mName4\nprivate @android.annotation.Nullable android.view.accessibility.AccessibilityNodeInfo mOtherParcelable\nprivate @com.android.internal.util.DataClass.ParcelWith(com.android.codegentest.MyDateParcelling.class) @android.annotation.NonNull java.util.Date mDate\nprivate @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForPattern.class) @android.annotation.NonNull java.util.regex.Pattern mPattern\nprivate @android.annotation.NonNull java.util.List<android.net.LinkAddress> mLinkAddresses2\nprivate @com.android.internal.util.DataClass.PluralOf(\"linkAddress\") @android.annotation.NonNull java.util.ArrayList<android.net.LinkAddress> mLinkAddresses\nprivate @android.annotation.Nullable android.net.LinkAddress[] mLinkAddresses4\nprivate @com.android.codegentest.SampleDataClass.StateName @android.annotation.NonNull java.lang.String mStateName\nprivate @com.android.codegentest.SampleDataClass.RequestFlags int mFlags\nprivate @com.android.codegentest.SampleDataClass.State int mState\npublic @android.annotation.NonNull java.lang.CharSequence charSeq\nprivate final @android.annotation.Nullable android.net.LinkAddress[] mLinkAddresses5\nprivate transient  android.net.LinkAddress[] mLinkAddresses6\ntransient  int[] mTmpStorage\nprivate @android.annotation.StringRes int mStringRes\nprivate @android.annotation.IntRange(from=0L, to=6L) int mDayOfWeek\nprivate @android.annotation.Size(2L) @android.annotation.NonNull @com.android.internal.util.DataClass.Each @android.annotation.FloatRange(from=0.0) float[] mCoords\nprivate static  java.lang.String defaultName4()\nprivate  int[] lazyInitTmpStorage()\npublic  android.net.LinkAddress[] getLinkAddresses4()\nprivate  boolean patternEquals(java.util.regex.Pattern)\nprivate  int patternHashCode()\nprivate  void onConstructed()\npublic  void dump(java.io.PrintWriter)\nclass SampleDataClass extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genBuilder=true, genConstructor=true, genEqualsHashCode=true, genToString=true, genForEachField=true, genSetters=true)")
     @Deprecated
diff --git a/tests/Codegen/src/com/android/codegentest/SampleWithCustomBuilder.java b/tests/Codegen/src/com/android/codegentest/SampleWithCustomBuilder.java
index b5f6c73..1c87e8f 100644
--- a/tests/Codegen/src/com/android/codegentest/SampleWithCustomBuilder.java
+++ b/tests/Codegen/src/com/android/codegentest/SampleWithCustomBuilder.java
@@ -85,13 +85,17 @@
 
 
 
-    // Code below generated by codegen v1.0.7.
+    // Code below generated by codegen v1.0.8.
     //
     // DO NOT MODIFY!
     // CHECKSTYLE:OFF Generated code
     //
     // To regenerate run:
     // $ codegen $ANDROID_BUILD_TOP/frameworks/base/tests/Codegen/src/com/android/codegentest/SampleWithCustomBuilder.java
+    //
+    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+    //   Settings > Editor > Code Style > Formatter Control
+    //@formatter:off
 
 
     @DataClass.Generated.Member
@@ -249,8 +253,8 @@
     }
 
     @DataClass.Generated(
-            time = 1570576453295L,
-            codegenVersion = "1.0.7",
+            time = 1570828330331L,
+            codegenVersion = "1.0.8",
             sourceFile = "frameworks/base/tests/Codegen/src/com/android/codegentest/SampleWithCustomBuilder.java",
             inputSignatures = "  long delayAmount\n @android.annotation.NonNull java.util.concurrent.TimeUnit delayUnit\n  long creationTimestamp\nprivate static  java.util.concurrent.TimeUnit unparcelDelayUnit(android.os.Parcel)\nprivate  void parcelDelayUnit(android.os.Parcel,int)\nclass SampleWithCustomBuilder extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genBuilder=true, genAidl=false, genToString=true)\nabstract  com.android.codegentest.SampleWithCustomBuilder.Builder setDelayAmount(long)\npublic abstract  com.android.codegentest.SampleWithCustomBuilder.Builder setDelayUnit(java.util.concurrent.TimeUnit)\npublic  com.android.codegentest.SampleWithCustomBuilder.Builder setDelay(long,java.util.concurrent.TimeUnit)\nclass BaseBuilder extends java.lang.Object implements []")
     @Deprecated
diff --git a/tests/Codegen/src/com/android/codegentest/StaleDataclassDetectorFalsePositivesTest.java b/tests/Codegen/src/com/android/codegentest/StaleDataclassDetectorFalsePositivesTest.java
index 0ce8aba..27af37f 100644
--- a/tests/Codegen/src/com/android/codegentest/StaleDataclassDetectorFalsePositivesTest.java
+++ b/tests/Codegen/src/com/android/codegentest/StaleDataclassDetectorFalsePositivesTest.java
@@ -51,18 +51,22 @@
 
 
 
-    // Code below generated by codegen v1.0.7.
+    // Code below generated by codegen v1.0.8.
     //
     // DO NOT MODIFY!
     // CHECKSTYLE:OFF Generated code
     //
     // To regenerate run:
     // $ codegen $ANDROID_BUILD_TOP/frameworks/base/tests/Codegen/src/com/android/codegentest/StaleDataclassDetectorFalsePositivesTest.java
+    //
+    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+    //   Settings > Editor > Code Style > Formatter Control
+    //@formatter:off
 
 
     @DataClass.Generated(
-            time = 1570576457249L,
-            codegenVersion = "1.0.7",
+            time = 1570828334384L,
+            codegenVersion = "1.0.8",
             sourceFile = "frameworks/base/tests/Codegen/src/com/android/codegentest/StaleDataclassDetectorFalsePositivesTest.java",
             inputSignatures = "public @android.annotation.NonNull java.lang.String someMethod(int)\nclass StaleDataclassDetectorFalsePositivesTest extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false, genBuilder=false)")
     @Deprecated
diff --git a/tests/Gating/Android.bp b/tests/Gating/Android.bp
new file mode 100644
index 0000000..b6c0094
--- /dev/null
+++ b/tests/Gating/Android.bp
@@ -0,0 +1,17 @@
+android_test {
+    name: "Gating",
+    // Only compile source java files in this apk.
+    srcs: ["src/**/*.java"],
+    certificate: "platform",
+    libs: [
+        "android.test.runner",
+        "android.test.base",
+    ],
+    static_libs: [
+        "junit",
+        "android-support-test",
+        "mockito-target-minus-junit4",
+        "truth-prebuilt",
+        "platform_compat-test-rules"
+    ],
+}
diff --git a/tests/Gating/AndroidManifest.xml b/tests/Gating/AndroidManifest.xml
new file mode 100644
index 0000000..7f14b83
--- /dev/null
+++ b/tests/Gating/AndroidManifest.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.tests.gating">
+    <application android:label="GatingTest">
+        <uses-library android:name="android.test.runner" />
+    </application>
+
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+                     android:targetPackage="com.android.tests.gating"/>
+</manifest>
diff --git a/tests/Gating/AndroidTest.xml b/tests/Gating/AndroidTest.xml
new file mode 100644
index 0000000..730e74a
--- /dev/null
+++ b/tests/Gating/AndroidTest.xml
@@ -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.
+-->
+<configuration description="Test compatibility change gating.">
+    <target_preparer class="com.android.tradefed.targetprep.TestFilePushSetup"/>
+    <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
+        <option name="test-file-name" value="Gating.apk"/>
+    </target_preparer>
+    <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer"/>
+    <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer"/>
+    <option name="test-suite-tag" value="apct"/>
+    <option name="test-tag" value="Gating"/>
+
+    <test class="com.android.tradefed.testtype.AndroidJUnitTest">
+        <option name="package" value="com.android.tests.gating"/>
+        <option name="runner" value="android.support.test.runner.AndroidJUnitRunner"/>
+        <option name="hidden-api-checks" value="false"/>
+    </test>
+</configuration>
diff --git a/tests/Gating/src/com/android/compat/DummyApi.java b/tests/Gating/src/com/android/compat/DummyApi.java
new file mode 100644
index 0000000..93d60d0
--- /dev/null
+++ b/tests/Gating/src/com/android/compat/DummyApi.java
@@ -0,0 +1,90 @@
+/*
+ * 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.compat;
+
+import android.compat.Compatibility;
+import android.content.Context;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+
+import com.android.internal.compat.IPlatformCompat;
+
+/**
+ * This is a dummy API to test gating
+ *
+ * @hide
+ */
+public class DummyApi {
+
+    public static final long CHANGE_ID = 666013;
+    public static final long CHANGE_ID_1 = 666014;
+    public static final long CHANGE_ID_2 = 666015;
+    public static final long CHANGE_SYSTEM_SERVER = 666016;
+
+    /**
+     * Dummy method
+     * @return "A" if change is enabled, "B" otherwise.
+     */
+    public static String dummyFunc() {
+        if (Compatibility.isChangeEnabled(CHANGE_ID)) {
+            return "A";
+        }
+        return "B";
+    }
+
+    /**
+     * Dummy combined method
+     * @return "0" if {@link CHANGE_ID_1} is disabled and {@link CHANGE_ID_2} is disabled,
+               "1" if {@link CHANGE_ID_1} is disabled and {@link CHANGE_ID_2} is enabled,
+               "2" if {@link CHANGE_ID_1} is enabled and {@link CHANGE_ID_2} is disabled,
+               "3" if {@link CHANGE_ID_1} is enabled and {@link CHANGE_ID_2} is enabled.
+     */
+    public static String dummyCombinedFunc() {
+        if (!Compatibility.isChangeEnabled(CHANGE_ID_1)
+                && !Compatibility.isChangeEnabled(CHANGE_ID_2)) {
+            return "0";
+        } else if (!Compatibility.isChangeEnabled(CHANGE_ID_1)
+                && Compatibility.isChangeEnabled(CHANGE_ID_2)) {
+            return "1";
+        } else if (Compatibility.isChangeEnabled(CHANGE_ID_1)
+                && !Compatibility.isChangeEnabled(CHANGE_ID_2)) {
+            return "2";
+        }
+        return "3";
+    }
+
+    /**
+     * Dummy api using system server API.
+     */
+    public static String dummySystemServer(Context context) {
+        IPlatformCompat platformCompat = IPlatformCompat.Stub
+                .asInterface(ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE));
+        if (platformCompat == null) {
+            throw new RuntimeException("Could not obtain IPlatformCompat instance!");
+        }
+        String packageName = context.getPackageName();
+        try {
+            if (platformCompat.isChangeEnabledByPackageName(CHANGE_SYSTEM_SERVER, packageName)) {
+                return "True";
+            } else {
+                return "False";
+            }
+        } catch (RemoteException e) {
+            throw new RuntimeException("Could not get change value!", e);
+        }
+    }
+}
diff --git a/tests/Gating/src/com/android/tests/gating/GatingTest.java b/tests/Gating/src/com/android/tests/gating/GatingTest.java
new file mode 100644
index 0000000..0867624
--- /dev/null
+++ b/tests/Gating/src/com/android/tests/gating/GatingTest.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.tests.gating;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.compat.CompatChangeRule;
+import android.compat.CompatChangeRule.DisableCompatChanges;
+import android.compat.CompatChangeRule.EnableCompatChanges;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+
+import com.android.compat.DummyApi;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TestRule;
+import org.junit.runner.RunWith;
+
+/**
+ * Tests for platform compatibility change gating.
+ */
+@RunWith(AndroidJUnit4.class)
+public class GatingTest {
+
+    @Rule
+    public TestRule compatChangeRule = new CompatChangeRule();
+
+    @Test
+    @EnableCompatChanges({DummyApi.CHANGE_ID})
+    public void testDummyGatingPositive() {
+        assertThat(DummyApi.dummyFunc()).isEqualTo("A");
+    }
+
+    @Test
+    @DisableCompatChanges({DummyApi.CHANGE_ID})
+    public void testDummyGatingNegative() {
+        assertThat(DummyApi.dummyFunc()).isEqualTo("B");
+    }
+
+    @Test
+    @DisableCompatChanges({DummyApi.CHANGE_ID_1, DummyApi.CHANGE_ID_2})
+    public void testDummyGatingCombined0() {
+        assertThat(DummyApi.dummyCombinedFunc()).isEqualTo("0");
+    }
+
+    @Test
+    @DisableCompatChanges({DummyApi.CHANGE_ID_1})
+    @EnableCompatChanges({DummyApi.CHANGE_ID_2})
+    public void testDummyGatingCombined1() {
+        assertThat(DummyApi.dummyCombinedFunc()).isEqualTo("1");
+    }
+
+    @Test
+    @EnableCompatChanges({DummyApi.CHANGE_ID_1})
+    @DisableCompatChanges({DummyApi.CHANGE_ID_2})
+    public void testDummyGatingCombined2() {
+        assertThat(DummyApi.dummyCombinedFunc()).isEqualTo("2");
+    }
+
+    @Test
+    @EnableCompatChanges({DummyApi.CHANGE_ID_1, DummyApi.CHANGE_ID_2})
+    public void testDummyGatingCombined3() {
+        assertThat(DummyApi.dummyCombinedFunc()).isEqualTo("3");
+    }
+
+    @Test
+    @EnableCompatChanges({DummyApi.CHANGE_SYSTEM_SERVER})
+    public void testDummyGatingPositiveSystemServer() {
+        assertThat(
+                DummyApi.dummySystemServer(InstrumentationRegistry.getTargetContext())).isEqualTo(
+                "True");
+    }
+
+    @Test
+    @DisableCompatChanges({DummyApi.CHANGE_SYSTEM_SERVER})
+    public void testDummyGatingNegativeSystemServer() {
+        assertThat(
+                DummyApi.dummySystemServer(InstrumentationRegistry.getTargetContext())).isEqualTo(
+                "False");
+    }
+}
diff --git a/tests/RollbackTest/Android.bp b/tests/RollbackTest/Android.bp
index 231d045b..085c53c 100644
--- a/tests/RollbackTest/Android.bp
+++ b/tests/RollbackTest/Android.bp
@@ -19,7 +19,6 @@
     static_libs: ["androidx.test.rules", "cts-rollback-lib", "cts-install-lib"],
     test_suites: ["general-tests"],
     test_config: "RollbackTest.xml",
-    // TODO: sdk_version: "test_current" when Intent#resolveSystemservice is TestApi
 }
 
 java_test_host {
diff --git a/tools/codegen/src/com/android/codegen/FieldInfo.kt b/tools/codegen/src/com/android/codegen/FieldInfo.kt
index ba00264..1a7fd6e 100644
--- a/tools/codegen/src/com/android/codegen/FieldInfo.kt
+++ b/tools/codegen/src/com/android/codegen/FieldInfo.kt
@@ -191,7 +191,7 @@
      * Parcel.write* and Parcel.read* method name wildcard values
      */
     val ParcelMethodsSuffix = when {
-        FieldClass in PRIMITIVE_TYPES - "char" - "boolean" +
+        FieldClass in PRIMITIVE_TYPES - "char" - "boolean" + BOXED_PRIMITIVE_TYPES +
                 listOf("String", "CharSequence", "Exception", "Size", "SizeF", "Bundle",
                         "FileDescriptor", "SparseBooleanArray", "SparseIntArray", "SparseArray") ->
             FieldClass
diff --git a/tools/codegen/src/com/android/codegen/Generators.kt b/tools/codegen/src/com/android/codegen/Generators.kt
index 5a95676..0ebb3cf 100644
--- a/tools/codegen/src/com/android/codegen/Generators.kt
+++ b/tools/codegen/src/com/android/codegen/Generators.kt
@@ -854,6 +854,7 @@
         it.nameAsString == intOrStringDef?.AnnotationName
                 || it.nameAsString in knownNonValidationAnnotations
                 || it in perElementValidations
+                || it.args.any { (_, value) -> value is ArrayInitializerExpr }
     }.forEach { annotation ->
         appendValidateCall(annotation,
                 valueToValidate = if (annotation.nameAsString == Size) sizeExpr else name)
@@ -874,14 +875,7 @@
     val validate = memberRef("com.android.internal.util.AnnotationValidations.validate")
     "$validate(" {
         !"${annotation.nameAsString}.class, null, $valueToValidate"
-        val params = when (annotation) {
-            is MarkerAnnotationExpr -> emptyMap()
-            is SingleMemberAnnotationExpr -> mapOf("value" to annotation.memberValue)
-            is NormalAnnotationExpr ->
-                annotation.pairs.map { it.name.asString() to it.value }.toMap()
-            else -> throw IllegalStateException()
-        }
-        params.forEach { name, value ->
+        annotation.args.forEach { name, value ->
             !",\n\"$name\", $value"
         }
     }
diff --git a/tools/codegen/src/com/android/codegen/InputSignaturesComputation.kt b/tools/codegen/src/com/android/codegen/InputSignaturesComputation.kt
index 24cf469..d6953c0 100644
--- a/tools/codegen/src/com/android/codegen/InputSignaturesComputation.kt
+++ b/tools/codegen/src/com/android/codegen/InputSignaturesComputation.kt
@@ -87,6 +87,14 @@
         is IntegerLiteralExpr -> sb.append(ex.asInt()).append("L")
         is LongLiteralExpr -> sb.append(ex.asLong()).append("L")
         is DoubleLiteralExpr -> sb.append(ex.asDouble())
+        is ArrayInitializerExpr -> {
+            sb.append("{")
+            ex.values.forEachLastAware { arrayElem, isLast ->
+                appendExpr(sb, arrayElem)
+                if (!isLast) sb.append(", ")
+            }
+            sb.append("}")
+        }
         else -> sb.append(ex)
     }
 }
diff --git a/tools/codegen/src/com/android/codegen/Main.kt b/tools/codegen/src/com/android/codegen/Main.kt
index 039f7b2..ce83d3d 100755
--- a/tools/codegen/src/com/android/codegen/Main.kt
+++ b/tools/codegen/src/com/android/codegen/Main.kt
@@ -9,6 +9,7 @@
 const val INDENT_SINGLE = "    "
 
 val PRIMITIVE_TYPES = listOf("byte", "short", "int", "long", "char", "float", "double", "boolean")
+val BOXED_PRIMITIVE_TYPES = PRIMITIVE_TYPES.map { it.capitalize() } - "Int" + "Integer" - "Char" + "Character"
 
 val BUILTIN_SPECIAL_PARCELLINGS = listOf("Pattern")
 
@@ -142,6 +143,10 @@
         //
         // To regenerate run:
         // $ $cliExecutable ${cliArgs.dropLast(1).joinToString("") { "$it " }}$fileEscaped
+        //
+        // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+        //   Settings > Editor > Code Style > Formatter Control
+        //@formatter:off
 
         """
 
diff --git a/tools/codegen/src/com/android/codegen/SharedConstants.kt b/tools/codegen/src/com/android/codegen/SharedConstants.kt
index 47f774f..8c4583f 100644
--- a/tools/codegen/src/com/android/codegen/SharedConstants.kt
+++ b/tools/codegen/src/com/android/codegen/SharedConstants.kt
@@ -1,7 +1,7 @@
 package com.android.codegen
 
 const val CODEGEN_NAME = "codegen"
-const val CODEGEN_VERSION = "1.0.7"
+const val CODEGEN_VERSION = "1.0.8"
 
 const val CANONICAL_BUILDER_CLASS = "Builder"
 const val BASE_BUILDER_CLASS = "BaseBuilder"
diff --git a/tools/codegen/src/com/android/codegen/Utils.kt b/tools/codegen/src/com/android/codegen/Utils.kt
index a1f068a..e703397 100644
--- a/tools/codegen/src/com/android/codegen/Utils.kt
+++ b/tools/codegen/src/com/android/codegen/Utils.kt
@@ -1,9 +1,6 @@
 package com.android.codegen
 
-import com.github.javaparser.ast.Modifier
-import com.github.javaparser.ast.expr.AnnotationExpr
-import com.github.javaparser.ast.expr.Expression
-import com.github.javaparser.ast.expr.SingleMemberAnnotationExpr
+import com.github.javaparser.ast.expr.*
 import com.github.javaparser.ast.nodeTypes.NodeWithModifiers
 import java.time.Instant
 import java.time.ZoneId
@@ -88,3 +85,10 @@
 }
 
 fun bitAtExpr(bitIndex: Int) = "0x${java.lang.Long.toHexString(1L shl bitIndex)}"
+
+val AnnotationExpr.args: Map<String, Expression> get() = when (this) {
+    is MarkerAnnotationExpr -> emptyMap()
+    is SingleMemberAnnotationExpr -> mapOf("value" to memberValue)
+    is NormalAnnotationExpr -> pairs.map { it.name.asString() to it.value }.toMap()
+    else -> throw IllegalArgumentException("Unknown annotation expression: $this")
+}
diff --git a/tools/streaming_proto/Android.bp b/tools/streaming_proto/Android.bp
index 14eead8..1390f63 100644
--- a/tools/streaming_proto/Android.bp
+++ b/tools/streaming_proto/Android.bp
@@ -29,7 +29,7 @@
         "-Werror",
     ],
 
-    shared_libs: ["libprotoc"],
+    static_libs: ["libprotoc"],
 }
 
 cc_binary_host {