Merge "Toast on broadcast receiver notification trampolines"
diff --git a/apct-tests/perftests/contentcapture/Android.bp b/apct-tests/perftests/contentcapture/Android.bp
new file mode 100644
index 0000000..66d7348
--- /dev/null
+++ b/apct-tests/perftests/contentcapture/Android.bp
@@ -0,0 +1,28 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+android_test {
+    name: "ContentCapturePerfTests",
+    srcs: ["src/**/*.java"],
+    resource_dirs: ["res"],
+    static_libs: [
+        "androidx.test.rules",
+        "androidx.annotation_annotation",
+        "apct-perftests-utils",
+        "collector-device-lib-platform",
+        "compatibility-device-util-axt",
+    ],
+    platform_apis: true,
+    test_suites: ["device-tests"],
+}
diff --git a/apct-tests/perftests/contentcapture/AndroidManifest.xml b/apct-tests/perftests/contentcapture/AndroidManifest.xml
new file mode 100644
index 0000000..ee5577f
--- /dev/null
+++ b/apct-tests/perftests/contentcapture/AndroidManifest.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.perftests.contentcapture">
+
+    <uses-sdk android:targetSdkVersion="28" />
+
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+    <uses-permission android:name="android.permission.REAL_GET_TASKS" />
+
+    <application>
+        <uses-library android:name="android.test.runner" />
+        <activity android:name="android.view.contentcapture.CustomTestActivity"
+                android:exported="true">
+        </activity>
+
+        <service
+            android:name="android.view.contentcapture.MyContentCaptureService"
+            android:label="PERF ContentCaptureService"
+            android:permission="android.permission.BIND_CONTENT_CAPTURE_SERVICE"
+            android:exported="true">
+            <intent-filter>
+                <action android:name="android.service.contentcapture.ContentCaptureService" />
+            </intent-filter>
+        </service>
+
+    </application>
+
+    <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+        android:targetPackage="com.android.perftests.contentcapture" />
+</manifest>
diff --git a/apct-tests/perftests/contentcapture/AndroidTest.xml b/apct-tests/perftests/contentcapture/AndroidTest.xml
new file mode 100644
index 0000000..d2386bb
--- /dev/null
+++ b/apct-tests/perftests/contentcapture/AndroidTest.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<configuration description="Runs ContentCapturePerfTests metric instrumentation.">
+    <option name="test-suite-tag" value="apct" />
+    <option name="test-suite-tag" value="apct-metric-instrumentation" />
+    <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+        <option name="cleanup-apks" value="true" />
+        <option name="test-file-name" value="ContentCapturePerfTests.apk" />
+    </target_preparer>
+
+    <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+        <option name="package" value="com.android.perftests.contentcapture" />
+    </test>
+</configuration>
diff --git a/apct-tests/perftests/contentcapture/res/layout/test_container_activity.xml b/apct-tests/perftests/contentcapture/res/layout/test_container_activity.xml
new file mode 100644
index 0000000..ca1a11a
--- /dev/null
+++ b/apct-tests/perftests/contentcapture/res/layout/test_container_activity.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              xmlns:tools="http://schemas.android.com/tools"
+              android:id="@+id/root_view"
+              android:layout_width="match_parent"
+              android:layout_height="match_parent"
+              android:focusable="true"
+              android:focusableInTouchMode="true"
+              android:orientation="vertical" >
+</LinearLayout>
diff --git a/apct-tests/perftests/contentcapture/res/layout/test_login_activity.xml b/apct-tests/perftests/contentcapture/res/layout/test_login_activity.xml
new file mode 100644
index 0000000..9bab32c
--- /dev/null
+++ b/apct-tests/perftests/contentcapture/res/layout/test_login_activity.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              xmlns:tools="http://schemas.android.com/tools"
+              android:id="@+id/root_view"
+              android:layout_width="match_parent"
+              android:layout_height="match_parent"
+              android:focusable="true"
+              android:focusableInTouchMode="true"
+              android:orientation="vertical" >
+
+    <TextView
+        android:id="@+id/username_label"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="Username" />
+
+    <EditText
+        android:id="@+id/username"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content" />
+
+    <TextView
+        android:id="@+id/password_label"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="Password" />
+
+    <EditText
+        android:id="@+id/password"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:inputType="textPassword" />
+
+</LinearLayout>
diff --git a/apct-tests/perftests/contentcapture/src/android/view/contentcapture/AbstractContentCapturePerfTestCase.java b/apct-tests/perftests/contentcapture/src/android/view/contentcapture/AbstractContentCapturePerfTestCase.java
new file mode 100644
index 0000000..f02c96d
--- /dev/null
+++ b/apct-tests/perftests/contentcapture/src/android/view/contentcapture/AbstractContentCapturePerfTestCase.java
@@ -0,0 +1,269 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.view.contentcapture;
+
+import static android.view.contentcapture.CustomTestActivity.INTENT_EXTRA_CUSTOM_VIEWS;
+import static android.view.contentcapture.CustomTestActivity.INTENT_EXTRA_LAYOUT_ID;
+
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
+import static com.android.compatibility.common.util.ShellUtils.runShellCommand;
+
+import android.app.Application;
+import android.content.ContentCaptureOptions;
+import android.content.Context;
+import android.content.Intent;
+import android.os.BatteryManager;
+import android.os.UserHandle;
+import android.perftests.utils.PerfStatusReporter;
+import android.provider.Settings;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+import androidx.test.filters.LargeTest;
+import androidx.test.rule.ActivityTestRule;
+
+import com.android.compatibility.common.util.ActivitiesWatcher;
+import com.android.compatibility.common.util.ActivitiesWatcher.ActivityWatcher;
+import com.android.perftests.contentcapture.R;
+
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Rule;
+import org.junit.rules.TestRule;
+import org.junit.runners.model.Statement;
+
+/**
+ * Base class for all content capture tests.
+ */
+@LargeTest
+public abstract class AbstractContentCapturePerfTestCase {
+
+    private static final String TAG = AbstractContentCapturePerfTestCase.class.getSimpleName();
+    private static final long GENERIC_TIMEOUT_MS = 10_000;
+
+    private static int sOriginalStayOnWhilePluggedIn;
+    private static Context sContext = getInstrumentation().getTargetContext();
+
+    protected ActivitiesWatcher mActivitiesWatcher;
+
+    private MyContentCaptureService.ServiceWatcher mServiceWatcher;
+
+    @Rule
+    public ActivityTestRule<CustomTestActivity> mActivityRule =
+            new ActivityTestRule<>(CustomTestActivity.class, false, false);
+
+    @Rule
+    public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+    @Rule
+    public TestRule mServiceDisablerRule = (base, description) -> {
+        return new Statement() {
+            @Override
+            public void evaluate() throws Throwable {
+                try {
+                    base.evaluate();
+                } finally {
+                    Log.v(TAG, "@mServiceDisablerRule: safelyDisableService()");
+                    safelyDisableService();
+                }
+            }
+        };
+    };
+
+    private void safelyDisableService() {
+        try {
+            resetService();
+            MyContentCaptureService.resetStaticState();
+
+            if (mServiceWatcher != null) {
+                mServiceWatcher.waitOnDestroy();
+            }
+        } catch (Throwable t) {
+            Log.e(TAG, "error disabling service", t);
+        }
+    }
+
+    /**
+     * Sets the content capture service.
+     */
+    private static void setService(@NonNull String service) {
+        final int userId = getCurrentUserId();
+        Log.d(TAG, "Setting service for user " + userId + " to " + service);
+        // TODO(b/123540602): use @TestingAPI to get max duration constant
+        runShellCommand("cmd content_capture set temporary-service %d %s 119000", userId, service);
+    }
+
+    /**
+     * Resets the content capture service.
+     */
+    private static void resetService() {
+        final int userId = getCurrentUserId();
+        Log.d(TAG, "Resetting back user " + userId + " to default service");
+        runShellCommand("cmd content_capture set temporary-service %d", userId);
+    }
+
+    private static int getCurrentUserId() {
+        return UserHandle.myUserId();
+    }
+
+    @BeforeClass
+    public static void setStayAwake() {
+        Log.v(TAG, "@BeforeClass: setStayAwake()");
+        // Some test cases will restart the activity, and stay awake is necessary to ensure that
+        // the test will not screen off during the test.
+        // Keeping the activity screen on is not enough, screen off may occur between the activity
+        // finished and the next start
+        final int stayOnWhilePluggedIn = Settings.Global.getInt(sContext.getContentResolver(),
+                Settings.Global.STAY_ON_WHILE_PLUGGED_IN, 0);
+        sOriginalStayOnWhilePluggedIn = -1;
+        if (stayOnWhilePluggedIn != BatteryManager.BATTERY_PLUGGED_ANY) {
+            sOriginalStayOnWhilePluggedIn = stayOnWhilePluggedIn;
+            // Keep the device awake during testing.
+            setStayOnWhilePluggedIn(BatteryManager.BATTERY_PLUGGED_ANY);
+        }
+    }
+
+    @AfterClass
+    public static void resetStayAwake() {
+        Log.v(TAG, "@AfterClass: resetStayAwake()");
+        if (sOriginalStayOnWhilePluggedIn != -1) {
+            setStayOnWhilePluggedIn(sOriginalStayOnWhilePluggedIn);
+        }
+    }
+
+    private static void setStayOnWhilePluggedIn(int value) {
+        runShellCommand(String.format("settings put global %s %d",
+                Settings.Global.STAY_ON_WHILE_PLUGGED_IN, value));
+    }
+
+    @BeforeClass
+    public static void setAllowSelf() {
+        final ContentCaptureOptions options = new ContentCaptureOptions(null);
+        Log.v(TAG, "@BeforeClass: setAllowSelf(): options=" + options);
+        sContext.getApplicationContext().setContentCaptureOptions(options);
+    }
+
+    @AfterClass
+    public static void unsetAllowSelf() {
+        Log.v(TAG, "@AfterClass: unsetAllowSelf()");
+        clearOptions();
+    }
+
+    protected static void clearOptions() {
+        sContext.getApplicationContext().setContentCaptureOptions(null);
+    }
+
+    @BeforeClass
+    public static void disableDefaultService() {
+        Log.v(TAG, "@BeforeClass: disableDefaultService()");
+        setDefaultServiceEnabled(false);
+    }
+
+    @AfterClass
+    public static void enableDefaultService() {
+        Log.v(TAG, "@AfterClass: enableDefaultService()");
+        setDefaultServiceEnabled(true);
+    }
+
+    /**
+     * Enables / disables the default service.
+     */
+    private static void setDefaultServiceEnabled(boolean enabled) {
+        final int userId = getCurrentUserId();
+        Log.d(TAG, "setDefaultServiceEnabled(user=" + userId + ", enabled= " + enabled + ")");
+        runShellCommand("cmd content_capture set default-service-enabled %d %s", userId,
+                Boolean.toString(enabled));
+    }
+
+    @Before
+    public void prepareDevice() throws Exception {
+        Log.v(TAG, "@Before: prepareDevice()");
+
+        // Unlock screen.
+        runShellCommand("input keyevent KEYCODE_WAKEUP");
+
+        // Dismiss keyguard, in case it's set as "Swipe to unlock".
+        runShellCommand("wm dismiss-keyguard");
+
+        // Collapse notifications.
+        runShellCommand("cmd statusbar collapse");
+    }
+
+    @Before
+    public void registerLifecycleCallback() {
+        Log.v(TAG, "@Before: Registering lifecycle callback");
+        final Application app = (Application) sContext.getApplicationContext();
+        mActivitiesWatcher = new ActivitiesWatcher(GENERIC_TIMEOUT_MS);
+        app.registerActivityLifecycleCallbacks(mActivitiesWatcher);
+    }
+
+    @After
+    public void unregisterLifecycleCallback() {
+        Log.d(TAG, "@After: Unregistering lifecycle callback: " + mActivitiesWatcher);
+        if (mActivitiesWatcher != null) {
+            final Application app = (Application) sContext.getApplicationContext();
+            app.unregisterActivityLifecycleCallbacks(mActivitiesWatcher);
+        }
+    }
+
+    /**
+     * Sets {@link MyContentCaptureService} as the service for the current user and waits until
+     * its created, then add the perf test package into allow list.
+     */
+    public MyContentCaptureService enableService() throws InterruptedException {
+        if (mServiceWatcher != null) {
+            throw new IllegalStateException("There Can Be Only One!");
+        }
+
+        mServiceWatcher = MyContentCaptureService.setServiceWatcher();
+        setService(MyContentCaptureService.SERVICE_NAME);
+        mServiceWatcher.setAllowSelf();
+        return mServiceWatcher.waitOnCreate();
+    }
+
+    @NonNull
+    protected ActivityWatcher startWatcher() {
+        return mActivitiesWatcher.watch(CustomTestActivity.class);
+    }
+
+    /**
+     * Launch test activity with default login layout
+     */
+    protected CustomTestActivity launchActivity() {
+        return launchActivity(R.layout.test_login_activity, 0);
+    }
+
+    /**
+     * Launch test activity with give layout and parameter
+     */
+    protected CustomTestActivity launchActivity(int layoutId, int numViews) {
+        final Intent intent = new Intent(sContext, CustomTestActivity.class);
+        intent.putExtra(INTENT_EXTRA_LAYOUT_ID, layoutId);
+        intent.putExtra(INTENT_EXTRA_CUSTOM_VIEWS, numViews);
+        return mActivityRule.launchActivity(intent);
+    }
+
+    protected void finishActivity() {
+        try {
+            mActivityRule.finishActivity();
+        } catch (IllegalStateException e) {
+            // no op
+        }
+    }
+}
diff --git a/apct-tests/perftests/contentcapture/src/android/view/contentcapture/CustomTestActivity.java b/apct-tests/perftests/contentcapture/src/android/view/contentcapture/CustomTestActivity.java
new file mode 100644
index 0000000..e509837
--- /dev/null
+++ b/apct-tests/perftests/contentcapture/src/android/view/contentcapture/CustomTestActivity.java
@@ -0,0 +1,88 @@
+/*
+ * 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.view.contentcapture;
+
+import android.app.Activity;
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import com.android.perftests.contentcapture.R;
+
+/**
+ * A simple activity used for testing, e.g. performance of activity switching, or as a base
+ * container of testing view.
+ */
+public class CustomTestActivity extends Activity {
+    public static final String INTENT_EXTRA_LAYOUT_ID = "layout_id";
+    public static final String INTENT_EXTRA_CUSTOM_VIEWS = "custom_view_number";
+    public static final int MAX_VIEWS = 500;
+    private static final int CUSTOM_CONTAINER_LAYOUT_ID = R.layout.test_container_activity;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        if (getIntent().hasExtra(INTENT_EXTRA_LAYOUT_ID)) {
+            final int layoutId = getIntent().getIntExtra(INTENT_EXTRA_LAYOUT_ID,
+                    /* defaultValue= */0);
+            setContentView(layoutId);
+            if (layoutId == CUSTOM_CONTAINER_LAYOUT_ID) {
+                createCustomViews(findViewById(R.id.root_view),
+                        getIntent().getIntExtra(INTENT_EXTRA_CUSTOM_VIEWS, MAX_VIEWS));
+            }
+        }
+    }
+
+    private void createCustomViews(LinearLayout root, int number) {
+        LinearLayout horizontalLayout = null;
+        for (int i = 0; i < number; i++) {
+            final int j = i % 8;
+            if (horizontalLayout != null && j == 0) {
+                root.addView(horizontalLayout);
+                horizontalLayout = null;
+            }
+            if (horizontalLayout == null) {
+                horizontalLayout = createHorizontalLayout();
+            }
+            horizontalLayout.addView(createItem(null, i));
+        }
+        if (horizontalLayout != null) {
+            root.addView(horizontalLayout);
+        }
+    }
+
+    private LinearLayout createHorizontalLayout() {
+        final LinearLayout layout = new LinearLayout(getApplicationContext());
+        layout.setOrientation(LinearLayout.HORIZONTAL);
+        return layout;
+    }
+
+    private LinearLayout createItem(Drawable drawable, int index) {
+        final LinearLayout group = new LinearLayout(getApplicationContext());
+        group.setOrientation(LinearLayout.VERTICAL);
+        group.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT,
+                LinearLayout.LayoutParams.WRAP_CONTENT, /* weight= */ 1.0f));
+
+        final TextView text = new TextView(this);
+        text.setText("i = " + index);
+        group.addView(text);
+
+        return group;
+    }
+}
diff --git a/apct-tests/perftests/contentcapture/src/android/view/contentcapture/LoginTest.java b/apct-tests/perftests/contentcapture/src/android/view/contentcapture/LoginTest.java
new file mode 100644
index 0000000..750d3b4
--- /dev/null
+++ b/apct-tests/perftests/contentcapture/src/android/view/contentcapture/LoginTest.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.view.contentcapture;
+
+import static com.android.compatibility.common.util.ActivitiesWatcher.ActivityLifecycle.CREATED;
+import static com.android.compatibility.common.util.ActivitiesWatcher.ActivityLifecycle.DESTROYED;
+
+import android.perftests.utils.BenchmarkState;
+import android.view.View;
+
+import com.android.compatibility.common.util.ActivitiesWatcher.ActivityWatcher;
+import com.android.perftests.contentcapture.R;
+
+import org.junit.Test;
+
+public class LoginTest extends AbstractContentCapturePerfTestCase {
+
+    @Test
+    public void testLaunchActivity() throws Throwable {
+        enableService();
+
+        testActivityLaunchTime(R.layout.test_login_activity, 0);
+    }
+
+    @Test
+    public void testLaunchActivity_contain100Views() throws Throwable {
+        enableService();
+
+        testActivityLaunchTime(R.layout.test_container_activity, 100);
+    }
+
+    @Test
+    public void testLaunchActivity_contain300Views() throws Throwable {
+        enableService();
+
+        testActivityLaunchTime(R.layout.test_container_activity, 300);
+    }
+
+    @Test
+    public void testLaunchActivity_contain500Views() throws Throwable {
+        enableService();
+
+        testActivityLaunchTime(R.layout.test_container_activity, 500);
+    }
+
+    @Test
+    public void testLaunchActivity_noService() throws Throwable {
+        testActivityLaunchTime(R.layout.test_login_activity, 0);
+    }
+
+    @Test
+    public void testLaunchActivity_noService_contain100Views() throws Throwable {
+        testActivityLaunchTime(R.layout.test_container_activity, 100);
+    }
+
+    @Test
+    public void testLaunchActivity_noService_contain300Views() throws Throwable {
+        testActivityLaunchTime(R.layout.test_container_activity, 300);
+    }
+
+    @Test
+    public void testLaunchActivity_noService_contain500Views() throws Throwable {
+        testActivityLaunchTime(R.layout.test_container_activity, 500);
+    }
+
+    private void testActivityLaunchTime(int layoutId, int numViews) throws Throwable {
+        final ActivityWatcher watcher = startWatcher();
+
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            launchActivity(layoutId, numViews);
+
+            // Ignore the time to finish the activity
+            state.pauseTiming();
+            watcher.waitFor(CREATED);
+            finishActivity();
+            watcher.waitFor(DESTROYED);
+            state.resumeTiming();
+        }
+    }
+
+    @Test
+    public void testOnVisibilityAggregated_visibleChanged() throws Throwable {
+        enableService();
+        final CustomTestActivity activity = launchActivity();
+        final View root = activity.getWindow().getDecorView();
+        final View username = root.findViewById(R.id.username);
+
+        testOnVisibilityAggregated(username);
+    }
+
+    @Test
+    public void testOnVisibilityAggregated_visibleChanged_noService() throws Throwable {
+        final CustomTestActivity activity = launchActivity();
+        final View root = activity.getWindow().getDecorView();
+        final View username = root.findViewById(R.id.username);
+
+        testOnVisibilityAggregated(username);
+    }
+
+    @Test
+    public void testOnVisibilityAggregated_visibleChanged_noOptions() throws Throwable {
+        enableService();
+        clearOptions();
+        final CustomTestActivity activity = launchActivity();
+        final View root = activity.getWindow().getDecorView();
+        final View username = root.findViewById(R.id.username);
+
+        testOnVisibilityAggregated(username);
+    }
+
+    @Test
+    public void testOnVisibilityAggregated_visibleChanged_notImportant() throws Throwable {
+        enableService();
+        final CustomTestActivity activity = launchActivity();
+        final View root = activity.getWindow().getDecorView();
+        final View username = root.findViewById(R.id.username);
+        username.setImportantForContentCapture(View.IMPORTANT_FOR_CONTENT_CAPTURE_NO);
+
+        testOnVisibilityAggregated(username);
+    }
+
+    private void testOnVisibilityAggregated(View view) throws Throwable {
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+
+        while (state.keepRunning()) {
+            // Only count the time of onVisibilityAggregated()
+            state.pauseTiming();
+            mActivityRule.runOnUiThread(() -> {
+                state.resumeTiming();
+                view.onVisibilityAggregated(false);
+                state.pauseTiming();
+            });
+            mActivityRule.runOnUiThread(() -> {
+                state.resumeTiming();
+                view.onVisibilityAggregated(true);
+                state.pauseTiming();
+            });
+            state.resumeTiming();
+        }
+    }
+}
diff --git a/apct-tests/perftests/contentcapture/src/android/view/contentcapture/MyContentCaptureService.java b/apct-tests/perftests/contentcapture/src/android/view/contentcapture/MyContentCaptureService.java
new file mode 100644
index 0000000..b1dbb28
--- /dev/null
+++ b/apct-tests/perftests/contentcapture/src/android/view/contentcapture/MyContentCaptureService.java
@@ -0,0 +1,181 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.view.contentcapture;
+
+import android.content.ComponentName;
+import android.service.contentcapture.ActivityEvent;
+import android.service.contentcapture.ContentCaptureService;
+import android.util.ArraySet;
+import android.util.Log;
+import android.util.Pair;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import java.util.Set;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+public class MyContentCaptureService extends ContentCaptureService {
+
+    private static final String TAG = MyContentCaptureService.class.getSimpleName();
+    private static final String MY_PACKAGE = "com.android.perftests.contentcapture";
+    public static final String SERVICE_NAME = MY_PACKAGE + "/"
+            + MyContentCaptureService.class.getName();
+
+    private static ServiceWatcher sServiceWatcher;
+
+    @NonNull
+    public static ServiceWatcher setServiceWatcher() {
+        if (sServiceWatcher != null) {
+            throw new IllegalStateException("There Can Be Only One!");
+        }
+        sServiceWatcher = new ServiceWatcher();
+        return sServiceWatcher;
+    }
+
+    public static void resetStaticState() {
+        sServiceWatcher = null;
+    }
+
+    public static void clearServiceWatcher() {
+        if (sServiceWatcher != null) {
+            if (sServiceWatcher.mReadyToClear) {
+                sServiceWatcher.mService = null;
+                sServiceWatcher = null;
+            } else {
+                sServiceWatcher.mReadyToClear = true;
+            }
+        }
+    }
+
+    @Override
+    public void onConnected() {
+        Log.i(TAG, "onConnected: sServiceWatcher=" + sServiceWatcher);
+
+        if (sServiceWatcher == null) {
+            Log.e(TAG, "onConnected() without a watcher");
+            return;
+        }
+
+        if (!sServiceWatcher.mReadyToClear && sServiceWatcher.mService != null) {
+            Log.e(TAG, "onConnected(): already created: " + sServiceWatcher);
+            return;
+        }
+
+        sServiceWatcher.mService = this;
+        sServiceWatcher.mCreated.countDown();
+        sServiceWatcher.mReadyToClear = false;
+    }
+
+    @Override
+    public void onDisconnected() {
+        Log.i(TAG, "onDisconnected: sServiceWatcher=" + sServiceWatcher);
+
+        if (sServiceWatcher == null) {
+            Log.e(TAG, "onDisconnected() without a watcher");
+            return;
+        }
+        if (sServiceWatcher.mService == null) {
+            Log.e(TAG, "onDisconnected(): no service on " + sServiceWatcher);
+            return;
+        }
+
+        sServiceWatcher.mDestroyed.countDown();
+        clearServiceWatcher();
+    }
+
+    @Override
+    public void onCreateContentCaptureSession(ContentCaptureContext context,
+            ContentCaptureSessionId sessionId) {
+        Log.i(TAG, "onCreateContentCaptureSession(ctx=" + context + ", session=" + sessionId);
+    }
+
+    @Override
+    public void onDestroyContentCaptureSession(ContentCaptureSessionId sessionId) {
+        Log.i(TAG, "onDestroyContentCaptureSession(session=" + sessionId + ")");
+    }
+
+    @Override
+    public void onContentCaptureEvent(ContentCaptureSessionId sessionId,
+            ContentCaptureEvent event) {
+        Log.i(TAG, "onContentCaptureEventsRequest(session=" + sessionId + "): " + event);
+    }
+
+    @Override
+    public void onActivityEvent(ActivityEvent event) {
+        Log.i(TAG, "onActivityEvent(): " + event);
+    }
+
+    public static final class ServiceWatcher {
+
+        private static final long GENERIC_TIMEOUT_MS = 10_000;
+        private final CountDownLatch mCreated = new CountDownLatch(1);
+        private final CountDownLatch mDestroyed = new CountDownLatch(1);
+        private boolean mReadyToClear = true;
+        private Pair<Set<String>, Set<ComponentName>> mAllowList;
+
+        private MyContentCaptureService mService;
+
+        @NonNull
+        public MyContentCaptureService waitOnCreate() throws InterruptedException {
+            await(mCreated, "not created");
+
+            if (mService == null) {
+                throw new IllegalStateException("not created");
+            }
+
+            if (mAllowList != null) {
+                Log.d(TAG, "Allow after created: " + mAllowList);
+                mService.setContentCaptureWhitelist(mAllowList.first, mAllowList.second);
+            }
+
+            return mService;
+        }
+
+        public void waitOnDestroy() throws InterruptedException {
+            await(mDestroyed, "not destroyed");
+        }
+
+        /**
+         * Allow just this package.
+         */
+        public void setAllowSelf() {
+            final ArraySet<String> pkgs = new ArraySet<>(1);
+            pkgs.add(MY_PACKAGE);
+            mAllowList = new Pair<>(pkgs, null);
+        }
+
+        @Override
+        public String toString() {
+            return "mService: " + mService + " created: " + (mCreated.getCount() == 0)
+                    + " destroyed: " + (mDestroyed.getCount() == 0);
+        }
+
+        /**
+         * Awaits for a latch to be counted down.
+         */
+        private static void await(@NonNull CountDownLatch latch, @NonNull String fmt,
+                @Nullable Object... args)
+                throws InterruptedException {
+            final boolean called = latch.await(GENERIC_TIMEOUT_MS, TimeUnit.MILLISECONDS);
+            if (!called) {
+                throw new IllegalStateException(String.format(fmt, args)
+                        + " in " + GENERIC_TIMEOUT_MS + "ms");
+            }
+        }
+    }
+}
diff --git a/apct-tests/perftests/core/src/android/view/CutoutSpecificationBenchmark.java b/apct-tests/perftests/core/src/android/view/CutoutSpecificationBenchmark.java
index abd39f8..860c134 100644
--- a/apct-tests/perftests/core/src/android/view/CutoutSpecificationBenchmark.java
+++ b/apct-tests/perftests/core/src/android/view/CutoutSpecificationBenchmark.java
@@ -16,24 +16,20 @@
 
 package android.view;
 
-import android.content.Context;
 import android.graphics.Matrix;
 import android.graphics.Path;
 import android.graphics.Rect;
 import android.graphics.RectF;
 import android.graphics.Region;
 import android.text.TextUtils;
-import android.util.DisplayMetrics;
 import android.util.Log;
 import android.util.PathParser;
 
 import androidx.benchmark.BenchmarkState;
 import androidx.benchmark.junit4.BenchmarkRule;
 import androidx.test.filters.LargeTest;
-import androidx.test.platform.app.InstrumentationRegistry;
 import androidx.test.runner.AndroidJUnit4;
 
-import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -41,6 +37,10 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class CutoutSpecificationBenchmark {
+    private static final int DISPLAY_WIDTH = 1080;
+    private static final int DISPLAY_HEIGHT = 1920;
+    private static final float DISPLAY_DENSITY = 3.5f;
+
     private static final String TAG = "CutoutSpecificationBenchmark";
 
     private static final String BOTTOM_MARKER = "@bottom";
@@ -69,21 +69,6 @@
     @Rule
     public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
 
-    private Context mContext;
-    private DisplayMetrics mDisplayMetrics;
-
-    /**
-     * Setup the necessary member field used by test methods.
-     */
-    @Before
-    public void setUp() {
-        mContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
-
-        mDisplayMetrics = new DisplayMetrics();
-        mContext.getDisplay().getRealMetrics(mDisplayMetrics);
-    }
-
-
     private static void toRectAndAddToRegion(Path p, Region inoutRegion, Rect inoutRect) {
         final RectF rectF = new RectF();
         p.computeBounds(rectF, false /* unused */);
@@ -170,8 +155,8 @@
     public void parseByOldMethodForDoubleCutout() {
         final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
-            oldMethodParsingSpec(DOUBLE_CUTOUT_SPEC, mDisplayMetrics.widthPixels,
-                    mDisplayMetrics.heightPixels, mDisplayMetrics.density);
+            oldMethodParsingSpec(DOUBLE_CUTOUT_SPEC, DISPLAY_WIDTH, DISPLAY_HEIGHT,
+                    DISPLAY_DENSITY);
         }
     }
 
@@ -179,8 +164,7 @@
     public void parseByNewMethodForDoubleCutout() {
         final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
-            new CutoutSpecification.Parser(mDisplayMetrics.density,
-                    mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels)
+            new CutoutSpecification.Parser(DISPLAY_DENSITY, DISPLAY_WIDTH, DISPLAY_HEIGHT)
                     .parse(DOUBLE_CUTOUT_SPEC);
         }
     }
@@ -211,8 +195,8 @@
 
         final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
-            new CutoutSpecification.Parser(mDisplayMetrics.density,
-                    mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels).parse(spec);
+            new CutoutSpecification.Parser(DISPLAY_DENSITY, DISPLAY_WIDTH, DISPLAY_HEIGHT)
+                    .parse(spec);
         }
     }
 
@@ -233,8 +217,8 @@
 
         final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
-            new CutoutSpecification.Parser(mDisplayMetrics.density,
-                    mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels).parse(spec);
+            new CutoutSpecification.Parser(DISPLAY_DENSITY, DISPLAY_WIDTH, DISPLAY_HEIGHT)
+                    .parse(spec);
         }
     }
 }
diff --git a/apex/jobscheduler/framework/java/com/android/server/DeviceIdleInternal.java b/apex/jobscheduler/framework/java/com/android/server/DeviceIdleInternal.java
index 6475f57..18643ed 100644
--- a/apex/jobscheduler/framework/java/com/android/server/DeviceIdleInternal.java
+++ b/apex/jobscheduler/framework/java/com/android/server/DeviceIdleInternal.java
@@ -37,7 +37,7 @@
             String reason);
 
     // duration in milliseconds
-    long getNotificationWhitelistDuration();
+    long getNotificationAllowlistDuration();
 
     void setJobsActive(boolean active);
 
diff --git a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
index f26549c..b1bafee 100644
--- a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
+++ b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
@@ -890,7 +890,8 @@
                 "mms_temp_app_whitelist_duration";
         private static final String KEY_SMS_TEMP_APP_WHITELIST_DURATION =
                 "sms_temp_app_whitelist_duration";
-        private static final String KEY_NOTIFICATION_WHITELIST_DURATION =
+        // TODO(b/124466289): update value to match the name
+        private static final String KEY_NOTIFICATION_ALLOWLIST_DURATION =
                 "notification_whitelist_duration";
         /**
          * Whether to wait for the user to unlock the device before causing screen-on to
@@ -1124,9 +1125,9 @@
          * Amount of time we would like to whitelist an app that is handling a
          * {@link android.app.PendingIntent} triggered by a {@link android.app.Notification}.
          * @see Settings.Global#DEVICE_IDLE_CONSTANTS
-         * @see #KEY_NOTIFICATION_WHITELIST_DURATION
+         * @see #KEY_NOTIFICATION_ALLOWLIST_DURATION
          */
-        public long NOTIFICATION_WHITELIST_DURATION;
+        public long NOTIFICATION_ALLOWLIST_DURATION;
 
         /**
          * Pre idle time factor use to make idle delay longer
@@ -1230,8 +1231,8 @@
                         KEY_MMS_TEMP_APP_WHITELIST_DURATION, 60 * 1000L);
                 SMS_TEMP_APP_WHITELIST_DURATION = mParser.getDurationMillis(
                         KEY_SMS_TEMP_APP_WHITELIST_DURATION, 20 * 1000L);
-                NOTIFICATION_WHITELIST_DURATION = mParser.getDurationMillis(
-                        KEY_NOTIFICATION_WHITELIST_DURATION, 30 * 1000L);
+                NOTIFICATION_ALLOWLIST_DURATION = mParser.getDurationMillis(
+                        KEY_NOTIFICATION_ALLOWLIST_DURATION, 30 * 1000L);
                 WAIT_FOR_UNLOCK = mParser.getBoolean(KEY_WAIT_FOR_UNLOCK, true);
                 PRE_IDLE_FACTOR_LONG = mParser.getFloat(KEY_PRE_IDLE_FACTOR_LONG, 1.67f);
                 PRE_IDLE_FACTOR_SHORT = mParser.getFloat(KEY_PRE_IDLE_FACTOR_SHORT, 0.33f);
@@ -1343,8 +1344,8 @@
             TimeUtils.formatDuration(SMS_TEMP_APP_WHITELIST_DURATION, pw);
             pw.println();
 
-            pw.print("    "); pw.print(KEY_NOTIFICATION_WHITELIST_DURATION); pw.print("=");
-            TimeUtils.formatDuration(NOTIFICATION_WHITELIST_DURATION, pw);
+            pw.print("    "); pw.print(KEY_NOTIFICATION_ALLOWLIST_DURATION); pw.print("=");
+            TimeUtils.formatDuration(NOTIFICATION_ALLOWLIST_DURATION, pw);
             pw.println();
 
             pw.print("    "); pw.print(KEY_WAIT_FOR_UNLOCK); pw.print("=");
@@ -1792,8 +1793,8 @@
 
         // duration in milliseconds
         @Override
-        public long getNotificationWhitelistDuration() {
-            return mConstants.NOTIFICATION_WHITELIST_DURATION;
+        public long getNotificationAllowlistDuration() {
+            return mConstants.NOTIFICATION_ALLOWLIST_DURATION;
         }
 
         @Override
diff --git a/api/current.txt b/api/current.txt
index 7119902..1d2286b 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -24079,6 +24079,7 @@
     field public static final int TYPE_IP = 20; // 0x14
     field public static final int TYPE_LINE_ANALOG = 5; // 0x5
     field public static final int TYPE_LINE_DIGITAL = 6; // 0x6
+    field public static final int TYPE_REMOTE_SUBMIX = 25; // 0x19
     field public static final int TYPE_TELEPHONY = 18; // 0x12
     field public static final int TYPE_TV_TUNER = 17; // 0x11
     field public static final int TYPE_UNKNOWN = 0; // 0x0
diff --git a/api/module-lib-current.txt b/api/module-lib-current.txt
index 360e44f..c91c39a 100644
--- a/api/module-lib-current.txt
+++ b/api/module-lib-current.txt
@@ -5,6 +5,10 @@
     field public static final String OPSTR_NO_ISOLATED_STORAGE = "android:no_isolated_storage";
   }
 
+  public class NotificationManager {
+    method public boolean hasEnabledNotificationListener(@NonNull String, @NonNull android.os.UserHandle);
+  }
+
 }
 
 package android.content.rollback {
@@ -69,6 +73,10 @@
 
 package android.os {
 
+  public class Binder implements android.os.IBinder {
+    method public final void markVintfStability();
+  }
+
   public class StatsFrameworkInitializer {
     method public static void registerServiceWrappers();
     method public static void setStatsServiceManager(@NonNull android.os.StatsServiceManager);
diff --git a/api/removed.txt b/api/removed.txt
index 5a24f62..58dbeb8 100644
--- a/api/removed.txt
+++ b/api/removed.txt
@@ -1,10 +1,6 @@
 // Signature format: 2.0
 package android.app {
 
-  public class ActivityManager {
-    method @Deprecated public static int getMaxNumPictureInPictureActions();
-  }
-
   public class Notification implements android.os.Parcelable {
     method @Deprecated public String getChannel();
     method public static Class<? extends android.app.Notification.Style> getNotificationStyleClass(String);
diff --git a/api/system-current.txt b/api/system-current.txt
index fa45430..11db781 100755
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -4172,10 +4172,6 @@
     field public static final int ROLE_OUTPUT = 2; // 0x2
   }
 
-  public final class AudioDeviceInfo {
-    field public static final int TYPE_REMOTE_SUBMIX = 25; // 0x19
-  }
-
   public final class AudioFocusInfo implements android.os.Parcelable {
     method public int describeContents();
     method @NonNull public android.media.AudioAttributes getAttributes();
diff --git a/config/preloaded-classes b/config/preloaded-classes
index 881cfa3..e43c7d4 100644
--- a/config/preloaded-classes
+++ b/config/preloaded-classes
@@ -417,9 +417,6 @@
 android.app.IProcessObserver$Stub$Proxy
 android.app.IProcessObserver$Stub
 android.app.IProcessObserver
-android.app.IRequestFinishCallback$Stub$Proxy
-android.app.IRequestFinishCallback$Stub
-android.app.IRequestFinishCallback
 android.app.ISearchManager$Stub$Proxy
 android.app.ISearchManager$Stub
 android.app.ISearchManager
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 107fb263..4a982dd 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -150,7 +150,6 @@
 import java.io.PrintWriter;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
-import java.lang.ref.WeakReference;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
@@ -3798,22 +3797,6 @@
         return false;
     }
 
-    private static final class RequestFinishCallback extends IRequestFinishCallback.Stub {
-        private final WeakReference<Activity> mActivityRef;
-
-        RequestFinishCallback(WeakReference<Activity> activityRef) {
-            mActivityRef = activityRef;
-        }
-
-        @Override
-        public void requestFinish() {
-            Activity activity = mActivityRef.get();
-            if (activity != null) {
-                activity.mHandler.post(activity::finishAfterTransition);
-            }
-        }
-    }
-
     /**
      * Called when the activity has detected the user's press of the back
      * key.  The default implementation simply finishes the current activity,
@@ -3837,9 +3820,8 @@
         try {
             // Inform activity task manager that the activity received a back press
             // while at the root of the task. This call allows ActivityTaskManager
-            // to intercept or defer finishing.
-            ActivityTaskManager.getService().onBackPressedOnTaskRoot(mToken,
-                    new RequestFinishCallback(new WeakReference<>(this)));
+            // to intercept or move the task to the back.
+            ActivityTaskManager.getService().onBackPressedOnTaskRoot(mToken);
         } catch (RemoteException e) {
             finishAfterTransition();
         }
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 7a6f558..a88c6a8 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -1019,12 +1019,6 @@
         return ActivityTaskManager.getMaxRecentTasksStatic();
     }
 
-    /** @removed */
-    @Deprecated
-    public static int getMaxNumPictureInPictureActions() {
-        return 3;
-    }
-
     /**
      * Information you can set and retrieve about the current activity within the recent task list.
      */
@@ -3739,7 +3733,8 @@
      * manner, excessive calls to this API could result a {@link java.lang.RuntimeException}.
      * </p>
      *
-     * @param state The state data
+     * @param state The state data. To be advised, <b>DO NOT</b> include sensitive information/data
+     * (PII, SPII, or other sensitive user data) here. Maximum length is 128 bytes.
      */
     public void setProcessStateSummary(@Nullable byte[] state) {
         try {
diff --git a/core/java/android/app/ApplicationLoaders.java b/core/java/android/app/ApplicationLoaders.java
index bac432e4..15237be 100644
--- a/core/java/android/app/ApplicationLoaders.java
+++ b/core/java/android/app/ApplicationLoaders.java
@@ -48,17 +48,18 @@
                                ClassLoader parent, String classLoaderName) {
         return getClassLoaderWithSharedLibraries(zip, targetSdkVersion, isBundled,
                               librarySearchPath, libraryPermittedPath, parent, classLoaderName,
-                              null);
+                              null, null);
     }
 
     ClassLoader getClassLoaderWithSharedLibraries(
             String zip, int targetSdkVersion, boolean isBundled,
             String librarySearchPath, String libraryPermittedPath,
             ClassLoader parent, String classLoaderName,
-            List<ClassLoader> sharedLibraries) {
+            List<ClassLoader> sharedLibraries, List<String> nativeSharedLibraries) {
         // For normal usage the cache key used is the same as the zip path.
         return getClassLoader(zip, targetSdkVersion, isBundled, librarySearchPath,
-                              libraryPermittedPath, parent, zip, classLoaderName, sharedLibraries);
+                              libraryPermittedPath, parent, zip, classLoaderName, sharedLibraries,
+                              nativeSharedLibraries);
     }
 
     /**
@@ -77,14 +78,22 @@
             return loader;
         }
 
+        // TODO(b/142191088): allow (Java) shared libraries to have <uses-native-library>
+        // Until that is supported, assume that all native shared libraries are used.
+        // "ALL" is a magic string that libnativeloader uses to unconditionally add all available
+        // native shared libraries to the classloader.
+        List<String> nativeSharedLibraries = new ArrayList<>();
+        nativeSharedLibraries.add("ALL");
         return getClassLoaderWithSharedLibraries(zip, targetSdkVersion, isBundled,
-              librarySearchPath, libraryPermittedPath, parent, classLoaderName, sharedLibraries);
+              librarySearchPath, libraryPermittedPath, parent, classLoaderName, sharedLibraries,
+              nativeSharedLibraries);
     }
 
     private ClassLoader getClassLoader(String zip, int targetSdkVersion, boolean isBundled,
                                        String librarySearchPath, String libraryPermittedPath,
                                        ClassLoader parent, String cacheKey,
-                                       String classLoaderName, List<ClassLoader> sharedLibraries) {
+                                       String classLoaderName, List<ClassLoader> sharedLibraries,
+                                       List<String> nativeSharedLibraries) {
         /*
          * This is the parent we use if they pass "null" in.  In theory
          * this should be the "system" class loader; in practice we
@@ -113,7 +122,8 @@
 
                 ClassLoader classloader = ClassLoaderFactory.createClassLoader(
                         zip,  librarySearchPath, libraryPermittedPath, parent,
-                        targetSdkVersion, isBundled, classLoaderName, sharedLibraries);
+                        targetSdkVersion, isBundled, classLoaderName, sharedLibraries,
+                        nativeSharedLibraries);
 
                 Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
 
@@ -185,7 +195,8 @@
         // assume cached libraries work with current sdk since they are built-in
         ClassLoader classLoader = getClassLoader(path, Build.VERSION.SDK_INT, true /*isBundled*/,
                 null /*librarySearchPath*/, null /*libraryPermittedPath*/, null /*parent*/,
-                null /*cacheKey*/, null /*classLoaderName*/, sharedLibraries /*sharedLibraries*/);
+                null /*cacheKey*/, null /*classLoaderName*/, sharedLibraries /*sharedLibraries*/,
+                null /* nativeSharedLibraries */);
 
         if (classLoader == null) {
             // bad configuration or break in classloading code
@@ -255,7 +266,8 @@
         // The cache key is passed separately to enable the stub WebView to be cached under the
         // stub's APK path, when the actual package path is the donor APK.
         return getClassLoader(packagePath, Build.VERSION.SDK_INT, false, libsPath, null, null,
-                              cacheKey, null /* classLoaderName */, null /* sharedLibraries */);
+                              cacheKey, null /* classLoaderName */, null /* sharedLibraries */,
+                              null /* nativeSharedLibraries */);
     }
 
     /**
diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl
index f428b48..72a3637d 100644
--- a/core/java/android/app/IActivityTaskManager.aidl
+++ b/core/java/android/app/IActivityTaskManager.aidl
@@ -26,7 +26,6 @@
 import android.app.IAssistDataReceiver;
 import android.app.IInstrumentationWatcher;
 import android.app.IProcessObserver;
-import android.app.IRequestFinishCallback;
 import android.app.IServiceConnection;
 import android.app.IStopUserCallback;
 import android.app.ITaskStackListener;
@@ -458,9 +457,7 @@
 
     /**
      * Reports that an Activity received a back key press when there were no additional activities
-     * on the back stack. If the Activity should be finished, the callback will be invoked. A
-     * callback is used instead of finishing the activity directly from the server such that the
-     * client may perform actions prior to finishing.
+     * on the back stack.
      */
-    void onBackPressedOnTaskRoot(in IBinder activityToken, in IRequestFinishCallback callback);
+    void onBackPressedOnTaskRoot(in IBinder activityToken);
 }
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index 0deef53..a970322 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -185,6 +185,7 @@
     List<ComponentName> getEnabledNotificationListeners(int userId);
     ComponentName getAllowedNotificationAssistantForUser(int userId);
     ComponentName getAllowedNotificationAssistant();
+    boolean hasEnabledNotificationListener(String packageName, int userId);
 
     @UnsupportedAppUsage
     int getZenMode();
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index f9b48e7..aa6a08b 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -412,6 +412,12 @@
             return;
         }
         for (SharedLibraryInfo lib : sharedLibraries) {
+            if (lib.isNative()) {
+                // Native shared lib doesn't contribute to the native lib search path. Its name is
+                // sent to libnativeloader and then the native shared lib is exported from the
+                // default linker namespace.
+                continue;
+            }
             List<String> paths = lib.getAllCodePaths();
             outSeenPaths.addAll(paths);
             for (String path : paths) {
@@ -696,6 +702,12 @@
         }
         List<ClassLoader> loaders = new ArrayList<>();
         for (SharedLibraryInfo info : sharedLibraries) {
+            if (info.isNative()) {
+                // Native shared lib doesn't contribute to the native lib search path. Its name is
+                // sent to libnativeloader and then the native shared lib is exported from the
+                // default linker namespace.
+                continue;
+            }
             loaders.add(createSharedLibraryLoader(
                     info, isBundledApp, librarySearchPath, libraryPermittedPath));
         }
@@ -802,12 +814,9 @@
 
         makePaths(mActivityThread, isBundledApp, mApplicationInfo, zipPaths, libPaths);
 
-        String libraryPermittedPath = mDataDir;
-        if (mActivityThread == null) {
-            // In a zygote context where mActivityThread is null we can't access the app data dir
-            // and including this in libraryPermittedPath would cause SELinux denials.
-            libraryPermittedPath = "";
-        }
+        // Including an inaccessible dir in libraryPermittedPath would cause SELinux denials
+        // when the loader attempts to canonicalise the path. so we don't.
+        String libraryPermittedPath = canAccessDataDir() ? mDataDir : "";
 
         if (isBundledApp) {
             // For bundled apps, add the base directory of the app (e.g.,
@@ -898,10 +907,19 @@
                     mApplicationInfo.sharedLibraryInfos, isBundledApp, librarySearchPath,
                     libraryPermittedPath);
 
+            List<String> nativeSharedLibraries = new ArrayList<>();
+            if (mApplicationInfo.sharedLibraryInfos != null) {
+                for (SharedLibraryInfo info : mApplicationInfo.sharedLibraryInfos) {
+                    if (info.isNative()) {
+                        nativeSharedLibraries.add(info.getName());
+                    }
+                }
+            }
+
             mDefaultClassLoader = ApplicationLoaders.getDefault().getClassLoaderWithSharedLibraries(
                     zip, mApplicationInfo.targetSdkVersion, isBundledApp, librarySearchPath,
                     libraryPermittedPath, mBaseClassLoader,
-                    mApplicationInfo.classLoaderName, sharedLibraries);
+                    mApplicationInfo.classLoaderName, sharedLibraries, nativeSharedLibraries);
             mAppComponentFactory = createAppFactory(mApplicationInfo, mDefaultClassLoader);
 
             setThreadPolicy(oldPolicy);
@@ -951,6 +969,33 @@
         }
     }
 
+    /**
+     * Return whether we can access the package's private data directory in order to be able to
+     * load code from it.
+     */
+    private boolean canAccessDataDir() {
+        // In a zygote context where mActivityThread is null we can't access the app data dir.
+        if (mActivityThread == null) {
+            return false;
+        }
+
+        // A package can access its own data directory (the common case, so short-circuit it).
+        if (Objects.equals(mPackageName, ActivityThread.currentPackageName())) {
+            return true;
+        }
+
+        // Temporarily disable logging of disk reads on the Looper thread as this is necessary -
+        // and the loader will access the directory anyway if we don't check it.
+        StrictMode.ThreadPolicy oldPolicy = allowThreadDiskReads();
+        try {
+            // We are constructing a classloader for a different package. It is likely,
+            // but not certain, that we can't acccess its app data dir - so check.
+            return new File(mDataDir).canExecute();
+        } finally {
+            setThreadPolicy(oldPolicy);
+        }
+    }
+
     @UnsupportedAppUsage
     public ClassLoader getClassLoader() {
         synchronized (this) {
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index f82ab7b..68e6561 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -958,7 +958,7 @@
      *
      * @hide
      */
-    private IBinder mWhitelistToken;
+    private IBinder mAllowlistToken;
 
     /**
      * Must be set by a process to start associating tokens with Notification objects
@@ -966,7 +966,7 @@
      *
      * @hide
      */
-    static public IBinder processWhitelistToken;
+    static public IBinder processAllowlistToken;
 
     /**
      * {@link #extras} key: this is the title of the notification,
@@ -2245,12 +2245,12 @@
     {
         int version = parcel.readInt();
 
-        mWhitelistToken = parcel.readStrongBinder();
-        if (mWhitelistToken == null) {
-            mWhitelistToken = processWhitelistToken;
+        mAllowlistToken = parcel.readStrongBinder();
+        if (mAllowlistToken == null) {
+            mAllowlistToken = processAllowlistToken;
         }
         // Propagate this token to all pending intents that are unmarshalled from the parcel.
-        parcel.setClassCookie(PendingIntent.class, mWhitelistToken);
+        parcel.setClassCookie(PendingIntent.class, mAllowlistToken);
 
         when = parcel.readLong();
         creationTime = parcel.readLong();
@@ -2368,7 +2368,7 @@
      * @hide
      */
     public void cloneInto(Notification that, boolean heavy) {
-        that.mWhitelistToken = this.mWhitelistToken;
+        that.mAllowlistToken = this.mAllowlistToken;
         that.when = this.when;
         that.creationTime = this.creationTime;
         that.mSmallIcon = this.mSmallIcon;
@@ -2678,7 +2678,7 @@
     private void writeToParcelImpl(Parcel parcel, int flags) {
         parcel.writeInt(1);
 
-        parcel.writeStrongBinder(mWhitelistToken);
+        parcel.writeStrongBinder(mAllowlistToken);
         parcel.writeLong(when);
         parcel.writeLong(creationTime);
         if (mSmallIcon == null && icon != 0) {
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index eef9c02..adbe6bb 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -20,6 +20,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SdkConstant;
+import android.annotation.SuppressLint;
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
 import android.annotation.TestApi;
@@ -801,8 +802,8 @@
      *
      * <p>The name and description should only be changed if the locale changes
      * or in response to the user renaming this channel. For example, if a user has a channel
-     * named 'John Doe' that represents messages from a 'John Doe', and 'John Doe' changes his name
-     * to 'John Smith,' the channel can be renamed to match.
+     * named 'Messages' and the user changes their locale, this channel's name should be updated
+     * with the translation of 'Messages' in the new locale.
      *
      * <p>The importance of an existing channel will only be changed if the new importance is lower
      * than the current value and the user has not altered any settings on this channel.
@@ -1539,6 +1540,25 @@
         }
     }
 
+    /**
+     * Whether the given user has an enabled
+     * {@link android.service.notification.NotificationListenerService} with the given package name.
+     *
+     * @param packageName the package name of the NotificationListenerService class
+     * @param userHandle the handle of the user that set the listener
+     * @hide
+     */
+    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+    @SuppressLint("UserHandle")
+    public boolean hasEnabledNotificationListener(@NonNull String packageName,
+            @NonNull UserHandle userHandle) {
+        INotificationManager service = getService();
+        try {
+            return service.hasEnabledNotificationListener(packageName, userHandle.getIdentifier());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
 
     private Context mContext;
 
diff --git a/core/java/android/content/pm/PermissionInfo.java b/core/java/android/content/pm/PermissionInfo.java
index 5f6befd..e990fd7 100644
--- a/core/java/android/content/pm/PermissionInfo.java
+++ b/core/java/android/content/pm/PermissionInfo.java
@@ -525,6 +525,9 @@
         if ((level & PermissionInfo.PROTECTION_FLAG_APP_PREDICTOR) != 0) {
             protLevel += "|appPredictor";
         }
+        if ((level & PermissionInfo.PROTECTION_FLAG_COMPANION) != 0) {
+            protLevel += "|companion";
+        }
         if ((level & PermissionInfo.PROTECTION_FLAG_RETAIL_DEMO) != 0) {
             protLevel += "|retailDemo";
         }
diff --git a/core/java/android/content/pm/SharedLibraryInfo.java b/core/java/android/content/pm/SharedLibraryInfo.java
index da2a3d8..8625637 100644
--- a/core/java/android/content/pm/SharedLibraryInfo.java
+++ b/core/java/android/content/pm/SharedLibraryInfo.java
@@ -79,6 +79,7 @@
 
     private final long mVersion;
     private final @Type int mType;
+    private final boolean mIsNative;
     private final VersionedPackage mDeclaringPackage;
     private final List<VersionedPackage> mDependentPackages;
     private List<SharedLibraryInfo> mDependencies;
@@ -93,13 +94,14 @@
      * @param type The lib type.
      * @param declaringPackage The package that declares the library.
      * @param dependentPackages The packages that depend on the library.
+     * @param isNative indicate if this shared lib is a native lib or not (i.e. java)
      *
      * @hide
      */
     public SharedLibraryInfo(String path, String packageName, List<String> codePaths,
             String name, long version, int type,
             VersionedPackage declaringPackage, List<VersionedPackage> dependentPackages,
-            List<SharedLibraryInfo> dependencies) {
+            List<SharedLibraryInfo> dependencies, boolean isNative) {
         mPath = path;
         mPackageName = packageName;
         mCodePaths = codePaths;
@@ -109,6 +111,16 @@
         mDeclaringPackage = declaringPackage;
         mDependentPackages = dependentPackages;
         mDependencies = dependencies;
+        mIsNative = isNative;
+    }
+
+    /** @hide */
+    public SharedLibraryInfo(String path, String packageName, List<String> codePaths,
+            String name, long version, int type,
+            VersionedPackage declaringPackage, List<VersionedPackage> dependentPackages,
+            List<SharedLibraryInfo> dependencies) {
+        this(path, packageName, codePaths, name, version, type, declaringPackage, dependentPackages,
+            dependencies, false /* isNative */);
     }
 
     private SharedLibraryInfo(Parcel parcel) {
@@ -125,6 +137,7 @@
         mDeclaringPackage = parcel.readParcelable(null);
         mDependentPackages = parcel.readArrayList(null);
         mDependencies = parcel.createTypedArrayList(SharedLibraryInfo.CREATOR);
+        mIsNative = parcel.readBoolean();
     }
 
     /**
@@ -137,6 +150,15 @@
     }
 
     /**
+     * Tells whether this library is a native shared library or not.
+     *
+     * @hide
+     */
+    public boolean isNative() {
+        return mIsNative;
+    }
+
+    /**
      * Gets the library name an app defines in its manifest
      * to depend on the library.
      *
@@ -320,6 +342,7 @@
         parcel.writeParcelable(mDeclaringPackage, flags);
         parcel.writeList(mDependentPackages);
         parcel.writeTypedList(mDependencies);
+        parcel.writeBoolean(mIsNative);
     }
 
     private static String typeToString(int type) {
diff --git a/core/java/android/content/pm/parsing/ParsingPackage.java b/core/java/android/content/pm/parsing/ParsingPackage.java
index 2ee0ad6..872098c 100644
--- a/core/java/android/content/pm/parsing/ParsingPackage.java
+++ b/core/java/android/content/pm/parsing/ParsingPackage.java
@@ -92,6 +92,10 @@
 
     ParsingPackage addUsesOptionalLibrary(String libraryName);
 
+    ParsingPackage addUsesNativeLibrary(String libraryName);
+
+    ParsingPackage addUsesOptionalNativeLibrary(String libraryName);
+
     ParsingPackage addUsesStaticLibrary(String libraryName);
 
     ParsingPackage addUsesStaticLibraryCertDigests(String[] certSha256Digests);
@@ -219,6 +223,8 @@
 
     ParsingPackage removeUsesOptionalLibrary(String libraryName);
 
+    ParsingPackage removeUsesOptionalNativeLibrary(String libraryName);
+
     ParsingPackage setAnyDensity(int anyDensity);
 
     ParsingPackage setAppComponentFactory(String appComponentFactory);
diff --git a/core/java/android/content/pm/parsing/ParsingPackageImpl.java b/core/java/android/content/pm/parsing/ParsingPackageImpl.java
index f932bc2..0c0dc31 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageImpl.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageImpl.java
@@ -186,6 +186,13 @@
 
     @NonNull
     @DataClass.ParcelWith(ForInternedStringList.class)
+    protected List<String> usesNativeLibraries = emptyList();
+    @NonNull
+    @DataClass.ParcelWith(ForInternedStringList.class)
+    protected List<String> usesOptionalNativeLibraries = emptyList();
+
+    @NonNull
+    @DataClass.ParcelWith(ForInternedStringList.class)
     private List<String> usesStaticLibraries = emptyList();
     @Nullable
     private long[] usesStaticLibrariesVersions;
@@ -669,6 +676,27 @@
     }
 
     @Override
+    public final ParsingPackageImpl addUsesOptionalNativeLibrary(String libraryName) {
+        this.usesOptionalNativeLibraries = CollectionUtils.add(this.usesOptionalNativeLibraries,
+                TextUtils.safeIntern(libraryName));
+        return this;
+    }
+
+    @Override
+    public final ParsingPackageImpl addUsesNativeLibrary(String libraryName) {
+        this.usesNativeLibraries = CollectionUtils.add(this.usesNativeLibraries,
+                TextUtils.safeIntern(libraryName));
+        return this;
+    }
+
+
+    @Override public ParsingPackageImpl removeUsesOptionalNativeLibrary(String libraryName) {
+        this.usesOptionalNativeLibraries = CollectionUtils.remove(this.usesOptionalNativeLibraries,
+                libraryName);
+        return this;
+    }
+
+    @Override
     public ParsingPackageImpl addUsesStaticLibrary(String libraryName) {
         this.usesStaticLibraries = CollectionUtils.add(this.usesStaticLibraries,
                 TextUtils.safeIntern(libraryName));
@@ -982,6 +1010,8 @@
         sForInternedStringList.parcel(this.libraryNames, dest, flags);
         sForInternedStringList.parcel(this.usesLibraries, dest, flags);
         sForInternedStringList.parcel(this.usesOptionalLibraries, dest, flags);
+        sForInternedStringList.parcel(this.usesNativeLibraries, dest, flags);
+        sForInternedStringList.parcel(this.usesOptionalNativeLibraries, dest, flags);
         sForInternedStringList.parcel(this.usesStaticLibraries, dest, flags);
         dest.writeLongArray(this.usesStaticLibrariesVersions);
 
@@ -1144,6 +1174,8 @@
         this.libraryNames = sForInternedStringList.unparcel(in);
         this.usesLibraries = sForInternedStringList.unparcel(in);
         this.usesOptionalLibraries = sForInternedStringList.unparcel(in);
+        this.usesNativeLibraries = sForInternedStringList.unparcel(in);
+        this.usesOptionalNativeLibraries = sForInternedStringList.unparcel(in);
         this.usesStaticLibraries = sForInternedStringList.unparcel(in);
         this.usesStaticLibrariesVersions = in.createLongArray();
 
@@ -1417,6 +1449,18 @@
 
     @NonNull
     @Override
+    public List<String> getUsesNativeLibraries() {
+        return usesNativeLibraries;
+    }
+
+    @NonNull
+    @Override
+    public List<String> getUsesOptionalNativeLibraries() {
+        return usesOptionalNativeLibraries;
+    }
+
+    @NonNull
+    @Override
     public List<String> getUsesStaticLibraries() {
         return usesStaticLibraries;
     }
diff --git a/core/java/android/content/pm/parsing/ParsingPackageRead.java b/core/java/android/content/pm/parsing/ParsingPackageRead.java
index 5b53c18..7e0fe7d 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageRead.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageRead.java
@@ -230,6 +230,19 @@
     @NonNull
     List<String> getUsesOptionalLibraries();
 
+    /** @see R.styleabele#AndroidManifestUsesNativeLibrary */
+    @NonNull
+    List<String> getUsesNativeLibraries();
+
+    /**
+     * Like {@link #getUsesNativeLibraries()}, but marked optional by setting
+     * {@link R.styleable#AndroidManifestUsesNativeLibrary_required} to false . Application is
+     * expected to handle absence manually.
+     * @see R.styleable#AndroidManifestUsesNativeLibrary
+     */
+    @NonNull
+    List<String> getUsesOptionalNativeLibraries();
+
     /**
      * TODO(b/135203078): Move static library stuff to an inner data class
      * @see R.styleable#AndroidManifestUsesStaticLibrary
diff --git a/core/java/android/content/pm/parsing/ParsingPackageUtils.java b/core/java/android/content/pm/parsing/ParsingPackageUtils.java
index 3688f1b..e1f08f3 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageUtils.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageUtils.java
@@ -701,6 +701,8 @@
                 return parseUsesStaticLibrary(input, pkg, res, parser);
             case "uses-library":
                 return parseUsesLibrary(input, pkg, res, parser);
+            case "uses-native-library":
+                return parseUsesNativeLibrary(input, pkg, res, parser);
             case "uses-package":
                 // Dependencies for app installers; we don't currently try to
                 // enforce this.
@@ -2017,6 +2019,8 @@
                 return parseUsesStaticLibrary(input, pkg, res, parser);
             case "uses-library":
                 return parseUsesLibrary(input, pkg, res, parser);
+            case "uses-native-library":
+                return parseUsesNativeLibrary(input, pkg, res, parser);
             case "processes":
                 return parseProcesses(input, pkg, res, parser, mSeparateProcesses, flags);
             case "uses-package":
@@ -2178,6 +2182,37 @@
     }
 
     @NonNull
+    private static ParseResult<ParsingPackage> parseUsesNativeLibrary(ParseInput input,
+            ParsingPackage pkg, Resources res, XmlResourceParser parser) {
+        TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestUsesNativeLibrary);
+        try {
+            // Note: don't allow this value to be a reference to a resource
+            // that may change.
+            String lname = sa.getNonResourceString(
+                    R.styleable.AndroidManifestUsesNativeLibrary_name);
+            boolean req = sa.getBoolean(R.styleable.AndroidManifestUsesNativeLibrary_required,
+                    true);
+
+            if (lname != null) {
+                if (req) {
+                    // Upgrade to treat as stronger constraint
+                    pkg.addUsesNativeLibrary(lname)
+                            .removeUsesOptionalNativeLibrary(lname);
+                } else {
+                    // Ignore if someone already defined as required
+                    if (!ArrayUtils.contains(pkg.getUsesNativeLibraries(), lname)) {
+                        pkg.addUsesOptionalNativeLibrary(lname);
+                    }
+                }
+            }
+
+            return input.success(pkg);
+        } finally {
+            sa.recycle();
+        }
+    }
+
+    @NonNull
     private static ParseResult<ParsingPackage> parseProcesses(ParseInput input, ParsingPackage pkg,
             Resources res, XmlResourceParser parser, String[] separateProcesses, int flags)
             throws IOException, XmlPullParserException {
diff --git a/core/java/android/hardware/hdmi/IHdmiVendorCommandListener.aidl b/core/java/android/hardware/hdmi/IHdmiVendorCommandListener.aidl
index c708d20..a16e878 100644
--- a/core/java/android/hardware/hdmi/IHdmiVendorCommandListener.aidl
+++ b/core/java/android/hardware/hdmi/IHdmiVendorCommandListener.aidl
@@ -22,7 +22,7 @@
  *
  * @hide
  */
-interface IHdmiVendorCommandListener {
+oneway interface IHdmiVendorCommandListener {
     void onReceived(int logicalAddress, int destAddress, in byte[] operands, boolean hasVendorId);
     void onControlStateChanged(boolean enabled, int reason);
 }
diff --git a/core/java/android/metrics/LogMaker.java b/core/java/android/metrics/LogMaker.java
index 5496e17..d8a2082 100644
--- a/core/java/android/metrics/LogMaker.java
+++ b/core/java/android/metrics/LogMaker.java
@@ -39,7 +39,7 @@
     /**
      * Min required eventlog line length.
      * See: android/util/cts/EventLogTest.java
-     * Size checks enforced here are intended only as sanity checks;
+     * Size limits enforced here are intended only as a precaution;
      * your logs may be truncated earlier. Please log responsibly.
      *
      * @hide
diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java
index a0207c8..515704c 100644
--- a/core/java/android/os/Binder.java
+++ b/core/java/android/os/Binder.java
@@ -543,15 +543,17 @@
 
     /**
      * Mark as being built with VINTF-level stability promise. This API should
-     * only ever be invoked by the build system. It means that the interface
-     * represented by this binder is guaranteed to be kept stable for several
-     * years, and the build system also keeps snapshots of these APIs and
-     * invokes the AIDL compiler to make sure that these snapshots are
-     * backwards compatible. Instead of using this API, use an @VintfStability
-     * interface.
+     * only ever be invoked by generated code from the aidl compiler. It means
+     * that the interface represented by this binder is guaranteed to be kept
+     * stable for several years, according to the VINTF compatibility lifecycle,
+     * and the build system also keeps snapshots of these APIs and invokes the
+     * AIDL compiler to make sure that these snapshots are backwards compatible.
+     * Instead of using this API, use the @VintfStability annotation on your
+     * AIDL interface.
      *
      * @hide
      */
+    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
     public final native void markVintfStability();
 
     /**
diff --git a/core/java/android/os/Parcelable.java b/core/java/android/os/Parcelable.java
index 9b360ed..bedbba0 100644
--- a/core/java/android/os/Parcelable.java
+++ b/core/java/android/os/Parcelable.java
@@ -99,6 +99,35 @@
     @Retention(RetentionPolicy.SOURCE)
     public @interface ContentsFlags {}
 
+    /** @hide */
+    @IntDef(flag = true, prefix = { "PARCELABLE_STABILITY_" }, value = {
+            PARCELABLE_STABILITY_LOCAL,
+            PARCELABLE_STABILITY_VINTF,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface Stability {}
+
+    /**
+     * Something that is not meant to cross compilation boundaries.
+     *
+     * Note: unlike binder/Stability.h which uses bitsets to detect stability,
+     * since we don't currently have a notion of different local locations,
+     * higher stability levels are formed at higher levels.
+     *
+     * For instance, contained entirely within system partitions.
+     * @see #getStability()
+     * @see ParcelableHolder
+     * @hide
+     */
+    public static final int PARCELABLE_STABILITY_LOCAL = 0x0000;
+    /**
+     * Something that is meant to be used between system and vendor.
+     * @see #getStability()
+     * @see ParcelableHolder
+     * @hide
+     */
+    public static final int PARCELABLE_STABILITY_VINTF = 0x0001;
+
     /**
      * Descriptor bit used with {@link #describeContents()}: indicates that
      * the Parcelable object's flattened representation includes a file descriptor.
@@ -129,8 +158,8 @@
      * @return true if this parcelable is stable.
      * @hide
      */
-    default boolean isStable() {
-        return false;
+    default @Stability int getStability() {
+        return PARCELABLE_STABILITY_LOCAL;
     }
 
     /**
diff --git a/core/java/android/os/ParcelableHolder.java b/core/java/android/os/ParcelableHolder.java
index c37a2ff..181f94b 100644
--- a/core/java/android/os/ParcelableHolder.java
+++ b/core/java/android/os/ParcelableHolder.java
@@ -37,10 +37,10 @@
      * if {@link ParcelableHolder} contains value, otherwise, both are null.
      */
     private Parcel mParcel;
-    private boolean mIsStable = false;
+    private @Parcelable.Stability int mStability = Parcelable.PARCELABLE_STABILITY_LOCAL;
 
-    public ParcelableHolder(boolean isStable) {
-        mIsStable = isStable;
+    public ParcelableHolder(@Parcelable.Stability int stability) {
+        mStability = stability;
     }
 
     private ParcelableHolder() {
@@ -50,11 +50,11 @@
     /**
      * {@link ParcelableHolder}'s stability is determined by the parcelable
      * which contains this ParcelableHolder.
-     * For more detail refer to {@link Parcelable#isStable}.
+     * For more detail refer to {@link Parcelable#getStability}.
      */
     @Override
-    public boolean isStable() {
-        return mIsStable;
+    public @Parcelable.Stability int getStability() {
+        return mStability;
     }
 
     @NonNull
@@ -81,7 +81,8 @@
      * @return {@code false} if the parcelable's stability is more unstable ParcelableHolder.
      */
     public synchronized boolean setParcelable(@Nullable Parcelable p) {
-        if (p != null && this.isStable() && !p.isStable()) {
+        // a ParcelableHolder can only hold things at its stability or higher
+        if (p != null && this.getStability() > p.getStability()) {
             return false;
         }
         mParcelable = p;
@@ -123,7 +124,7 @@
      * Read ParcelableHolder from a parcel.
      */
     public synchronized void readFromParcel(@NonNull Parcel parcel) {
-        this.mIsStable = parcel.readBoolean();
+        this.mStability = parcel.readInt();
 
         mParcelable = null;
 
@@ -145,7 +146,7 @@
 
     @Override
     public synchronized void writeToParcel(@NonNull Parcel parcel, int flags) {
-        parcel.writeBoolean(this.mIsStable);
+        parcel.writeInt(this.mStability);
 
         if (mParcel != null) {
             parcel.writeInt(mParcel.dataSize());
diff --git a/core/java/android/os/VibrationAttributes.java b/core/java/android/os/VibrationAttributes.java
index f4aa354..8a7cf60 100644
--- a/core/java/android/os/VibrationAttributes.java
+++ b/core/java/android/os/VibrationAttributes.java
@@ -310,6 +310,7 @@
                 @Nullable VibrationEffect effect) {
             mAudioAttributes = audio;
             setUsage(audio);
+            setFlags(audio);
             applyHapticFeedbackHeuristics(effect);
         }
 
@@ -366,6 +367,12 @@
             }
         }
 
+        private void setFlags(@NonNull AudioAttributes audio) {
+            if ((audio.getAllFlags() & AudioAttributes.FLAG_BYPASS_INTERRUPTION_POLICY) != 0) {
+                mFlags |= FLAG_BYPASS_INTERRUPTION_POLICY;
+            }
+        }
+
         /**
          * Combines all of the attributes that have been set and returns a new
          * {@link VibrationAttributes} object.
diff --git a/core/java/android/text/Html.java b/core/java/android/text/Html.java
index e3cb382..ab19fa9 100644
--- a/core/java/android/text/Html.java
+++ b/core/java/android/text/Html.java
@@ -551,7 +551,7 @@
                     out.append(((ImageSpan) style[j]).getSource());
                     out.append("\">");
 
-                    // Don't output the dummy character underlying the image.
+                    // Don't output the placeholder character underlying the image.
                     i = next;
                 }
                 if (style[j] instanceof AbsoluteSizeSpan) {
diff --git a/core/java/android/text/format/DateFormat.java b/core/java/android/text/format/DateFormat.java
index 4fe6752..38e3b39 100755
--- a/core/java/android/text/format/DateFormat.java
+++ b/core/java/android/text/format/DateFormat.java
@@ -26,8 +26,6 @@
 import android.text.Spanned;
 import android.text.SpannedString;
 
-import libcore.icu.LocaleData;
-
 import java.text.SimpleDateFormat;
 import java.util.Calendar;
 import java.util.Date;
@@ -287,8 +285,10 @@
      */
     @UnsupportedAppUsage
     public static String getTimeFormatString(Context context, int userHandle) {
-        final LocaleData d = LocaleData.get(context.getResources().getConfiguration().locale);
-        return is24HourFormat(context, userHandle) ? d.timeFormat_Hm : d.timeFormat_hm;
+        DateTimePatternGenerator dtpg = DateTimePatternGenerator.getInstance(
+                context.getResources().getConfiguration().locale);
+        return is24HourFormat(context, userHandle) ? dtpg.getBestPattern("Hm")
+            : dtpg.getBestPattern("hm");
     }
 
     /**
@@ -475,7 +475,6 @@
         SpannableStringBuilder s = new SpannableStringBuilder(inFormat);
         int count;
 
-        LocaleData localeData = LocaleData.get(Locale.getDefault());
         DateFormatSymbols dfs = getIcuDateFormatSymbols(Locale.getDefault());
         String[] amPm = dfs.getAmPmStrings();
 
@@ -506,7 +505,7 @@
                     break;
                 case 'c':
                 case 'E':
-                    replacement = getDayOfWeekString(localeData,
+                    replacement = getDayOfWeekString(dfs,
                                                      inDate.get(Calendar.DAY_OF_WEEK), count, c);
                     break;
                 case 'K': // hour in am/pm (0-11)
@@ -534,8 +533,7 @@
                     break;
                 case 'L':
                 case 'M':
-                    replacement = getMonthString(localeData,
-                                                 inDate.get(Calendar.MONTH), count, c);
+                    replacement = getMonthString(dfs, inDate.get(Calendar.MONTH), count, c);
                     break;
                 case 'm':
                     replacement = zeroPad(inDate.get(Calendar.MINUTE), count);
@@ -568,25 +566,29 @@
         }
     }
 
-    private static String getDayOfWeekString(LocaleData ld, int day, int count, int kind) {
+    private static String getDayOfWeekString(DateFormatSymbols dfs, int day, int count, int kind) {
         boolean standalone = (kind == 'c');
+        int context = standalone ? DateFormatSymbols.STANDALONE : DateFormatSymbols.FORMAT;
+        final int width;
         if (count == 5) {
-            return standalone ? ld.tinyStandAloneWeekdayNames[day] : ld.tinyWeekdayNames[day];
+            width = DateFormatSymbols.NARROW;
         } else if (count == 4) {
-            return standalone ? ld.longStandAloneWeekdayNames[day] : ld.longWeekdayNames[day];
+            width = DateFormatSymbols.WIDE;
         } else {
-            return standalone ? ld.shortStandAloneWeekdayNames[day] : ld.shortWeekdayNames[day];
+            width = DateFormatSymbols.ABBREVIATED;
         }
+        return dfs.getWeekdays(context, width)[day];
     }
 
-    private static String getMonthString(LocaleData ld, int month, int count, int kind) {
+    private static String getMonthString(DateFormatSymbols dfs, int month, int count, int kind) {
         boolean standalone = (kind == 'L');
+        int monthContext = standalone ? DateFormatSymbols.STANDALONE : DateFormatSymbols.FORMAT;
         if (count == 5) {
-            return standalone ? ld.tinyStandAloneMonthNames[month] : ld.tinyMonthNames[month];
+            return dfs.getMonths(monthContext, DateFormatSymbols.NARROW)[month];
         } else if (count == 4) {
-            return standalone ? ld.longStandAloneMonthNames[month] : ld.longMonthNames[month];
+            return dfs.getMonths(monthContext, DateFormatSymbols.WIDE)[month];
         } else if (count == 3) {
-            return standalone ? ld.shortStandAloneMonthNames[month] : ld.shortMonthNames[month];
+            return dfs.getMonths(monthContext, DateFormatSymbols.ABBREVIATED)[month];
         } else {
             // Calendar.JANUARY == 0, so add 1 to month.
             return zeroPad(month+1, count);
diff --git a/core/java/android/text/format/DateUtils.java b/core/java/android/text/format/DateUtils.java
index 741312f..ff08269 100644
--- a/core/java/android/text/format/DateUtils.java
+++ b/core/java/android/text/format/DateUtils.java
@@ -20,6 +20,7 @@
 import android.content.Context;
 import android.content.res.Configuration;
 import android.content.res.Resources;
+import android.icu.text.DateFormatSymbols;
 import android.icu.text.MeasureFormat;
 import android.icu.text.MeasureFormat.FormatWidth;
 import android.icu.util.Measure;
@@ -27,8 +28,6 @@
 
 import com.android.internal.R;
 
-import libcore.icu.LocaleData;
-
 import java.io.IOException;
 import java.time.Instant;
 import java.time.LocalDateTime;
@@ -203,17 +202,23 @@
      */
     @Deprecated
     public static String getDayOfWeekString(int dayOfWeek, int abbrev) {
-        LocaleData d = LocaleData.get(Locale.getDefault());
-        String[] names;
+        DateFormatSymbols dfs = DateFormatSymbols.getInstance();
+        final int width;
         switch (abbrev) {
-            case LENGTH_LONG:       names = d.longWeekdayNames;  break;
-            case LENGTH_MEDIUM:     names = d.shortWeekdayNames; break;
-            case LENGTH_SHORT:      names = d.shortWeekdayNames; break; // TODO
-            case LENGTH_SHORTER:    names = d.shortWeekdayNames; break; // TODO
-            case LENGTH_SHORTEST:   names = d.tinyWeekdayNames;  break;
-            default:                names = d.shortWeekdayNames; break;
+            case LENGTH_LONG:
+                width = DateFormatSymbols.WIDE;
+                break;
+            case LENGTH_SHORTEST:
+                width = DateFormatSymbols.NARROW;
+                break;
+            case LENGTH_MEDIUM:
+            case LENGTH_SHORT:   // TODO
+            case LENGTH_SHORTER: // TODO
+            default:
+                width = DateFormatSymbols.ABBREVIATED;
+                break;
         }
-        return names[dayOfWeek];
+        return dfs.getWeekdays(DateFormatSymbols.FORMAT, width)[dayOfWeek];
     }
 
     /**
@@ -242,17 +247,23 @@
      */
     @Deprecated
     public static String getMonthString(int month, int abbrev) {
-        LocaleData d = LocaleData.get(Locale.getDefault());
-        String[] names;
+        DateFormatSymbols dfs = DateFormat.getIcuDateFormatSymbols(Locale.getDefault());
+        final int width;
         switch (abbrev) {
-            case LENGTH_LONG:       names = d.longMonthNames;  break;
-            case LENGTH_MEDIUM:     names = d.shortMonthNames; break;
-            case LENGTH_SHORT:      names = d.shortMonthNames; break;
-            case LENGTH_SHORTER:    names = d.shortMonthNames; break;
-            case LENGTH_SHORTEST:   names = d.tinyMonthNames;  break;
-            default:                names = d.shortMonthNames; break;
+            case LENGTH_LONG:
+                width = DateFormatSymbols.WIDE;
+                break;
+            case LENGTH_SHORTEST:
+                width = DateFormatSymbols.NARROW;
+                break;
+            case LENGTH_MEDIUM:
+            case LENGTH_SHORT:
+            case LENGTH_SHORTER:
+            default:
+                width = DateFormatSymbols.ABBREVIATED;
+                break;
         }
-        return names[month];
+        return dfs.getMonths(DateFormatSymbols.FORMAT, width)[month];
     }
 
     /**
diff --git a/core/java/android/text/format/OWNERS b/core/java/android/text/format/OWNERS
new file mode 100644
index 0000000..32adc12
--- /dev/null
+++ b/core/java/android/text/format/OWNERS
@@ -0,0 +1,3 @@
+# Inherits OWNERS from parent directory, plus the following
+
+vichang@google.com
diff --git a/core/java/android/text/format/Time.java b/core/java/android/text/format/Time.java
index 80c8ec8..5b5c854 100644
--- a/core/java/android/text/format/Time.java
+++ b/core/java/android/text/format/Time.java
@@ -361,7 +361,7 @@
      */
     @Override
     public String toString() {
-        // toString() uses its own TimeCalculator rather than the shared one. Otherwise crazy stuff
+        // toString() uses its own TimeCalculator rather than the shared one. Otherwise weird stuff
         // happens during debugging when the debugger calls toString().
         TimeCalculator calculator = new TimeCalculator(this.timezone);
         calculator.copyFieldsFromTime(this);
diff --git a/core/java/android/text/format/TimeFormatter.java b/core/java/android/text/format/TimeFormatter.java
index 9393f36..c71dfbb 100644
--- a/core/java/android/text/format/TimeFormatter.java
+++ b/core/java/android/text/format/TimeFormatter.java
@@ -22,11 +22,10 @@
 
 import android.content.res.Resources;
 import android.icu.text.DateFormatSymbols;
+import android.icu.text.DecimalFormatSymbols;
 
 import com.android.i18n.timezone.ZoneInfoData;
 
-import libcore.icu.LocaleData;
-
 import java.nio.CharBuffer;
 import java.time.Instant;
 import java.time.LocalDateTime;
@@ -53,17 +52,17 @@
     private static final int DAYSPERNYEAR = 365;
 
     /**
-     * The Locale for which the cached LocaleData and formats have been loaded.
+     * The Locale for which the cached symbols and formats have been loaded.
      */
     private static Locale sLocale;
     private static DateFormatSymbols sDateFormatSymbols;
-    private static LocaleData sLocaleData;
+    private static DecimalFormatSymbols sDecimalFormatSymbols;
     private static String sTimeOnlyFormat;
     private static String sDateOnlyFormat;
     private static String sDateTimeFormat;
 
     private final DateFormatSymbols dateFormatSymbols;
-    private final LocaleData localeData;
+    private final DecimalFormatSymbols decimalFormatSymbols;
     private final String dateTimeFormat;
     private final String timeOnlyFormat;
     private final String dateOnlyFormat;
@@ -78,7 +77,7 @@
             if (sLocale == null || !(locale.equals(sLocale))) {
                 sLocale = locale;
                 sDateFormatSymbols = DateFormat.getIcuDateFormatSymbols(locale);
-                sLocaleData = LocaleData.get(locale);
+                sDecimalFormatSymbols = DecimalFormatSymbols.getInstance(locale);
 
                 Resources r = Resources.getSystem();
                 sTimeOnlyFormat = r.getString(com.android.internal.R.string.time_of_day);
@@ -87,10 +86,10 @@
             }
 
             this.dateFormatSymbols = sDateFormatSymbols;
+            this.decimalFormatSymbols = sDecimalFormatSymbols;
             this.dateTimeFormat = sDateTimeFormat;
             this.timeOnlyFormat = sTimeOnlyFormat;
             this.dateOnlyFormat = sDateOnlyFormat;
-            localeData = sLocaleData;
         }
     }
 
@@ -172,12 +171,12 @@
     }
 
     private String localizeDigits(String s) {
-        if (localeData.zeroDigit == '0') {
+        if (decimalFormatSymbols.getZeroDigit() == '0') {
             return s;
         }
 
         int length = s.length();
-        int offsetToLocalizedDigits = localeData.zeroDigit - '0';
+        int offsetToLocalizedDigits = decimalFormatSymbols.getZeroDigit() - '0';
         StringBuilder result = new StringBuilder(length);
         for (int i = 0; i < length; ++i) {
             char ch = s.charAt(i);
@@ -220,35 +219,44 @@
             char currentChar = formatBuffer.get(formatBuffer.position());
             switch (currentChar) {
                 case 'A':
-                    modifyAndAppend((wallTime.getWeekDay() < 0
-                                    || wallTime.getWeekDay() >= DAYSPERWEEK)
-                                    ? "?" : localeData.longWeekdayNames[wallTime.getWeekDay() + 1],
+                    modifyAndAppend(
+                        (wallTime.getWeekDay() < 0 || wallTime.getWeekDay() >= DAYSPERWEEK)
+                            ? "?"
+                            : dateFormatSymbols.getWeekdays(DateFormatSymbols.FORMAT,
+                                DateFormatSymbols.WIDE)[wallTime.getWeekDay() + 1],
                             modifier);
                     return false;
                 case 'a':
-                    modifyAndAppend((wallTime.getWeekDay() < 0
-                                    || wallTime.getWeekDay() >= DAYSPERWEEK)
-                                    ? "?" : localeData.shortWeekdayNames[wallTime.getWeekDay() + 1],
+                    modifyAndAppend(
+                        (wallTime.getWeekDay() < 0 || wallTime.getWeekDay() >= DAYSPERWEEK)
+                            ? "?"
+                            : dateFormatSymbols.getWeekdays(DateFormatSymbols.FORMAT,
+                                DateFormatSymbols.ABBREVIATED)[wallTime.getWeekDay() + 1],
                             modifier);
                     return false;
                 case 'B':
                     if (modifier == '-') {
-                        modifyAndAppend((wallTime.getMonth() < 0
-                                        || wallTime.getMonth() >= MONSPERYEAR)
-                                        ? "?"
-                                        : localeData.longStandAloneMonthNames[wallTime.getMonth()],
+                        modifyAndAppend(
+                            (wallTime.getMonth() < 0 || wallTime.getMonth() >= MONSPERYEAR)
+                                ? "?"
+                                : dateFormatSymbols.getMonths(DateFormatSymbols.STANDALONE,
+                                    DateFormatSymbols.WIDE)[wallTime.getMonth()],
                                 modifier);
                     } else {
-                        modifyAndAppend((wallTime.getMonth() < 0
-                                        || wallTime.getMonth() >= MONSPERYEAR)
-                                        ? "?" : localeData.longMonthNames[wallTime.getMonth()],
+                        modifyAndAppend(
+                            (wallTime.getMonth() < 0 || wallTime.getMonth() >= MONSPERYEAR)
+                                ? "?"
+                                : dateFormatSymbols.getMonths(DateFormatSymbols.FORMAT,
+                                    DateFormatSymbols.WIDE)[wallTime.getMonth()],
                                 modifier);
                     }
                     return false;
                 case 'b':
                 case 'h':
                     modifyAndAppend((wallTime.getMonth() < 0 || wallTime.getMonth() >= MONSPERYEAR)
-                                    ? "?" : localeData.shortMonthNames[wallTime.getMonth()],
+                            ? "?"
+                            : dateFormatSymbols.getMonths(DateFormatSymbols.FORMAT,
+                                DateFormatSymbols.ABBREVIATED)[wallTime.getMonth()],
                             modifier);
                     return false;
                 case 'C':
diff --git a/core/java/android/view/FocusFinder.java b/core/java/android/view/FocusFinder.java
index 064bc69..713cfb4 100644
--- a/core/java/android/view/FocusFinder.java
+++ b/core/java/android/view/FocusFinder.java
@@ -311,9 +311,6 @@
         }
 
         final int count = focusables.size();
-        if (count < 2) {
-            return null;
-        }
         switch (direction) {
             case View.FOCUS_FORWARD:
                 return getNextFocusable(focused, focusables, count);
@@ -376,29 +373,29 @@
     }
 
     private static View getNextFocusable(View focused, ArrayList<View> focusables, int count) {
-        if (count < 2) {
-            return null;
-        }
         if (focused != null) {
             int position = focusables.lastIndexOf(focused);
             if (position >= 0 && position + 1 < count) {
                 return focusables.get(position + 1);
             }
         }
-        return focusables.get(0);
+        if (!focusables.isEmpty()) {
+            return focusables.get(0);
+        }
+        return null;
     }
 
     private static View getPreviousFocusable(View focused, ArrayList<View> focusables, int count) {
-        if (count < 2) {
-            return null;
-        }
         if (focused != null) {
             int position = focusables.indexOf(focused);
             if (position > 0) {
                 return focusables.get(position - 1);
             }
         }
-        return focusables.get(count - 1);
+        if (!focusables.isEmpty()) {
+            return focusables.get(count - 1);
+        }
+        return null;
     }
 
     private static View getNextKeyboardNavigationCluster(
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index fe6f33d..6552e30 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -4032,7 +4032,7 @@
             yOffset -= surfaceInsets.top;
 
             // Offset dirty rect for surface insets.
-            dirty.offset(surfaceInsets.left, surfaceInsets.right);
+            dirty.offset(surfaceInsets.left, surfaceInsets.top);
         }
 
         boolean accessibilityFocusDirty = false;
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index fbfeda6..b398cf6 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -727,16 +727,22 @@
             mState = savedInstanceState.getInt(STATE_TAG, STATE_UNKNOWN);
 
             if (mSessionId != NO_SESSION) {
-                ensureServiceClientAddedIfNeededLocked();
+                final boolean clientAdded = tryAddServiceClientIfNeededLocked();
 
                 final AutofillClient client = getClient();
                 if (client != null) {
                     final SyncResultReceiver receiver = new SyncResultReceiver(
                             SYNC_CALLS_TIMEOUT_MS);
                     try {
-                        mService.restoreSession(mSessionId, client.autofillClientGetActivityToken(),
-                                mServiceClient.asBinder(), receiver);
-                        final boolean sessionWasRestored = receiver.getIntResult() == 1;
+                        boolean sessionWasRestored = false;
+                        if (clientAdded) {
+                            mService.restoreSession(mSessionId,
+                                    client.autofillClientGetActivityToken(),
+                                    mServiceClient.asBinder(), receiver);
+                            sessionWasRestored = receiver.getIntResult() == 1;
+                        } else {
+                            Log.w(TAG, "No service client for session " + mSessionId);
+                        }
 
                         if (!sessionWasRestored) {
                             Log.w(TAG, "Session " + mSessionId + " could not be restored");
@@ -850,8 +856,8 @@
             if (isDisabledByServiceLocked()) {
                 return false;
             }
-            ensureServiceClientAddedIfNeededLocked();
-            return mEnabled;
+            final boolean clientAdded = tryAddServiceClientIfNeededLocked();
+            return clientAdded ? mEnabled : false;
         }
     }
 
@@ -1007,7 +1013,12 @@
 
         AutofillCallback callback = null;
 
-        ensureServiceClientAddedIfNeededLocked();
+        final boolean clientAdded = tryAddServiceClientIfNeededLocked();
+
+        if (!clientAdded) {
+            if (sVerbose) Log.v(TAG, "ignoring notifyViewEntered(" + id + "): no service client");
+            return callback;
+        }
 
         if (!mEnabled && !mEnabledForAugmentedAutofillOnly) {
             if (sVerbose) Log.v(TAG, "ignoring notifyViewEntered(" + id + "): disabled");
@@ -1060,9 +1071,10 @@
 
     @GuardedBy("mLock")
     void notifyViewExitedLocked(@NonNull View view) {
-        ensureServiceClientAddedIfNeededLocked();
+        final boolean clientAdded = tryAddServiceClientIfNeededLocked();
 
-        if ((mEnabled || mEnabledForAugmentedAutofillOnly) && isActiveLocked()) {
+        if (clientAdded && (mEnabled || mEnabledForAugmentedAutofillOnly)
+                && isActiveLocked()) {
             // dont notify exited when Activity is already in background
             if (!isClientDisablingEnterExitEvent()) {
                 final AutofillId id = view.getAutofillId();
@@ -1178,7 +1190,12 @@
         AutofillCallback callback = null;
         if (shouldIgnoreViewEnteredLocked(id, flags)) return callback;
 
-        ensureServiceClientAddedIfNeededLocked();
+        final boolean clientAdded = tryAddServiceClientIfNeededLocked();
+
+        if (!clientAdded) {
+            if (sVerbose) Log.v(TAG, "ignoring notifyViewEntered(" + id + "): no service client");
+            return callback;
+        }
 
         if (!mEnabled && !mEnabledForAugmentedAutofillOnly) {
             if (sVerbose) {
@@ -1241,9 +1258,10 @@
 
     @GuardedBy("mLock")
     private void notifyViewExitedLocked(@NonNull View view, int virtualId) {
-        ensureServiceClientAddedIfNeededLocked();
+        final boolean clientAdded = tryAddServiceClientIfNeededLocked();
 
-        if ((mEnabled || mEnabledForAugmentedAutofillOnly) && isActiveLocked()) {
+        if (clientAdded && (mEnabled || mEnabledForAugmentedAutofillOnly)
+                && isActiveLocked()) {
             // don't notify exited when Activity is already in background
             if (!isClientDisablingEnterExitEvent()) {
                 final AutofillId id = getAutofillId(view, virtualId);
@@ -1905,11 +1923,16 @@
         }
     }
 
+    /**
+     * Tries to add AutofillManagerClient to service if it does not been added. Returns {@code true}
+     * if the AutofillManagerClient is added successfully or is already added. Otherwise,
+     * returns {@code false}.
+     */
     @GuardedBy("mLock")
-    private void ensureServiceClientAddedIfNeededLocked() {
+    private boolean tryAddServiceClientIfNeededLocked() {
         final AutofillClient client = getClient();
         if (client == null) {
-            return;
+            return false;
         }
 
         if (mServiceClient == null) {
@@ -1924,7 +1947,10 @@
                     flags = receiver.getIntResult();
                 } catch (SyncResultReceiver.TimeoutException e) {
                     Log.w(TAG, "Failed to initialize autofill: " + e);
-                    return;
+                    // Reset the states initialized above.
+                    mService.removeClient(mServiceClient, userId);
+                    mServiceClient = null;
+                    return false;
                 }
                 mEnabled = (flags & FLAG_ADD_CLIENT_ENABLED) != 0;
                 sDebug = (flags & FLAG_ADD_CLIENT_DEBUG) != 0;
@@ -1949,6 +1975,7 @@
                 throw e.rethrowFromSystemServer();
             }
         }
+        return true;
     }
 
     @GuardedBy("mLock")
@@ -1962,12 +1989,13 @@
                 && view.isLaidOut()
                 && view.isVisibleToUser()) {
 
-            ensureServiceClientAddedIfNeededLocked();
+            final boolean clientAdded = tryAddServiceClientIfNeededLocked();
 
             if (sVerbose) {
-                Log.v(TAG, "startAutofillIfNeededLocked(): enabled=" + mEnabled);
+                Log.v(TAG, "startAutofillIfNeededLocked(): enabled=" + mEnabled + " mServiceClient="
+                        + mServiceClient);
             }
-            if (mEnabled && !isClientDisablingEnterExitEvent()) {
+            if (clientAdded && mEnabled && !isClientDisablingEnterExitEvent()) {
                 final AutofillId id = view.getAutofillId();
                 final AutofillValue value = view.getAutofillValue();
                 // Starts new session.
@@ -2692,6 +2720,7 @@
         pw.print(pfx); pw.print("sessionId: "); pw.println(mSessionId);
         pw.print(pfx); pw.print("state: "); pw.println(getStateAsStringLocked());
         pw.print(pfx); pw.print("context: "); pw.println(mContext);
+        pw.print(pfx); pw.print("service client: "); pw.println(mServiceClient);
         final AutofillClient client = getClient();
         if (client != null) {
             pw.print(pfx); pw.print("client: "); pw.print(client);
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 1c75232..052bca5 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -1511,7 +1511,7 @@
      *
      * @param hosts the list of hosts
      * @param callback will be called with {@code true} if hosts are successfully added to the
-     * whitelist. It will be called with {@code false} if any hosts are malformed. The callback
+     * allowlist. It will be called with {@code false} if any hosts are malformed. The callback
      * will be run on the UI thread
      */
     public static void setSafeBrowsingWhitelist(@NonNull List<String> hosts,
diff --git a/core/java/android/webkit/WebViewClient.java b/core/java/android/webkit/WebViewClient.java
index 150fa88..7b6e1a3 100644
--- a/core/java/android/webkit/WebViewClient.java
+++ b/core/java/android/webkit/WebViewClient.java
@@ -173,8 +173,9 @@
      * when accessing private data or the view system.
      *
      * <p class="note"><b>Note:</b> When Safe Browsing is enabled, these URLs still undergo Safe
-     * Browsing checks. If this is undesired, whitelist the URL with {@link
-     * WebView#setSafeBrowsingWhitelist} or ignore the warning with {@link #onSafeBrowsingHit}.
+     * Browsing checks. If this is undesired, you can use {@link WebView#setSafeBrowsingWhitelist}
+     * to skip Safe Browsing checks for that host or dismiss the warning in {@link
+     * #onSafeBrowsingHit} by calling {@link SafeBrowsingResponse#proceed}.
      *
      * @param view The {@link android.webkit.WebView} that is requesting the
      *             resource.
@@ -211,8 +212,9 @@
      * when accessing private data or the view system.
      *
      * <p class="note"><b>Note:</b> When Safe Browsing is enabled, these URLs still undergo Safe
-     * Browsing checks. If this is undesired, whitelist the URL with {@link
-     * WebView#setSafeBrowsingWhitelist} or ignore the warning with {@link #onSafeBrowsingHit}.
+     * Browsing checks. If this is undesired, you can use {@link WebView#setSafeBrowsingWhitelist}
+     * to skip Safe Browsing checks for that host or dismiss the warning in {@link
+     * #onSafeBrowsingHit} by calling {@link SafeBrowsingResponse#proceed}.
      *
      * @param view The {@link android.webkit.WebView} that is requesting the
      *             resource.
diff --git a/core/java/android/widget/CalendarViewLegacyDelegate.java b/core/java/android/widget/CalendarViewLegacyDelegate.java
index 1b899db..33e64f4 100644
--- a/core/java/android/widget/CalendarViewLegacyDelegate.java
+++ b/core/java/android/widget/CalendarViewLegacyDelegate.java
@@ -38,8 +38,6 @@
 
 import com.android.internal.R;
 
-import libcore.icu.LocaleData;
-
 import java.util.Locale;
 
 /**
@@ -264,7 +262,7 @@
         mShowWeekNumber = a.getBoolean(R.styleable.CalendarView_showWeekNumber,
                 DEFAULT_SHOW_WEEK_NUMBER);
         mFirstDayOfWeek = a.getInt(R.styleable.CalendarView_firstDayOfWeek,
-                LocaleData.get(Locale.getDefault()).firstDayOfWeek);
+                Calendar.getInstance().getFirstDayOfWeek());
         final String minDate = a.getString(R.styleable.CalendarView_minDate);
         if (!CalendarView.parseDate(minDate, mMinDate)) {
             CalendarView.parseDate(DEFAULT_MIN_DATE, mMinDate);
diff --git a/core/java/android/widget/DayPickerView.java b/core/java/android/widget/DayPickerView.java
index 67fef13..7de2bd1 100644
--- a/core/java/android/widget/DayPickerView.java
+++ b/core/java/android/widget/DayPickerView.java
@@ -33,10 +33,6 @@
 import com.android.internal.widget.ViewPager;
 import com.android.internal.widget.ViewPager.OnPageChangeListener;
 
-import libcore.icu.LocaleData;
-
-import java.util.Locale;
-
 class DayPickerView extends ViewGroup {
     private static final int DEFAULT_LAYOUT = R.layout.day_picker_content_material;
     private static final int DEFAULT_START_YEAR = 1900;
@@ -86,7 +82,7 @@
                 attrs, a, defStyleAttr, defStyleRes);
 
         final int firstDayOfWeek = a.getInt(R.styleable.CalendarView_firstDayOfWeek,
-                LocaleData.get(Locale.getDefault()).firstDayOfWeek);
+                Calendar.getInstance().getFirstDayOfWeek());
 
         final String minDate = a.getString(R.styleable.CalendarView_minDate);
         final String maxDate = a.getString(R.styleable.CalendarView_maxDate);
diff --git a/core/java/android/widget/Magnifier.java b/core/java/android/widget/Magnifier.java
index 23bbe69..89206fda 100644
--- a/core/java/android/widget/Magnifier.java
+++ b/core/java/android/widget/Magnifier.java
@@ -1142,7 +1142,7 @@
             bitmapRenderNode.setOutline(outline);
             bitmapRenderNode.setClipToOutline(true);
 
-            // Create a dummy draw, which will be replaced later with real drawing.
+            // Create a placeholder draw, which will be replaced later with real drawing.
             final RecordingCanvas canvas = bitmapRenderNode.beginRecording(
                     mContentWidth, mContentHeight);
             try {
diff --git a/core/java/android/widget/NumberPicker.java b/core/java/android/widget/NumberPicker.java
index baaf2a7..3b482a8 100644
--- a/core/java/android/widget/NumberPicker.java
+++ b/core/java/android/widget/NumberPicker.java
@@ -34,6 +34,7 @@
 import android.graphics.Paint.Align;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
+import android.icu.text.DecimalFormatSymbols;
 import android.os.Build;
 import android.os.Bundle;
 import android.text.InputFilter;
@@ -61,8 +62,6 @@
 
 import com.android.internal.R;
 
-import libcore.icu.LocaleData;
-
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
@@ -209,7 +208,7 @@
         }
 
         private static char getZeroDigit(Locale locale) {
-            return LocaleData.get(locale).zeroDigit;
+            return DecimalFormatSymbols.getInstance(locale).getZeroDigit();
         }
 
         private java.util.Formatter createFormatter(Locale locale) {
diff --git a/core/java/android/widget/SimpleMonthView.java b/core/java/android/widget/SimpleMonthView.java
index 61c77bc..695a253 100644
--- a/core/java/android/widget/SimpleMonthView.java
+++ b/core/java/android/widget/SimpleMonthView.java
@@ -27,6 +27,7 @@
 import android.graphics.Paint.Style;
 import android.graphics.Rect;
 import android.graphics.Typeface;
+import android.icu.text.DateFormatSymbols;
 import android.icu.text.DisplayContext;
 import android.icu.text.RelativeDateTimeFormatter;
 import android.icu.text.SimpleDateFormat;
@@ -50,8 +51,6 @@
 import com.android.internal.R;
 import com.android.internal.widget.ExploreByTouchHelper;
 
-import libcore.icu.LocaleData;
-
 import java.text.NumberFormat;
 import java.util.Locale;
 
@@ -194,7 +193,8 @@
     private void updateDayOfWeekLabels() {
         // Use tiny (e.g. single-character) weekday names from ICU. The indices
         // for this list correspond to Calendar days, e.g. SUNDAY is index 1.
-        final String[] tinyWeekdayNames = LocaleData.get(mLocale).tinyWeekdayNames;
+        final String[] tinyWeekdayNames = DateFormatSymbols.getInstance(mLocale)
+            .getWeekdays(DateFormatSymbols.FORMAT, DateFormatSymbols.NARROW);
         for (int i = 0; i < DAYS_IN_WEEK; i++) {
             mDayOfWeekLabels[i] = tinyWeekdayNames[(mWeekStart + i - 1) % DAYS_IN_WEEK + 1];
         }
diff --git a/core/java/android/widget/TextClock.java b/core/java/android/widget/TextClock.java
index 6432438..95c0e86 100644
--- a/core/java/android/widget/TextClock.java
+++ b/core/java/android/widget/TextClock.java
@@ -30,6 +30,7 @@
 import android.content.IntentFilter;
 import android.content.res.TypedArray;
 import android.database.ContentObserver;
+import android.icu.text.DateTimePatternGenerator;
 import android.net.Uri;
 import android.os.Handler;
 import android.os.SystemClock;
@@ -43,8 +44,6 @@
 
 import com.android.internal.R;
 
-import libcore.icu.LocaleData;
-
 import java.util.Calendar;
 import java.util.TimeZone;
 
@@ -262,14 +261,11 @@
     }
 
     private void init() {
-        if (mFormat12 == null || mFormat24 == null) {
-            LocaleData ld = LocaleData.get(getContext().getResources().getConfiguration().locale);
-            if (mFormat12 == null) {
-                mFormat12 = ld.timeFormat_hm;
-            }
-            if (mFormat24 == null) {
-                mFormat24 = ld.timeFormat_Hm;
-            }
+        if (mFormat12 == null) {
+            mFormat12 = getBestDateTimePattern("hm");
+        }
+        if (mFormat24 == null) {
+            mFormat24 = getBestDateTimePattern("Hm");
         }
 
         createTime(mTimeZone);
@@ -510,13 +506,11 @@
     private void chooseFormat() {
         final boolean format24Requested = is24HourModeEnabled();
 
-        LocaleData ld = LocaleData.get(getContext().getResources().getConfiguration().locale);
-
         if (format24Requested) {
-            mFormat = abc(mFormat24, mFormat12, ld.timeFormat_Hm);
+            mFormat = abc(mFormat24, mFormat12, getBestDateTimePattern("Hm"));
             mDescFormat = abc(mDescFormat24, mDescFormat12, mFormat);
         } else {
-            mFormat = abc(mFormat12, mFormat24, ld.timeFormat_hm);
+            mFormat = abc(mFormat12, mFormat24, getBestDateTimePattern("hm"));
             mDescFormat = abc(mDescFormat12, mDescFormat24, mFormat);
         }
 
@@ -529,6 +523,12 @@
         }
     }
 
+    private String getBestDateTimePattern(String skeleton) {
+        DateTimePatternGenerator dtpg = DateTimePatternGenerator.getInstance(
+                getContext().getResources().getConfiguration().locale);
+        return dtpg.getBestPattern(skeleton);
+    }
+
     /**
      * Returns a if not null, else return b if not null, else return c.
      */
diff --git a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
index d238d0e..ea3d2de 100644
--- a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
+++ b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
@@ -120,6 +120,13 @@
      */
     public static final String HASH_SALT_MAX_DAYS = "hash_salt_max_days";
 
+    // Flag related to Privacy Indicators
+
+    /**
+     * Whether the Permissions Hub is showing.
+     */
+    public static final String PROPERTY_PERMISSIONS_HUB_ENABLED = "permissions_hub_2_enabled";
+
     // Flags related to Assistant
 
     /**
diff --git a/core/java/com/android/internal/listeners/ListenerExecutor.java b/core/java/com/android/internal/listeners/ListenerExecutor.java
index e78e32b..9979e60 100644
--- a/core/java/com/android/internal/listeners/ListenerExecutor.java
+++ b/core/java/com/android/internal/listeners/ListenerExecutor.java
@@ -40,7 +40,7 @@
         /**
          * Called before this operation is to be run. Some operations may be canceled before they
          * are run, in which case this method may not be called. {@link #onPostExecute(boolean)}
-         * will only be run if this method was run.
+         * will only be run if this method was run. This callback is invoked on the calling thread.
          */
         default void onPreExecute() {}
 
@@ -49,7 +49,7 @@
          * RuntimeException, which will propagate normally. Implementations of
          * {@link ListenerExecutor} have the option to override
          * {@link ListenerExecutor#onOperationFailure(ListenerOperation, Exception)} instead to
-         * intercept failures at the class level.
+         * intercept failures at the class level. This callback is invoked on the executor thread.
          */
         default void onFailure(Exception e) {
             // implementations should handle any exceptions that may be thrown
@@ -59,21 +59,24 @@
         /**
          * Called after the operation is run. This method will always be called if
          * {@link #onPreExecute()} is called. Success implies that the operation was run to
-         * completion with no failures.
+         * completion with no failures. This callback may be invoked on the calling thread or
+         * executor thread.
          */
         default void onPostExecute(boolean success) {}
 
         /**
          * Called after this operation is complete (which does not imply that it was necessarily
          * run). Will always be called once per operation, no matter if the operation was run or
-         * not. Success implies that the operation was run to completion with no failures.
+         * not. Success implies that the operation was run to completion with no failures. This
+         * callback may be invoked on the calling thread or executor thread.
          */
         default void onComplete(boolean success) {}
     }
 
     /**
      * May be override to handle operation failures at a class level. Will not be invoked in the
-     * event of a RuntimeException, which will propagate normally.
+     * event of a RuntimeException, which will propagate normally. This callback is invoked on the
+     * executor thread.
      */
     default <TListener> void onOperationFailure(ListenerOperation<TListener> operation,
             Exception exception) {
@@ -83,7 +86,8 @@
     /**
      * Executes the given listener operation on the given executor, using the provided listener
      * supplier. If the supplier returns a null value, or a value during the operation that does not
-     * match the value prior to the operation, then the operation is considered canceled.
+     * match the value prior to the operation, then the operation is considered canceled. If a null
+     * operation is supplied, nothing happens.
      */
     default <TListener> void executeSafely(Executor executor, Supplier<TListener> listenerSupplier,
             @Nullable ListenerOperation<TListener> operation) {
diff --git a/core/java/com/android/internal/os/ClassLoaderFactory.java b/core/java/com/android/internal/os/ClassLoaderFactory.java
index a18943c..f83c5bd 100644
--- a/core/java/com/android/internal/os/ClassLoaderFactory.java
+++ b/core/java/com/android/internal/os/ClassLoaderFactory.java
@@ -101,7 +101,7 @@
             String librarySearchPath, String libraryPermittedPath, ClassLoader parent,
             int targetSdkVersion, boolean isNamespaceShared, String classLoaderName) {
         return createClassLoader(dexPath, librarySearchPath, libraryPermittedPath,
-            parent, targetSdkVersion, isNamespaceShared, classLoaderName, null);
+            parent, targetSdkVersion, isNamespaceShared, classLoaderName, null, null);
     }
 
 
@@ -111,18 +111,24 @@
     public static ClassLoader createClassLoader(String dexPath,
             String librarySearchPath, String libraryPermittedPath, ClassLoader parent,
             int targetSdkVersion, boolean isNamespaceShared, String classLoaderName,
-            List<ClassLoader> sharedLibraries) {
+            List<ClassLoader> sharedLibraries, List<String> nativeSharedLibraries) {
 
         final ClassLoader classLoader = createClassLoader(dexPath, librarySearchPath, parent,
                 classLoaderName, sharedLibraries);
 
+        String sonameList = "";
+        if (nativeSharedLibraries != null) {
+            sonameList = String.join(":", nativeSharedLibraries);
+        }
+
         Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "createClassloaderNamespace");
         String errorMessage = createClassloaderNamespace(classLoader,
                                                          targetSdkVersion,
                                                          librarySearchPath,
                                                          libraryPermittedPath,
                                                          isNamespaceShared,
-                                                         dexPath);
+                                                         dexPath,
+                                                         sonameList);
         Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
 
         if (errorMessage != null) {
@@ -139,5 +145,6 @@
                                                             String librarySearchPath,
                                                             String libraryPermittedPath,
                                                             boolean isNamespaceShared,
-                                                            String dexPath);
+                                                            String dexPath,
+                                                            String sonameList);
 }
diff --git a/core/java/com/android/server/SystemConfig.java b/core/java/com/android/server/SystemConfig.java
index 0eb3981..c6a1153 100644
--- a/core/java/com/android/server/SystemConfig.java
+++ b/core/java/com/android/server/SystemConfig.java
@@ -49,10 +49,12 @@
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
 
+import java.io.BufferedReader;
 import java.io.File;
 import java.io.FileNotFoundException;
 import java.io.FileReader;
 import java.io.IOException;
+import java.util.Arrays;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
@@ -104,11 +106,17 @@
         public final String name;
         public final String filename;
         public final String[] dependencies;
+        public final boolean isNative;
 
         SharedLibraryEntry(String name, String filename, String[] dependencies) {
+            this(name, filename, dependencies, false /* isNative */);
+        }
+
+        SharedLibraryEntry(String name, String filename, String[] dependencies, boolean isNative) {
             this.name = name;
             this.filename = filename;
             this.dependencies = dependencies;
+            this.isNative = isNative;
         }
     }
 
@@ -170,12 +178,6 @@
     // URL-handling state upon factory reset.
     final ArraySet<String> mLinkedApps = new ArraySet<>();
 
-    // These are the packages that are whitelisted to be able to run as system user
-    final ArraySet<String> mSystemUserWhitelistedApps = new ArraySet<>();
-
-    // These are the packages that should not run under system user
-    final ArraySet<String> mSystemUserBlacklistedApps = new ArraySet<>();
-
     // These are the components that are enabled by default as VR mode listener services.
     final ArraySet<ComponentName> mDefaultVrComponents = new ArraySet<>();
 
@@ -309,14 +311,6 @@
         return mLinkedApps;
     }
 
-    public ArraySet<String> getSystemUserWhitelistedApps() {
-        return mSystemUserWhitelistedApps;
-    }
-
-    public ArraySet<String> getSystemUserBlacklistedApps() {
-        return mSystemUserBlacklistedApps;
-    }
-
     public ArraySet<String> getHiddenApiWhitelistedApps() {
         return mHiddenApiPackageWhitelist;
     }
@@ -457,6 +451,7 @@
         log.traceBegin("readAllPermissions");
         try {
             readAllPermissions();
+            readPublicNativeLibrariesList();
         } finally {
             log.traceEnd();
         }
@@ -895,34 +890,6 @@
                         }
                         XmlUtils.skipCurrentTag(parser);
                     } break;
-                    case "system-user-whitelisted-app": {
-                        if (allowAppConfigs) {
-                            String pkgname = parser.getAttributeValue(null, "package");
-                            if (pkgname == null) {
-                                Slog.w(TAG, "<" + name + "> without package in "
-                                        + permFile + " at " + parser.getPositionDescription());
-                            } else {
-                                mSystemUserWhitelistedApps.add(pkgname);
-                            }
-                        } else {
-                            logNotAllowedInPartition(name, permFile, parser);
-                        }
-                        XmlUtils.skipCurrentTag(parser);
-                    } break;
-                    case "system-user-blacklisted-app": {
-                        if (allowAppConfigs) {
-                            String pkgname = parser.getAttributeValue(null, "package");
-                            if (pkgname == null) {
-                                Slog.w(TAG, "<" + name + "> without package in "
-                                        + permFile + " at " + parser.getPositionDescription());
-                            } else {
-                                mSystemUserBlacklistedApps.add(pkgname);
-                            }
-                        } else {
-                            logNotAllowedInPartition(name, permFile, parser);
-                        }
-                        XmlUtils.skipCurrentTag(parser);
-                    } break;
                     case "default-enabled-vr-app": {
                         if (allowAppConfigs) {
                             String pkgname = parser.getAttributeValue(null, "package");
@@ -1513,6 +1480,37 @@
         }
     }
 
+    private void readPublicNativeLibrariesList() {
+        readPublicLibrariesListFile(new File("/vendor/etc/public.libraries.txt"));
+        String[] dirs = {"/system/etc", "/system_ext/etc", "/product/etc"};
+        for (String dir : dirs) {
+            for (File f : (new File(dir)).listFiles()) {
+                String name = f.getName();
+                if (name.startsWith("public.libraries-") && name.endsWith(".txt")) {
+                    readPublicLibrariesListFile(f);
+                }
+            }
+        }
+    }
+
+    private void readPublicLibrariesListFile(File listFile) {
+        try (BufferedReader br = new BufferedReader(new FileReader(listFile))) {
+            String line;
+            while ((line = br.readLine()) != null) {
+                if (line.isEmpty() || line.startsWith("#")) {
+                    continue;
+                }
+                // Line format is <soname> [abi]. We take the soname part.
+                String soname = line.trim().split(" ")[0];
+                SharedLibraryEntry entry = new SharedLibraryEntry(
+                        soname, soname, new String[0], true);
+                mSharedLibraries.put(entry.name, entry);
+            }
+        } catch (IOException e) {
+            Slog.w(TAG, "Failed to read public libraries file " + listFile, e);
+        }
+    }
+
     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 3da2fa2..d6e8531 100644
--- a/core/jni/android_os_Debug.cpp
+++ b/core/jni/android_os_Debug.cpp
@@ -853,9 +853,8 @@
     } cfg_state = CONFIG_UNKNOWN;
 
     if (cfg_state == CONFIG_UNKNOWN) {
-        auto runtime_info = vintf::VintfObject::GetInstance()
-                                    ->getRuntimeInfo(false /* skip cache */,
-                                                     vintf::RuntimeInfo::FetchFlag::CONFIG_GZ);
+        auto runtime_info = vintf::VintfObject::GetInstance()->getRuntimeInfo(
+                vintf::RuntimeInfo::FetchFlag::CONFIG_GZ);
         CHECK(runtime_info != nullptr) << "Kernel configs cannot be fetched. b/151092221";
         const std::map<std::string, std::string>& configs = runtime_info->kernelConfigs();
         std::map<std::string, std::string>::const_iterator it = configs.find("CONFIG_VMAP_STACK");
diff --git a/core/jni/android_os_VintfRuntimeInfo.cpp b/core/jni/android_os_VintfRuntimeInfo.cpp
index 9379ea6..b0271b9 100644
--- a/core/jni/android_os_VintfRuntimeInfo.cpp
+++ b/core/jni/android_os_VintfRuntimeInfo.cpp
@@ -29,14 +29,12 @@
 using vintf::RuntimeInfo;
 using vintf::VintfObject;
 
-#define MAP_STRING_METHOD(javaMethod, cppString, flags)                                \
-    static jstring android_os_VintfRuntimeInfo_##javaMethod(JNIEnv* env, jclass clazz) \
-    {                                                                                  \
-        std::shared_ptr<const RuntimeInfo> info = VintfObject::GetRuntimeInfo(         \
-                false /* skipCache */, flags);                                         \
-        if (info == nullptr) return nullptr;                                           \
-        return env->NewStringUTF((cppString).c_str());                                 \
-    }                                                                                  \
+#define MAP_STRING_METHOD(javaMethod, cppString, flags)                                  \
+    static jstring android_os_VintfRuntimeInfo_##javaMethod(JNIEnv* env, jclass clazz) { \
+        std::shared_ptr<const RuntimeInfo> info = VintfObject::GetRuntimeInfo(flags);    \
+        if (info == nullptr) return nullptr;                                             \
+        return env->NewStringUTF((cppString).c_str());                                   \
+    }
 
 MAP_STRING_METHOD(getCpuInfo, info->cpuInfo(), RuntimeInfo::FetchFlag::CPU_INFO);
 MAP_STRING_METHOD(getOsName, info->osName(), RuntimeInfo::FetchFlag::CPU_VERSION);
@@ -54,8 +52,8 @@
 
 static jlong android_os_VintfRuntimeInfo_getKernelSepolicyVersion(JNIEnv *env, jclass clazz)
 {
-    std::shared_ptr<const RuntimeInfo> info = VintfObject::GetRuntimeInfo(
-        false /* skipCache */, RuntimeInfo::FetchFlag::POLICYVERS);
+    std::shared_ptr<const RuntimeInfo> info =
+            VintfObject::GetRuntimeInfo(RuntimeInfo::FetchFlag::POLICYVERS);
     if (info == nullptr) return 0;
     return static_cast<jlong>(info->kernelSepolicyVersion());
 }
diff --git a/core/jni/com_android_internal_os_ClassLoaderFactory.cpp b/core/jni/com_android_internal_os_ClassLoaderFactory.cpp
index f8d41e4..59c413f 100644
--- a/core/jni/com_android_internal_os_ClassLoaderFactory.cpp
+++ b/core/jni/com_android_internal_os_ClassLoaderFactory.cpp
@@ -28,16 +28,19 @@
                                               jstring librarySearchPath,
                                               jstring libraryPermittedPath,
                                               jboolean isShared,
-                                              jstring dexPath) {
+                                              jstring dexPath,
+                                              jstring sonameList) {
     return android::CreateClassLoaderNamespace(env, targetSdkVersion,
                                                classLoader, isShared == JNI_TRUE,
                                                dexPath,
-                                               librarySearchPath, libraryPermittedPath);
+                                               librarySearchPath,
+                                               libraryPermittedPath,
+                                               sonameList);
 }
 
 static const JNINativeMethod g_methods[] = {
     { "createClassloaderNamespace",
-      "(Ljava/lang/ClassLoader;ILjava/lang/String;Ljava/lang/String;ZLjava/lang/String;)Ljava/lang/String;",
+      "(Ljava/lang/ClassLoader;ILjava/lang/String;Ljava/lang/String;ZLjava/lang/String;Ljava/lang/String;)Ljava/lang/String;",
       reinterpret_cast<void*>(createClassloaderNamespace_native) },
 };
 
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index fe290f3..bae3bf90 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -355,6 +355,7 @@
     <protected-broadcast android:name="com.android.server.wifi.action.CarrierNetwork.USER_ALLOWED_CARRIER" />
     <protected-broadcast android:name="com.android.server.wifi.action.CarrierNetwork.USER_DISALLOWED_CARRIER" />
     <protected-broadcast android:name="com.android.server.wifi.action.CarrierNetwork.USER_DISMISSED" />
+    <protected-broadcast android:name="com.android.server.wifi.action.CarrierNetwork.USER_CLICKED" />
     <protected-broadcast android:name="com.android.server.wifi.ConnectToNetworkNotification.USER_DISMISSED_NOTIFICATION" />
     <protected-broadcast android:name="com.android.server.wifi.ConnectToNetworkNotification.CONNECT_TO_NETWORK" />
     <protected-broadcast android:name="com.android.server.wifi.ConnectToNetworkNotification.PICK_WIFI_NETWORK" />
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index 3b61c36..03c682f 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -832,7 +832,8 @@
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Druk kieslys om oop te sluit of maak noodoproep."</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Druk kieslys om oop te maak."</string>
     <string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Teken patroon om te ontsluit"</string>
-    <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Noodgeval"</string>
+    <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+    <skip />
     <string name="lockscreen_return_to_call" msgid="3156883574692006382">"Keer terug na oproep"</string>
     <string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Reg!"</string>
     <string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Probeer weer"</string>
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index 9ce214f..f80da89 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -832,7 +832,8 @@
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"ለመክፈት ምናሌ ተጫንወይም የአደጋ ጊዜ ጥሪ አድርግ።"</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"ለመክፈት ምናሌ ተጫን"</string>
     <string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"ለመክፈት ስርዓተ ጥለት ሳል"</string>
-    <string name="lockscreen_emergency_call" msgid="7500692654885445299">"ድንገተኛ አደጋ"</string>
+    <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+    <skip />
     <string name="lockscreen_return_to_call" msgid="3156883574692006382">"ወደ ጥሪ ተመለስ"</string>
     <string name="lockscreen_pattern_correct" msgid="8050630103651508582">"ትክክል!"</string>
     <string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"እንደገና ሞክር"</string>
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index e80720d..41e3e26 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -844,7 +844,8 @@
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"اضغط على \"القائمة\" لإلغاء التأمين أو إجراء اتصال بالطوارئ."</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"اضغط على \"القائمة\" لإلغاء التأمين."</string>
     <string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"رسم نقش لإلغاء التأمين"</string>
-    <string name="lockscreen_emergency_call" msgid="7500692654885445299">"الطوارئ"</string>
+    <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+    <skip />
     <string name="lockscreen_return_to_call" msgid="3156883574692006382">"العودة إلى الاتصال"</string>
     <string name="lockscreen_pattern_correct" msgid="8050630103651508582">"صحيح!"</string>
     <string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"أعد المحاولة"</string>
diff --git a/core/res/res/values-as/strings.xml b/core/res/res/values-as/strings.xml
index 881a127..c6c8bb5 100644
--- a/core/res/res/values-as/strings.xml
+++ b/core/res/res/values-as/strings.xml
@@ -832,7 +832,8 @@
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"আনলক কৰিবলৈ বা জৰুৰীকালীন কল কৰিবলৈ মেনু টিপক।"</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"আনলক কৰিবলৈ মেনু টিপক।"</string>
     <string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"আনলক কৰিবলৈ আর্হি আঁকক"</string>
-    <string name="lockscreen_emergency_call" msgid="7500692654885445299">"জৰুৰীকালীন"</string>
+    <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+    <skip />
     <string name="lockscreen_return_to_call" msgid="3156883574692006382">"কললৈ উভতি যাওক"</string>
     <string name="lockscreen_pattern_correct" msgid="8050630103651508582">"শুদ্ধ!"</string>
     <string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"আকৌ চেষ্টা কৰক"</string>
diff --git a/core/res/res/values-az/strings.xml b/core/res/res/values-az/strings.xml
index 6408ef6..f0ff883 100644
--- a/core/res/res/values-az/strings.xml
+++ b/core/res/res/values-az/strings.xml
@@ -832,7 +832,8 @@
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Təcili zəng kilidini açmaq və ya yerləşdirmək üçün Menyu düyməsinə basın."</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Kilidi açmaq üçün Menyu düyməsinə basın."</string>
     <string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Kilidi açmaq üçün model çəkin"</string>
-    <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Təcili"</string>
+    <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+    <skip />
     <string name="lockscreen_return_to_call" msgid="3156883574692006382">"Zəngə qayıt"</string>
     <string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Düzdür!"</string>
     <string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Bir də cəhd edin"</string>
diff --git a/core/res/res/values-b+sr+Latn/strings.xml b/core/res/res/values-b+sr+Latn/strings.xml
index 2bcf909..8319da7 100644
--- a/core/res/res/values-b+sr+Latn/strings.xml
+++ b/core/res/res/values-b+sr+Latn/strings.xml
@@ -835,7 +835,8 @@
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Pritisnite „Meni“ da biste otključali telefon ili uputite hitan poziv."</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Pritisnite „Meni“ za otključavanje."</string>
     <string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Unesite šablon za otključavanje"</string>
-    <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Hitne službe"</string>
+    <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+    <skip />
     <string name="lockscreen_return_to_call" msgid="3156883574692006382">"Nazad na poziv"</string>
     <string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Tačno!"</string>
     <string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Probajte ponovo"</string>
@@ -1145,7 +1146,7 @@
     <string name="capital_off" msgid="7443704171014626777">"NE"</string>
     <string name="checked" msgid="9179896827054513119">"označeno je"</string>
     <string name="not_checked" msgid="7972320087569023342">"nije označeno"</string>
-    <string name="whichApplication" msgid="5432266899591255759">"Dovršavanje radnje pomoću"</string>
+    <string name="whichApplication" msgid="5432266899591255759">"Dovrši radnju preko"</string>
     <string name="whichApplicationNamed" msgid="6969946041713975681">"Završite radnju pomoću aplikacije %1$s"</string>
     <string name="whichApplicationLabel" msgid="7852182961472531728">"Završi radnju"</string>
     <string name="whichViewApplication" msgid="5733194231473132945">"Otvorite pomoću"</string>
diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml
index ed781fb..3540516 100644
--- a/core/res/res/values-be/strings.xml
+++ b/core/res/res/values-be/strings.xml
@@ -838,7 +838,8 @@
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Націсніце \"Меню\", каб разблакаваць, або зрабіце экстраны выклік."</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Націсніце \"Меню\", каб разблакаваць."</string>
     <string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Намалюйце камбінацыю разблакоўкі, каб разблакаваць"</string>
-    <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Экстранны выклік"</string>
+    <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+    <skip />
     <string name="lockscreen_return_to_call" msgid="3156883574692006382">"Вярнуцца да выкліку"</string>
     <string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Правільна!"</string>
     <string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Паспрабуйце яшчэ раз"</string>
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index f089d62..6643bcf 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -832,7 +832,8 @@
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Натиснете „Меню“, за да отключите или да извършите спешно обаждане."</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Натиснете „Меню“, за да отключите."</string>
     <string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Нарисувайте фигура, за да отключите"</string>
-    <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Спешни случаи"</string>
+    <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+    <skip />
     <string name="lockscreen_return_to_call" msgid="3156883574692006382">"Назад към обаждането"</string>
     <string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Правилно!"</string>
     <string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Опитайте отново"</string>
diff --git a/core/res/res/values-bn/strings.xml b/core/res/res/values-bn/strings.xml
index 1ac213f0..cbec4ac 100644
--- a/core/res/res/values-bn/strings.xml
+++ b/core/res/res/values-bn/strings.xml
@@ -832,7 +832,8 @@
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"আনলক করতে বা জরুরি কল করতে মেনু টিপুন৷"</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"আনলক করতে মেনু টিপুন৷"</string>
     <string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"আনলক করতে প্যাটার্ন আঁকুন"</string>
-    <string name="lockscreen_emergency_call" msgid="7500692654885445299">"জরুরী"</string>
+    <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+    <skip />
     <string name="lockscreen_return_to_call" msgid="3156883574692006382">"কলে ফিরুন"</string>
     <string name="lockscreen_pattern_correct" msgid="8050630103651508582">"সঠিক!"</string>
     <string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"আবার চেষ্টা করুন"</string>
diff --git a/core/res/res/values-bs/strings.xml b/core/res/res/values-bs/strings.xml
index f27441a..87b02f5 100644
--- a/core/res/res/values-bs/strings.xml
+++ b/core/res/res/values-bs/strings.xml
@@ -835,7 +835,8 @@
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Pritisnite dugme Meni kako biste otključali uređaj ili obavili hitni poziv."</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Pritisnite dugme Meni za otključavanje uređaja."</string>
     <string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Nacrtajte uzorak za otključavanje"</string>
-    <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Hitno"</string>
+    <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+    <skip />
     <string name="lockscreen_return_to_call" msgid="3156883574692006382">"Povratak na poziv"</string>
     <string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Ispravno!"</string>
     <string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Pokušajte ponovo"</string>
@@ -1145,8 +1146,8 @@
     <string name="capital_off" msgid="7443704171014626777">"Isključeno"</string>
     <string name="checked" msgid="9179896827054513119">"označeno"</string>
     <string name="not_checked" msgid="7972320087569023342">"nije označeno"</string>
-    <string name="whichApplication" msgid="5432266899591255759">"Izvrši akciju koristeći"</string>
-    <string name="whichApplicationNamed" msgid="6969946041713975681">"Dovršite akciju koristeći %1$s"</string>
+    <string name="whichApplication" msgid="5432266899591255759">"Završite radnju pomoću aplikacije"</string>
+    <string name="whichApplicationNamed" msgid="6969946041713975681">"Završite radnju pomoću aplikacije %1$s"</string>
     <string name="whichApplicationLabel" msgid="7852182961472531728">"Izvršiti akciju"</string>
     <string name="whichViewApplication" msgid="5733194231473132945">"Otvori koristeći"</string>
     <string name="whichViewApplicationNamed" msgid="415164730629690105">"Otvori koristeći %1$s"</string>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index 8561764..c69ca37d 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -838,7 +838,8 @@
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Chcete-li odemknout telefon nebo provést tísňové volání, stiskněte Menu."</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Telefon odemknete stisknutím tlačítka Menu."</string>
     <string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Odblokujte pomocí gesta"</string>
-    <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Stav nouze"</string>
+    <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+    <skip />
     <string name="lockscreen_return_to_call" msgid="3156883574692006382">"Zavolat zpět"</string>
     <string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Správně!"</string>
     <string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Zkusit znovu"</string>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index 1b3dab2..fb10077 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -832,7 +832,8 @@
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Tryk på Menu for at låse op eller foretage et nødopkald."</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Tryk på Menu for at låse op."</string>
     <string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Tegn oplåsningsmønster"</string>
-    <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Nødsituation"</string>
+    <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+    <skip />
     <string name="lockscreen_return_to_call" msgid="3156883574692006382">"Tilbage til opkald"</string>
     <string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Rigtigt!"</string>
     <string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Prøv igen"</string>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index 296200f..4adcf0a 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -832,7 +832,8 @@
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Drücke die Menütaste, um das Telefon zu entsperren oder einen Notruf zu tätigen."</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Zum Entsperren die Menütaste drücken"</string>
     <string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Muster zum Entsperren zeichnen"</string>
-    <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Notfall"</string>
+    <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+    <skip />
     <string name="lockscreen_return_to_call" msgid="3156883574692006382">"Zurück zum Anruf"</string>
     <string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Korrekt!"</string>
     <string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Erneut versuchen"</string>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index e790c9a..23895f4 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -832,7 +832,8 @@
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Πατήστε \"Menu\" για ξεκλείδωμα ή για κλήση έκτακτης ανάγκης."</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Πατήστε \"Μενού\" για ξεκλείδωμα."</string>
     <string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Σχεδιασμός μοτίβου για ξεκλείδωμα"</string>
-    <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Κλήση έκτακτης ανάγκης"</string>
+    <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+    <skip />
     <string name="lockscreen_return_to_call" msgid="3156883574692006382">"Επιστροφή στην κλήση"</string>
     <string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Σωστό!"</string>
     <string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Προσπαθήστε ξανά"</string>
diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml
index 0f1566b..0484ccd 100644
--- a/core/res/res/values-en-rAU/strings.xml
+++ b/core/res/res/values-en-rAU/strings.xml
@@ -832,7 +832,8 @@
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Press Menu to unlock or place emergency call."</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Press Menu to unlock."</string>
     <string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Draw pattern to unlock"</string>
-    <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Emergency"</string>
+    <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+    <skip />
     <string name="lockscreen_return_to_call" msgid="3156883574692006382">"Return to call"</string>
     <string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Correct!"</string>
     <string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Try again"</string>
diff --git a/core/res/res/values-en-rCA/strings.xml b/core/res/res/values-en-rCA/strings.xml
index 84b3ce1..9981c12 100644
--- a/core/res/res/values-en-rCA/strings.xml
+++ b/core/res/res/values-en-rCA/strings.xml
@@ -832,7 +832,8 @@
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Press Menu to unlock or place emergency call."</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Press Menu to unlock."</string>
     <string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Draw pattern to unlock"</string>
-    <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Emergency"</string>
+    <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+    <skip />
     <string name="lockscreen_return_to_call" msgid="3156883574692006382">"Return to call"</string>
     <string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Correct!"</string>
     <string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Try again"</string>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index d9a2bdb..02cdf69a 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -832,7 +832,8 @@
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Press Menu to unlock or place emergency call."</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Press Menu to unlock."</string>
     <string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Draw pattern to unlock"</string>
-    <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Emergency"</string>
+    <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+    <skip />
     <string name="lockscreen_return_to_call" msgid="3156883574692006382">"Return to call"</string>
     <string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Correct!"</string>
     <string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Try again"</string>
diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml
index 1209f2b..3b18aca 100644
--- a/core/res/res/values-en-rIN/strings.xml
+++ b/core/res/res/values-en-rIN/strings.xml
@@ -832,7 +832,8 @@
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Press Menu to unlock or place emergency call."</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Press Menu to unlock."</string>
     <string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Draw pattern to unlock"</string>
-    <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Emergency"</string>
+    <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+    <skip />
     <string name="lockscreen_return_to_call" msgid="3156883574692006382">"Return to call"</string>
     <string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Correct!"</string>
     <string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Try again"</string>
diff --git a/core/res/res/values-en-rXC/strings.xml b/core/res/res/values-en-rXC/strings.xml
index 7f29fd4..6503f7c 100644
--- a/core/res/res/values-en-rXC/strings.xml
+++ b/core/res/res/values-en-rXC/strings.xml
@@ -832,7 +832,7 @@
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‏‏‎‏‏‎‎‎‏‏‏‎‏‎‏‎‏‏‏‏‏‏‎‎‎‎‏‏‏‏‏‎‏‎‏‎‏‎‎‏‎‏‏‏‎‎‏‎‏‏‎‏‎‏‎‏‏‎‎‎Press Menu to unlock or place emergency call.‎‏‎‎‏‎"</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‏‏‏‎‎‏‎‏‎‏‏‎‎‎‏‎‏‏‏‏‏‎‎‎‏‏‎‎‎‎‏‎‎‎‏‏‏‎‎‏‏‎‏‏‏‎‏‎‏‏‏‎‏‎‏‏‎‎‏‎Press Menu to unlock.‎‏‎‎‏‎"</string>
     <string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‎‏‏‏‏‏‏‏‏‏‎‎‎‎‏‎‎‏‎‏‏‎‎‏‎‎‎‎‎‎‎‏‏‏‎‏‏‏‏‎‏‏‏‎‎‏‎‎‏‎‏‎‎‎‎‏‏‎‏‎Draw pattern to unlock‎‏‎‎‏‎"</string>
-    <string name="lockscreen_emergency_call" msgid="7500692654885445299">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‎‎‎‎‎‏‎‏‏‏‏‏‎‏‎‎‎‎‎‎‏‏‏‎‏‎‏‎‏‎‏‏‏‏‏‏‎‏‎‏‎‎‎‎‎‏‏‏‏‎‏‎‏‏‎‎‏‏‎Emergency‎‏‎‎‏‎"</string>
+    <string name="lockscreen_emergency_call" msgid="7549683825868928636">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‎‎‏‏‎‎‎‏‎‏‏‏‎‏‏‏‎‏‎‏‏‏‎‎‎‎‏‎‎‎‏‏‏‏‎‏‏‎‏‏‎‏‏‏‏‏‎‏‏‎‎‏‏‏‏‏‎‎‎Emergency call‎‏‎‎‏‎"</string>
     <string name="lockscreen_return_to_call" msgid="3156883574692006382">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‎‏‏‏‏‎‎‏‏‏‏‏‎‎‎‎‎‎‎‏‏‏‎‎‎‏‎‏‎‏‎‎‎‏‎‏‏‎‎‏‎‎‎‎‏‎‎‏‎‎‏‏‏‏‎‏‏‏‎‎Return to call‎‏‎‎‏‎"</string>
     <string name="lockscreen_pattern_correct" msgid="8050630103651508582">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‏‏‏‏‎‏‏‏‎‎‏‏‎‎‏‎‏‎‏‎‏‏‎‏‏‎‎‏‏‎‏‏‎‏‎‎‎‏‏‎‏‏‎‏‏‏‎‎‎‎‏‎‏‏‎‎‏‏‎‎Correct!‎‏‎‎‏‎"</string>
     <string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‎‎‎‏‏‎‎‏‏‎‏‎‏‏‏‏‎‎‎‏‎‎‏‎‏‎‏‎‏‎‏‎‎‏‎‎‏‎‎‎‎‏‎‏‎‎‏‏‎‎‏‎‎‏‏‏‎‏‎‎Try again‎‏‎‎‏‎"</string>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index f8ead279..7e8a89a 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -832,7 +832,8 @@
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Presiona el Menú para desbloquear o realizar una llamada de emergencia."</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Presionar Menú para desbloquear."</string>
     <string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Dibujar el patrón de desbloqueo"</string>
-    <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Emergencia"</string>
+    <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+    <skip />
     <string name="lockscreen_return_to_call" msgid="3156883574692006382">"Regresar a llamada"</string>
     <string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Correcto"</string>
     <string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Vuelve a intentarlo."</string>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index 741c89f..02aafa7 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -832,7 +832,8 @@
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Pulsa la tecla de menú para desbloquear el teléfono o realizar una llamada de emergencia."</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Pulsa la tecla de menú para desbloquear la pantalla."</string>
     <string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Dibujar patrón de desbloqueo"</string>
-    <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Emergencia"</string>
+    <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+    <skip />
     <string name="lockscreen_return_to_call" msgid="3156883574692006382">"Volver a llamada"</string>
     <string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Correcto"</string>
     <string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Vuelve a intentarlo"</string>
diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml
index d724046..8136294 100644
--- a/core/res/res/values-et/strings.xml
+++ b/core/res/res/values-et/strings.xml
@@ -832,7 +832,8 @@
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Vajutage avamiseks või hädaabikõne tegemiseks menüünuppu"</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Vajutage avamiseks menüüklahvi."</string>
     <string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Avamiseks joonistage muster"</string>
-    <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Hädaabi"</string>
+    <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+    <skip />
     <string name="lockscreen_return_to_call" msgid="3156883574692006382">"Kõne juurde tagasi"</string>
     <string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Õige."</string>
     <string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Proovige uuesti"</string>
diff --git a/core/res/res/values-eu/strings.xml b/core/res/res/values-eu/strings.xml
index 24d3eef..1b88f27 100644
--- a/core/res/res/values-eu/strings.xml
+++ b/core/res/res/values-eu/strings.xml
@@ -832,7 +832,8 @@
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Desblokeatzeko edo larrialdi-deia egiteko, sakatu Menua."</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Desblokeatzeko, sakatu Menua."</string>
     <string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Desblokeatzeko, marraztu eredua"</string>
-    <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Larrialdi-deiak"</string>
+    <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+    <skip />
     <string name="lockscreen_return_to_call" msgid="3156883574692006382">"Itzuli deira"</string>
     <string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Eredua zuzena da!"</string>
     <string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Saiatu berriro"</string>
@@ -846,7 +847,7 @@
     <string name="lockscreen_missing_sim_instructions" msgid="8473601862688263903">"Sartu SIM txartela."</string>
     <string name="lockscreen_missing_sim_instructions_long" msgid="3664999892038416334">"SIM txartela falta da edo ezin da irakurri. Sartu SIM txartel bat."</string>
     <string name="lockscreen_permanent_disabled_sim_message_short" msgid="3812893366715730539">"SIM txartela hondatuta dago."</string>
-    <string name="lockscreen_permanent_disabled_sim_instructions" msgid="4358929052509450807">"SIM txartela behin betiko desgaitu zaizu.\n Beste SIM txartel bat lortzeko, jarri zerbitzu-hornitzailearekin harremanetan."</string>
+    <string name="lockscreen_permanent_disabled_sim_instructions" msgid="4358929052509450807">"SIM txartela betiko desgaitu zaizu.\n Beste SIM txartel bat lortzeko, jarri zerbitzu-hornitzailearekin harremanetan."</string>
     <string name="lockscreen_transport_prev_description" msgid="2879469521751181478">"Aurreko pista"</string>
     <string name="lockscreen_transport_next_description" msgid="2931509904881099919">"Hurrengo pista"</string>
     <string name="lockscreen_transport_pause_description" msgid="6705284702135372494">"Pausatu"</string>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index b0b5338..506a7ee 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -832,7 +832,8 @@
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"برای بازگشایی قفل یا انجام تماس اضطراری روی منو فشار دهید."</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"برای بازگشایی قفل روی منو فشار دهید."</string>
     <string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"الگو را بکشید تا قفل آن باز شود"</string>
-    <string name="lockscreen_emergency_call" msgid="7500692654885445299">"اضطراری"</string>
+    <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+    <skip />
     <string name="lockscreen_return_to_call" msgid="3156883574692006382">"بازگشت به تماس"</string>
     <string name="lockscreen_pattern_correct" msgid="8050630103651508582">"صحیح است!"</string>
     <string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"دوباره امتحان کنید"</string>
@@ -1125,8 +1126,8 @@
     <string name="capital_off" msgid="7443704171014626777">"خاموش"</string>
     <string name="checked" msgid="9179896827054513119">"علامت‌زده‌شده"</string>
     <string name="not_checked" msgid="7972320087569023342">"بدون علامت"</string>
-    <string name="whichApplication" msgid="5432266899591255759">"تکمیل عملکرد با استفاده از"</string>
-    <string name="whichApplicationNamed" msgid="6969946041713975681">"‏تکمیل عملکرد با استفاده از %1$s"</string>
+    <string name="whichApplication" msgid="5432266899591255759">"تکمیل کنش بااستفاده از"</string>
+    <string name="whichApplicationNamed" msgid="6969946041713975681">"‏تکمیل کنش بااستفاده از %1$s"</string>
     <string name="whichApplicationLabel" msgid="7852182961472531728">"تکمیل عملکرد"</string>
     <string name="whichViewApplication" msgid="5733194231473132945">"باز کردن با"</string>
     <string name="whichViewApplicationNamed" msgid="415164730629690105">"‏باز کردن با %1$s"</string>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index 815daaa..01db781 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -832,7 +832,8 @@
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Poista lukitus tai soita hätäpuhelu painamalla Valikko-painiketta."</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Poista lukitus painamalla Valikko-painiketta."</string>
     <string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Poista lukitus piirtämällä kuvio"</string>
-    <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Hätäpuhelu"</string>
+    <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+    <skip />
     <string name="lockscreen_return_to_call" msgid="3156883574692006382">"Palaa puheluun"</string>
     <string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Oikein!"</string>
     <string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Yritä uudelleen"</string>
diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml
index d3323d7..f5adc77 100644
--- a/core/res/res/values-fr-rCA/strings.xml
+++ b/core/res/res/values-fr-rCA/strings.xml
@@ -832,7 +832,8 @@
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Appuyez sur \"Menu\" pour débloquer le téléphone ou appeler un numéro d\'urgence."</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Appuyez sur \"Menu\" pour déverrouiller l\'appareil."</string>
     <string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Dessinez un schéma pour déverrouiller le téléphone"</string>
-    <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Urgence"</string>
+    <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+    <skip />
     <string name="lockscreen_return_to_call" msgid="3156883574692006382">"Retour à l\'appel"</string>
     <string name="lockscreen_pattern_correct" msgid="8050630103651508582">"C\'est exact!"</string>
     <string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Réessayer"</string>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index 4a1646f..8880dc6 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -832,7 +832,8 @@
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Appuyez sur \"Menu\" pour déverrouiller le téléphone ou appeler un numéro d\'urgence"</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Appuyez sur \"Menu\" pour déverrouiller le téléphone."</string>
     <string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Dessinez un schéma pour déverrouiller le téléphone"</string>
-    <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Urgences"</string>
+    <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+    <skip />
     <string name="lockscreen_return_to_call" msgid="3156883574692006382">"Retour à l\'appel"</string>
     <string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Combinaison correcte !"</string>
     <string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Veuillez réessayer."</string>
diff --git a/core/res/res/values-gl/strings.xml b/core/res/res/values-gl/strings.xml
index 54e17fa..dffca1c 100644
--- a/core/res/res/values-gl/strings.xml
+++ b/core/res/res/values-gl/strings.xml
@@ -832,7 +832,8 @@
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Preme Menú para desbloquear ou realizar unha chamada de emerxencia."</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Preme Menú para desbloquear."</string>
     <string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Crea o padrón de desbloqueo"</string>
-    <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Emerxencia"</string>
+    <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+    <skip />
     <string name="lockscreen_return_to_call" msgid="3156883574692006382">"Volver á chamada"</string>
     <string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Correcto!"</string>
     <string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Téntao de novo"</string>
diff --git a/core/res/res/values-gu/strings.xml b/core/res/res/values-gu/strings.xml
index be17cfd..784e85f 100644
--- a/core/res/res/values-gu/strings.xml
+++ b/core/res/res/values-gu/strings.xml
@@ -832,7 +832,8 @@
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"અનલૉક કરવા માટે અથવા કટોકટીનો કૉલ કરવા માટે મેનૂ દબાવો."</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"અનલૉક કરવા માટે મેનૂ દબાવો."</string>
     <string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"અનલૉક કરવા માટે પૅટર્ન દોરો."</string>
-    <string name="lockscreen_emergency_call" msgid="7500692654885445299">"ઇમર્જન્સી"</string>
+    <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+    <skip />
     <string name="lockscreen_return_to_call" msgid="3156883574692006382">"કૉલ પર પાછા ફરો"</string>
     <string name="lockscreen_pattern_correct" msgid="8050630103651508582">"સાચું!"</string>
     <string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"ફરી પ્રયાસ કરો"</string>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index 97118f8..82a1062 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -832,7 +832,8 @@
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"लॉक खोलने के लिए मेन्यू दबाएं या आपातलकालीन कॉल करें."</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"लॉक खोलने के लिए मेन्यू दबाएं."</string>
     <string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"अनलॉक करने के लिए आकार आरेखित करें"</string>
-    <string name="lockscreen_emergency_call" msgid="7500692654885445299">"आपातकाल"</string>
+    <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+    <skip />
     <string name="lockscreen_return_to_call" msgid="3156883574692006382">"कॉल पर वापस लौटें"</string>
     <string name="lockscreen_pattern_correct" msgid="8050630103651508582">"सही!"</string>
     <string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"फिर से कोशिश करें"</string>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index a1b303e..f1bcd39 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -835,7 +835,8 @@
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Pritisnite Izbornik za otključavanje ili pozivanje hitnih službi."</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Pritisnite Izbornik za otključavanje."</string>
     <string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Iscrtajte uzorak za otključavanje"</string>
-    <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Hitne službe"</string>
+    <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+    <skip />
     <string name="lockscreen_return_to_call" msgid="3156883574692006382">"Uzvrati poziv"</string>
     <string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Ispravno!"</string>
     <string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Pokušajte ponovo"</string>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index ff0d470..3106371 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -832,7 +832,8 @@
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"A feloldáshoz vagy segélyhívás kezdeményezéséhez nyomja meg a Menü gombot."</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"A feloldáshoz nyomja meg a Menü gombot."</string>
     <string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Rajzolja le a mintát a feloldáshoz"</string>
-    <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Segélyhívás"</string>
+    <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+    <skip />
     <string name="lockscreen_return_to_call" msgid="3156883574692006382">"Hívás folytatása"</string>
     <string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Helyes!"</string>
     <string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Próbálja újra"</string>
diff --git a/core/res/res/values-hy/strings.xml b/core/res/res/values-hy/strings.xml
index 05c447d..85afc27 100644
--- a/core/res/res/values-hy/strings.xml
+++ b/core/res/res/values-hy/strings.xml
@@ -832,7 +832,8 @@
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Ապակողպելու կամ շտապ կանչ անելու համար սեղմեք «Ընտրացանկ»"</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Ապակողպելու համար սեղմեք Ցանկը:"</string>
     <string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Հավաքեք սխեման` ապակողպելու համար"</string>
-    <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Շտապ կանչ"</string>
+    <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+    <skip />
     <string name="lockscreen_return_to_call" msgid="3156883574692006382">"Վերադառնալ զանգին"</string>
     <string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Ճիշտ է:"</string>
     <string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Կրկին փորձեք"</string>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index a6f68d1..8ec1f50 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -832,7 +832,8 @@
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Tekan Menu untuk membuka atau melakukan panggilan darurat."</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Tekan Menu untuk membuka."</string>
     <string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Buat pola untuk membuka"</string>
-    <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Darurat"</string>
+    <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+    <skip />
     <string name="lockscreen_return_to_call" msgid="3156883574692006382">"Kembali ke panggilan"</string>
     <string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Perbaiki!"</string>
     <string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Coba lagi"</string>
@@ -1125,7 +1126,7 @@
     <string name="capital_off" msgid="7443704171014626777">"MATI"</string>
     <string name="checked" msgid="9179896827054513119">"dicentang"</string>
     <string name="not_checked" msgid="7972320087569023342">"tidak dicentang"</string>
-    <string name="whichApplication" msgid="5432266899591255759">"Tindakan lengkap menggunakan"</string>
+    <string name="whichApplication" msgid="5432266899591255759">"Selesaikan tindakan menggunakan"</string>
     <string name="whichApplicationNamed" msgid="6969946041713975681">"Selesaikan tindakan menggunakan %1$s"</string>
     <string name="whichApplicationLabel" msgid="7852182961472531728">"Selesaikan tindakan"</string>
     <string name="whichViewApplication" msgid="5733194231473132945">"Buka dengan"</string>
diff --git a/core/res/res/values-is/strings.xml b/core/res/res/values-is/strings.xml
index 92f6f24..1b0c2fe 100644
--- a/core/res/res/values-is/strings.xml
+++ b/core/res/res/values-is/strings.xml
@@ -832,7 +832,8 @@
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Ýttu á valmyndartakkann til að taka úr lás eða hringja neyðarsímtal."</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Ýttu á valmyndartakkann til að taka úr lás."</string>
     <string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Teiknaðu mynstur til að taka úr lás"</string>
-    <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Neyðarsímtal"</string>
+    <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+    <skip />
     <string name="lockscreen_return_to_call" msgid="3156883574692006382">"Aftur í símtal"</string>
     <string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Rétt!"</string>
     <string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Reyndu aftur"</string>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index 8f857fa..d2bf62d 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -832,7 +832,8 @@
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Premi Menu per sbloccare o effettuare chiamate di emergenza."</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Premi Menu per sbloccare."</string>
     <string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Traccia la sequenza di sblocco"</string>
-    <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Emergenza"</string>
+    <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+    <skip />
     <string name="lockscreen_return_to_call" msgid="3156883574692006382">"Torna a chiamata"</string>
     <string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Corretta."</string>
     <string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Riprova"</string>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index 27889fb..f9674bd 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -832,7 +832,8 @@
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"MENUキーでロック解除(または緊急通報)"</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"MENUキーでロック解除"</string>
     <string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"パターンを入力"</string>
-    <string name="lockscreen_emergency_call" msgid="7500692654885445299">"緊急通報"</string>
+    <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+    <skip />
     <string name="lockscreen_return_to_call" msgid="3156883574692006382">"通話に戻る"</string>
     <string name="lockscreen_pattern_correct" msgid="8050630103651508582">"一致しました"</string>
     <string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"もう一度お試しください"</string>
diff --git a/core/res/res/values-ka/strings.xml b/core/res/res/values-ka/strings.xml
index 6547185..3d1e9c7 100644
--- a/core/res/res/values-ka/strings.xml
+++ b/core/res/res/values-ka/strings.xml
@@ -832,7 +832,8 @@
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"განბლოკვისთვის ან გადაუდებელი ზარისთვის დააჭირეთ მენიუს."</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"განბლოკვისთვის დააჭირეთ მენიუს."</string>
     <string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"განსაბლოკად დახატეთ ნიმუში"</string>
-    <string name="lockscreen_emergency_call" msgid="7500692654885445299">"საგანგებო სამსახურები"</string>
+    <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+    <skip />
     <string name="lockscreen_return_to_call" msgid="3156883574692006382">"ზარზე დაბრუნება"</string>
     <string name="lockscreen_pattern_correct" msgid="8050630103651508582">"სწორია!"</string>
     <string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"კიდევ სცადეთ"</string>
diff --git a/core/res/res/values-kk/strings.xml b/core/res/res/values-kk/strings.xml
index 3d53912..38a50ab 100644
--- a/core/res/res/values-kk/strings.xml
+++ b/core/res/res/values-kk/strings.xml
@@ -832,7 +832,8 @@
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Бекітпесін ашу үшін немесе төтенше қоңырауды табу үшін Мәзір тармағын басыңыз."</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Ашу үшін Мәзір пернесін басыңыз."</string>
     <string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Бекітпесін ашу үшін кескінді сызыңыз"</string>
-    <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Төтенше жағдай"</string>
+    <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+    <skip />
     <string name="lockscreen_return_to_call" msgid="3156883574692006382">"Қоңырауға оралу"</string>
     <string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Дұрыс!"</string>
     <string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Қайталап көріңіз"</string>
diff --git a/core/res/res/values-km/strings.xml b/core/res/res/values-km/strings.xml
index 760d256..4e646c5 100644
--- a/core/res/res/values-km/strings.xml
+++ b/core/res/res/values-km/strings.xml
@@ -832,7 +832,8 @@
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"ចុច​ម៉ឺនុយ ដើម្បី​ដោះ​សោ​ ឬ​ហៅ​ពេល​អាសន្ន។"</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"ចុច​ម៉ឺនុយ ដើម្បី​ដោះ​សោ។"</string>
     <string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"គូរ​លំនាំ ដើម្បី​ដោះ​សោ"</string>
-    <string name="lockscreen_emergency_call" msgid="7500692654885445299">"បន្ទាន់"</string>
+    <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+    <skip />
     <string name="lockscreen_return_to_call" msgid="3156883574692006382">"ត្រឡប់​ទៅ​ការ​ហៅ"</string>
     <string name="lockscreen_pattern_correct" msgid="8050630103651508582">"ត្រឹមត្រូវ!"</string>
     <string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"ព្យាយាម​ម្ដង​ទៀត"</string>
diff --git a/core/res/res/values-kn/strings.xml b/core/res/res/values-kn/strings.xml
index 6513481..8d74215 100644
--- a/core/res/res/values-kn/strings.xml
+++ b/core/res/res/values-kn/strings.xml
@@ -832,7 +832,8 @@
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"ಅನ್‌ಲಾಕ್ ಮಾಡಲು ಮೆನು ಒತ್ತಿರಿ ಇಲ್ಲವೇ ತುರ್ತು ಕರೆಯನ್ನು ಮಾಡಿ."</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"ಅನ್‌ಲಾಕ್ ಮಾಡಲು ಮೆನು ಒತ್ತಿರಿ."</string>
     <string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"ಅನ್‌ಲಾಕ್ ಮಾಡಲು ಪ್ಯಾಟರ್ನ್ ಚಿತ್ರಿಸಿ"</string>
-    <string name="lockscreen_emergency_call" msgid="7500692654885445299">"ತುರ್ತು"</string>
+    <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+    <skip />
     <string name="lockscreen_return_to_call" msgid="3156883574692006382">"ಕರೆಗೆ ಹಿಂತಿರುಗು"</string>
     <string name="lockscreen_pattern_correct" msgid="8050630103651508582">"ಸರಿಯಾಗಿದೆ!"</string>
     <string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ"</string>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index db8d379..5d960dc 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -832,7 +832,8 @@
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"비상 전화를 걸거나 잠금해제하려면 메뉴를 누르세요."</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"잠금해제하려면 메뉴를 누르세요."</string>
     <string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"잠금해제를 위해 패턴 그리기"</string>
-    <string name="lockscreen_emergency_call" msgid="7500692654885445299">"긴급 전화"</string>
+    <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+    <skip />
     <string name="lockscreen_return_to_call" msgid="3156883574692006382">"통화로 돌아가기"</string>
     <string name="lockscreen_pattern_correct" msgid="8050630103651508582">"맞습니다."</string>
     <string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"다시 시도"</string>
diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml
index c208396..c97324f 100644
--- a/core/res/res/values-ky/strings.xml
+++ b/core/res/res/values-ky/strings.xml
@@ -832,7 +832,8 @@
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Кулпусун ачып же Шашылыш чалуу аткаруу үчүн менюну басыңыз."</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Бөгөттөн чыгаруу үчүн Менюну басыңыз."</string>
     <string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Кулпуну ачуу үчүн, үлгүнү тартыңыз"</string>
-    <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Тез жардам"</string>
+    <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+    <skip />
     <string name="lockscreen_return_to_call" msgid="3156883574692006382">"Чалууга кайтуу"</string>
     <string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Туура!"</string>
     <string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Дагы аракет кылыңыз"</string>
@@ -1125,7 +1126,7 @@
     <string name="capital_off" msgid="7443704171014626777">"ӨЧҮК"</string>
     <string name="checked" msgid="9179896827054513119">"белгиленген"</string>
     <string name="not_checked" msgid="7972320087569023342">"белгилене элек"</string>
-    <string name="whichApplication" msgid="5432266899591255759">"Аракет колдонууну бүтүрүү"</string>
+    <string name="whichApplication" msgid="5432266899591255759">"Кайсынысын колдоносуз?"</string>
     <string name="whichApplicationNamed" msgid="6969946041713975681">"%1$s аркылуу аракетти аягына чейин чыгаруу"</string>
     <string name="whichApplicationLabel" msgid="7852182961472531728">"Аракетти аягына чыгаруу"</string>
     <string name="whichViewApplication" msgid="5733194231473132945">"Төмөнкү менен ачуу"</string>
diff --git a/core/res/res/values-lo/strings.xml b/core/res/res/values-lo/strings.xml
index f388cfa..c1b3fe4 100644
--- a/core/res/res/values-lo/strings.xml
+++ b/core/res/res/values-lo/strings.xml
@@ -832,7 +832,8 @@
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"ກົດ ເມນູ ເພື່ອປົດລັອກ ຫຼື ໂທອອກຫາເບີສຸກເສີນ."</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"ກົດ \"ເມນູ\" ເພື່ອປົດລັອກ."</string>
     <string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"ແຕ້ມຮູບແບບເພື່ອປົດລັອກ"</string>
-    <string name="lockscreen_emergency_call" msgid="7500692654885445299">"ສຸກ​ເສີນ"</string>
+    <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+    <skip />
     <string name="lockscreen_return_to_call" msgid="3156883574692006382">"ກັບໄປຫາການໂທ"</string>
     <string name="lockscreen_pattern_correct" msgid="8050630103651508582">"ຖືກຕ້ອງ!"</string>
     <string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"ລອງໃໝ່ອີກຄັ້ງ"</string>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index d68cd8d..fc5709c 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -838,7 +838,8 @@
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Paspauskite „Meniu“, kad atrakintumėte ar skambintumėte pagalbos numeriu."</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Paspauskite „Meniu“, jei norite atrakinti."</string>
     <string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Nustatyti modelį, kad atrakintų"</string>
-    <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Skambutis pagalbos numeriu"</string>
+    <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+    <skip />
     <string name="lockscreen_return_to_call" msgid="3156883574692006382">"grįžti prie skambučio"</string>
     <string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Teisingai!"</string>
     <string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Bandykite dar kartą"</string>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index 9462c22..d780b14 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -835,7 +835,8 @@
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Nospiediet Izvēlne, lai atbloķētu, vai veiciet ārkārtas zvanu."</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Lai atbloķētu, nospiediet vienumu Izvēlne."</string>
     <string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Zīmējiet kombināciju, lai atbloķētu."</string>
-    <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Ārkārtas situācija"</string>
+    <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+    <skip />
     <string name="lockscreen_return_to_call" msgid="3156883574692006382">"Atpakaļ pie zvana"</string>
     <string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Pareizi!"</string>
     <string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Mēģināt vēlreiz"</string>
diff --git a/core/res/res/values-mcc260/config.xml b/core/res/res/values-mcc260/config.xml
new file mode 100644
index 0000000..79eefb7
--- /dev/null
+++ b/core/res/res/values-mcc260/config.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2020, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<!-- These resources are around just to allow their values to be customized
+     for different hardware and product builds. -->
+<resources>
+  <!-- Set to false to disable emergency alert. -->
+  <bool name="config_cellBroadcastAppLinks">false</bool>
+</resources>
\ No newline at end of file
diff --git a/core/res/res/values-mcc262/config.xml b/core/res/res/values-mcc262/config.xml
new file mode 100644
index 0000000..79eefb7
--- /dev/null
+++ b/core/res/res/values-mcc262/config.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2020, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<!-- These resources are around just to allow their values to be customized
+     for different hardware and product builds. -->
+<resources>
+  <!-- Set to false to disable emergency alert. -->
+  <bool name="config_cellBroadcastAppLinks">false</bool>
+</resources>
\ No newline at end of file
diff --git a/core/res/res/values-mk/strings.xml b/core/res/res/values-mk/strings.xml
index aee1fca..b50c608 100644
--- a/core/res/res/values-mk/strings.xml
+++ b/core/res/res/values-mk/strings.xml
@@ -832,7 +832,8 @@
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Притисни „Мени“ да се отклучи или да направи итен повик."</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Притиснете „Мени“ за да се отклучи."</string>
     <string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Употребете ја шемата за да се отклучи"</string>
-    <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Итен случај"</string>
+    <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+    <skip />
     <string name="lockscreen_return_to_call" msgid="3156883574692006382">"Врати се на повик"</string>
     <string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Точно!"</string>
     <string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Обидете се повторно"</string>
@@ -1125,7 +1126,7 @@
     <string name="capital_off" msgid="7443704171014626777">"ИСКЛУЧЕНО"</string>
     <string name="checked" msgid="9179896827054513119">"штиклирано"</string>
     <string name="not_checked" msgid="7972320087569023342">"не е штиклирано"</string>
-    <string name="whichApplication" msgid="5432266899591255759">"Заврши дејство со"</string>
+    <string name="whichApplication" msgid="5432266899591255759">"Активирај со"</string>
     <string name="whichApplicationNamed" msgid="6969946041713975681">"Остварете го дејството со %1$s"</string>
     <string name="whichApplicationLabel" msgid="7852182961472531728">"Заврши го дејството"</string>
     <string name="whichViewApplication" msgid="5733194231473132945">"Отвори со"</string>
diff --git a/core/res/res/values-ml/strings.xml b/core/res/res/values-ml/strings.xml
index 375cb98..b769fd2 100644
--- a/core/res/res/values-ml/strings.xml
+++ b/core/res/res/values-ml/strings.xml
@@ -832,7 +832,8 @@
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"അൺലോക്ക് ചെയ്യുന്നതിനായി മെനു അമർത്തുക അല്ലെങ്കിൽ അടിയന്തര കോൾ വിളിക്കുക."</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"അൺലോക്കുചെയ്യാൻ മെനു അമർത്തുക."</string>
     <string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"അൺലോക്ക് ചെയ്യാൻ പാറ്റേൺ വരയ്‌ക്കുക"</string>
-    <string name="lockscreen_emergency_call" msgid="7500692654885445299">"എമർജൻസി"</string>
+    <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+    <skip />
     <string name="lockscreen_return_to_call" msgid="3156883574692006382">"കോളിലേക്ക് മടങ്ങുക"</string>
     <string name="lockscreen_pattern_correct" msgid="8050630103651508582">"ശരി!"</string>
     <string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"വീണ്ടും ശ്രമിക്കുക"</string>
diff --git a/core/res/res/values-mn/strings.xml b/core/res/res/values-mn/strings.xml
index 1472c4e..7b41501 100644
--- a/core/res/res/values-mn/strings.xml
+++ b/core/res/res/values-mn/strings.xml
@@ -832,7 +832,8 @@
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Яаралтай дуудлага хийх буюу эсвэл түгжээг тайлах бол цэсийг дарна уу."</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Тайлах бол цэсийг дарна уу."</string>
     <string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Тайлах хээгээ зурна уу"</string>
-    <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Яаралтай тусламж"</string>
+    <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+    <skip />
     <string name="lockscreen_return_to_call" msgid="3156883574692006382">"Дуудлагаруу буцах"</string>
     <string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Зөв!"</string>
     <string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Дахин оролдох"</string>
diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml
index 6695de6..5778033 100644
--- a/core/res/res/values-mr/strings.xml
+++ b/core/res/res/values-mr/strings.xml
@@ -832,7 +832,8 @@
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"अनलॉक करण्‍यासाठी मेनू दाबा किंवा आणीबाणीचा कॉल करा."</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"अनलॉक करण्यासाठी मेनू दाबा."</string>
     <string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"अनलॉक करण्यासाठी पॅटर्न काढा"</string>
-    <string name="lockscreen_emergency_call" msgid="7500692654885445299">"आणीबाणी"</string>
+    <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+    <skip />
     <string name="lockscreen_return_to_call" msgid="3156883574692006382">"कॉलवर परत या"</string>
     <string name="lockscreen_pattern_correct" msgid="8050630103651508582">"अचूक!"</string>
     <string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"पुन्हा प्रयत्न करा"</string>
diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml
index c8f37c3..c30bcfc 100644
--- a/core/res/res/values-ms/strings.xml
+++ b/core/res/res/values-ms/strings.xml
@@ -832,7 +832,8 @@
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Tekan Menu untuk menyahsekat atau membuat panggilan kecemasan."</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Tekan Menu untuk membuka kunci."</string>
     <string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Lukiskan corak untuk membuka kunci"</string>
-    <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Kecemasan"</string>
+    <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+    <skip />
     <string name="lockscreen_return_to_call" msgid="3156883574692006382">"Kembali ke panggilan"</string>
     <string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Betul!"</string>
     <string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Cuba lagi"</string>
diff --git a/core/res/res/values-my/strings.xml b/core/res/res/values-my/strings.xml
index dcb4e3c..243357e 100644
--- a/core/res/res/values-my/strings.xml
+++ b/core/res/res/values-my/strings.xml
@@ -832,7 +832,8 @@
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"ဖွင့်ရန်သို့မဟုတ်အရေးပေါ်ခေါ်ဆိုခြင်းပြုလုပ်ရန် မီနူးကိုနှိပ်ပါ"</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"မီးနူးကို နှိပ်ခြင်းဖြင့် သော့ဖွင့်ပါ"</string>
     <string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"ဖွင့်ရန်ပုံစံဆွဲပါ"</string>
-    <string name="lockscreen_emergency_call" msgid="7500692654885445299">"အရေးပေါ်"</string>
+    <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+    <skip />
     <string name="lockscreen_return_to_call" msgid="3156883574692006382">"ခေါ်ဆိုမှုထံပြန်သွားရန်"</string>
     <string name="lockscreen_pattern_correct" msgid="8050630103651508582">"မှန်ပါသည်"</string>
     <string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"ထပ် စမ်းပါ"</string>
@@ -1102,7 +1103,7 @@
     <string name="delete" msgid="1514113991712129054">"ဖျက်ရန်"</string>
     <string name="copyUrl" msgid="6229645005987260230">"URLအား ကူးခြင်း"</string>
     <string name="selectTextMode" msgid="3225108910999318778">"စာသား ရွေးရန်"</string>
-    <string name="undo" msgid="3175318090002654673">"ပြန်ဖျက်ရန်"</string>
+    <string name="undo" msgid="3175318090002654673">"တစ်ဆင့်နောက်ပြန်ရန်"</string>
     <string name="redo" msgid="7231448494008532233">"ထပ်လုပ်ပါ"</string>
     <string name="autofill" msgid="511224882647795296">"အော်တိုဖြည့်"</string>
     <string name="textSelectionCABTitle" msgid="5151441579532476940">"စာတိုရွေးချယ်မှု"</string>
@@ -1125,7 +1126,7 @@
     <string name="capital_off" msgid="7443704171014626777">"ပိတ်"</string>
     <string name="checked" msgid="9179896827054513119">"အမှန်ခြစ်ပြီး"</string>
     <string name="not_checked" msgid="7972320087569023342">"ခြစ် မထား"</string>
-    <string name="whichApplication" msgid="5432266899591255759">"အသုံးပြု၍ ဆောင်ရွက်မှုအားပြီးဆုံးစေခြင်း"</string>
+    <string name="whichApplication" msgid="5432266899591255759">"အောက်ပါတို့ကို အသုံးပြုမှု အပြီးသတ်ခြင်း"</string>
     <string name="whichApplicationNamed" msgid="6969946041713975681">"%1$s ကို သုံးပြီး လုပ်ဆောင်ချက် ပြီးဆုံးပါစေ"</string>
     <string name="whichApplicationLabel" msgid="7852182961472531728">"လုပ်ဆောင်ချက်ကို အပြီးသတ်ပါ"</string>
     <string name="whichViewApplication" msgid="5733194231473132945">"...ဖြင့် ဖွင့်မည်"</string>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index 6926d65..0a31b49 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -832,7 +832,8 @@
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Trykk på menyknappen for å låse opp eller ringe et nødnummer."</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Trykk på menyknappen for å låse opp."</string>
     <string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Tegn mønster for å låse opp"</string>
-    <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Nødssituasjon"</string>
+    <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+    <skip />
     <string name="lockscreen_return_to_call" msgid="3156883574692006382">"Tilbake til samtale"</string>
     <string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Riktig!"</string>
     <string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Prøv på nytt"</string>
diff --git a/core/res/res/values-ne/strings.xml b/core/res/res/values-ne/strings.xml
index c9ac6ec..9712b48 100644
--- a/core/res/res/values-ne/strings.xml
+++ b/core/res/res/values-ne/strings.xml
@@ -832,7 +832,8 @@
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"अनलक वा आपतकालीन कल गर्न मेनु थिच्नुहोस्।"</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"अनलक गर्न मेनु थिच्नुहोस्।"</string>
     <string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"अनलक गर्नु ढाँचा खिच्नुहोस्"</string>
-    <string name="lockscreen_emergency_call" msgid="7500692654885445299">"आपतकालीन"</string>
+    <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+    <skip />
     <string name="lockscreen_return_to_call" msgid="3156883574692006382">"कलमा फर्किनुहोस्"</string>
     <string name="lockscreen_pattern_correct" msgid="8050630103651508582">"सही!"</string>
     <string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"फेरि प्रयास गर्नुहोस्"</string>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index 904c906..d1a342c 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -832,7 +832,8 @@
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Druk op \'Menu\' om te ontgrendelen of noodoproep te plaatsen."</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Druk op \'Menu\' om te ontgrendelen."</string>
     <string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Patroon tekenen om te ontgrendelen"</string>
-    <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Noodgeval"</string>
+    <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+    <skip />
     <string name="lockscreen_return_to_call" msgid="3156883574692006382">"Terug naar gesprek"</string>
     <string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Juist!"</string>
     <string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Opnieuw proberen"</string>
diff --git a/core/res/res/values-or/strings.xml b/core/res/res/values-or/strings.xml
index 62505a3..84815f7 100644
--- a/core/res/res/values-or/strings.xml
+++ b/core/res/res/values-or/strings.xml
@@ -832,7 +832,8 @@
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"ଅନଲକ୍‌ କରିବା ପାଇଁ ମେନୁକୁ ଦବାନ୍ତୁ କିମ୍ବା ଜରୁରୀକାଳୀନ କଲ୍‌ କରନ୍ତୁ।"</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"ଅନଲକ୍‌ କରିବା ପାଇଁ ମେନୁକୁ ଦବାନ୍ତୁ।"</string>
     <string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"ଅନଲକ୍‌ କରିବା ପାଇଁ ପାଟର୍ନ ଆଙ୍କନ୍ତୁ"</string>
-    <string name="lockscreen_emergency_call" msgid="7500692654885445299">"ଜରୁରୀକାଳୀନ"</string>
+    <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+    <skip />
     <string name="lockscreen_return_to_call" msgid="3156883574692006382">"କଲ୍‌କୁ ଫେରନ୍ତୁ"</string>
     <string name="lockscreen_pattern_correct" msgid="8050630103651508582">"ଠିକ୍!"</string>
     <string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"ପୁଣି ଚେଷ୍ଟା କରନ୍ତୁ"</string>
diff --git a/core/res/res/values-pa/strings.xml b/core/res/res/values-pa/strings.xml
index 346ee9a..c29308f 100644
--- a/core/res/res/values-pa/strings.xml
+++ b/core/res/res/values-pa/strings.xml
@@ -832,7 +832,8 @@
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"ਅਣਲਾਕ ਕਰਨ ਲਈ ਮੀਨੂ ਦਬਾਓ ਜਾਂ ਸੰਕਟਕਾਲੀਨ ਕਾਲ ਕਰੋ।"</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"ਅਣਲਾਕ ਕਰਨ ਲਈ ਮੀਨੂ ਦਬਾਓ।"</string>
     <string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"ਅਣਲਾਕ ਕਰਨ ਲਈ ਪੈਟਰਨ ਡ੍ਰਾ ਕਰੋ"</string>
-    <string name="lockscreen_emergency_call" msgid="7500692654885445299">"ਸੰਕਟਕਾਲ"</string>
+    <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+    <skip />
     <string name="lockscreen_return_to_call" msgid="3156883574692006382">"ਕਾਲ ਤੇ ਵਾਪਸ ਜਾਓ"</string>
     <string name="lockscreen_pattern_correct" msgid="8050630103651508582">"ਸਹੀ!"</string>
     <string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ"</string>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index 7d2795e..df56457 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -838,7 +838,8 @@
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Naciśnij Menu, aby odblokować lub wykonać połączenie alarmowe."</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Naciśnij Menu, aby odblokować."</string>
     <string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Narysuj wzór, aby odblokować"</string>
-    <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Alarmowe"</string>
+    <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+    <skip />
     <string name="lockscreen_return_to_call" msgid="3156883574692006382">"Powrót do połączenia"</string>
     <string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Poprawnie!"</string>
     <string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Spróbuj ponownie."</string>
diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml
index 4fec5b3..3ecf28d 100644
--- a/core/res/res/values-pt-rBR/strings.xml
+++ b/core/res/res/values-pt-rBR/strings.xml
@@ -832,7 +832,8 @@
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Pressione Menu para desbloquear ou fazer uma chamada de emergência."</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Pressione Menu para desbloquear."</string>
     <string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Desenhe o padrão para desbloquear"</string>
-    <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Emergência"</string>
+    <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+    <skip />
     <string name="lockscreen_return_to_call" msgid="3156883574692006382">"Retornar à chamada"</string>
     <string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Correto!"</string>
     <string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Tente novamente"</string>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index 5f6fb5c..dcc8e17 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -286,7 +286,7 @@
     <string name="notification_channel_retail_mode" msgid="3732239154256431213">"Demonstração para retalho"</string>
     <string name="notification_channel_usb" msgid="1528280969406244896">"Ligação USB"</string>
     <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"Aplicação em execução"</string>
-    <string name="notification_channel_foreground_service" msgid="7102189948158885178">"Aplicações que estão a consumir bateria"</string>
+    <string name="notification_channel_foreground_service" msgid="7102189948158885178">"Apps que estão a consumir bateria"</string>
     <string name="foreground_service_app_in_background" msgid="1439289699671273555">"A app <xliff:g id="APP_NAME">%1$s</xliff:g> está a consumir bateria."</string>
     <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> aplicações estão a consumir bateria."</string>
     <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Toque para obter detalhes acerca da utilização da bateria e dos dados"</string>
@@ -832,7 +832,8 @@
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Prima Menu para desbloquear ou efectuar uma chamada de emergência."</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Prima Menu para desbloquear."</string>
     <string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Desenhar padrão para desbloquear"</string>
-    <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Emergência"</string>
+    <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+    <skip />
     <string name="lockscreen_return_to_call" msgid="3156883574692006382">"Regressar à chamada"</string>
     <string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Correcto!"</string>
     <string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Tentar novamente"</string>
@@ -1153,7 +1154,7 @@
     <string name="whichImageCaptureApplicationLabel" msgid="6505433734824988277">"Capturar imagem"</string>
     <string name="alwaysUse" msgid="3153558199076112903">"Utilizar por predefinição para esta ação."</string>
     <string name="use_a_different_app" msgid="4987790276170972776">"Utilizar outra app"</string>
-    <string name="clearDefaultHintMsg" msgid="1325866337702524936">"Limpar a predefinição nas Definições do Sistema &gt; Aplicações &gt; Transferidas."</string>
+    <string name="clearDefaultHintMsg" msgid="1325866337702524936">"Limpar a predefinição nas Definições do Sistema &gt; Apps &gt; Transferidas."</string>
     <string name="chooseActivity" msgid="8563390197659779956">"Escolha uma ação"</string>
     <string name="chooseUsbActivity" msgid="2096269989990986612">"Escolher uma app para o dispositivo USB"</string>
     <string name="noApplications" msgid="1186909265235544019">"Nenhuma app pode efetuar esta ação."</string>
@@ -1181,7 +1182,7 @@
     <string name="launch_warning_original" msgid="3332206576800169626">"<xliff:g id="APP_NAME">%1$s</xliff:g> foi originalmente iniciado."</string>
     <string name="screen_compat_mode_scale" msgid="8627359598437527726">"Escala"</string>
     <string name="screen_compat_mode_show" msgid="5080361367584709857">"Mostrar sempre"</string>
-    <string name="screen_compat_mode_hint" msgid="4032272159093750908">"Reative este modo nas Definições do Sistema &gt; Aplicações &gt; Transferidas."</string>
+    <string name="screen_compat_mode_hint" msgid="4032272159093750908">"Reative este modo nas Definições do Sistema &gt; Apps &gt; Transferidas."</string>
     <string name="unsupported_display_size_message" msgid="7265211375269394699">"<xliff:g id="APP_NAME">%1$s</xliff:g> não suporta a definição de Tamanho do ecrã atual e pode ter um comportamento inesperado."</string>
     <string name="unsupported_display_size_show" msgid="980129850974919375">"Mostrar sempre"</string>
     <string name="unsupported_compile_sdk_message" msgid="7326293500707890537">"A app <xliff:g id="APP_NAME">%1$s</xliff:g> foi concebida para uma versão incompatível do SO Android e pode ter um comportamento inesperado. Pode estar disponível uma versão atualizada da app."</string>
@@ -1274,7 +1275,7 @@
     <string name="sms_short_code_confirm_allow" msgid="920477594325526691">"Enviar"</string>
     <string name="sms_short_code_confirm_deny" msgid="1356917469323768230">"Cancelar"</string>
     <string name="sms_short_code_remember_choice" msgid="1374526438647744862">"Memorizar a minha escolha"</string>
-    <string name="sms_short_code_remember_undo_instruction" msgid="2620984439143080410">"Pode alterar mais tarde em Definições &gt; Aplicações"</string>
+    <string name="sms_short_code_remember_undo_instruction" msgid="2620984439143080410">"Pode alterar mais tarde em Definições &gt; Apps"</string>
     <string name="sms_short_code_confirm_always_allow" msgid="2223014893129755950">"Permitir Sempre"</string>
     <string name="sms_short_code_confirm_never_allow" msgid="2688828813521652079">"Nunca Permitir"</string>
     <string name="sim_removed_title" msgid="5387212933992546283">"Cartão SIM removido"</string>
@@ -1284,8 +1285,8 @@
     <string name="sim_added_message" msgid="6602906609509958680">"Reinicie o aparelho para aceder à rede de telemóvel."</string>
     <string name="sim_restart_button" msgid="8481803851341190038">"Reiniciar"</string>
     <string name="install_carrier_app_notification_title" msgid="5712723402213090102">"Ativar o serviço móvel"</string>
-    <string name="install_carrier_app_notification_text" msgid="2781317581274192728">"Transfira a app do operador para ativar o seu novo SIM."</string>
-    <string name="install_carrier_app_notification_text_app_name" msgid="4086877327264106484">"Transfira a app <xliff:g id="APP_NAME">%1$s</xliff:g> para ativar o novo SIM."</string>
+    <string name="install_carrier_app_notification_text" msgid="2781317581274192728">"Descarregue a app do operador para ativar o seu novo SIM."</string>
+    <string name="install_carrier_app_notification_text_app_name" msgid="4086877327264106484">"Descarregue a app <xliff:g id="APP_NAME">%1$s</xliff:g> para ativar o novo SIM."</string>
     <string name="install_carrier_app_notification_button" msgid="6257740533102594290">"Transferir app"</string>
     <string name="carrier_app_notification_title" msgid="5815477368072060250">"Novo SIM inserido"</string>
     <string name="carrier_app_notification_text" msgid="6567057546341958637">"Toque para configurar"</string>
@@ -2035,7 +2036,7 @@
     <string name="usb_device_resolve_prompt_warn" msgid="325871329788064199">"Esta app não recebeu autorização de gravação, mas pode capturar áudio através deste dispositivo USB."</string>
     <string name="accessibility_system_action_home_label" msgid="3234748160850301870">"Página inicial"</string>
     <string name="accessibility_system_action_back_label" msgid="4205361367345537608">"Anterior"</string>
-    <string name="accessibility_system_action_recents_label" msgid="4782875610281649728">"Aplicações recentes"</string>
+    <string name="accessibility_system_action_recents_label" msgid="4782875610281649728">"Apps recentes"</string>
     <string name="accessibility_system_action_notifications_label" msgid="6083767351772162010">"Notificações"</string>
     <string name="accessibility_system_action_quick_settings_label" msgid="4583900123506773783">"Definições rápidas"</string>
     <string name="accessibility_system_action_power_dialog_label" msgid="8095341821683910781">"Caixa de diálogo de energia"</string>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index 4fec5b3..3ecf28d 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -832,7 +832,8 @@
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Pressione Menu para desbloquear ou fazer uma chamada de emergência."</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Pressione Menu para desbloquear."</string>
     <string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Desenhe o padrão para desbloquear"</string>
-    <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Emergência"</string>
+    <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+    <skip />
     <string name="lockscreen_return_to_call" msgid="3156883574692006382">"Retornar à chamada"</string>
     <string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Correto!"</string>
     <string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Tente novamente"</string>
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index a0e3598..6493668 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -835,7 +835,8 @@
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Apăsați Meniu pentru a debloca sau pentru a efectua apeluri de urgență."</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Apăsați Meniu pentru deblocare."</string>
     <string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Desenați modelul pentru a debloca"</string>
-    <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Urgență"</string>
+    <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+    <skip />
     <string name="lockscreen_return_to_call" msgid="3156883574692006382">"Reveniți la apel"</string>
     <string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Corect!"</string>
     <string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Încercați din nou"</string>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index 1a65847..07abf6d 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -838,7 +838,8 @@
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Нажмите \"Меню\", чтобы разблокировать экран или вызвать службу экстренной помощи."</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Для разблокировки нажмите \"Меню\"."</string>
     <string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Введите графический ключ"</string>
-    <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Экстренный вызов"</string>
+    <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+    <skip />
     <string name="lockscreen_return_to_call" msgid="3156883574692006382">"Вернуться к вызову"</string>
     <string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Правильно!"</string>
     <string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Повторите попытку"</string>
diff --git a/core/res/res/values-si/strings.xml b/core/res/res/values-si/strings.xml
index db759f6..d494a36 100644
--- a/core/res/res/values-si/strings.xml
+++ b/core/res/res/values-si/strings.xml
@@ -832,7 +832,8 @@
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"අගුළු හැරීමට මෙනුව ඔබන්න හෝ හදිසි ඇමතුම ලබාගන්න."</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"අගුළු හැරීමට මෙනු ඔබන්න."</string>
     <string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"අගුළු ඇරීමට රටාව අඳින්න"</string>
-    <string name="lockscreen_emergency_call" msgid="7500692654885445299">"හදිසි"</string>
+    <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+    <skip />
     <string name="lockscreen_return_to_call" msgid="3156883574692006382">"ඇමතුම වෙත නැවත යන්න"</string>
     <string name="lockscreen_pattern_correct" msgid="8050630103651508582">"නිවැරදියි!"</string>
     <string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"නැවත උත්සාහ කරන්න"</string>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index 5f80364..52c5301 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -838,7 +838,8 @@
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Ak chcete odomknúť telefón alebo uskutočniť tiesňové volanie, stlačte Menu."</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Telefón odomknete stlačením tlačidla Menu."</string>
     <string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Odomknite nakreslením vzoru"</string>
-    <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Stav tiesne"</string>
+    <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+    <skip />
     <string name="lockscreen_return_to_call" msgid="3156883574692006382">"Zavolať späť"</string>
     <string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Správne!"</string>
     <string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Skúsiť znova"</string>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index 4e37059..a43c900 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -838,7 +838,8 @@
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Če želite odkleniti napravo ali opraviti klic v sili, pritisnite meni."</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Če želite odkleniti, pritisnite meni."</string>
     <string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Če želite odkleniti, narišite vzorec"</string>
-    <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Klic v sili"</string>
+    <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+    <skip />
     <string name="lockscreen_return_to_call" msgid="3156883574692006382">"Nazaj na klic"</string>
     <string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Pravilno."</string>
     <string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Poskusi znova"</string>
diff --git a/core/res/res/values-sq/strings.xml b/core/res/res/values-sq/strings.xml
index 30e75558..625962f 100644
--- a/core/res/res/values-sq/strings.xml
+++ b/core/res/res/values-sq/strings.xml
@@ -832,7 +832,8 @@
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Shtyp \"Meny\" për të shkyçur ose për të kryer telefonatë urgjence."</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Shtyp \"Meny\" për të shkyçur."</string>
     <string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Vizato modelin për ta shkyçur"</string>
-    <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Urgjenca"</string>
+    <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+    <skip />
     <string name="lockscreen_return_to_call" msgid="3156883574692006382">"Kthehu te telefonata"</string>
     <string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Saktë!"</string>
     <string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Provo sërish"</string>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index 6890b80..6101dac 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -835,7 +835,8 @@
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Притисните „Мени“ да бисте откључали телефон или упутите хитан позив."</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Притисните „Мени“ за откључавање."</string>
     <string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Унесите шаблон за откључавање"</string>
-    <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Хитне службе"</string>
+    <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+    <skip />
     <string name="lockscreen_return_to_call" msgid="3156883574692006382">"Назад на позив"</string>
     <string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Тачно!"</string>
     <string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Пробајте поново"</string>
@@ -1145,7 +1146,7 @@
     <string name="capital_off" msgid="7443704171014626777">"НЕ"</string>
     <string name="checked" msgid="9179896827054513119">"означено је"</string>
     <string name="not_checked" msgid="7972320087569023342">"није означено"</string>
-    <string name="whichApplication" msgid="5432266899591255759">"Довршавање радње помоћу"</string>
+    <string name="whichApplication" msgid="5432266899591255759">"Доврши радњу преко"</string>
     <string name="whichApplicationNamed" msgid="6969946041713975681">"Завршите радњу помоћу апликације %1$s"</string>
     <string name="whichApplicationLabel" msgid="7852182961472531728">"Заврши радњу"</string>
     <string name="whichViewApplication" msgid="5733194231473132945">"Отворите помоћу"</string>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index 87b0872..e30bb14 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -832,7 +832,8 @@
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Tryck på Menu för att låsa upp eller ringa nödsamtal."</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Tryck på Menu för att låsa upp."</string>
     <string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Rita grafiskt lösenord för att låsa upp"</string>
-    <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Nödsamtal"</string>
+    <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+    <skip />
     <string name="lockscreen_return_to_call" msgid="3156883574692006382">"Tillbaka till samtal"</string>
     <string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Korrekt!"</string>
     <string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Försök igen"</string>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index fbf4cca..4e4c0f6 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -832,7 +832,8 @@
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Bonyeza Menyu ili kufungua au kupiga simu ya dharura."</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Bonyeza Menyu ili kufungua."</string>
     <string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Chora ruwaza ili kufungua"</string>
-    <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Dharura"</string>
+    <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+    <skip />
     <string name="lockscreen_return_to_call" msgid="3156883574692006382">"Rudi kwa kupiga simu"</string>
     <string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Sahihi!"</string>
     <string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Jaribu tena"</string>
diff --git a/core/res/res/values-ta/strings.xml b/core/res/res/values-ta/strings.xml
index 731a796..a617248 100644
--- a/core/res/res/values-ta/strings.xml
+++ b/core/res/res/values-ta/strings.xml
@@ -832,7 +832,8 @@
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"தடைநீக்க மெனுவை அழுத்தவும் அல்லது அவசர அழைப்பை மேற்கொள்ளவும்."</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"திறக்க, மெனுவை அழுத்தவும்."</string>
     <string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"திறக்க வடிவத்தை வரையவும்"</string>
-    <string name="lockscreen_emergency_call" msgid="7500692654885445299">"அவசர அழைப்பு"</string>
+    <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+    <skip />
     <string name="lockscreen_return_to_call" msgid="3156883574692006382">"அழைப்பிற்குத் திரும்பு"</string>
     <string name="lockscreen_pattern_correct" msgid="8050630103651508582">"சரி!"</string>
     <string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"மீண்டும் முயற்சிக்கவும்"</string>
diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml
index a2ec787..794fec8 100644
--- a/core/res/res/values-te/strings.xml
+++ b/core/res/res/values-te/strings.xml
@@ -832,7 +832,8 @@
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"అన్‌లాక్ చేయడానికి లేదా అత్యవసర కాల్ చేయడానికి మెను నొక్కండి."</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"అన్‌లాక్ చేయడానికి మెను నొక్కండి."</string>
     <string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"అన్‌లాక్ చేయడానికి నమూనాను గీయండి"</string>
-    <string name="lockscreen_emergency_call" msgid="7500692654885445299">"అత్యవసరం"</string>
+    <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+    <skip />
     <string name="lockscreen_return_to_call" msgid="3156883574692006382">"కాల్‌కు తిరిగి వెళ్లు"</string>
     <string name="lockscreen_pattern_correct" msgid="8050630103651508582">"సరైనది!"</string>
     <string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"మళ్లీ ప్రయత్నించండి"</string>
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index 6773014..3deb9b2 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -832,7 +832,8 @@
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"กด เมนู เพื่อปลดล็อกหรือโทรฉุกเฉิน"</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"กด เมนู เพื่อปลดล็อก"</string>
     <string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"วาดรูปแบบเพื่อปลดล็อก"</string>
-    <string name="lockscreen_emergency_call" msgid="7500692654885445299">"เหตุฉุกเฉิน"</string>
+    <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+    <skip />
     <string name="lockscreen_return_to_call" msgid="3156883574692006382">"กลับสู่การโทร"</string>
     <string name="lockscreen_pattern_correct" msgid="8050630103651508582">"ถูกต้อง!"</string>
     <string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"ลองอีกครั้ง"</string>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index da3e437..ccda1c4 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -832,7 +832,8 @@
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Pindutin ang Menu upang i-unlock o magsagawa ng pang-emergency na tawag."</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Pindutin ang Menu upang i-unlock."</string>
     <string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Iguhit ang pattern upang i-unlock"</string>
-    <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Emergency"</string>
+    <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+    <skip />
     <string name="lockscreen_return_to_call" msgid="3156883574692006382">"Bumalik sa tawag"</string>
     <string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Tama!"</string>
     <string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Subukang muli"</string>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index bf17f6c..85c6986 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -832,7 +832,8 @@
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Kilidi açmak veya acil çağrı yapmak için Menü\'ye basın."</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Kilidi açmak için Menü\'ye basın."</string>
     <string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Kilit açmak için deseni çizin"</string>
-    <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Acil durum çağrısı"</string>
+    <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+    <skip />
     <string name="lockscreen_return_to_call" msgid="3156883574692006382">"Çağrıya dön"</string>
     <string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Doğru!"</string>
     <string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Tekrar deneyin"</string>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index 9464efa..8ae20a5 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -838,7 +838,8 @@
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Натис. меню, щоб розбл. чи зробити авар. виклик."</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Натисн. меню, щоб розбл."</string>
     <string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Намал. ключ, щоб розбл."</string>
-    <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Екстрений виклик"</string>
+    <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+    <skip />
     <string name="lockscreen_return_to_call" msgid="3156883574692006382">"Поверн. до дзвін."</string>
     <string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Правильно!"</string>
     <string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Повторіть спробу"</string>
@@ -1165,7 +1166,7 @@
     <string name="capital_off" msgid="7443704171014626777">"ВИМК"</string>
     <string name="checked" msgid="9179896827054513119">"вибрано"</string>
     <string name="not_checked" msgid="7972320087569023342">"не вибрано"</string>
-    <string name="whichApplication" msgid="5432266899591255759">"Завершити дію за доп."</string>
+    <string name="whichApplication" msgid="5432266899591255759">"Що використовувати?"</string>
     <string name="whichApplicationNamed" msgid="6969946041713975681">"Завершити дію за допомогою %1$s"</string>
     <string name="whichApplicationLabel" msgid="7852182961472531728">"Завершити дію"</string>
     <string name="whichViewApplication" msgid="5733194231473132945">"Відкрити за допомогою"</string>
diff --git a/core/res/res/values-ur/strings.xml b/core/res/res/values-ur/strings.xml
index 15a1fe7..94c6cd4 100644
--- a/core/res/res/values-ur/strings.xml
+++ b/core/res/res/values-ur/strings.xml
@@ -832,7 +832,8 @@
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"غیر مقفل کرنے کیلئے مینو دبائیں یا ہنگامی کال کریں۔"</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"غیر مقفل کرنے کیلئے مینو دبائیں۔"</string>
     <string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"غیر مقفل کرنے کیلئے پیٹرن کو ڈرا کریں"</string>
-    <string name="lockscreen_emergency_call" msgid="7500692654885445299">"ہنگامی"</string>
+    <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+    <skip />
     <string name="lockscreen_return_to_call" msgid="3156883574692006382">"کال پر واپس جائیں"</string>
     <string name="lockscreen_pattern_correct" msgid="8050630103651508582">"صحیح!"</string>
     <string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"دوبارہ کوشش کریں"</string>
diff --git a/core/res/res/values-uz/strings.xml b/core/res/res/values-uz/strings.xml
index 76d2c60..74676df 100644
--- a/core/res/res/values-uz/strings.xml
+++ b/core/res/res/values-uz/strings.xml
@@ -832,7 +832,8 @@
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Qulfdan chiqarish yoki favqulodda qo‘ng‘iroqni amalga oshirish uchun \"Menyu\"ni bosing."</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Qulfni ochish uchun \"Menyu\"ga bosing."</string>
     <string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Qulfni ochish uchun grafik kalitni chizing"</string>
-    <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Favqulodda chaqiruv"</string>
+    <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+    <skip />
     <string name="lockscreen_return_to_call" msgid="3156883574692006382">"Qo‘ng‘iroqni qaytarish"</string>
     <string name="lockscreen_pattern_correct" msgid="8050630103651508582">"To‘g‘ri!"</string>
     <string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Qaytadan urining"</string>
@@ -1125,7 +1126,7 @@
     <string name="capital_off" msgid="7443704171014626777">"O"</string>
     <string name="checked" msgid="9179896827054513119">"belgilandi"</string>
     <string name="not_checked" msgid="7972320087569023342">"belgilanmadi"</string>
-    <string name="whichApplication" msgid="5432266899591255759">"Ilovani tanlang"</string>
+    <string name="whichApplication" msgid="5432266899591255759">"Nima ishlatilsin?"</string>
     <string name="whichApplicationNamed" msgid="6969946041713975681">"“%1$s” bilan ochish"</string>
     <string name="whichApplicationLabel" msgid="7852182961472531728">"Amalni bajarish"</string>
     <string name="whichViewApplication" msgid="5733194231473132945">"Ochish…"</string>
@@ -1548,7 +1549,7 @@
     <string name="launchBrowserDefault" msgid="6328349989932924119">"Brauzer ishga tushirilsinmi?"</string>
     <string name="SetupCallDefault" msgid="5581740063237175247">"Qo‘ng‘iroqni qabul qilasizmi?"</string>
     <string name="activity_resolver_use_always" msgid="5575222334666843269">"Har doim"</string>
-    <string name="activity_resolver_use_once" msgid="948462794469672658">"Faqat hozir"</string>
+    <string name="activity_resolver_use_once" msgid="948462794469672658">"Faqat shu safar"</string>
     <string name="activity_resolver_work_profiles_support" msgid="4071345609235361269">"“%1$s” ishchi profilni qo‘llab-quvvatlamaydi"</string>
     <string name="default_audio_route_name" product="tablet" msgid="367936735632195517">"Planshet"</string>
     <string name="default_audio_route_name" product="tv" msgid="4908971385068087367">"TV"</string>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index 9f44841..48f3858 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -832,7 +832,8 @@
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Nhấn vào Menu để mở khóa hoặc thực hiện cuộc gọi khẩn cấp."</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Nhấn vào Menu để mở khóa."</string>
     <string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Vẽ hình để mở khóa"</string>
-    <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Khẩn cấp"</string>
+    <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+    <skip />
     <string name="lockscreen_return_to_call" msgid="3156883574692006382">"Quay lại cuộc gọi"</string>
     <string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Chính xác!"</string>
     <string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Thử lại"</string>
@@ -1125,8 +1126,8 @@
     <string name="capital_off" msgid="7443704171014626777">"TẮT"</string>
     <string name="checked" msgid="9179896827054513119">"đã chọn"</string>
     <string name="not_checked" msgid="7972320087569023342">"chưa chọn"</string>
-    <string name="whichApplication" msgid="5432266899591255759">"Hoàn tất tác vụ đang sử dụng"</string>
-    <string name="whichApplicationNamed" msgid="6969946041713975681">"Hoàn tất tác vụ bằng %1$s"</string>
+    <string name="whichApplication" msgid="5432266899591255759">"Hoàn tất thao tác bằng"</string>
+    <string name="whichApplicationNamed" msgid="6969946041713975681">"Hoàn tất thao tác bằng %1$s"</string>
     <string name="whichApplicationLabel" msgid="7852182961472531728">"Hoàn thành tác vụ"</string>
     <string name="whichViewApplication" msgid="5733194231473132945">"Mở bằng"</string>
     <string name="whichViewApplicationNamed" msgid="415164730629690105">"Mở bằng %1$s"</string>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index 32ac0cf..bf381ec 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -832,7 +832,8 @@
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"按 Menu 解锁或进行紧急呼救。"</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"按 MENU 解锁。"</string>
     <string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"绘制解锁图案"</string>
-    <string name="lockscreen_emergency_call" msgid="7500692654885445299">"紧急呼救"</string>
+    <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+    <skip />
     <string name="lockscreen_return_to_call" msgid="3156883574692006382">"返回通话"</string>
     <string name="lockscreen_pattern_correct" msgid="8050630103651508582">"正确!"</string>
     <string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"重试"</string>
diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml
index 82b079f..a3b20d1 100644
--- a/core/res/res/values-zh-rHK/strings.xml
+++ b/core/res/res/values-zh-rHK/strings.xml
@@ -832,7 +832,8 @@
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"按選單鍵解鎖或撥打緊急電話。"</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"按選單鍵解鎖。"</string>
     <string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"畫出解鎖圖形以解除鎖定螢幕"</string>
-    <string name="lockscreen_emergency_call" msgid="7500692654885445299">"緊急電話"</string>
+    <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+    <skip />
     <string name="lockscreen_return_to_call" msgid="3156883574692006382">"返回通話"</string>
     <string name="lockscreen_pattern_correct" msgid="8050630103651508582">"正確!"</string>
     <string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"再試一次"</string>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index 02c0200..f6543c9 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -832,7 +832,8 @@
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"按下 [Menu] 解鎖或撥打緊急電話。"</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"按下 Menu 鍵解鎖。"</string>
     <string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"畫出解鎖圖案"</string>
-    <string name="lockscreen_emergency_call" msgid="7500692654885445299">"緊急撥號"</string>
+    <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+    <skip />
     <string name="lockscreen_return_to_call" msgid="3156883574692006382">"返回通話"</string>
     <string name="lockscreen_pattern_correct" msgid="8050630103651508582">"正確!"</string>
     <string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"再試一次"</string>
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index 7fe333d..85f54e9 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -832,7 +832,8 @@
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Chofoza Menyu ukuvula noma ukwenza ikholi ephuthumayo."</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Chofoza Menyu ukuvula."</string>
     <string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Dweba iphathini ukuvula"</string>
-    <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Isimo esiphuthumayo"</string>
+    <!-- no translation found for lockscreen_emergency_call (7549683825868928636) -->
+    <skip />
     <string name="lockscreen_return_to_call" msgid="3156883574692006382">"Buyela ekholini"</string>
     <string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Lungile!"</string>
     <string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Zama futhi"</string>
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index eb30c9b..ac08d96 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -2173,6 +2173,29 @@
         <attr name="required" />
     </declare-styleable>
 
+    <!-- The <code>uses-native-library</code> specifies a native shared library that this
+         package requires to be linked against.  Specifying this flag tells the
+         system to make the native library to be available to your app.
+
+         <p>On devices running R or lower, this is ignored and the app has access to all
+         the public native shared libraries that are exported from the platform. This is
+         also ignored if the app is targeting R or lower.
+
+         <p>This appears as a child tag of the
+         {@link #AndroidManifestApplication application} tag. -->
+    <declare-styleable name="AndroidManifestUsesNativeLibrary" parent="AndroidManifestApplication">
+        <!-- Required name of the library you use. -->
+        <attr name="name" />
+        <!--  Specify whether this native library is required for the application.
+              The default is true, meaning the application requires the
+              library, and does not want to be installed on devices that
+              don't support it. If you set this to false, then this will
+              allow the application to be installed even if the library
+              doesn't exist, and you will need to check for its presence
+              dynamically at runtime. -->
+        <attr name="required" />
+    </declare-styleable>
+
     <!-- The <code>uses-static-library</code> specifies a shared <strong>static</strong>
          library that this package requires to be statically linked against. Specifying
          this tag tells the system to include this library's code in your class loader.
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 28a164f..bc4c099 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2286,7 +2286,7 @@
     </integer-array>
 
     <!-- Set to true to add links to Cell Broadcast app from Settings and MMS app. -->
-    <bool name="config_cellBroadcastAppLinks">false</bool>
+    <bool name="config_cellBroadcastAppLinks">true</bool>
 
     <!-- The default value if the SyncStorageEngine should sync automatically or not -->
     <bool name="config_syncstorageengine_masterSyncAutomatically">true</bool>
diff --git a/core/tests/coretests/src/android/graphics/PathTest.java b/core/tests/coretests/src/android/graphics/PathTest.java
index c6d6d1f..b50792c 100644
--- a/core/tests/coretests/src/android/graphics/PathTest.java
+++ b/core/tests/coretests/src/android/graphics/PathTest.java
@@ -28,7 +28,9 @@
 
         final Path.FillType defaultFillType = path.getFillType();
         final Path.FillType fillType = Path.FillType.INVERSE_EVEN_ODD;
-        assertFalse(fillType.equals(defaultFillType));  // Sanity check for the test itself.
+
+        // This test is only meaningful if it changes from the default.
+        assertFalse(fillType.equals(defaultFillType));
 
         path.setFillType(fillType);
         path.reset();
diff --git a/data/etc/com.android.systemui.xml b/data/etc/com.android.systemui.xml
index a5a2221..ada8b00 100644
--- a/data/etc/com.android.systemui.xml
+++ b/data/etc/com.android.systemui.xml
@@ -39,6 +39,7 @@
         <permission name="android.permission.MODIFY_PHONE_STATE"/>
         <permission name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
         <permission name="android.permission.OBSERVE_NETWORK_POLICY"/>
+        <permission name="android.permission.OBSERVE_GRANT_REVOKE_PERMISSIONS" />
         <permission name="android.permission.OVERRIDE_WIFI_CONFIG"/>
         <permission name="android.permission.PACKAGE_USAGE_STATS" />
         <permission name="android.permission.READ_DREAM_STATE"/>
diff --git a/data/etc/platform.xml b/data/etc/platform.xml
index e1f6b2a..dd8f40d 100644
--- a/data/etc/platform.xml
+++ b/data/etc/platform.xml
@@ -263,10 +263,4 @@
          be able to connect to the internet when such a proxy is in use, since
          all outgoing connections originate from this app. -->
     <allow-in-power-save-except-idle package="com.android.proxyhandler" />
-
-    <!-- These are the packages that are white-listed to be able to run as system user -->
-    <system-user-whitelisted-app package="com.android.settings" />
-
-    <!-- These are the packages that shouldn't run as system user -->
-    <system-user-blacklisted-app package="com.android.wallpaper.livepicker" />
 </permissions>
diff --git a/data/keyboards/Generic.kl b/data/keyboards/Generic.kl
index d92dbb2..4cdfbb8 100644
--- a/data/keyboards/Generic.kl
+++ b/data/keyboards/Generic.kl
@@ -412,6 +412,7 @@
 key 583   ASSIST
 
 # Keys defined by HID usages
+key usage 0x0c0067 WINDOW
 key usage 0x0c006F BRIGHTNESS_UP
 key usage 0x0c0070 BRIGHTNESS_DOWN
 
diff --git a/data/keyboards/Vendor_2e95_Product_7725.kl b/data/keyboards/Vendor_2e95_Product_7725.kl
new file mode 100644
index 0000000..7672e22
--- /dev/null
+++ b/data/keyboards/Vendor_2e95_Product_7725.kl
@@ -0,0 +1,64 @@
+# Copyright (C) 2020 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# Scuf Vantage Controller
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+# Square
+key 0x130   BUTTON_X
+# Cross
+key 0x131   BUTTON_A
+# Circle
+key 0x132   BUTTON_B
+# Triangle
+key 0x133   BUTTON_Y
+
+key 0x134   BUTTON_L1
+key 0x135   BUTTON_R1
+key 0x136   BUTTON_L2
+key 0x137   BUTTON_R2
+
+# L2 Trigger axis
+axis 0x03   LTRIGGER
+# R2 Trigger axis
+axis 0x04   RTRIGGER
+
+# Left Analog Stick
+axis 0x00   X
+axis 0x01   Y
+# Right Analog Stick
+axis 0x02   Z
+axis 0x05   RZ
+
+# Left stick click
+key 0x13a   BUTTON_THUMBL
+# Right stick click
+key 0x13b   BUTTON_THUMBR
+
+# Hat
+axis 0x10   HAT_X
+axis 0x11   HAT_Y
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Share
+key 0x138   BUTTON_SELECT
+# Options
+key 0x139   BUTTON_START
+# PS key
+key 0x13c   BUTTON_MODE
+# Touchpad press
+key 0x13d   BUTTON_1
diff --git a/graphics/java/android/graphics/Region.java b/graphics/java/android/graphics/Region.java
index d8d9641..43373ff 100644
--- a/graphics/java/android/graphics/Region.java
+++ b/graphics/java/android/graphics/Region.java
@@ -409,10 +409,10 @@
         mNativeRegion = ni;
     }
 
-    /* add dummy parameter so constructor can be called from jni without
+    /* Add an unused parameter so constructor can be called from jni without
        triggering 'not cloneable' exception */
     @UnsupportedAppUsage
-    private Region(long ni, int dummy) {
+    private Region(long ni, int unused) {
         this(ni);
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java
index cb64796..338ece5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java
@@ -30,6 +30,7 @@
 import android.util.Slog;
 import android.util.SparseArray;
 import android.view.IDisplayWindowInsetsController;
+import android.view.IWindowManager;
 import android.view.InsetsSource;
 import android.view.InsetsSourceControl;
 import android.view.InsetsState;
@@ -60,20 +61,20 @@
     private static final int DIRECTION_HIDE = 2;
     private static final int FLOATING_IME_BOTTOM_INSET = -80;
 
-    protected final SystemWindows mSystemWindows;
+    protected final IWindowManager mWmService;
     protected final Handler mHandler;
-    final TransactionPool mTransactionPool;
+    private final TransactionPool mTransactionPool;
+    private final DisplayController mDisplayController;
+    private final SparseArray<PerDisplay> mImePerDisplay = new SparseArray<>();
+    private final ArrayList<ImePositionProcessor> mPositionProcessors = new ArrayList<>();
 
-    final SparseArray<PerDisplay> mImePerDisplay = new SparseArray<>();
-
-    final ArrayList<ImePositionProcessor> mPositionProcessors = new ArrayList<>();
-
-    public DisplayImeController(SystemWindows syswin, DisplayController displayController,
-            Handler handler, TransactionPool transactionPool) {
-        mHandler = handler;
-        mSystemWindows = syswin;
+    public DisplayImeController(IWindowManager wmService, DisplayController displayController,
+            Handler mainHandler, TransactionPool transactionPool) {
+        mHandler = mainHandler;
+        mWmService = wmService;
         mTransactionPool = transactionPool;
-        displayController.addDisplayWindowListener(this);
+        mDisplayController = displayController;
+        mDisplayController.addDisplayWindowListener(this);
     }
 
     @Override
@@ -81,9 +82,9 @@
         // Add's a system-ui window-manager specifically for ime. This type is special because
         // WM will defer IME inset handling to it in multi-window scenarious.
         PerDisplay pd = new PerDisplay(displayId,
-                mSystemWindows.mDisplayController.getDisplayLayout(displayId).rotation());
+                mDisplayController.getDisplayLayout(displayId).rotation());
         try {
-            mSystemWindows.mWmService.setDisplayWindowInsetsController(displayId, pd);
+            mWmService.setDisplayWindowInsetsController(displayId, pd);
         } catch (RemoteException e) {
             Slog.w(TAG, "Unable to set insets controller on display " + displayId);
         }
@@ -96,7 +97,7 @@
         if (pd == null) {
             return;
         }
-        if (mSystemWindows.mDisplayController.getDisplayLayout(displayId).rotation()
+        if (mDisplayController.getDisplayLayout(displayId).rotation()
                 != pd.mRotation && isImeShowing(displayId)) {
             pd.startAnimation(true, false /* forceRestart */);
         }
@@ -105,7 +106,7 @@
     @Override
     public void onDisplayRemoved(int displayId) {
         try {
-            mSystemWindows.mWmService.setDisplayWindowInsetsController(displayId, null);
+            mWmService.setDisplayWindowInsetsController(displayId, null);
         } catch (RemoteException e) {
             Slog.w(TAG, "Unable to remove insets controller on display " + displayId);
         }
@@ -263,7 +264,7 @@
         private void setVisibleDirectly(boolean visible) {
             mInsetsState.getSource(InsetsState.ITYPE_IME).setVisible(visible);
             try {
-                mSystemWindows.mWmService.modifyDisplayWindowInsets(mDisplayId, mInsetsState);
+                mWmService.modifyDisplayWindowInsets(mDisplayId, mInsetsState);
             } catch (RemoteException e) {
             }
         }
@@ -282,7 +283,7 @@
             // an IME inset). For now, we assume that no non-floating IME will be <= this nav bar
             // frame height so any reported frame that is <= nav-bar frame height is assumed to
             // be floating.
-            return frame.height() <= mSystemWindows.mDisplayController.getDisplayLayout(mDisplayId)
+            return frame.height() <= mDisplayController.getDisplayLayout(mDisplayId)
                     .navBarFrameHeight();
         }
 
@@ -297,8 +298,7 @@
                 // This is a "floating" or "expanded" IME, so to get animations, just
                 // pretend the ime has some size just below the screen.
                 mImeFrame.set(newFrame);
-                final int floatingInset = (int) (
-                        mSystemWindows.mDisplayController.getDisplayLayout(mDisplayId)
+                final int floatingInset = (int) (mDisplayController.getDisplayLayout(mDisplayId)
                         .density() * FLOATING_IME_BOTTOM_INSET);
                 mImeFrame.bottom -= floatingInset;
             } else if (newFrame.height() != 0) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java
index 34018e8..8abe9ee 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java
@@ -60,11 +60,10 @@
     private static final String TAG = "SystemWindows";
 
     private final SparseArray<PerDisplay> mPerDisplay = new SparseArray<>();
-    final HashMap<View, SurfaceControlViewHost> mViewRoots = new HashMap<>();
-    public Context mContext;
-    public IWindowManager mWmService;
-    IWindowSession mSession;
-    DisplayController mDisplayController;
+    private final HashMap<View, SurfaceControlViewHost> mViewRoots = new HashMap<>();
+    private final DisplayController mDisplayController;
+    private final IWindowManager mWmService;
+    private IWindowSession mSession;
 
     private final DisplayController.OnDisplaysChangedListener mDisplayListener =
             new DisplayController.OnDisplaysChangedListener() {
@@ -84,9 +83,7 @@
                 public void onDisplayRemoved(int displayId) { }
             };
 
-    public SystemWindows(Context context, DisplayController displayController,
-            IWindowManager wmService) {
-        mContext = context;
+    public SystemWindows(DisplayController displayController, IWindowManager wmService) {
         mWmService = wmService;
         mDisplayController = displayController;
         mDisplayController.addDisplayWindowListener(mDisplayListener);
@@ -210,8 +207,8 @@
             }
             final Display display = mDisplayController.getDisplay(mDisplayId);
             SurfaceControlViewHost viewRoot =
-                    new SurfaceControlViewHost(mContext, display, wwm,
-                            true /* useSfChoreographer */);
+                    new SurfaceControlViewHost(
+                            view.getContext(), display, wwm, true /* useSfChoreographer */);
             attrs.flags |= FLAG_HARDWARE_ACCELERATED;
             viewRoot.setView(view, attrs);
             mViewRoots.put(view, viewRoot);
@@ -313,7 +310,7 @@
         }
     }
 
-    class ContainerWindow extends IWindow.Stub {
+    static class ContainerWindow extends IWindow.Stub {
         ContainerWindow() {}
 
         @Override
diff --git a/libs/hwui/JankTracker.cpp b/libs/hwui/JankTracker.cpp
index d25fc4b..b2c39c9 100644
--- a/libs/hwui/JankTracker.cpp
+++ b/libs/hwui/JankTracker.cpp
@@ -139,6 +139,9 @@
         (*mGlobalData)->reportJank();
     }
 
+    if (mSwapDeadline < 0) {
+        mSwapDeadline = frame[FrameInfoIndex::IntendedVsync] + mFrameInterval;
+    }
     bool isTripleBuffered = (mSwapDeadline - frame[FrameInfoIndex::IntendedVsync]) > (mFrameInterval * 0.1);
 
     mSwapDeadline = std::max(mSwapDeadline + mFrameInterval,
diff --git a/libs/hwui/JankTracker.h b/libs/hwui/JankTracker.h
index 4460266..b3fbbfe 100644
--- a/libs/hwui/JankTracker.h
+++ b/libs/hwui/JankTracker.h
@@ -75,7 +75,7 @@
 
     std::array<int64_t, NUM_BUCKETS> mThresholds;
     int64_t mFrameInterval;
-    nsecs_t mSwapDeadline;
+    nsecs_t mSwapDeadline = -1;
     // The amount of time we will erase from the total duration to account
     // for SF vsync offsets with HWC2 blocking dequeueBuffers.
     // (Vsync + mDequeueBlockTolerance) is the point at which we expect
diff --git a/libs/hwui/hwui/Bitmap.cpp b/libs/hwui/hwui/Bitmap.cpp
index c0a2443..1a89cfd 100644
--- a/libs/hwui/hwui/Bitmap.cpp
+++ b/libs/hwui/hwui/Bitmap.cpp
@@ -209,11 +209,8 @@
 void Bitmap::reconfigure(const SkImageInfo& newInfo, size_t rowBytes) {
     mInfo = validateAlpha(newInfo);
 
-    // Dirty hack is dirty
-    // TODO: Figure something out here, Skia's current design makes this
-    // really hard to work with. Skia really, really wants immutable objects,
-    // but with the nested-ref-count hackery going on that's just not
-    // feasible without going insane trying to figure it out
+    // TODO: Skia intends for SkPixelRef to be immutable, but this method
+    // modifies it. Find another way to support reusing the same pixel memory.
     this->android_only_reset(mInfo.width(), mInfo.height(), rowBytes);
 }
 
diff --git a/location/java/android/location/GpsStatus.java b/location/java/android/location/GpsStatus.java
index 496885c..997339e 100644
--- a/location/java/android/location/GpsStatus.java
+++ b/location/java/android/location/GpsStatus.java
@@ -151,6 +151,16 @@
         return status;
     }
 
+    /**
+     * Builds an empty GpsStatus. Should only be used for legacy reasons.
+     *
+     * @hide
+     */
+    @NonNull
+    static GpsStatus createEmpty() {
+        return new GpsStatus();
+    }
+
     private GpsStatus() {
     }
 
diff --git a/location/java/android/location/ILocationListener.aidl b/location/java/android/location/ILocationListener.aidl
index 6e7f6a5..29b483a 100644
--- a/location/java/android/location/ILocationListener.aidl
+++ b/location/java/android/location/ILocationListener.aidl
@@ -24,6 +24,6 @@
  */
 oneway interface ILocationListener
 {
-    void onLocationChanged(in Location location, in IRemoteCallback onCompleteCallback);
+    void onLocationChanged(in Location location, in @nullable IRemoteCallback onCompleteCallback);
     void onProviderEnabledChanged(String provider, boolean enabled);
 }
diff --git a/location/java/android/location/ILocationManager.aidl b/location/java/android/location/ILocationManager.aidl
index bb8f81d..0e7eaa2 100644
--- a/location/java/android/location/ILocationManager.aidl
+++ b/location/java/android/location/ILocationManager.aidl
@@ -113,7 +113,7 @@
     List<LocationRequest> getTestProviderCurrentRequests(String provider);
     LocationTime getGnssTimeMillis();
 
-    boolean sendExtraCommand(String provider, String command, inout Bundle extras);
+    void sendExtraCommand(String provider, String command, inout Bundle extras);
 
     // used by gts tests to verify whitelists
     String[] getBackgroundThrottlingWhitelist();
diff --git a/location/java/android/location/LocationListener.java b/location/java/android/location/LocationListener.java
index 8df0834..2738ff4f 100644
--- a/location/java/android/location/LocationListener.java
+++ b/location/java/android/location/LocationListener.java
@@ -36,7 +36,9 @@
 public interface LocationListener {
 
     /**
-     * Called when the location has changed.
+     * Called when the location has changed. A wakelock is held on behalf on the listener for some
+     * brief amount of time as this callback executes. If this callback performs long running
+     * operations, it is the client's responsibility to obtain their own wakelock.
      *
      * @param location the updated location
      */
@@ -52,18 +54,17 @@
     default void onStatusChanged(String provider, int status, Bundle extras) {}
 
     /**
-     * Called when the provider is enabled by the user.
+     * Called when a provider this listener is registered with becomes enabled.
      *
-     * @param provider the name of the location provider that has become enabled
+     * @param provider the name of the location provider
      */
     default void onProviderEnabled(@NonNull String provider) {}
 
     /**
-     * Called when the provider is disabled by the user. If requestLocationUpdates
-     * is called on an already disabled provider, this method is called
-     * immediately.
+     * Called when the provider this listener is registered with becomes disabled. If a provider is
+     * disabled when this listener is registered, this callback will be invoked immediately.
      *
-     * @param provider the name of the location provider that has become disabled
+     * @param provider the name of the location provider
      */
     default void onProviderDisabled(@NonNull String provider) {}
 }
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index c0b8e1b..1803027 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -303,7 +303,6 @@
     public static final String METADATA_SETTINGS_FOOTER_STRING =
             "com.android.settings.location.FOOTER_STRING";
 
-
     private static final long MAX_SINGLE_LOCATION_TIMEOUT_MS = 30 * 1000;
 
     @GuardedBy("sLocationListeners")
@@ -311,7 +310,9 @@
             sLocationListeners = new WeakHashMap<>();
 
     final Context mContext;
-    @UnsupportedAppUsage
+
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, publicAlternatives = "{@link "
+            + "LocationManager}")
     final ILocationManager mService;
 
     private final Object mLock = new Object();
@@ -421,8 +422,7 @@
         try {
             return mService.getExtraLocationControllerPackage();
         } catch (RemoteException e) {
-            e.rethrowFromSystemServer();
-            return null;
+            throw e.rethrowFromSystemServer();
         }
     }
 
@@ -437,7 +437,7 @@
         try {
             mService.setExtraLocationControllerPackage(packageName);
         } catch (RemoteException e) {
-            e.rethrowFromSystemServer();
+            throw e.rethrowFromSystemServer();
         }
     }
 
@@ -452,7 +452,7 @@
         try {
             mService.setExtraLocationControllerPackageEnabled(enabled);
         } catch (RemoteException e) {
-            e.rethrowFromSystemServer();
+            throw e.rethrowFromSystemServer();
         }
     }
 
@@ -466,8 +466,7 @@
         try {
             return mService.isExtraLocationControllerPackageEnabled();
         } catch (RemoteException e) {
-            e.rethrowFromSystemServer();
-            return false;
+            throw e.rethrowFromSystemServer();
         }
     }
 
@@ -485,7 +484,7 @@
         try {
             mService.setExtraLocationControllerPackage(packageName);
         } catch (RemoteException e) {
-            e.rethrowFromSystemServer();
+            throw e.rethrowFromSystemServer();
         }
     }
 
@@ -503,7 +502,7 @@
         try {
             mService.setExtraLocationControllerPackageEnabled(enabled);
         } catch (RemoteException e) {
-            e.rethrowFromSystemServer();
+            throw e.rethrowFromSystemServer();
         }
     }
 
@@ -1199,7 +1198,7 @@
                         mContext.getPackageName(), mContext.getAttributionTag(),
                         AppOpsManager.toReceiverId(listener));
             } catch (RemoteException e) {
-                e.rethrowFromSystemServer();
+                throw e.rethrowFromSystemServer();
             }
         }
     }
@@ -1301,7 +1300,7 @@
                     // unregistration is complete.
                     mService.unregisterLocationListener(transport);
                 } catch (RemoteException e) {
-                    e.rethrowFromSystemServer();
+                    throw e.rethrowFromSystemServer();
                 }
             }
         }
@@ -1517,7 +1516,8 @@
         Preconditions.checkArgument(command != null, "invalid null command");
 
         try {
-            return mService.sendExtraCommand(provider, command, extras);
+            mService.sendExtraCommand(provider, command, extras);
+            return true;
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -1835,6 +1835,10 @@
             } else {
                 status.setStatus(gnssStatus, ttff);
             }
+        } else if (status == null) {
+            // even though this method is marked as nullable, legacy behavior was to never return
+            // a null result, and there are applications that rely on this behavior.
+            status = GpsStatus.createEmpty();
         }
         return status;
     }
@@ -2424,7 +2428,7 @@
                 try {
                     cancellationSignal.cancel();
                 } catch (RemoteException e) {
-                    e.rethrowFromSystemServer();
+                    throw e.rethrowFromSystemServer();
                 }
             }
         }
@@ -2464,7 +2468,8 @@
         }
 
         @Override
-        public void onLocationChanged(Location location, IRemoteCallback onCompleteCallback) {
+        public void onLocationChanged(Location location,
+                @Nullable IRemoteCallback onCompleteCallback) {
             executeSafely(mExecutor, () -> mListener, new ListenerOperation<LocationListener>() {
                 @Override
                 public void operate(LocationListener listener) {
@@ -2473,7 +2478,13 @@
 
                 @Override
                 public void onComplete(boolean success) {
-                    markComplete(onCompleteCallback);
+                    if (onCompleteCallback != null) {
+                        try {
+                            onCompleteCallback.sendResult(null);
+                        } catch (RemoteException e) {
+                            throw e.rethrowFromSystemServer();
+                        }
+                    }
                 }
             });
         }
@@ -2488,14 +2499,6 @@
                 }
             });
         }
-
-        private void markComplete(IRemoteCallback onCompleteCallback) {
-            try {
-                onCompleteCallback.sendResult(null);
-            } catch (RemoteException e) {
-                e.rethrowFromSystemServer();
-            }
-        }
     }
 
     private static class NmeaAdapter extends GnssStatus.Callback implements OnNmeaMessageListener {
diff --git a/media/java/android/media/AudioDeviceInfo.java b/media/java/android/media/AudioDeviceInfo.java
index 00a4c7e..6e3fb19 100644
--- a/media/java/android/media/AudioDeviceInfo.java
+++ b/media/java/android/media/AudioDeviceInfo.java
@@ -18,7 +18,6 @@
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
-import android.annotation.SystemApi;
 import android.util.SparseIntArray;
 
 import java.lang.annotation.Retention;
@@ -137,13 +136,15 @@
      */
     public static final int TYPE_BUILTIN_SPEAKER_SAFE = 24;
     /**
-     * @hide
      * A device type for rerouting audio within the Android framework between mixes and
-     * system applications. Typically created when using
-     * {@link android.media.audiopolicy.AudioPolicy} for mixes created with the
-     * {@link android.media.audiopolicy.AudioMix#ROUTE_FLAG_RENDER} flag.
+     * system applications.
+     * This type is for instance encountered when querying the output device of a track
+     * (with {@link AudioTrack#getRoutedDevice()} playing from a device in screen mirroring mode,
+     * where the audio is not heard on the device, but on the remote device.
      */
-    @SystemApi
+    // Typically created when using
+    // {@link android.media.audiopolicy.AudioPolicy} for mixes created with the
+    // {@link android.media.audiopolicy.AudioMix#ROUTE_FLAG_LOOP_BACK} flag.
     public static final int TYPE_REMOTE_SUBMIX = 25;
 
     /** @hide */
diff --git a/media/java/android/media/ExifInterface.java b/media/java/android/media/ExifInterface.java
index 6d690f0..4a6724a 100644
--- a/media/java/android/media/ExifInterface.java
+++ b/media/java/android/media/ExifInterface.java
@@ -1411,9 +1411,9 @@
     private static final int IMAGE_TYPE_WEBP = 14;
 
     static {
-        sFormatter = new SimpleDateFormat("yyyy:MM:dd HH:mm:ss");
+        sFormatter = new SimpleDateFormat("yyyy:MM:dd HH:mm:ss", Locale.US);
         sFormatter.setTimeZone(TimeZone.getTimeZone("UTC"));
-        sFormatterTz = new SimpleDateFormat("yyyy:MM:dd HH:mm:ss XXX");
+        sFormatterTz = new SimpleDateFormat("yyyy:MM:dd HH:mm:ss XXX", Locale.US);
         sFormatterTz.setTimeZone(TimeZone.getTimeZone("UTC"));
 
         // Build up the hash tables to look up Exif tags for reading Exif tags.
diff --git a/media/java/android/media/browse/MediaBrowser.java b/media/java/android/media/browse/MediaBrowser.java
index 3c2be5f..029e614 100644
--- a/media/java/android/media/browse/MediaBrowser.java
+++ b/media/java/android/media/browse/MediaBrowser.java
@@ -25,7 +25,6 @@
 import android.content.ServiceConnection;
 import android.content.pm.ParceledListSlice;
 import android.media.MediaDescription;
-import android.media.session.MediaController;
 import android.media.session.MediaSession;
 import android.os.Binder;
 import android.os.Bundle;
@@ -757,8 +756,8 @@
          * Flag: Indicates that the item is playable.
          * <p>
          * The id of this item may be passed to
-         * {@link MediaController.TransportControls#playFromMediaId(String, Bundle)}
-         * to start playing it.
+         * {@link android.media.session.MediaController.TransportControls
+         * #playFromMediaId(String, Bundle)} to start playing it.
          * </p>
          */
         public static final int FLAG_PLAYABLE = 1 << 1;
@@ -1107,13 +1106,7 @@
         }
 
         @Override
-        public void onLoadChildren(String parentId, ParceledListSlice list) {
-            onLoadChildrenWithOptions(parentId, list, null);
-        }
-
-        @Override
-        public void onLoadChildrenWithOptions(String parentId, ParceledListSlice list,
-                final Bundle options) {
+        public void onLoadChildren(String parentId, ParceledListSlice list, Bundle options) {
             MediaBrowser mediaBrowser = mMediaBrowser.get();
             if (mediaBrowser != null) {
                 mediaBrowser.onLoadChildren(this, parentId, list, options);
diff --git a/media/java/android/service/media/IMediaBrowserServiceCallbacks.aidl b/media/java/android/service/media/IMediaBrowserServiceCallbacks.aidl
index 7e3f2f8..a877207 100644
--- a/media/java/android/service/media/IMediaBrowserServiceCallbacks.aidl
+++ b/media/java/android/service/media/IMediaBrowserServiceCallbacks.aidl
@@ -23,7 +23,5 @@
     void onConnect(String root, in MediaSession.Token session, in Bundle extras);
     @UnsupportedAppUsage
     void onConnectFailed();
-    void onLoadChildren(String mediaId, in ParceledListSlice list);
-    void onLoadChildrenWithOptions(String mediaId, in ParceledListSlice list,
-            in Bundle options);
+    void onLoadChildren(String mediaId, in ParceledListSlice list, in Bundle options);
 }
diff --git a/media/java/android/service/media/MediaBrowserService.java b/media/java/android/service/media/MediaBrowserService.java
index 06adf30..39c7682 100644
--- a/media/java/android/service/media/MediaBrowserService.java
+++ b/media/java/android/service/media/MediaBrowserService.java
@@ -687,7 +687,7 @@
                 final ParceledListSlice<MediaBrowser.MediaItem> pls =
                         filteredList == null ? null : new ParceledListSlice<>(filteredList);
                 try {
-                    connection.callbacks.onLoadChildrenWithOptions(parentId, pls, options);
+                    connection.callbacks.onLoadChildren(parentId, pls, options);
                 } catch (RemoteException ex) {
                     // The other side is in the process of crashing.
                     Log.w(TAG, "Calling onLoadChildren() failed for id=" + parentId
diff --git a/media/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp
index 6e26f2c..3cd4081 100644
--- a/media/jni/android_media_tv_Tuner.cpp
+++ b/media/jni/android_media_tv_Tuner.cpp
@@ -337,33 +337,44 @@
     }
     mIonHandle = new C2HandleIon(dup(mAvHandle->data[0]), mDataLength);
     std::shared_ptr<C2LinearBlock> block = _C2BlockFactory::CreateLinearBlock(mIonHandle);
-
-    JNIEnv *env = AndroidRuntime::getJNIEnv();
-    std::unique_ptr<JMediaCodecLinearBlock> context{new JMediaCodecLinearBlock};
-    context->mBlock = block;
-    std::shared_ptr<C2Buffer> pC2Buffer = context->toC2Buffer(0, mDataLength);
-    context->mBuffer = pC2Buffer;
-    mC2Buffer = pC2Buffer;
-    if (mAvHandle->numInts > 0) {
-        // use first int in the native_handle as the index
-        int index = mAvHandle->data[mAvHandle->numFds];
-        std::shared_ptr<C2Param> c2param = std::make_shared<C2DataIdInfo>(index, mDataId);
-        std::shared_ptr<C2Info> info(std::static_pointer_cast<C2Info>(c2param));
-        pC2Buffer->setInfo(info);
+    if (block != nullptr) {
+        // CreateLinearBlock delete mIonHandle after it create block successfully.
+        // ToDo: coordinate who is response to delete mIonHandle
+        mIonHandle = NULL;
+        JNIEnv *env = AndroidRuntime::getJNIEnv();
+        std::unique_ptr<JMediaCodecLinearBlock> context{new JMediaCodecLinearBlock};
+        context->mBlock = block;
+        std::shared_ptr<C2Buffer> pC2Buffer = context->toC2Buffer(0, mDataLength);
+        context->mBuffer = pC2Buffer;
+        mC2Buffer = pC2Buffer;
+        if (mAvHandle->numInts > 0) {
+            // use first int in the native_handle as the index
+            int index = mAvHandle->data[mAvHandle->numFds];
+            std::shared_ptr<C2Param> c2param = std::make_shared<C2DataIdInfo>(index, mDataId);
+            std::shared_ptr<C2Info> info(std::static_pointer_cast<C2Info>(c2param));
+            pC2Buffer->setInfo(info);
+        }
+        pC2Buffer->registerOnDestroyNotify(&DestroyCallback, this);
+        jobject linearBlock =
+                env->NewObject(
+                        env->FindClass("android/media/MediaCodec$LinearBlock"),
+                        gFields.linearBlockInitID);
+        env->CallVoidMethod(
+                linearBlock,
+                gFields.linearBlockSetInternalStateID,
+                (jlong)context.release(),
+                true);
+        mLinearBlockObj = env->NewWeakGlobalRef(linearBlock);
+        mAvHandleRefCnt++;
+        return mLinearBlockObj;
+    } else {
+        native_handle_close(const_cast<native_handle_t*>(
+                    reinterpret_cast<const native_handle_t*>(mIonHandle)));
+        native_handle_delete(const_cast<native_handle_t*>(
+                    reinterpret_cast<const native_handle_t*>(mIonHandle)));
+        mIonHandle = NULL;
+        return NULL;
     }
-    pC2Buffer->registerOnDestroyNotify(&DestroyCallback, this);
-    jobject linearBlock =
-            env->NewObject(
-                    env->FindClass("android/media/MediaCodec$LinearBlock"),
-                    gFields.linearBlockInitID);
-    env->CallVoidMethod(
-            linearBlock,
-            gFields.linearBlockSetInternalStateID,
-            (jlong)context.release(),
-            true);
-    mLinearBlockObj = env->NewWeakGlobalRef(linearBlock);
-    mAvHandleRefCnt++;
-    return mLinearBlockObj;
 }
 
 uint64_t MediaEvent::getAudioHandle() {
diff --git a/non-updatable-api/current.txt b/non-updatable-api/current.txt
index 0dc7631..2b95992 100644
--- a/non-updatable-api/current.txt
+++ b/non-updatable-api/current.txt
@@ -24061,6 +24061,7 @@
     field public static final int TYPE_IP = 20; // 0x14
     field public static final int TYPE_LINE_ANALOG = 5; // 0x5
     field public static final int TYPE_LINE_DIGITAL = 6; // 0x6
+    field public static final int TYPE_REMOTE_SUBMIX = 25; // 0x19
     field public static final int TYPE_TELEPHONY = 18; // 0x12
     field public static final int TYPE_TV_TUNER = 17; // 0x11
     field public static final int TYPE_UNKNOWN = 0; // 0x0
diff --git a/non-updatable-api/module-lib-current.txt b/non-updatable-api/module-lib-current.txt
index a5ca196..c1dbfb7 100644
--- a/non-updatable-api/module-lib-current.txt
+++ b/non-updatable-api/module-lib-current.txt
@@ -5,6 +5,10 @@
     field public static final String OPSTR_NO_ISOLATED_STORAGE = "android:no_isolated_storage";
   }
 
+  public class NotificationManager {
+    method public boolean hasEnabledNotificationListener(@NonNull String, @NonNull android.os.UserHandle);
+  }
+
 }
 
 package android.content.rollback {
@@ -29,6 +33,10 @@
 
 package android.os {
 
+  public class Binder implements android.os.IBinder {
+    method public final void markVintfStability();
+  }
+
   public class StatsServiceManager {
     method @NonNull public android.os.StatsServiceManager.ServiceRegisterer getStatsCompanionServiceRegisterer();
     method @NonNull public android.os.StatsServiceManager.ServiceRegisterer getStatsManagerServiceRegisterer();
diff --git a/non-updatable-api/removed.txt b/non-updatable-api/removed.txt
index ba05a1b..f2dfb84 100644
--- a/non-updatable-api/removed.txt
+++ b/non-updatable-api/removed.txt
@@ -1,10 +1,6 @@
 // Signature format: 2.0
 package android.app {
 
-  public class ActivityManager {
-    method @Deprecated public static int getMaxNumPictureInPictureActions();
-  }
-
   public class Notification implements android.os.Parcelable {
     method @Deprecated public String getChannel();
     method public static Class<? extends android.app.Notification.Style> getNotificationStyleClass(String);
diff --git a/non-updatable-api/system-current.txt b/non-updatable-api/system-current.txt
index b41ab93..222e563 100644
--- a/non-updatable-api/system-current.txt
+++ b/non-updatable-api/system-current.txt
@@ -4112,10 +4112,6 @@
     field public static final int ROLE_OUTPUT = 2; // 0x2
   }
 
-  public final class AudioDeviceInfo {
-    field public static final int TYPE_REMOTE_SUBMIX = 25; // 0x19
-  }
-
   public final class AudioFocusInfo implements android.os.Parcelable {
     method public int describeContents();
     method @NonNull public android.media.AudioAttributes getAttributes();
diff --git a/packages/CarSystemUI/res/layout/car_user_switching_dialog.xml b/packages/CarSystemUI/res/layout/car_user_switching_dialog.xml
index 0a29424..09fbf7a 100644
--- a/packages/CarSystemUI/res/layout/car_user_switching_dialog.xml
+++ b/packages/CarSystemUI/res/layout/car_user_switching_dialog.xml
@@ -15,7 +15,6 @@
   ~ limitations under the License.
   -->
 <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:fitsSystemWindows="true"
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
     android:gravity="center"
diff --git a/packages/CarSystemUI/res/layout/super_notification_shade.xml b/packages/CarSystemUI/res/layout/super_notification_shade.xml
deleted file mode 100644
index db71c91..0000000
--- a/packages/CarSystemUI/res/layout/super_notification_shade.xml
+++ /dev/null
@@ -1,80 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-**
-** Copyright 2020, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-**     http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-
-<!-- This is the notification shade window. -->
-<com.android.systemui.statusbar.phone.NotificationShadeWindowView
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:sysui="http://schemas.android.com/apk/res-auto"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    android:fitsSystemWindows="true">
-
-    <com.android.systemui.statusbar.BackDropView
-        android:id="@+id/backdrop"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:visibility="gone"
-        sysui:ignoreRightInset="true"
-    >
-        <ImageView android:id="@+id/backdrop_back"
-            android:layout_width="match_parent"
-            android:layout_height="match_parent"
-            android:scaleType="centerCrop"/>
-        <ImageView android:id="@+id/backdrop_front"
-            android:layout_width="match_parent"
-            android:layout_height="match_parent"
-            android:scaleType="centerCrop"
-            android:visibility="invisible"/>
-    </com.android.systemui.statusbar.BackDropView>
-
-    <com.android.systemui.statusbar.ScrimView
-        android:id="@+id/scrim_behind"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:importantForAccessibility="no"
-        sysui:ignoreRightInset="true"
-    />
-
-    <include layout="@layout/brightness_mirror"/>
-
-    <ViewStub android:id="@+id/fullscreen_user_switcher_stub"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:layout="@layout/car_fullscreen_user_switcher"/>
-
-    <include layout="@layout/notification_center_activity"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:layout_marginBottom="@dimen/navigation_bar_height"
-        android:visibility="invisible"/>
-
-    <include layout="@layout/status_bar_expanded"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:visibility="invisible"/>
-
-    <com.android.systemui.statusbar.ScrimView
-        android:id="@+id/scrim_in_front"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:importantForAccessibility="no"
-        sysui:ignoreRightInset="true"
-    />
-
-</com.android.systemui.statusbar.phone.NotificationShadeWindowView>
diff --git a/packages/CarSystemUI/res/layout/super_status_bar.xml b/packages/CarSystemUI/res/layout/super_status_bar.xml
deleted file mode 100644
index d93f62f..0000000
--- a/packages/CarSystemUI/res/layout/super_status_bar.xml
+++ /dev/null
@@ -1,46 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-**
-** Copyright 2020, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-**     http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-
-<!-- This is the status bar window. -->
-<com.android.systemui.statusbar.phone.StatusBarWindowView
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:sysui="http://schemas.android.com/apk/res-auto"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    android:fitsSystemWindows="true">
-
-    <LinearLayout
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:orientation="vertical"
-    >
-        <FrameLayout
-            android:id="@+id/status_bar_container"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:visibility="gone"
-        />
-
-        <FrameLayout
-            android:id="@+id/car_top_navigation_bar_container"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"/>
-    </LinearLayout>
-
-</com.android.systemui.statusbar.phone.StatusBarWindowView>
diff --git a/packages/CarSystemUI/res/layout/sysui_overlay_window.xml b/packages/CarSystemUI/res/layout/sysui_overlay_window.xml
index 2dc499c..2c97889 100644
--- a/packages/CarSystemUI/res/layout/sysui_overlay_window.xml
+++ b/packages/CarSystemUI/res/layout/sysui_overlay_window.xml
@@ -22,12 +22,10 @@
     android:layout_width="match_parent"
     android:layout_height="match_parent">
 
-    <!-- TODO(b/151617493): replace marginBottom with insets. -->
     <ViewStub android:id="@+id/notification_panel_stub"
               android:layout_width="match_parent"
               android:layout_height="match_parent"
-              android:layout="@layout/notification_panel_container"
-              android:layout_marginBottom="@dimen/navigation_bar_height"/>
+              android:layout="@layout/notification_panel_container"/>
 
     <ViewStub android:id="@+id/keyguard_stub"
               android:layout_width="match_parent"
diff --git a/packages/CarSystemUI/res/values/dimens.xml b/packages/CarSystemUI/res/values/dimens.xml
index cb321cd..8359dac 100644
--- a/packages/CarSystemUI/res/values/dimens.xml
+++ b/packages/CarSystemUI/res/values/dimens.xml
@@ -81,6 +81,21 @@
     <dimen name="car_keyline_2">96dp</dimen>
     <dimen name="car_keyline_3">128dp</dimen>
 
+    <!-- Height of icons in Ongoing App Ops dialog. Both App Op icon and application icon -->
+    <dimen name="ongoing_appops_dialog_icon_height">48dp</dimen>
+    <!-- Margin between text lines in Ongoing App Ops dialog -->
+    <dimen name="ongoing_appops_dialog_text_margin">15dp</dimen>
+    <!-- Padding around Ongoing App Ops dialog content -->
+    <dimen name="ongoing_appops_dialog_content_padding">24dp</dimen>
+    <!-- Margins around the Ongoing App Ops chip. In landscape, the side margins are 0 -->
+    <dimen name="ongoing_appops_chip_margin">12dp</dimen>
+    <!-- Start and End padding for Ongoing App Ops chip -->
+    <dimen name="ongoing_appops_chip_side_padding">6dp</dimen>
+    <!-- Padding between background of Ongoing App Ops chip and content -->
+    <dimen name="ongoing_appops_chip_bg_padding">4dp</dimen>
+    <!-- Radius of Ongoing App Ops chip corners -->
+    <dimen name="ongoing_appops_chip_bg_corner_radius">12dp</dimen>
+
     <!-- Car volume dimens. -->
     <dimen name="car_volume_item_icon_size">@dimen/car_primary_icon_size</dimen>
     <dimen name="car_volume_item_height">@*android:dimen/car_single_line_list_item_height</dimen>
diff --git a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIBinder.java b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIBinder.java
index ffdf378..797a178 100644
--- a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIBinder.java
+++ b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIBinder.java
@@ -21,8 +21,6 @@
 import com.android.systemui.car.navigationbar.CarNavigationBar;
 import com.android.systemui.car.notification.CarNotificationModule;
 import com.android.systemui.car.sideloaded.SideLoadedAppController;
-import com.android.systemui.car.statusbar.CarStatusBar;
-import com.android.systemui.car.statusbar.CarStatusBarModule;
 import com.android.systemui.car.voicerecognition.ConnectedDeviceVoiceRecognitionNotifier;
 import com.android.systemui.car.volume.VolumeUI;
 import com.android.systemui.car.window.OverlayWindowModule;
@@ -37,10 +35,10 @@
 import com.android.systemui.recents.RecentsModule;
 import com.android.systemui.shortcut.ShortcutKeyDispatcher;
 import com.android.systemui.stackdivider.Divider;
+import com.android.systemui.statusbar.dagger.StatusBarModule;
 import com.android.systemui.statusbar.notification.InstantAppNotifier;
 import com.android.systemui.statusbar.notification.dagger.NotificationsModule;
 import com.android.systemui.statusbar.phone.StatusBar;
-import com.android.systemui.statusbar.tv.TvStatusBar;
 import com.android.systemui.theme.ThemeOverlayController;
 import com.android.systemui.toast.ToastUI;
 import com.android.systemui.util.leak.GarbageMonitor;
@@ -51,7 +49,7 @@
 import dagger.multibindings.IntoMap;
 
 /** Binder for car specific {@link SystemUI} modules. */
-@Module(includes = {RecentsModule.class, CarStatusBarModule.class, NotificationsModule.class,
+@Module(includes = {RecentsModule.class, StatusBarModule.class, NotificationsModule.class,
         BubbleModule.class, KeyguardModule.class, OverlayWindowModule.class,
         CarNotificationModule.class})
 public abstract class CarSystemUIBinder {
@@ -162,19 +160,7 @@
     @Binds
     @IntoMap
     @ClassKey(StatusBar.class)
-    public abstract SystemUI bindsStatusBar(CarStatusBar sysui);
-
-    /** Inject into TvStatusBar. */
-    @Binds
-    @IntoMap
-    @ClassKey(TvStatusBar.class)
-    public abstract SystemUI bindsTvStatusBar(TvStatusBar sysui);
-
-    /** Inject into StatusBarGoogle. */
-    @Binds
-    @IntoMap
-    @ClassKey(CarStatusBar.class)
-    public abstract SystemUI bindsCarStatusBar(CarStatusBar sysui);
+    public abstract SystemUI bindsStatusBar(StatusBar sysui);
 
     /** Inject into VolumeUI. */
     @Binds
diff --git a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIFactory.java b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIFactory.java
index 1a1b93b..03ea941 100644
--- a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIFactory.java
+++ b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIFactory.java
@@ -32,7 +32,7 @@
     @Override
     protected SystemUIRootComponent buildSystemUIRootComponent(Context context) {
         return DaggerCarSystemUIRootComponent.builder()
-                .contextHolder(new ContextHolder(context))
+                .context(context)
                 .build();
     }
 
diff --git a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java
index e5fbbd8..7b6dceb 100644
--- a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java
+++ b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java
@@ -29,8 +29,6 @@
 import com.android.systemui.car.CarDeviceProvisionedController;
 import com.android.systemui.car.CarDeviceProvisionedControllerImpl;
 import com.android.systemui.car.keyguard.CarKeyguardViewController;
-import com.android.systemui.car.statusbar.CarStatusBar;
-import com.android.systemui.car.statusbar.CarStatusBarKeyguardViewManager;
 import com.android.systemui.car.statusbar.DozeServiceHost;
 import com.android.systemui.car.statusbar.DummyNotificationShadeWindowController;
 import com.android.systemui.car.volume.CarVolumeDialogComponent;
@@ -60,8 +58,6 @@
 import com.android.systemui.statusbar.phone.NotificationShadeWindowController;
 import com.android.systemui.statusbar.phone.ShadeController;
 import com.android.systemui.statusbar.phone.ShadeControllerImpl;
-import com.android.systemui.statusbar.phone.StatusBar;
-import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
 import com.android.systemui.statusbar.policy.BatteryController;
 import com.android.systemui.statusbar.policy.BatteryControllerImpl;
 import com.android.systemui.statusbar.policy.ConfigurationController;
@@ -132,9 +128,9 @@
 
     @Singleton
     @Provides
-    static SystemWindows provideSystemWindows(Context context, DisplayController displayController,
+    static SystemWindows provideSystemWindows(DisplayController displayController,
             IWindowManager wmService) {
-        return new SystemWindows(context, displayController, wmService);
+        return new SystemWindows(displayController, wmService);
     }
 
     @Singleton
@@ -183,17 +179,10 @@
             CarSystemUIRootComponent systemUIRootComponent);
 
     @Binds
-    public abstract StatusBar bindStatusBar(CarStatusBar statusBar);
-
-    @Binds
     abstract VolumeDialogComponent bindVolumeDialogComponent(
             CarVolumeDialogComponent carVolumeDialogComponent);
 
     @Binds
-    abstract StatusBarKeyguardViewManager bindStatusBarKeyguardViewManager(
-            CarStatusBarKeyguardViewManager keyguardViewManager);
-
-    @Binds
     abstract KeyguardViewController bindKeyguardViewController(
             CarKeyguardViewController carKeyguardViewController);
 
diff --git a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIRootComponent.java b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIRootComponent.java
index 088420e..ece3bee 100644
--- a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIRootComponent.java
+++ b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIRootComponent.java
@@ -36,12 +36,17 @@
                 DependencyBinder.class,
                 PipModule.class,
                 OneHandedModule.class,
-                SystemUIFactory.ContextHolder.class,
                 SystemServicesModule.class,
                 SystemUIModule.class,
                 CarSystemUIModule.class,
                 CarSystemUIBinder.class
         })
 public interface CarSystemUIRootComponent extends SystemUIRootComponent {
-
+    /**
+     * Builder for a CarSystemUIRootComponent.
+     */
+    @Component.Builder
+    interface Builder extends SystemUIRootComponent.Builder {
+        CarSystemUIRootComponent build();
+    }
 }
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/keyguard/CarKeyguardViewController.java b/packages/CarSystemUI/src/com/android/systemui/car/keyguard/CarKeyguardViewController.java
index 69766cc..51a7245 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/keyguard/CarKeyguardViewController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/keyguard/CarKeyguardViewController.java
@@ -141,6 +141,11 @@
     }
 
     @Override
+    protected boolean shouldShowNavigationBar() {
+        return true;
+    }
+
+    @Override
     public void onFinishInflate() {
         mBouncer = SystemUIFactory.getInstance().createKeyguardBouncer(mContext,
                 mViewMediatorCallback, mLockPatternUtils,
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewController.java b/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewController.java
index 1eead62..8d58436 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewController.java
@@ -16,6 +16,8 @@
 
 package com.android.systemui.car.notification;
 
+import static android.view.WindowInsets.Type.navigationBars;
+
 import android.app.ActivityManager;
 import android.car.Car;
 import android.car.drivingstate.CarUxRestrictionsManager;
@@ -197,6 +199,16 @@
     }
 
     @Override
+    protected boolean shouldShowStatusBar() {
+        return true;
+    }
+
+    @Override
+    protected int getInsetTypesToFit() {
+        return navigationBars();
+    }
+
+    @Override
     protected boolean shouldShowHUN() {
         return mEnableHeadsUpNotificationWhenNotificationShadeOpen;
     }
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/statusbar/CarStatusBar.java b/packages/CarSystemUI/src/com/android/systemui/car/statusbar/CarStatusBar.java
deleted file mode 100644
index d692487..0000000
--- a/packages/CarSystemUI/src/com/android/systemui/car/statusbar/CarStatusBar.java
+++ /dev/null
@@ -1,519 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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.car.statusbar;
-
-import static com.android.systemui.Dependency.TIME_TICK_HANDLER_NAME;
-
-import android.annotation.Nullable;
-import android.content.Context;
-import android.graphics.drawable.Drawable;
-import android.os.Handler;
-import android.os.PowerManager;
-import android.util.DisplayMetrics;
-import android.util.Log;
-import android.view.View;
-
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.statusbar.RegisterStatusBarResult;
-import com.android.keyguard.KeyguardUpdateMonitor;
-import com.android.keyguard.ViewMediatorCallback;
-import com.android.systemui.BatteryMeterView;
-import com.android.systemui.Dependency;
-import com.android.systemui.InitController;
-import com.android.systemui.Prefs;
-import com.android.systemui.R;
-import com.android.systemui.assist.AssistManager;
-import com.android.systemui.broadcast.BroadcastDispatcher;
-import com.android.systemui.bubbles.BubbleController;
-import com.android.systemui.car.CarDeviceProvisionedController;
-import com.android.systemui.car.CarDeviceProvisionedListener;
-import com.android.systemui.car.bluetooth.CarBatteryController;
-import com.android.systemui.car.navigationbar.CarNavigationBarController;
-import com.android.systemui.classifier.FalsingLog;
-import com.android.systemui.colorextraction.SysuiColorExtractor;
-import com.android.systemui.dagger.qualifiers.UiBackground;
-import com.android.systemui.fragments.FragmentHostManager;
-import com.android.systemui.keyguard.DismissCallbackRegistry;
-import com.android.systemui.keyguard.KeyguardViewMediator;
-import com.android.systemui.keyguard.ScreenLifecycle;
-import com.android.systemui.keyguard.WakefulnessLifecycle;
-import com.android.systemui.plugins.DarkIconDispatcher;
-import com.android.systemui.plugins.FalsingManager;
-import com.android.systemui.plugins.PluginDependencyProvider;
-import com.android.systemui.plugins.qs.QS;
-import com.android.systemui.recents.Recents;
-import com.android.systemui.recents.ScreenPinningRequest;
-import com.android.systemui.shared.plugins.PluginManager;
-import com.android.systemui.stackdivider.Divider;
-import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.KeyguardIndicationController;
-import com.android.systemui.statusbar.NavigationBarController;
-import com.android.systemui.statusbar.NotificationLockscreenUserManager;
-import com.android.systemui.statusbar.NotificationMediaManager;
-import com.android.systemui.statusbar.NotificationRemoteInputManager;
-import com.android.systemui.statusbar.NotificationShadeDepthController;
-import com.android.systemui.statusbar.NotificationViewHierarchyManager;
-import com.android.systemui.statusbar.PulseExpansionHandler;
-import com.android.systemui.statusbar.SuperStatusBarViewFactory;
-import com.android.systemui.statusbar.SysuiStatusBarStateController;
-import com.android.systemui.statusbar.VibratorHelper;
-import com.android.systemui.statusbar.notification.DynamicPrivacyController;
-import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
-import com.android.systemui.statusbar.notification.VisualStabilityManager;
-import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.init.NotificationsController;
-import com.android.systemui.statusbar.notification.interruption.BypassHeadsUpNotifier;
-import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
-import com.android.systemui.statusbar.notification.interruption.NotificationInterruptSuppressor;
-import com.android.systemui.statusbar.notification.logging.NotificationLogger;
-import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
-import com.android.systemui.statusbar.phone.AutoHideController;
-import com.android.systemui.statusbar.phone.BiometricUnlockController;
-import com.android.systemui.statusbar.phone.CollapsedStatusBarFragment;
-import com.android.systemui.statusbar.phone.DozeParameters;
-import com.android.systemui.statusbar.phone.DozeScrimController;
-import com.android.systemui.statusbar.phone.DozeServiceHost;
-import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
-import com.android.systemui.statusbar.phone.KeyguardBypassController;
-import com.android.systemui.statusbar.phone.KeyguardDismissUtil;
-import com.android.systemui.statusbar.phone.LightBarController;
-import com.android.systemui.statusbar.phone.LightsOutNotifController;
-import com.android.systemui.statusbar.phone.LockscreenLockIconController;
-import com.android.systemui.statusbar.phone.LockscreenWallpaper;
-import com.android.systemui.statusbar.phone.NotificationGroupManager;
-import com.android.systemui.statusbar.phone.NotificationShadeWindowController;
-import com.android.systemui.statusbar.phone.PhoneStatusBarPolicy;
-import com.android.systemui.statusbar.phone.ScrimController;
-import com.android.systemui.statusbar.phone.ShadeController;
-import com.android.systemui.statusbar.phone.StatusBar;
-import com.android.systemui.statusbar.phone.StatusBarIconController;
-import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
-import com.android.systemui.statusbar.phone.StatusBarNotificationActivityStarter;
-import com.android.systemui.statusbar.phone.StatusBarTouchableRegionManager;
-import com.android.systemui.statusbar.phone.dagger.StatusBarComponent;
-import com.android.systemui.statusbar.policy.BatteryController;
-import com.android.systemui.statusbar.policy.ConfigurationController;
-import com.android.systemui.statusbar.policy.ExtensionController;
-import com.android.systemui.statusbar.policy.KeyguardStateController;
-import com.android.systemui.statusbar.policy.NetworkController;
-import com.android.systemui.statusbar.policy.RemoteInputQuickSettingsDisabler;
-import com.android.systemui.statusbar.policy.UserInfoControllerImpl;
-import com.android.systemui.statusbar.policy.UserSwitcherController;
-import com.android.systemui.volume.VolumeComponent;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.util.Map;
-import java.util.Optional;
-import java.util.concurrent.Executor;
-
-import javax.inject.Named;
-import javax.inject.Provider;
-
-import dagger.Lazy;
-
-/**
- * A status bar tailored for the automotive use case.
- */
-public class CarStatusBar extends StatusBar implements CarBatteryController.BatteryViewHandler {
-    private static final String TAG = "CarStatusBar";
-
-    private final UserSwitcherController mUserSwitcherController;
-    private final ScrimController mScrimController;
-
-    private CarBatteryController mCarBatteryController;
-    private BatteryMeterView mBatteryMeterView;
-    private Drawable mNotificationPanelBackground;
-
-    private final Object mQueueLock = new Object();
-    private final CarNavigationBarController mCarNavigationBarController;
-    private final CarDeviceProvisionedController mCarDeviceProvisionedController;
-    private final ScreenLifecycle mScreenLifecycle;
-
-    private boolean mDeviceIsSetUpForUser = true;
-    private boolean mIsUserSetupInProgress = false;
-
-    public CarStatusBar(
-            Context context,
-            NotificationsController notificationsController,
-            LightBarController lightBarController,
-            AutoHideController autoHideController,
-            KeyguardUpdateMonitor keyguardUpdateMonitor,
-            StatusBarIconController statusBarIconController,
-            PulseExpansionHandler pulseExpansionHandler,
-            NotificationWakeUpCoordinator notificationWakeUpCoordinator,
-            KeyguardBypassController keyguardBypassController,
-            KeyguardStateController keyguardStateController,
-            HeadsUpManagerPhone headsUpManagerPhone,
-            DynamicPrivacyController dynamicPrivacyController,
-            BypassHeadsUpNotifier bypassHeadsUpNotifier,
-            FalsingManager falsingManager,
-            BroadcastDispatcher broadcastDispatcher,
-            RemoteInputQuickSettingsDisabler remoteInputQuickSettingsDisabler,
-            NotificationGutsManager notificationGutsManager,
-            NotificationLogger notificationLogger,
-            NotificationInterruptStateProvider notificationInterruptStateProvider,
-            NotificationViewHierarchyManager notificationViewHierarchyManager,
-            KeyguardViewMediator keyguardViewMediator,
-            DisplayMetrics displayMetrics,
-            MetricsLogger metricsLogger,
-            @UiBackground Executor uiBgExecutor,
-            NotificationMediaManager notificationMediaManager,
-            NotificationLockscreenUserManager lockScreenUserManager,
-            NotificationRemoteInputManager remoteInputManager,
-            UserSwitcherController userSwitcherController,
-            NetworkController networkController,
-            BatteryController batteryController,
-            SysuiColorExtractor colorExtractor,
-            ScreenLifecycle screenLifecycle,
-            WakefulnessLifecycle wakefulnessLifecycle,
-            SysuiStatusBarStateController statusBarStateController,
-            VibratorHelper vibratorHelper,
-            BubbleController bubbleController,
-            NotificationGroupManager groupManager,
-            VisualStabilityManager visualStabilityManager,
-            CarDeviceProvisionedController carDeviceProvisionedController,
-            NavigationBarController navigationBarController,
-            Lazy<AssistManager> assistManagerLazy,
-            ConfigurationController configurationController,
-            NotificationShadeWindowController notificationShadeWindowController,
-            LockscreenLockIconController lockscreenLockIconController,
-            DozeParameters dozeParameters,
-            ScrimController scrimController,
-            Lazy<LockscreenWallpaper> lockscreenWallpaperLazy,
-            Lazy<BiometricUnlockController> biometricUnlockControllerLazy,
-            DozeServiceHost dozeServiceHost,
-            PowerManager powerManager,
-            ScreenPinningRequest screenPinningRequest,
-            DozeScrimController dozeScrimController,
-            VolumeComponent volumeComponent,
-            CommandQueue commandQueue,
-            Optional<Recents> recents,
-            Provider<StatusBarComponent.Builder> statusBarComponentBuilder,
-            PluginManager pluginManager,
-            Optional<Divider> dividerOptional,
-            SuperStatusBarViewFactory superStatusBarViewFactory,
-            LightsOutNotifController lightsOutNotifController,
-            StatusBarNotificationActivityStarter.Builder
-                    statusBarNotificationActivityStarterBuilder,
-            ShadeController shadeController,
-            StatusBarKeyguardViewManager statusBarKeyguardViewManager,
-            ViewMediatorCallback viewMediatorCallback,
-            InitController initController,
-            DarkIconDispatcher darkIconDispatcher,
-            @Named(TIME_TICK_HANDLER_NAME) Handler timeTickHandler,
-            PluginDependencyProvider pluginDependencyProvider,
-            KeyguardDismissUtil keyguardDismissUtil,
-            ExtensionController extensionController,
-            UserInfoControllerImpl userInfoControllerImpl,
-            PhoneStatusBarPolicy phoneStatusBarPolicy,
-            KeyguardIndicationController keyguardIndicationController,
-            DismissCallbackRegistry dismissCallbackRegistry,
-            StatusBarTouchableRegionManager statusBarTouchableRegionManager,
-            Lazy<NotificationShadeDepthController> depthControllerLazy,
-            /* Car Settings injected components. */
-            CarNavigationBarController carNavigationBarController) {
-        super(
-                context,
-                notificationsController,
-                lightBarController,
-                autoHideController,
-                keyguardUpdateMonitor,
-                statusBarIconController,
-                pulseExpansionHandler,
-                notificationWakeUpCoordinator,
-                keyguardBypassController,
-                keyguardStateController,
-                headsUpManagerPhone,
-                dynamicPrivacyController,
-                bypassHeadsUpNotifier,
-                falsingManager,
-                broadcastDispatcher,
-                remoteInputQuickSettingsDisabler,
-                notificationGutsManager,
-                notificationLogger,
-                notificationInterruptStateProvider,
-                notificationViewHierarchyManager,
-                keyguardViewMediator,
-                displayMetrics,
-                metricsLogger,
-                uiBgExecutor,
-                notificationMediaManager,
-                lockScreenUserManager,
-                remoteInputManager,
-                userSwitcherController,
-                networkController,
-                batteryController,
-                colorExtractor,
-                screenLifecycle,
-                wakefulnessLifecycle,
-                statusBarStateController,
-                vibratorHelper,
-                bubbleController,
-                groupManager,
-                visualStabilityManager,
-                carDeviceProvisionedController,
-                navigationBarController,
-                assistManagerLazy,
-                configurationController,
-                notificationShadeWindowController,
-                lockscreenLockIconController,
-                dozeParameters,
-                scrimController,
-                null /* keyguardLiftController */,
-                lockscreenWallpaperLazy,
-                biometricUnlockControllerLazy,
-                dozeServiceHost,
-                powerManager,
-                screenPinningRequest,
-                dozeScrimController,
-                volumeComponent,
-                commandQueue,
-                recents,
-                statusBarComponentBuilder,
-                pluginManager,
-                dividerOptional,
-                lightsOutNotifController,
-                statusBarNotificationActivityStarterBuilder,
-                shadeController,
-                superStatusBarViewFactory,
-                statusBarKeyguardViewManager,
-                viewMediatorCallback,
-                initController,
-                darkIconDispatcher,
-                timeTickHandler,
-                pluginDependencyProvider,
-                keyguardDismissUtil,
-                extensionController,
-                userInfoControllerImpl,
-                phoneStatusBarPolicy,
-                keyguardIndicationController,
-                dismissCallbackRegistry,
-                depthControllerLazy,
-                statusBarTouchableRegionManager);
-        mUserSwitcherController = userSwitcherController;
-        mScrimController = scrimController;
-        mCarDeviceProvisionedController = carDeviceProvisionedController;
-        mCarNavigationBarController = carNavigationBarController;
-        mScreenLifecycle = screenLifecycle;
-    }
-
-    @Override
-    public void start() {
-        mDeviceIsSetUpForUser = mCarDeviceProvisionedController.isCurrentUserSetup();
-        mIsUserSetupInProgress = mCarDeviceProvisionedController.isCurrentUserSetupInProgress();
-
-        super.start();
-
-        createBatteryController();
-        mCarBatteryController.startListening();
-
-        mCarDeviceProvisionedController.addCallback(
-                new CarDeviceProvisionedListener() {
-                    @Override
-                    public void onUserSetupInProgressChanged() {
-                        mDeviceIsSetUpForUser = mCarDeviceProvisionedController
-                                .isCurrentUserSetup();
-                        mIsUserSetupInProgress = mCarDeviceProvisionedController
-                                .isCurrentUserSetupInProgress();
-                    }
-
-                    @Override
-                    public void onUserSetupChanged() {
-                        mDeviceIsSetUpForUser = mCarDeviceProvisionedController
-                                .isCurrentUserSetup();
-                        mIsUserSetupInProgress = mCarDeviceProvisionedController
-                                .isCurrentUserSetupInProgress();
-                    }
-
-                    @Override
-                    public void onUserSwitched() {
-                        mDeviceIsSetUpForUser = mCarDeviceProvisionedController
-                                .isCurrentUserSetup();
-                        mIsUserSetupInProgress = mCarDeviceProvisionedController
-                                .isCurrentUserSetupInProgress();
-                    }
-                });
-
-        mNotificationInterruptStateProvider.addSuppressor(new NotificationInterruptSuppressor() {
-            @Override
-            public String getName() {
-                return TAG;
-            }
-
-            @Override
-            public boolean suppressInterruptions(NotificationEntry entry) {
-                // Because space is usually constrained in the auto use-case, there should not be a
-                // pinned notification when the shade has been expanded.
-                // Ensure this by not allowing any interruptions (ie: pinning any notifications) if
-                // the shade is already opened.
-                return !getPresenter().isPresenterFullyCollapsed();
-            }
-        });
-    }
-
-    @Override
-    public boolean hideKeyguard() {
-        boolean result = super.hideKeyguard();
-        mCarNavigationBarController.hideAllKeyguardButtons(isDeviceSetupForUser());
-        return result;
-    }
-
-    @Override
-    public void showKeyguard() {
-        super.showKeyguard();
-        mCarNavigationBarController.showAllKeyguardButtons(isDeviceSetupForUser());
-    }
-
-    private boolean isDeviceSetupForUser() {
-        return mDeviceIsSetUpForUser && !mIsUserSetupInProgress;
-    }
-
-    @Override
-    protected void makeStatusBarView(@Nullable RegisterStatusBarResult result) {
-        super.makeStatusBarView(result);
-
-        mNotificationPanelBackground = getDefaultWallpaper();
-        mScrimController.setScrimBehindDrawable(mNotificationPanelBackground);
-
-        FragmentHostManager manager = FragmentHostManager.get(mPhoneStatusBarWindow);
-        manager.addTagListener(CollapsedStatusBarFragment.TAG, (tag, fragment) -> {
-            mBatteryMeterView = fragment.getView().findViewById(R.id.battery);
-
-            // By default, the BatteryMeterView should not be visible. It will be toggled
-            // when a device has connected by bluetooth.
-            mBatteryMeterView.setVisibility(View.GONE);
-        });
-    }
-
-    @Override
-    public void animateExpandNotificationsPanel() {
-        // No op.
-    }
-
-    @Override
-    protected QS createDefaultQSFragment() {
-        return null;
-    }
-
-    private BatteryController createBatteryController() {
-        mCarBatteryController = new CarBatteryController(mContext);
-        mCarBatteryController.addBatteryViewHandler(this);
-        return mCarBatteryController;
-    }
-
-    @Override
-    protected void createNavigationBar(@Nullable RegisterStatusBarResult result) {
-        // No op.
-    }
-
-    @Override
-    public void notifyBiometricAuthModeChanged() {
-        // No op.
-    }
-
-    @Override
-    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
-        //When executing dump() function simultaneously, we need to serialize them
-        //to get mStackScroller's position correctly.
-        synchronized (mQueueLock) {
-            pw.println("  mStackScroller: " + viewInfo(mStackScroller));
-            pw.println("  mStackScroller: " + viewInfo(mStackScroller)
-                    + " scroll " + mStackScroller.getScrollX()
-                    + "," + mStackScroller.getScrollY());
-        }
-        pw.print("  mCarBatteryController=");
-        pw.println(mCarBatteryController);
-        pw.print("  mBatteryMeterView=");
-        pw.println(mBatteryMeterView);
-
-        if (Dependency.get(KeyguardUpdateMonitor.class) != null) {
-            Dependency.get(KeyguardUpdateMonitor.class).dump(fd, pw, args);
-        }
-
-        FalsingLog.dump(pw);
-
-        pw.println("SharedPreferences:");
-        for (Map.Entry<String, ?> entry : Prefs.getAll(mContext).entrySet()) {
-            pw.print("  ");
-            pw.print(entry.getKey());
-            pw.print("=");
-            pw.println(entry.getValue());
-        }
-    }
-
-    @Override
-    public void showBatteryView() {
-        if (Log.isLoggable(TAG, Log.DEBUG)) {
-            Log.d(TAG, "showBatteryView(). mBatteryMeterView: " + mBatteryMeterView);
-        }
-
-        if (mBatteryMeterView != null) {
-            mBatteryMeterView.setVisibility(View.VISIBLE);
-        }
-    }
-
-    @Override
-    public void hideBatteryView() {
-        if (Log.isLoggable(TAG, Log.DEBUG)) {
-            Log.d(TAG, "hideBatteryView(). mBatteryMeterView: " + mBatteryMeterView);
-        }
-
-        if (mBatteryMeterView != null) {
-            mBatteryMeterView.setVisibility(View.GONE);
-        }
-    }
-
-    @Override
-    protected void createUserSwitcher() {
-        if (!mUserSwitcherController.useFullscreenUserSwitcher()) {
-            super.createUserSwitcher();
-        }
-    }
-
-    /**
-     * Dismisses the keyguard and shows bouncer if authentication is necessary.
-     */
-    public void dismissKeyguard() {
-        // Don't dismiss keyguard when the screen is off.
-        if (mScreenLifecycle.getScreenState() == ScreenLifecycle.SCREEN_OFF) {
-            return;
-        }
-        executeRunnableDismissingKeyguard(null/* runnable */, null /* cancelAction */,
-                true /* dismissShade */, true /* afterKeyguardGone */, true /* deferred */);
-    }
-
-    /**
-     * Ensures that relevant child views are appropriately recreated when the device's density
-     * changes.
-     */
-    @Override
-    public void onDensityOrFontScaleChanged() {
-        super.onDensityOrFontScaleChanged();
-        // Need to update the background on density changed in case the change was due to night
-        // mode.
-        mNotificationPanelBackground = getDefaultWallpaper();
-        mScrimController.setScrimBehindDrawable(mNotificationPanelBackground);
-    }
-
-    /**
-     * Returns the {@link Drawable} that represents the wallpaper that the user has currently set.
-     */
-    private Drawable getDefaultWallpaper() {
-        return mContext.getDrawable(com.android.internal.R.drawable.default_wallpaper);
-    }
-}
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/statusbar/CarStatusBarKeyguardViewManager.java b/packages/CarSystemUI/src/com/android/systemui/car/statusbar/CarStatusBarKeyguardViewManager.java
deleted file mode 100644
index 96a998a..0000000
--- a/packages/CarSystemUI/src/com/android/systemui/car/statusbar/CarStatusBarKeyguardViewManager.java
+++ /dev/null
@@ -1,137 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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.car.statusbar;
-
-import android.content.Context;
-import android.view.View;
-
-import com.android.internal.widget.LockPatternUtils;
-import com.android.keyguard.KeyguardUpdateMonitor;
-import com.android.keyguard.ViewMediatorCallback;
-import com.android.systemui.R;
-import com.android.systemui.car.navigationbar.CarNavigationBarController;
-import com.android.systemui.dock.DockManager;
-import com.android.systemui.statusbar.NotificationMediaManager;
-import com.android.systemui.statusbar.SysuiStatusBarStateController;
-import com.android.systemui.statusbar.phone.NavigationModeController;
-import com.android.systemui.statusbar.phone.NotificationShadeWindowController;
-import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
-import com.android.systemui.statusbar.policy.ConfigurationController;
-import com.android.systemui.statusbar.policy.KeyguardStateController;
-
-import java.util.HashSet;
-import java.util.Set;
-
-import javax.inject.Inject;
-import javax.inject.Singleton;
-
-/** Car implementation of the {@link StatusBarKeyguardViewManager}. */
-@Singleton
-public class CarStatusBarKeyguardViewManager extends StatusBarKeyguardViewManager {
-
-    protected boolean mShouldHideNavBar;
-    private final CarNavigationBarController mCarNavigationBarController;
-    private Set<OnKeyguardCancelClickedListener> mKeygaurdCancelClickedListenerSet;
-
-    @Inject
-    public CarStatusBarKeyguardViewManager(Context context,
-            ViewMediatorCallback callback,
-            LockPatternUtils lockPatternUtils,
-            SysuiStatusBarStateController sysuiStatusBarStateController,
-            ConfigurationController configurationController,
-            KeyguardUpdateMonitor keyguardUpdateMonitor,
-            NavigationModeController navigationModeController,
-            DockManager dockManager,
-            NotificationShadeWindowController notificationShadeWindowController,
-            KeyguardStateController keyguardStateController,
-            NotificationMediaManager notificationMediaManager,
-            CarNavigationBarController carNavigationBarController) {
-        super(context, callback, lockPatternUtils, sysuiStatusBarStateController,
-                configurationController, keyguardUpdateMonitor, navigationModeController,
-                dockManager, notificationShadeWindowController, keyguardStateController,
-                notificationMediaManager);
-        mShouldHideNavBar = context.getResources()
-                .getBoolean(R.bool.config_hideNavWhenKeyguardBouncerShown);
-        mCarNavigationBarController = carNavigationBarController;
-        mKeygaurdCancelClickedListenerSet = new HashSet<>();
-    }
-
-    @Override
-    protected void updateNavigationBarVisibility(boolean navBarVisible) {
-        if (!mShouldHideNavBar) {
-            return;
-        }
-        int visibility = navBarVisible ? View.VISIBLE : View.GONE;
-        mCarNavigationBarController.setBottomWindowVisibility(visibility);
-        mCarNavigationBarController.setLeftWindowVisibility(visibility);
-        mCarNavigationBarController.setRightWindowVisibility(visibility);
-    }
-
-    /**
-     * Car is a multi-user system.  There's a cancel button on the bouncer that allows the user to
-     * go back to the user switcher and select another user.  Different user may have different
-     * security mode which requires bouncer container to be resized.  For this reason, the bouncer
-     * view is destroyed on cancel.
-     */
-    @Override
-    protected boolean shouldDestroyViewOnReset() {
-        return true;
-    }
-
-    /**
-     * Called when cancel button in bouncer is pressed.
-     */
-    @Override
-    public void onCancelClicked() {
-        mKeygaurdCancelClickedListenerSet.forEach(OnKeyguardCancelClickedListener::onCancelClicked);
-    }
-
-    /**
-     * Do nothing on this change.
-     * The base class hides the keyguard which for automotive we want to avoid b/c this would happen
-     * on a configuration change due to day/night (headlight state).
-     */
-    @Override
-    public void onDensityOrFontScaleChanged() {  }
-
-    /**
-     * Add listener for keyguard cancel clicked.
-     */
-    public void addOnKeyguardCancelClickedListener(
-            OnKeyguardCancelClickedListener keyguardCancelClickedListener) {
-        mKeygaurdCancelClickedListenerSet.add(keyguardCancelClickedListener);
-    }
-
-    /**
-     * Remove listener for keyguard cancel clicked.
-     */
-    public void removeOnKeyguardCancelClickedListener(
-            OnKeyguardCancelClickedListener keyguardCancelClickedListener) {
-        mKeygaurdCancelClickedListenerSet.remove(keyguardCancelClickedListener);
-    }
-
-
-    /**
-     * Defines a callback for keyguard cancel button clicked listeners.
-     */
-    public interface OnKeyguardCancelClickedListener {
-        /**
-         * Called when keyguard cancel button is clicked.
-         */
-        void onCancelClicked();
-    }
-}
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/statusbar/CarStatusBarModule.java b/packages/CarSystemUI/src/com/android/systemui/car/statusbar/CarStatusBarModule.java
deleted file mode 100644
index dc2eb04..0000000
--- a/packages/CarSystemUI/src/com/android/systemui/car/statusbar/CarStatusBarModule.java
+++ /dev/null
@@ -1,283 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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.car.statusbar;
-
-import static com.android.systemui.Dependency.TIME_TICK_HANDLER_NAME;
-
-import android.content.Context;
-import android.os.Handler;
-import android.os.PowerManager;
-import android.util.DisplayMetrics;
-
-import com.android.internal.logging.MetricsLogger;
-import com.android.keyguard.KeyguardUpdateMonitor;
-import com.android.keyguard.ViewMediatorCallback;
-import com.android.systemui.InitController;
-import com.android.systemui.assist.AssistManager;
-import com.android.systemui.broadcast.BroadcastDispatcher;
-import com.android.systemui.bubbles.BubbleController;
-import com.android.systemui.car.CarDeviceProvisionedController;
-import com.android.systemui.car.navigationbar.CarNavigationBarController;
-import com.android.systemui.colorextraction.SysuiColorExtractor;
-import com.android.systemui.dagger.qualifiers.UiBackground;
-import com.android.systemui.keyguard.DismissCallbackRegistry;
-import com.android.systemui.keyguard.KeyguardViewMediator;
-import com.android.systemui.keyguard.ScreenLifecycle;
-import com.android.systemui.keyguard.WakefulnessLifecycle;
-import com.android.systemui.plugins.DarkIconDispatcher;
-import com.android.systemui.plugins.FalsingManager;
-import com.android.systemui.plugins.PluginDependencyProvider;
-import com.android.systemui.recents.Recents;
-import com.android.systemui.recents.ScreenPinningRequest;
-import com.android.systemui.shared.plugins.PluginManager;
-import com.android.systemui.stackdivider.Divider;
-import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.KeyguardIndicationController;
-import com.android.systemui.statusbar.NavigationBarController;
-import com.android.systemui.statusbar.NotificationLockscreenUserManager;
-import com.android.systemui.statusbar.NotificationMediaManager;
-import com.android.systemui.statusbar.NotificationRemoteInputManager;
-import com.android.systemui.statusbar.NotificationShadeDepthController;
-import com.android.systemui.statusbar.NotificationViewHierarchyManager;
-import com.android.systemui.statusbar.PulseExpansionHandler;
-import com.android.systemui.statusbar.SuperStatusBarViewFactory;
-import com.android.systemui.statusbar.SysuiStatusBarStateController;
-import com.android.systemui.statusbar.VibratorHelper;
-import com.android.systemui.statusbar.dagger.StatusBarDependenciesModule;
-import com.android.systemui.statusbar.notification.DynamicPrivacyController;
-import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
-import com.android.systemui.statusbar.notification.VisualStabilityManager;
-import com.android.systemui.statusbar.notification.init.NotificationsController;
-import com.android.systemui.statusbar.notification.interruption.BypassHeadsUpNotifier;
-import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
-import com.android.systemui.statusbar.notification.logging.NotificationLogger;
-import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
-import com.android.systemui.statusbar.notification.row.NotificationRowModule;
-import com.android.systemui.statusbar.phone.AutoHideController;
-import com.android.systemui.statusbar.phone.BiometricUnlockController;
-import com.android.systemui.statusbar.phone.DozeParameters;
-import com.android.systemui.statusbar.phone.DozeScrimController;
-import com.android.systemui.statusbar.phone.DozeServiceHost;
-import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
-import com.android.systemui.statusbar.phone.KeyguardBypassController;
-import com.android.systemui.statusbar.phone.KeyguardDismissUtil;
-import com.android.systemui.statusbar.phone.LightBarController;
-import com.android.systemui.statusbar.phone.LightsOutNotifController;
-import com.android.systemui.statusbar.phone.LockscreenLockIconController;
-import com.android.systemui.statusbar.phone.LockscreenWallpaper;
-import com.android.systemui.statusbar.phone.NotificationGroupManager;
-import com.android.systemui.statusbar.phone.NotificationShadeWindowController;
-import com.android.systemui.statusbar.phone.PhoneStatusBarPolicy;
-import com.android.systemui.statusbar.phone.ScrimController;
-import com.android.systemui.statusbar.phone.ShadeController;
-import com.android.systemui.statusbar.phone.StatusBarIconController;
-import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
-import com.android.systemui.statusbar.phone.StatusBarNotificationActivityStarter;
-import com.android.systemui.statusbar.phone.StatusBarTouchableRegionManager;
-import com.android.systemui.statusbar.phone.dagger.StatusBarComponent;
-import com.android.systemui.statusbar.phone.dagger.StatusBarPhoneDependenciesModule;
-import com.android.systemui.statusbar.policy.BatteryController;
-import com.android.systemui.statusbar.policy.ConfigurationController;
-import com.android.systemui.statusbar.policy.ExtensionController;
-import com.android.systemui.statusbar.policy.KeyguardStateController;
-import com.android.systemui.statusbar.policy.NetworkController;
-import com.android.systemui.statusbar.policy.RemoteInputQuickSettingsDisabler;
-import com.android.systemui.statusbar.policy.UserInfoControllerImpl;
-import com.android.systemui.statusbar.policy.UserSwitcherController;
-import com.android.systemui.volume.VolumeComponent;
-
-import java.util.Optional;
-import java.util.concurrent.Executor;
-
-import javax.inject.Named;
-import javax.inject.Provider;
-import javax.inject.Singleton;
-
-import dagger.Lazy;
-import dagger.Module;
-import dagger.Provides;
-
-/**
- * Dagger Module providing {@link CarStatusBar}.
- */
-@Module(includes = {StatusBarDependenciesModule.class, StatusBarPhoneDependenciesModule.class,
-        NotificationRowModule.class})
-public class CarStatusBarModule {
-    /**
-     * Provides our instance of StatusBar which is considered optional.
-     */
-    @Provides
-    @Singleton
-    static CarStatusBar provideStatusBar(
-            Context context,
-            NotificationsController notificationsController,
-            LightBarController lightBarController,
-            AutoHideController autoHideController,
-            KeyguardUpdateMonitor keyguardUpdateMonitor,
-            StatusBarIconController statusBarIconController,
-            PulseExpansionHandler pulseExpansionHandler,
-            NotificationWakeUpCoordinator notificationWakeUpCoordinator,
-            KeyguardBypassController keyguardBypassController,
-            KeyguardStateController keyguardStateController,
-            HeadsUpManagerPhone headsUpManagerPhone,
-            DynamicPrivacyController dynamicPrivacyController,
-            BypassHeadsUpNotifier bypassHeadsUpNotifier,
-            FalsingManager falsingManager,
-            BroadcastDispatcher broadcastDispatcher,
-            RemoteInputQuickSettingsDisabler remoteInputQuickSettingsDisabler,
-            NotificationGutsManager notificationGutsManager,
-            NotificationLogger notificationLogger,
-            NotificationInterruptStateProvider notificationInterruptionStateProvider,
-            NotificationViewHierarchyManager notificationViewHierarchyManager,
-            KeyguardViewMediator keyguardViewMediator,
-            DisplayMetrics displayMetrics,
-            MetricsLogger metricsLogger,
-            @UiBackground Executor uiBgExecutor,
-            NotificationMediaManager notificationMediaManager,
-            NotificationLockscreenUserManager lockScreenUserManager,
-            NotificationRemoteInputManager remoteInputManager,
-            UserSwitcherController userSwitcherController,
-            NetworkController networkController,
-            BatteryController batteryController,
-            SysuiColorExtractor colorExtractor,
-            ScreenLifecycle screenLifecycle,
-            WakefulnessLifecycle wakefulnessLifecycle,
-            SysuiStatusBarStateController statusBarStateController,
-            VibratorHelper vibratorHelper,
-            BubbleController bubbleController,
-            NotificationGroupManager groupManager,
-            VisualStabilityManager visualStabilityManager,
-            CarDeviceProvisionedController carDeviceProvisionedController,
-            NavigationBarController navigationBarController,
-            Lazy<AssistManager> assistManagerLazy,
-            ConfigurationController configurationController,
-            NotificationShadeWindowController notificationShadeWindowController,
-            LockscreenLockIconController lockscreenLockIconController,
-            DozeParameters dozeParameters,
-            ScrimController scrimController,
-            Lazy<LockscreenWallpaper> lockscreenWallpaperLazy,
-            Lazy<BiometricUnlockController> biometricUnlockControllerLazy,
-            DozeServiceHost dozeServiceHost,
-            PowerManager powerManager,
-            ScreenPinningRequest screenPinningRequest,
-            DozeScrimController dozeScrimController,
-            VolumeComponent volumeComponent,
-            CommandQueue commandQueue,
-            Optional<Recents> recentsOptional,
-            Provider<StatusBarComponent.Builder> statusBarComponentBuilder,
-            PluginManager pluginManager,
-            Optional<Divider> dividerOptional,
-            SuperStatusBarViewFactory superStatusBarViewFactory,
-            LightsOutNotifController lightsOutNotifController,
-            StatusBarNotificationActivityStarter.Builder
-                    statusBarNotificationActivityStarterBuilder,
-            ShadeController shadeController,
-            StatusBarKeyguardViewManager statusBarKeyguardViewManager,
-            ViewMediatorCallback viewMediatorCallback,
-            InitController initController,
-            DarkIconDispatcher darkIconDispatcher,
-            @Named(TIME_TICK_HANDLER_NAME) Handler timeTickHandler,
-            PluginDependencyProvider pluginDependencyProvider,
-            KeyguardDismissUtil keyguardDismissUtil,
-            ExtensionController extensionController,
-            UserInfoControllerImpl userInfoControllerImpl,
-            PhoneStatusBarPolicy phoneStatusBarPolicy,
-            KeyguardIndicationController keyguardIndicationController,
-            DismissCallbackRegistry dismissCallbackRegistry,
-            StatusBarTouchableRegionManager statusBarTouchableRegionManager,
-            Lazy<NotificationShadeDepthController> notificationShadeDepthControllerLazy,
-            CarNavigationBarController carNavigationBarController) {
-        return new CarStatusBar(
-                context,
-                notificationsController,
-                lightBarController,
-                autoHideController,
-                keyguardUpdateMonitor,
-                statusBarIconController,
-                pulseExpansionHandler,
-                notificationWakeUpCoordinator,
-                keyguardBypassController,
-                keyguardStateController,
-                headsUpManagerPhone,
-                dynamicPrivacyController,
-                bypassHeadsUpNotifier,
-                falsingManager,
-                broadcastDispatcher,
-                remoteInputQuickSettingsDisabler,
-                notificationGutsManager,
-                notificationLogger,
-                notificationInterruptionStateProvider,
-                notificationViewHierarchyManager,
-                keyguardViewMediator,
-                displayMetrics,
-                metricsLogger,
-                uiBgExecutor,
-                notificationMediaManager,
-                lockScreenUserManager,
-                remoteInputManager,
-                userSwitcherController,
-                networkController,
-                batteryController,
-                colorExtractor,
-                screenLifecycle,
-                wakefulnessLifecycle,
-                statusBarStateController,
-                vibratorHelper,
-                bubbleController,
-                groupManager,
-                visualStabilityManager,
-                carDeviceProvisionedController,
-                navigationBarController,
-                assistManagerLazy,
-                configurationController,
-                notificationShadeWindowController,
-                lockscreenLockIconController,
-                dozeParameters,
-                scrimController,
-                lockscreenWallpaperLazy,
-                biometricUnlockControllerLazy,
-                dozeServiceHost,
-                powerManager,
-                screenPinningRequest,
-                dozeScrimController,
-                volumeComponent,
-                commandQueue,
-                recentsOptional,
-                statusBarComponentBuilder,
-                pluginManager,
-                dividerOptional,
-                superStatusBarViewFactory,
-                lightsOutNotifController,
-                statusBarNotificationActivityStarterBuilder,
-                shadeController,
-                statusBarKeyguardViewManager,
-                viewMediatorCallback,
-                initController,
-                darkIconDispatcher,
-                timeTickHandler,
-                pluginDependencyProvider,
-                keyguardDismissUtil,
-                extensionController,
-                userInfoControllerImpl,
-                phoneStatusBarPolicy,
-                keyguardIndicationController,
-                dismissCallbackRegistry,
-                statusBarTouchableRegionManager,
-                notificationShadeDepthControllerLazy,
-                carNavigationBarController);
-    }
-}
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/UserSwitchTransitionViewController.java b/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/UserSwitchTransitionViewController.java
index 45f3d34..0d77c13 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/UserSwitchTransitionViewController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/UserSwitchTransitionViewController.java
@@ -91,6 +91,11 @@
                 R.integer.config_userSwitchTransitionViewShownTimeoutMs);
     }
 
+    @Override
+    protected int getInsetTypesToFit() {
+        return 0;
+    }
+
     /**
      * Makes the user switch transition view appear and draws the content inside of it if a user
      * that is different from the previous user is provided and if the dialog is not already
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewController.java b/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewController.java
index 3969f92..53deb9d 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewController.java
@@ -16,9 +16,12 @@
 
 package com.android.systemui.car.window;
 
+import static android.view.WindowInsets.Type.statusBars;
+
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.ViewStub;
+import android.view.WindowInsets;
 
 /**
  * Owns a {@link View} that is present in SystemUIOverlayWindow.
@@ -140,9 +143,25 @@
     }
 
     /**
+     * Returns {@code true} if status bar should be displayed over this view.
+     */
+    protected boolean shouldShowStatusBar() {
+        return false;
+    }
+
+    /**
      * Returns {@code true} if this view should be hidden during the occluded state.
      */
     protected boolean shouldShowWhenOccluded() {
         return false;
     }
+
+    /**
+     * Returns the insets types to fit to the sysui overlay window when this
+     * {@link OverlayViewController} is in the foreground.
+     */
+    @WindowInsets.Type.InsetsType
+    protected int getInsetTypesToFit() {
+        return statusBars();
+    }
 }
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewGlobalStateController.java b/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewGlobalStateController.java
index 8e94109..2494242 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewGlobalStateController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewGlobalStateController.java
@@ -16,13 +16,17 @@
 
 package com.android.systemui.car.window;
 
+import static android.view.WindowInsets.Type.navigationBars;
+import static android.view.WindowInsets.Type.statusBars;
+import static android.view.WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE;
+
 import android.annotation.Nullable;
 import android.util.Log;
+import android.view.WindowInsets.Type.InsetsType;
+import android.view.WindowInsetsController;
 
 import androidx.annotation.VisibleForTesting;
 
-import com.android.systemui.car.navigationbar.CarNavigationBarController;
-
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Map;
@@ -48,10 +52,7 @@
     private static final String TAG = OverlayViewGlobalStateController.class.getSimpleName();
     private static final int UNKNOWN_Z_ORDER = -1;
     private final SystemUIOverlayWindowController mSystemUIOverlayWindowController;
-    private final CarNavigationBarController mCarNavigationBarController;
-
-    private boolean mIsOccluded;
-
+    private final WindowInsetsController mWindowInsetsController;
     @VisibleForTesting
     Map<OverlayViewController, Integer> mZOrderMap;
     @VisibleForTesting
@@ -60,14 +61,15 @@
     Set<OverlayViewController> mViewsHiddenForOcclusion;
     @VisibleForTesting
     OverlayViewController mHighestZOrder;
+    private boolean mIsOccluded;
 
     @Inject
     public OverlayViewGlobalStateController(
-            CarNavigationBarController carNavigationBarController,
             SystemUIOverlayWindowController systemUIOverlayWindowController) {
         mSystemUIOverlayWindowController = systemUIOverlayWindowController;
         mSystemUIOverlayWindowController.attach();
-        mCarNavigationBarController = carNavigationBarController;
+        mWindowInsetsController =
+                mSystemUIOverlayWindowController.getBaseLayout().getWindowInsetsController();
         mZOrderMap = new HashMap<>();
         mZOrderVisibleSortedMap = new TreeMap<>();
         mViewsHiddenForOcclusion = new HashSet<>();
@@ -115,7 +117,9 @@
         }
 
         updateInternalsWhenShowingView(viewController);
+        refreshInsetTypesToFit();
         refreshNavigationBarVisibility();
+        refreshStatusBarVisibility();
 
         Log.d(TAG, "Content shown: " + viewController.getClass().getName());
         debugLog();
@@ -185,7 +189,9 @@
 
         mZOrderVisibleSortedMap.remove(mZOrderMap.get(viewController));
         refreshHighestZOrderWhenHidingView(viewController);
+        refreshInsetTypesToFit();
         refreshNavigationBarVisibility();
+        refreshStatusBarVisibility();
 
         if (mZOrderVisibleSortedMap.isEmpty()) {
             setWindowVisible(false);
@@ -208,10 +214,28 @@
     }
 
     private void refreshNavigationBarVisibility() {
+        mWindowInsetsController.setSystemBarsBehavior(BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE);
         if (mZOrderVisibleSortedMap.isEmpty() || mHighestZOrder.shouldShowNavigationBar()) {
-            mCarNavigationBarController.showBars();
+            mWindowInsetsController.show(navigationBars());
         } else {
-            mCarNavigationBarController.hideBars();
+            mWindowInsetsController.hide(navigationBars());
+        }
+    }
+
+    private void refreshStatusBarVisibility() {
+        mWindowInsetsController.setSystemBarsBehavior(BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE);
+        if (mZOrderVisibleSortedMap.isEmpty() || mHighestZOrder.shouldShowStatusBar()) {
+            mWindowInsetsController.show(statusBars());
+        } else {
+            mWindowInsetsController.hide(statusBars());
+        }
+    }
+
+    private void refreshInsetTypesToFit() {
+        if (mZOrderVisibleSortedMap.isEmpty()) {
+            setFitInsetsTypes(statusBars());
+        } else {
+            setFitInsetsTypes(mHighestZOrder.getInsetTypesToFit());
         }
     }
 
@@ -224,6 +248,10 @@
         mSystemUIOverlayWindowController.setWindowVisible(visible);
     }
 
+    private void setFitInsetsTypes(@InsetsType int types) {
+        mSystemUIOverlayWindowController.setFitInsetsTypes(types);
+    }
+
     /**
      * Sets the {@link android.view.WindowManager.LayoutParams#FLAG_ALT_FOCUSABLE_IM} flag of the
      * sysui overlay window.
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/window/SystemUIOverlayWindowController.java b/packages/CarSystemUI/src/com/android/systemui/car/window/SystemUIOverlayWindowController.java
index bcd96f6..029bd37 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/window/SystemUIOverlayWindowController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/window/SystemUIOverlayWindowController.java
@@ -25,6 +25,7 @@
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
+import android.view.WindowInsets;
 import android.view.WindowManager;
 
 import com.android.systemui.R;
@@ -99,7 +100,6 @@
                 PixelFormat.TRANSLUCENT);
         mLp.token = new Binder();
         mLp.gravity = Gravity.TOP;
-        mLp.setFitInsetsTypes(/* types= */ 0);
         mLp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
         mLp.setTitle("SystemUIOverlayWindow");
         mLp.packageName = mContext.getPackageName();
@@ -110,6 +110,12 @@
         setWindowVisible(false);
     }
 
+    /** Sets the types of insets to fit. Note: This should be rarely used. */
+    public void setFitInsetsTypes(@WindowInsets.Type.InsetsType int types) {
+        mLpChanged.setFitInsetsTypes(types);
+        updateWindow();
+    }
+
     /** Sets the window to the visible state. */
     public void setWindowVisible(boolean visible) {
         mVisible = visible;
diff --git a/packages/CarSystemUI/src/com/android/systemui/wm/DisplaySystemBarsController.java b/packages/CarSystemUI/src/com/android/systemui/wm/DisplaySystemBarsController.java
index 63f8c72..5c80202 100644
--- a/packages/CarSystemUI/src/com/android/systemui/wm/DisplaySystemBarsController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/wm/DisplaySystemBarsController.java
@@ -16,12 +16,14 @@
 
 package com.android.systemui.wm;
 
+import android.content.Context;
 import android.os.Handler;
 import android.os.RemoteException;
 import android.util.ArraySet;
 import android.util.Slog;
 import android.util.SparseArray;
 import android.view.IDisplayWindowInsetsController;
+import android.view.IWindowManager;
 import android.view.InsetsController;
 import android.view.InsetsSourceControl;
 import android.view.InsetsState;
@@ -32,7 +34,6 @@
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.wm.shell.common.DisplayController;
 import com.android.wm.shell.common.DisplayImeController;
-import com.android.wm.shell.common.SystemWindows;
 import com.android.wm.shell.common.TransactionPool;
 
 import javax.inject.Inject;
@@ -50,29 +51,32 @@
     private static final String TAG = "DisplaySystemBarsController";
 
     private SparseArray<PerDisplay> mPerDisplaySparseArray;
+    private final Context mContext;
 
     @Inject
     public DisplaySystemBarsController(
-            SystemWindows syswin,
+            Context context,
+            IWindowManager wmService,
             DisplayController displayController,
             @Main Handler mainHandler,
             TransactionPool transactionPool) {
-        super(syswin, displayController, mainHandler, transactionPool);
+        super(wmService, displayController, mainHandler, transactionPool);
+        mContext = context;
     }
 
     @Override
     public void onDisplayAdded(int displayId) {
         PerDisplay pd = new PerDisplay(displayId);
         try {
-            mSystemWindows.mWmService.setDisplayWindowInsetsController(displayId, pd);
+            mWmService.setDisplayWindowInsetsController(displayId, pd);
         } catch (RemoteException e) {
             Slog.w(TAG, "Unable to set insets controller on display " + displayId);
         }
         // Lazy loading policy control filters instead of during boot.
         if (mPerDisplaySparseArray == null) {
             mPerDisplaySparseArray = new SparseArray<>();
-            BarControlPolicy.reloadFromSetting(mSystemWindows.mContext);
-            BarControlPolicy.registerContentObserver(mSystemWindows.mContext, mHandler, () -> {
+            BarControlPolicy.reloadFromSetting(mContext);
+            BarControlPolicy.registerContentObserver(mContext, mHandler, () -> {
                 int size = mPerDisplaySparseArray.size();
                 for (int i = 0; i < size; i++) {
                     mPerDisplaySparseArray.valueAt(i).modifyDisplayWindowInsets();
@@ -85,7 +89,7 @@
     @Override
     public void onDisplayRemoved(int displayId) {
         try {
-            mSystemWindows.mWmService.setDisplayWindowInsetsController(displayId, null);
+            mWmService.setDisplayWindowInsetsController(displayId, null);
         } catch (RemoteException e) {
             Slog.w(TAG, "Unable to remove insets controller on display " + displayId);
         }
@@ -155,7 +159,7 @@
             showInsets(barVisibilities[0], /* fromIme= */ false);
             hideInsets(barVisibilities[1], /* fromIme= */ false);
             try {
-                mSystemWindows.mWmService.modifyDisplayWindowInsets(mDisplayId, mInsetsState);
+                mWmService.modifyDisplayWindowInsets(mDisplayId, mInsetsState);
             } catch (RemoteException e) {
                 Slog.w(TAG, "Unable to update window manager service.");
             }
diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/car/window/OverlayViewGlobalStateControllerTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/window/OverlayViewGlobalStateControllerTest.java
index 20f9bc8e..ff28665 100644
--- a/packages/CarSystemUI/tests/src/com/android/systemui/car/window/OverlayViewGlobalStateControllerTest.java
+++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/window/OverlayViewGlobalStateControllerTest.java
@@ -16,9 +16,14 @@
 
 package com.android.systemui.car.window;
 
+import static android.view.WindowInsets.Type.navigationBars;
+import static android.view.WindowInsets.Type.statusBars;
+
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
@@ -28,19 +33,18 @@
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.ViewStub;
+import android.view.WindowInsetsController;
 
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.car.CarSystemUiTest;
-import com.android.systemui.car.navigationbar.CarNavigationBarController;
 import com.android.systemui.tests.R;
 
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
-import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
 
 import java.util.Arrays;
@@ -58,8 +62,6 @@
     private ViewGroup mBaseLayout;
 
     @Mock
-    private CarNavigationBarController mCarNavigationBarController;
-    @Mock
     private SystemUIOverlayWindowController mSystemUIOverlayWindowController;
     @Mock
     private OverlayViewMediator mOverlayViewMediator;
@@ -71,18 +73,22 @@
     private OverlayPanelViewController mOverlayPanelViewController;
     @Mock
     private Runnable mRunnable;
+    @Mock
+    private WindowInsetsController mWindowInsetsController;
 
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(/* testClass= */ this);
 
-        mBaseLayout = (ViewGroup) LayoutInflater.from(mContext).inflate(
-                R.layout.overlay_view_global_state_controller_test, /* root= */ null);
+        mBaseLayout = spy((ViewGroup) LayoutInflater.from(mContext).inflate(
+                R.layout.overlay_view_global_state_controller_test, /* root= */ null));
+
+        when(mBaseLayout.getWindowInsetsController()).thenReturn(mWindowInsetsController);
 
         when(mSystemUIOverlayWindowController.getBaseLayout()).thenReturn(mBaseLayout);
 
         mOverlayViewGlobalStateController = new OverlayViewGlobalStateController(
-                mCarNavigationBarController, mSystemUIOverlayWindowController);
+                mSystemUIOverlayWindowController);
 
         verify(mSystemUIOverlayWindowController).attach();
     }
@@ -108,7 +114,7 @@
 
         mOverlayViewGlobalStateController.showView(mOverlayViewController1, mRunnable);
 
-        verify(mCarNavigationBarController).hideBars();
+        verify(mWindowInsetsController).hide(navigationBars());
     }
 
     @Test
@@ -118,7 +124,37 @@
 
         mOverlayViewGlobalStateController.showView(mOverlayViewController1, mRunnable);
 
-        verify(mCarNavigationBarController).showBars();
+        verify(mWindowInsetsController).show(navigationBars());
+    }
+
+    @Test
+    public void showView_nothingAlreadyShown_shouldShowStatusBarFalse_statusBarsHidden() {
+        setupOverlayViewController1();
+        when(mOverlayViewController1.shouldShowStatusBar()).thenReturn(false);
+
+        mOverlayViewGlobalStateController.showView(mOverlayViewController1, mRunnable);
+
+        verify(mWindowInsetsController).hide(statusBars());
+    }
+
+    @Test
+    public void showView_nothingAlreadyShown_shouldShowStatusBarTrue_statusBarsShown() {
+        setupOverlayViewController1();
+        when(mOverlayViewController1.shouldShowStatusBar()).thenReturn(true);
+
+        mOverlayViewGlobalStateController.showView(mOverlayViewController1, mRunnable);
+
+        verify(mWindowInsetsController).show(statusBars());
+    }
+
+    @Test
+    public void showView_nothingAlreadyShown_fitsNavBarInsets_insetsAdjusted() {
+        setupOverlayViewController1();
+        when(mOverlayViewController1.getInsetTypesToFit()).thenReturn(navigationBars());
+
+        mOverlayViewGlobalStateController.showView(mOverlayViewController1, mRunnable);
+
+        verify(mSystemUIOverlayWindowController).setFitInsetsTypes(navigationBars());
     }
 
     @Test
@@ -168,10 +204,11 @@
         setOverlayViewControllerAsShowing(mOverlayViewController1);
         setupOverlayViewController2();
         when(mOverlayViewController2.shouldShowNavigationBar()).thenReturn(false);
+        reset(mWindowInsetsController);
 
         mOverlayViewGlobalStateController.showView(mOverlayViewController2, mRunnable);
 
-        verify(mCarNavigationBarController).hideBars();
+        verify(mWindowInsetsController).hide(navigationBars());
     }
 
     @Test
@@ -183,7 +220,46 @@
 
         mOverlayViewGlobalStateController.showView(mOverlayViewController2, mRunnable);
 
-        verify(mCarNavigationBarController).showBars();
+        verify(mWindowInsetsController).show(navigationBars());
+    }
+
+    @Test
+    public void showView_newHighestZOrder_shouldShowStatusBarFalse_statusBarsHidden() {
+        setupOverlayViewController1();
+        setOverlayViewControllerAsShowing(mOverlayViewController1);
+        setupOverlayViewController2();
+        when(mOverlayViewController2.shouldShowStatusBar()).thenReturn(false);
+        reset(mWindowInsetsController);
+
+        mOverlayViewGlobalStateController.showView(mOverlayViewController2, mRunnable);
+
+        verify(mWindowInsetsController).hide(statusBars());
+    }
+
+    @Test
+    public void showView_newHighestZOrder_shouldShowStatusBarTrue_statusBarsShown() {
+        setupOverlayViewController1();
+        setOverlayViewControllerAsShowing(mOverlayViewController1);
+        setupOverlayViewController2();
+        when(mOverlayViewController2.shouldShowStatusBar()).thenReturn(true);
+
+        mOverlayViewGlobalStateController.showView(mOverlayViewController2, mRunnable);
+
+        verify(mWindowInsetsController).show(statusBars());
+    }
+
+    @Test
+    public void showView_newHighestZOrder_fitsNavBarInsets_insetsAdjusted() {
+        setupOverlayViewController1();
+        when(mOverlayViewController1.getInsetTypesToFit()).thenReturn(statusBars());
+        setOverlayViewControllerAsShowing(mOverlayViewController1);
+        setupOverlayViewController2();
+        when(mOverlayViewController2.getInsetTypesToFit()).thenReturn(navigationBars());
+        reset(mWindowInsetsController);
+
+        mOverlayViewGlobalStateController.showView(mOverlayViewController2, mRunnable);
+
+        verify(mSystemUIOverlayWindowController).setFitInsetsTypes(navigationBars());
     }
 
     @Test
@@ -216,10 +292,11 @@
         setOverlayViewControllerAsShowing(mOverlayViewController2);
         when(mOverlayViewController1.shouldShowNavigationBar()).thenReturn(true);
         when(mOverlayViewController2.shouldShowNavigationBar()).thenReturn(false);
+        reset(mWindowInsetsController);
 
         mOverlayViewGlobalStateController.showView(mOverlayViewController1, mRunnable);
 
-        verify(mCarNavigationBarController).hideBars();
+        verify(mWindowInsetsController).hide(navigationBars());
     }
 
     @Test
@@ -231,7 +308,44 @@
 
         mOverlayViewGlobalStateController.showView(mOverlayViewController1, mRunnable);
 
-        verify(mCarNavigationBarController).showBars();
+        verify(mWindowInsetsController).show(navigationBars());
+    }
+
+    @Test
+    public void showView_oldHighestZOrder_shouldShowStatusBarFalse_statusBarsHidden() {
+        setupOverlayViewController2();
+        setOverlayViewControllerAsShowing(mOverlayViewController2);
+        when(mOverlayViewController1.shouldShowStatusBar()).thenReturn(true);
+        when(mOverlayViewController2.shouldShowStatusBar()).thenReturn(false);
+        reset(mWindowInsetsController);
+
+        mOverlayViewGlobalStateController.showView(mOverlayViewController1, mRunnable);
+
+        verify(mWindowInsetsController).hide(statusBars());
+    }
+
+    @Test
+    public void showView_oldHighestZOrder_shouldShowStatusBarTrue_statusBarsShown() {
+        setupOverlayViewController2();
+        setOverlayViewControllerAsShowing(mOverlayViewController2);
+        when(mOverlayViewController1.shouldShowStatusBar()).thenReturn(false);
+        when(mOverlayViewController2.shouldShowStatusBar()).thenReturn(true);
+
+        mOverlayViewGlobalStateController.showView(mOverlayViewController1, mRunnable);
+
+        verify(mWindowInsetsController).show(statusBars());
+    }
+
+    @Test
+    public void showView_oldHighestZOrder_fitsNavBarInsets_insetsAdjusted() {
+        setupOverlayViewController2();
+        setOverlayViewControllerAsShowing(mOverlayViewController2);
+        when(mOverlayViewController1.getInsetTypesToFit()).thenReturn(statusBars());
+        when(mOverlayViewController2.getInsetTypesToFit()).thenReturn(navigationBars());
+
+        mOverlayViewGlobalStateController.showView(mOverlayViewController1, mRunnable);
+
+        verify(mSystemUIOverlayWindowController).setFitInsetsTypes(navigationBars());
     }
 
     @Test
@@ -402,10 +516,11 @@
         setupOverlayViewController2();
         setOverlayViewControllerAsShowing(mOverlayViewController2);
         when(mOverlayViewController1.shouldShowNavigationBar()).thenReturn(false);
+        reset(mWindowInsetsController);
 
         mOverlayViewGlobalStateController.hideView(mOverlayViewController2, mRunnable);
 
-        verify(mCarNavigationBarController).hideBars();
+        verify(mWindowInsetsController).hide(navigationBars());
     }
 
     @Test
@@ -418,7 +533,48 @@
 
         mOverlayViewGlobalStateController.hideView(mOverlayViewController2, mRunnable);
 
-        verify(mCarNavigationBarController).showBars();
+        verify(mWindowInsetsController).show(navigationBars());
+    }
+
+    @Test
+    public void hideView_newHighestZOrder_shouldShowStatusBarFalse_statusBarHidden() {
+        setupOverlayViewController1();
+        setOverlayViewControllerAsShowing(mOverlayViewController1);
+        setupOverlayViewController2();
+        setOverlayViewControllerAsShowing(mOverlayViewController2);
+        when(mOverlayViewController1.shouldShowStatusBar()).thenReturn(false);
+        reset(mWindowInsetsController);
+
+        mOverlayViewGlobalStateController.hideView(mOverlayViewController2, mRunnable);
+
+        verify(mWindowInsetsController).hide(statusBars());
+    }
+
+    @Test
+    public void hideView_newHighestZOrder_shouldShowStatusBarTrue_statusBarShown() {
+        setupOverlayViewController1();
+        setOverlayViewControllerAsShowing(mOverlayViewController1);
+        setupOverlayViewController2();
+        setOverlayViewControllerAsShowing(mOverlayViewController2);
+        when(mOverlayViewController1.shouldShowStatusBar()).thenReturn(true);
+
+        mOverlayViewGlobalStateController.hideView(mOverlayViewController2, mRunnable);
+
+        verify(mWindowInsetsController).show(statusBars());
+    }
+
+    @Test
+    public void hideView_newHighestZOrder_fitsNavBarInsets_insetsAdjusted() {
+        setupOverlayViewController1();
+        setOverlayViewControllerAsShowing(mOverlayViewController1);
+        setupOverlayViewController2();
+        setOverlayViewControllerAsShowing(mOverlayViewController2);
+        when(mOverlayViewController1.getInsetTypesToFit()).thenReturn(navigationBars());
+        when(mOverlayViewController2.getInsetTypesToFit()).thenReturn(statusBars());
+
+        mOverlayViewGlobalStateController.hideView(mOverlayViewController2, mRunnable);
+
+        verify(mSystemUIOverlayWindowController).setFitInsetsTypes(navigationBars());
     }
 
     @Test
@@ -441,10 +597,11 @@
         setupOverlayViewController2();
         setOverlayViewControllerAsShowing(mOverlayViewController2);
         when(mOverlayViewController2.shouldShowNavigationBar()).thenReturn(false);
+        reset(mWindowInsetsController);
 
         mOverlayViewGlobalStateController.hideView(mOverlayViewController1, mRunnable);
 
-        verify(mCarNavigationBarController).hideBars();
+        verify(mWindowInsetsController).hide(navigationBars());
     }
 
     @Test
@@ -457,7 +614,48 @@
 
         mOverlayViewGlobalStateController.hideView(mOverlayViewController1, mRunnable);
 
-        verify(mCarNavigationBarController).showBars();
+        verify(mWindowInsetsController).show(navigationBars());
+    }
+
+    @Test
+    public void hideView_oldHighestZOrder_shouldShowStatusBarFalse_statusBarHidden() {
+        setupOverlayViewController1();
+        setOverlayViewControllerAsShowing(mOverlayViewController1);
+        setupOverlayViewController2();
+        setOverlayViewControllerAsShowing(mOverlayViewController2);
+        when(mOverlayViewController2.shouldShowStatusBar()).thenReturn(false);
+        reset(mWindowInsetsController);
+
+        mOverlayViewGlobalStateController.hideView(mOverlayViewController1, mRunnable);
+
+        verify(mWindowInsetsController).hide(statusBars());
+    }
+
+    @Test
+    public void hideView_oldHighestZOrder_shouldShowStatusBarTrue_statusBarShown() {
+        setupOverlayViewController1();
+        setOverlayViewControllerAsShowing(mOverlayViewController1);
+        setupOverlayViewController2();
+        setOverlayViewControllerAsShowing(mOverlayViewController2);
+        when(mOverlayViewController2.shouldShowStatusBar()).thenReturn(true);
+
+        mOverlayViewGlobalStateController.hideView(mOverlayViewController1, mRunnable);
+
+        verify(mWindowInsetsController).show(statusBars());
+    }
+
+    @Test
+    public void hideView_oldHighestZOrder_fitsNavBarInsets_insetsAdjusted() {
+        setupOverlayViewController1();
+        setOverlayViewControllerAsShowing(mOverlayViewController1);
+        setupOverlayViewController2();
+        setOverlayViewControllerAsShowing(mOverlayViewController2);
+        when(mOverlayViewController1.getInsetTypesToFit()).thenReturn(statusBars());
+        when(mOverlayViewController2.getInsetTypesToFit()).thenReturn(navigationBars());
+
+        mOverlayViewGlobalStateController.hideView(mOverlayViewController1, mRunnable);
+
+        verify(mSystemUIOverlayWindowController).setFitInsetsTypes(navigationBars());
     }
 
     @Test
@@ -479,7 +677,27 @@
 
         mOverlayViewGlobalStateController.hideView(mOverlayViewController1, mRunnable);
 
-        verify(mCarNavigationBarController).showBars();
+        verify(mWindowInsetsController).show(navigationBars());
+    }
+
+    @Test
+    public void hideView_viewControllerOnlyShown_statusBarShown() {
+        setupOverlayViewController1();
+        setOverlayViewControllerAsShowing(mOverlayViewController1);
+
+        mOverlayViewGlobalStateController.hideView(mOverlayViewController1, mRunnable);
+
+        verify(mWindowInsetsController).show(statusBars());
+    }
+
+    @Test
+    public void hideView_viewControllerOnlyShown_insetsAdjustedToDefault() {
+        setupOverlayViewController1();
+        setOverlayViewControllerAsShowing(mOverlayViewController1);
+
+        mOverlayViewGlobalStateController.hideView(mOverlayViewController1, mRunnable);
+
+        verify(mSystemUIOverlayWindowController).setFitInsetsTypes(statusBars());
     }
 
     @Test
@@ -615,7 +833,7 @@
 
     private void setOverlayViewControllerAsShowing(OverlayViewController overlayViewController) {
         mOverlayViewGlobalStateController.showView(overlayViewController, /* show= */ null);
-        Mockito.reset(mCarNavigationBarController, mSystemUIOverlayWindowController);
+        reset(mSystemUIOverlayWindowController);
         when(mSystemUIOverlayWindowController.getBaseLayout()).thenReturn(mBaseLayout);
     }
 }
diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/wm/DisplaySystemBarsControllerTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/wm/DisplaySystemBarsControllerTest.java
index 0f28d38..391f75e 100644
--- a/packages/CarSystemUI/tests/src/com/android/systemui/wm/DisplaySystemBarsControllerTest.java
+++ b/packages/CarSystemUI/tests/src/com/android/systemui/wm/DisplaySystemBarsControllerTest.java
@@ -34,7 +34,6 @@
 
 import com.android.systemui.SysuiTestCase;
 import com.android.wm.shell.common.DisplayController;
-import com.android.wm.shell.common.SystemWindows;
 import com.android.wm.shell.common.TransactionPool;
 
 import org.junit.Before;
@@ -53,8 +52,6 @@
     private static final int DISPLAY_ID = 1;
 
     @Mock
-    private SystemWindows mSystemWindows;
-    @Mock
     private IWindowManager mIWindowManager;
     @Mock
     private DisplayController mDisplayController;
@@ -66,11 +63,10 @@
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
-        mSystemWindows.mContext = mContext;
-        mSystemWindows.mWmService = mIWindowManager;
 
         mController = new DisplaySystemBarsController(
-                mSystemWindows,
+                mContext,
+                mIWindowManager,
                 mDisplayController,
                 mHandler,
                 mTransactionPool
diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceChooserActivity.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceChooserActivity.java
index 16ef59f..02f4457 100644
--- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceChooserActivity.java
+++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceChooserActivity.java
@@ -29,6 +29,7 @@
 import android.util.Log;
 import android.view.Gravity;
 import android.view.View;
+import android.widget.AdapterView;
 import android.widget.ListView;
 import android.widget.ProgressBar;
 import android.widget.TextView;
@@ -75,6 +76,14 @@
             mDeviceListView = findViewById(R.id.device_list);
             final DeviceDiscoveryService.DevicesAdapter adapter = getService().mDevicesAdapter;
             mDeviceListView.setAdapter(adapter);
+            mDeviceListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
+                @Override
+                public void onItemClick(AdapterView<?> adapterView, View view, int pos, long l) {
+                    getService().mSelectedDevice =
+                            (DeviceFilterPair) adapterView.getItemAtPosition(pos);
+                    adapter.notifyDataSetChanged();
+                }
+            });
             adapter.registerDataSetObserver(new DataSetObserver() {
                 @Override
                 public void onChanged() {
@@ -157,4 +166,4 @@
                 new Intent().putExtra(CompanionDeviceManager.EXTRA_DEVICE, selectedDevice.device));
         finish();
     }
-}
\ No newline at end of file
+}
diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceDiscoveryService.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceDiscoveryService.java
index 7aa997e..bcaee36 100644
--- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceDiscoveryService.java
+++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceDiscoveryService.java
@@ -349,10 +349,6 @@
                         ? WIFI_ICON
                         : BLUETOOTH_ICON,
                     null, null, null);
-            textView.setOnClickListener((view) -> {
-                mSelectedDevice = device;
-                notifyDataSetChanged();
-            });
         }
 
         //TODO move to a layout file
diff --git a/packages/DynamicSystemInstallationService/tests/res/values/strings.xml b/packages/DynamicSystemInstallationService/tests/res/values/strings.xml
index fdb620b..019c0c9 100644
--- a/packages/DynamicSystemInstallationService/tests/res/values/strings.xml
+++ b/packages/DynamicSystemInstallationService/tests/res/values/strings.xml
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="utf-8"?>
 <resources>
     <!-- testFromJsonString -->
-    <string name="blacklist_json_string" translatable="false">
+    <string name="blocklist_json_string" translatable="false">
       {
         \"entries\":[
           {
diff --git a/packages/DynamicSystemInstallationService/tests/src/com/android/dynsystem/KeyRevocationListTest.java b/packages/DynamicSystemInstallationService/tests/src/com/android/dynsystem/KeyRevocationListTest.java
index 82ce542..c1233eb 100644
--- a/packages/DynamicSystemInstallationService/tests/src/com/android/dynsystem/KeyRevocationListTest.java
+++ b/packages/DynamicSystemInstallationService/tests/src/com/android/dynsystem/KeyRevocationListTest.java
@@ -47,32 +47,32 @@
 
     private static Context sContext;
 
-    private static String sBlacklistJsonString;
+    private static String sBlocklistJsonString;
 
     @BeforeClass
     public static void setUpClass() throws Exception {
         sContext = InstrumentationRegistry.getInstrumentation().getContext();
-        sBlacklistJsonString =
-                sContext.getString(com.android.dynsystem.tests.R.string.blacklist_json_string);
+        sBlocklistJsonString =
+                sContext.getString(com.android.dynsystem.tests.R.string.blocklist_json_string);
     }
 
     @Test
     @SmallTest
     public void testFromJsonString() throws JSONException {
-        KeyRevocationList blacklist;
-        blacklist = KeyRevocationList.fromJsonString(sBlacklistJsonString);
-        Assert.assertNotNull(blacklist);
-        Assert.assertFalse(blacklist.mEntries.isEmpty());
-        blacklist = KeyRevocationList.fromJsonString("{}");
-        Assert.assertNotNull(blacklist);
-        Assert.assertTrue(blacklist.mEntries.isEmpty());
+        KeyRevocationList blocklist;
+        blocklist = KeyRevocationList.fromJsonString(sBlocklistJsonString);
+        Assert.assertNotNull(blocklist);
+        Assert.assertFalse(blocklist.mEntries.isEmpty());
+        blocklist = KeyRevocationList.fromJsonString("{}");
+        Assert.assertNotNull(blocklist);
+        Assert.assertTrue(blocklist.mEntries.isEmpty());
     }
 
     @Test
     @SmallTest
     public void testFromUrl() throws IOException, JSONException {
         URLConnection mockConnection = mock(URLConnection.class);
-        doReturn(new ByteArrayInputStream(sBlacklistJsonString.getBytes()))
+        doReturn(new ByteArrayInputStream(sBlocklistJsonString.getBytes()))
                 .when(mockConnection).getInputStream();
         URL mockUrl = new URL(
                 "http",     // protocol
@@ -97,36 +97,36 @@
                     }
                 });
 
-        KeyRevocationList blacklist = KeyRevocationList.fromUrl(mockUrl);
-        Assert.assertNotNull(blacklist);
-        Assert.assertFalse(blacklist.mEntries.isEmpty());
+        KeyRevocationList blocklist = KeyRevocationList.fromUrl(mockUrl);
+        Assert.assertNotNull(blocklist);
+        Assert.assertFalse(blocklist.mEntries.isEmpty());
 
-        blacklist = null;
+        blocklist = null;
         try {
-            blacklist = KeyRevocationList.fromUrl(mockBadUrl);
+            blocklist = KeyRevocationList.fromUrl(mockBadUrl);
             // Up should throw, down should be unreachable
             Assert.fail("Expected IOException not thrown");
         } catch (IOException e) {
             // This is expected, do nothing
         }
-        Assert.assertNull(blacklist);
+        Assert.assertNull(blocklist);
     }
 
     @Test
     @SmallTest
     public void testIsRevoked() {
-        KeyRevocationList blacklist = new KeyRevocationList();
-        blacklist.addEntry("key1", "REVOKED", "reason for key1");
+        KeyRevocationList blocklist = new KeyRevocationList();
+        blocklist.addEntry("key1", "REVOKED", "reason for key1");
 
         KeyRevocationList.RevocationStatus revocationStatus =
-                blacklist.getRevocationStatusForKey("key1");
+                blocklist.getRevocationStatusForKey("key1");
         Assert.assertNotNull(revocationStatus);
         Assert.assertEquals(revocationStatus.mReason, "reason for key1");
 
-        revocationStatus = blacklist.getRevocationStatusForKey("key2");
+        revocationStatus = blocklist.getRevocationStatusForKey("key2");
         Assert.assertNull(revocationStatus);
 
-        Assert.assertTrue(blacklist.isRevoked("key1"));
-        Assert.assertFalse(blacklist.isRevoked("key2"));
+        Assert.assertTrue(blocklist.isRevoked("key1"));
+        Assert.assertFalse(blocklist.isRevoked("key2"));
     }
 }
diff --git a/packages/SettingsLib/res/values-pt-rPT/strings.xml b/packages/SettingsLib/res/values-pt-rPT/strings.xml
index d480ff6..508cbfc 100644
--- a/packages/SettingsLib/res/values-pt-rPT/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rPT/strings.xml
@@ -140,8 +140,8 @@
     <string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"Rede aberta"</string>
     <string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"Rede segura"</string>
     <string name="process_kernel_label" msgid="950292573930336765">"SO Android"</string>
-    <string name="data_usage_uninstalled_apps" msgid="1933665711856171491">"Aplicações removidas"</string>
-    <string name="data_usage_uninstalled_apps_users" msgid="5533981546921913295">"Aplicações e utilizadores removidos"</string>
+    <string name="data_usage_uninstalled_apps" msgid="1933665711856171491">"Apps removidas"</string>
+    <string name="data_usage_uninstalled_apps_users" msgid="5533981546921913295">"Apps e utilizadores removidos"</string>
     <string name="data_usage_ota" msgid="7984667793701597001">"Atualizações do sistema"</string>
     <string name="tether_settings_title_usb" msgid="3728686573430917722">"Ligação USB"</string>
     <string name="tether_settings_title_wifi" msgid="4803402057533895526">"Hotspot portátil"</string>
@@ -365,7 +365,7 @@
     <string name="transition_animation_scale_title" msgid="1278477690695439337">"Escala de animação de transição"</string>
     <string name="animator_duration_scale_title" msgid="7082913931326085176">"Escala de duração de animação"</string>
     <string name="overlay_display_devices_title" msgid="5411894622334469607">"Simular apresentações secundárias"</string>
-    <string name="debug_applications_category" msgid="5394089406638954196">"Aplicações"</string>
+    <string name="debug_applications_category" msgid="5394089406638954196">"Apps"</string>
     <string name="immediately_destroy_activities" msgid="1826287490705167403">"Não manter atividades"</string>
     <string name="immediately_destroy_activities_summary" msgid="6289590341144557614">"Destruir atividades assim que o utilizador sair"</string>
     <string name="app_process_limit_title" msgid="8361367869453043007">"Limite do processo em 2º plano"</string>
@@ -396,7 +396,7 @@
     <item msgid="4548987861791236754">"Cores naturais e realistas"</item>
     <item msgid="1282170165150762976">"Cores otimizadas para conteúdos digitais"</item>
   </string-array>
-    <string name="inactive_apps_title" msgid="5372523625297212320">"Aplicações em espera"</string>
+    <string name="inactive_apps_title" msgid="5372523625297212320">"Apps em espera"</string>
     <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"Inativo. Toque para ativar/desativar."</string>
     <string name="inactive_app_active_summary" msgid="8047630990208722344">"Ativo. Toque para ativar/desativar."</string>
     <string name="standby_bucket_summary" msgid="5128193447550429600">"Estado do Modo de espera das apps:<xliff:g id="BUCKET"> %s</xliff:g>"</string>
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index 2f1191a..6914f0a 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -55,11 +55,11 @@
             "hybrid_sysui_battery_warning_flags";
 
     /**
-     * The following blacklists contain settings that should *not* be backed up and restored to
+     * The following denylists contain settings that should *not* be backed up and restored to
      * another device.  As a general rule, anything that is not user configurable should be
-     * blacklisted (and conversely, things that *are* user configurable *should* be backed up)
+     * denied (and conversely, things that *are* user configurable *should* be backed up)
      */
-    private static final Set<String> BACKUP_BLACKLISTED_SYSTEM_SETTINGS =
+    private static final Set<String> BACKUP_DENY_LIST_SYSTEM_SETTINGS =
             newHashSet(
                     Settings.System.ADVANCED_SETTINGS, // candidate for backup?
                     Settings.System.ALARM_ALERT_CACHE, // internal cache
@@ -105,7 +105,7 @@
                     Settings.System.MULTI_AUDIO_FOCUS_ENABLED // form-factor/OEM specific
                     );
 
-    private static final Set<String> BACKUP_BLACKLISTED_GLOBAL_SETTINGS =
+    private static final Set<String> BACKUP_DENY_LIST_GLOBAL_SETTINGS =
             newHashSet(
                     Settings.Global.ACTIVITY_MANAGER_CONSTANTS,
                     Settings.Global.ACTIVITY_STARTS_LOGGING_ENABLED,
@@ -590,7 +590,7 @@
                     Settings.Global.APP_INTEGRITY_VERIFICATION_TIMEOUT,
                     Settings.Global.ADVANCED_BATTERY_USAGE_AMOUNT);
 
-    private static final Set<String> BACKUP_BLACKLISTED_SECURE_SETTINGS =
+    private static final Set<String> BACKUP_DENY_LIST_SECURE_SETTINGS =
              newHashSet(
                  Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE,
                  Settings.Secure.ACCESSIBILITY_SPEAK_PASSWORD, // Deprecated since O.
@@ -745,42 +745,42 @@
                  Settings.Secure.SUPPRESS_DOZE);
 
     @Test
-    public void systemSettingsBackedUpOrBlacklisted() {
-        checkSettingsBackedUpOrBlacklisted(
+    public void systemSettingsBackedUpOrDenied() {
+        checkSettingsBackedUpOrDenied(
                 getCandidateSettings(Settings.System.class),
                 newHashSet(SystemSettings.SETTINGS_TO_BACKUP),
-                BACKUP_BLACKLISTED_SYSTEM_SETTINGS);
+                BACKUP_DENY_LIST_SYSTEM_SETTINGS);
     }
 
     @Test
-    public void globalSettingsBackedUpOrBlacklisted() {
-        checkSettingsBackedUpOrBlacklisted(
+    public void globalSettingsBackedUpOrDenied() {
+        checkSettingsBackedUpOrDenied(
                 getCandidateSettings(Settings.Global.class),
                 newHashSet(GlobalSettings.SETTINGS_TO_BACKUP),
-                BACKUP_BLACKLISTED_GLOBAL_SETTINGS);
+                BACKUP_DENY_LIST_GLOBAL_SETTINGS);
     }
 
     @Test
     @Suppress //("b/148236308")
-    public void secureSettingsBackedUpOrBlacklisted() {
+    public void secureSettingsBackedUpOrDenied() {
         HashSet<String> keys = new HashSet<String>();
         Collections.addAll(keys, SecureSettings.SETTINGS_TO_BACKUP);
         Collections.addAll(keys, DEVICE_SPECIFIC_SETTINGS_TO_BACKUP);
-        checkSettingsBackedUpOrBlacklisted(
+        checkSettingsBackedUpOrDenied(
                 getCandidateSettings(Settings.Secure.class),
                 keys,
-                BACKUP_BLACKLISTED_SECURE_SETTINGS);
+                BACKUP_DENY_LIST_SECURE_SETTINGS);
     }
 
-    private static void checkSettingsBackedUpOrBlacklisted(
-            Set<String> settings, Set<String> settingsToBackup, Set<String> blacklist) {
+    private static void checkSettingsBackedUpOrDenied(
+            Set<String> settings, Set<String> settingsToBackup, Set<String> denylist) {
         Set<String> settingsNotBackedUp = difference(settings, settingsToBackup);
-        Set<String> settingsNotBackedUpOrBlacklisted = difference(settingsNotBackedUp, blacklist);
-        assertWithMessage("Settings not backed up or blacklisted")
-                .that(settingsNotBackedUpOrBlacklisted).isEmpty();
+        Set<String> settingsNotBackedUpOrDenied = difference(settingsNotBackedUp, denylist);
+        assertWithMessage("Settings not backed up or denied")
+                .that(settingsNotBackedUpOrDenied).isEmpty();
 
-        assertWithMessage("blacklisted settings backed up")
-                .that(intersect(settingsToBackup, blacklist)).isEmpty();
+        assertWithMessage("denied settings backed up")
+                .that(intersect(settingsToBackup, denylist)).isEmpty();
     }
 
     private static Set<String> getCandidateSettings(
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 6aa233b..98d3553 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -239,6 +239,7 @@
 
     <!-- Listen app op changes -->
     <uses-permission android:name="android.permission.WATCH_APPOPS" />
+    <uses-permission android:name="android.permission.OBSERVE_GRANT_REVOKE_PERMISSIONS" />
 
     <!-- to read and change hvac values in a car -->
     <uses-permission android:name="android.car.permission.CONTROL_CAR_CLIMATE" />
@@ -395,19 +396,15 @@
 
         <!-- Springboard for launching the share and edit activity. This needs to be in the main
              system ui process since we need to notify the status bar to dismiss the keyguard -->
-        <receiver android:name=".screenshot.GlobalScreenshot$ActionProxyReceiver"
-            android:exported="false" />
-
-        <!-- Callback for dismissing screenshot notification after a share target is picked -->
-        <receiver android:name=".screenshot.GlobalScreenshot$TargetChosenReceiver"
+        <receiver android:name=".screenshot.ActionProxyReceiver"
             android:exported="false" />
 
         <!-- Callback for deleting screenshot notification -->
-        <receiver android:name=".screenshot.GlobalScreenshot$DeleteScreenshotReceiver"
+        <receiver android:name=".screenshot.DeleteScreenshotReceiver"
             android:exported="false" />
 
         <!-- Callback for invoking a smart action from the screenshot notification. -->
-        <receiver android:name=".screenshot.GlobalScreenshot$SmartActionsReceiver"
+        <receiver android:name=".screenshot.SmartActionsReceiver"
                   android:exported="false"/>
 
         <!-- started from UsbDeviceSettingsManager -->
diff --git a/packages/SystemUI/docs/dagger.md b/packages/SystemUI/docs/dagger.md
index bb68647..8917013 100644
--- a/packages/SystemUI/docs/dagger.md
+++ b/packages/SystemUI/docs/dagger.md
@@ -41,8 +41,6 @@
 The root component is composed of root modules, which in turn provide the global singleton 
 dependencies across all of SystemUI.
 
-- `ContextHolder` is just a wrapper that provides a context.
-
 - `SystemUIFactory` `@Provides` dependencies that need to be overridden by SystemUI
 variants (like other form factors e.g. Car). 
 
@@ -52,41 +50,8 @@
 
 ### Adding injection to a new SystemUI object
 
-Anything that depends on any `@Singleton` provider from SystemUIRootComponent
-should be declared as a `@Subcomponent` of the root component. This requires
-declaring your own interface for generating your own modules or just the
-object you need injected. The subcomponent also needs to be added to
-SystemUIRootComponent in SystemUIFactory so it can be acquired.
-
-```java
-public interface SystemUIRootComponent {
-+    @Singleton
-+    Dependency.DependencyInjector createDependency();
-}
-
-public class Dependency extends SystemUI {
-  //...
-+  @Subcomponent
-+  public interface DependencyInjector {
-+      Dependency createSystemUI();
-+  }
-}
-```
-
-For objects which extend SystemUI and require injection, you can define an
-injector that creates the injected object for you. This other class should
-be referenced in [@string/config_systemUIServiceComponents](packages/SystemUI/res/values/config.xml).
-
-```java
-public static class DependencyCreator implements Injector {
-    @Override
-    public SystemUI apply(Context context) {
-        return SystemUIFactory.getInstance().getRootComponent()
-                .createDependency()
-                .createSystemUI();
-    }
-}
-```
+SystemUI object are made injectable by adding an entry in `SystemUIBinder`. SystemUIApplication uses
+information in that file to locate and construct an instance of the requested SystemUI class.
 
 ### Adding a new injectable object
 
@@ -147,7 +112,7 @@
 
 ```java
 public interface FragmentCreator {
-+   NavigationBarFragment createNavigationBar();
+    NavigationBarFragment createNavigationBar();
 }
 ```
 
@@ -160,49 +125,17 @@
 
 ### Using injection with Views
 
-Generally, you shouldn't need to inject for a view, as the view should
-be relatively self contained and logic that requires injection should be
-moved to a higher level construct such as a Fragment or a top-level SystemUI
-component, see above for how to do injection for both of which.
+DO NOT ADD NEW VIEW INJECTION. VIEW INJECTION IS BEING ACTIVELY DEPRECATED.
 
-Still here? Yeah, ok, sysui has a lot of pre-existing views that contain a
-lot of code that could benefit from injection and will need to be migrated
-off from Dependency#get uses. Similar to how fragments are injected, the view
-needs to be added to the interface
-com.android.systemui.util.InjectionInflationController$ViewInstanceCreator.
+Needing to inject objects into your View's constructor generally implies you
+are doing more work in your presentation layer than is advisable.
+Instead, create an injected controller for you view, inject into the
+controller, and then attach the view to the controller after inflation.
 
-```java
-public interface ViewInstanceCreator {
-+   QuickStatusBarHeader createQsHeader();
-}
-```
-
-Presumably you need to inflate that view from XML (otherwise why do you
-need anything special? see earlier sections about generic injection). To obtain
-an inflater that supports injected objects, call InjectionInflationController#injectable,
-which will wrap the inflater it is passed in one that can create injected
-objects when needed.
-
-```java
-@Override
-public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
-        Bundle savedInstanceState) {
-    return mInjectionInflater.injectable(inflater).inflate(R.layout.my_layout, container, false);
-}
-```
-
-There is one other important thing to note about injecting with views. SysUI
-already has a Context in its global dagger component, so if you simply inject
-a Context, you will not get the one that the view should have with proper
-theming. Because of this, always ensure to tag views that have @Inject with
-the @Named view context.
-
-```java
-public CustomView(@Named(VIEW_CONTEXT) Context themedViewContext, AttributeSet attrs,
-        OtherCustomDependency something) {
-    //...
-}
-```
+View injection generally causes headaches while testing, as inflating a view
+(which may in turn inflate other views) implicitly causes a Dagger graph to 
+be stood up, which may or may not contain the appropriately 
+faked/mocked/stubbed objects. It is a hard to control process.
 
 ## Updating Dagger2
 
diff --git a/packages/SystemUI/res/drawable/privacy_chip_bg.xml b/packages/SystemUI/res/drawable/privacy_chip_bg.xml
new file mode 100644
index 0000000..827cf4a
--- /dev/null
+++ b/packages/SystemUI/res/drawable/privacy_chip_bg.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2020 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+    <solid android:color="#242424" /> <!-- 14% of white -->
+    <padding android:paddingTop="@dimen/ongoing_appops_chip_bg_padding"
+        android:paddingBottom="@dimen/ongoing_appops_chip_bg_padding" />
+    <corners android:radius="@dimen/ongoing_appops_chip_bg_corner_radius" />
+</shape>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/bubbles_manage_button_education.xml b/packages/SystemUI/res/layout/bubbles_manage_button_education.xml
index 87dd58e..213bb92 100644
--- a/packages/SystemUI/res/layout/bubbles_manage_button_education.xml
+++ b/packages/SystemUI/res/layout/bubbles_manage_button_education.xml
@@ -14,7 +14,7 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License.
   -->
-<com.android.systemui.bubbles.BubbleManageEducationView
+<com.android.systemui.bubbles.ManageEducationView
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
@@ -87,4 +87,4 @@
                 />
         </LinearLayout>
     </LinearLayout>
-</com.android.systemui.bubbles.BubbleManageEducationView>
+</com.android.systemui.bubbles.ManageEducationView>
diff --git a/packages/SystemUI/res/layout/ongoing_privacy_chip.xml b/packages/SystemUI/res/layout/ongoing_privacy_chip.xml
new file mode 100644
index 0000000..3c30632
--- /dev/null
+++ b/packages/SystemUI/res/layout/ongoing_privacy_chip.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2020 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+
+<com.android.systemui.privacy.OngoingPrivacyChip
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/privacy_chip"
+    android:layout_height="match_parent"
+    android:layout_width="wrap_content"
+    android:layout_gravity="center_vertical|end"
+    android:focusable="true" >
+
+        <FrameLayout
+            android:id="@+id/background"
+            android:layout_height="@dimen/ongoing_appops_chip_height"
+            android:layout_width="wrap_content"
+            android:minWidth="48dp"
+            android:layout_gravity="center_vertical">
+                <LinearLayout
+                    android:id="@+id/icons_container"
+                    android:layout_height="match_parent"
+                    android:layout_width="wrap_content"
+                    android:gravity="center_vertical"
+                    />
+          </FrameLayout>
+</com.android.systemui.privacy.OngoingPrivacyChip>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/quick_status_bar_header_system_icons.xml b/packages/SystemUI/res/layout/quick_status_bar_header_system_icons.xml
index be86e5f..3c74801 100644
--- a/packages/SystemUI/res/layout/quick_status_bar_header_system_icons.xml
+++ b/packages/SystemUI/res/layout/quick_status_bar_header_system_icons.xml
@@ -14,7 +14,7 @@
 ** See the License for the specific language governing permissions and
 ** limitations under the License.
 -->
-<FrameLayout
+<LinearLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:systemui="http://schemas.android.com/apk/res-auto"
     android:id="@+id/quick_status_bar_system_icons"
@@ -27,6 +27,13 @@
     android:clickable="true"
     android:paddingTop="@dimen/status_bar_padding_top" >
 
+    <LinearLayout
+        android:layout_width="0dp"
+        android:layout_height="match_parent"
+        android:layout_weight="1"
+        android:orientation="horizontal"
+        android:gravity="center_vertical|start" >
+
     <com.android.systemui.statusbar.policy.Clock
         android:id="@+id/clock"
         android:layout_width="wrap_content"
@@ -38,5 +45,23 @@
         android:singleLine="true"
         android:textAppearance="@style/TextAppearance.StatusBar.Clock"
         systemui:showDark="false" />
+    </LinearLayout>
 
-</FrameLayout>
+    <android.widget.Space
+        android:id="@+id/space"
+        android:layout_width="0dp"
+        android:layout_height="match_parent"
+        android:layout_gravity="center_vertical|center_horizontal"
+        android:visibility="gone" />
+
+    <LinearLayout
+        android:layout_width="0dp"
+        android:layout_height="match_parent"
+        android:layout_weight="1"
+        android:orientation="horizontal"
+        android:gravity="center_vertical|end" >
+
+    <include layout="@layout/ongoing_privacy_chip" />
+
+    </LinearLayout>
+</LinearLayout>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 27db8cb..f407a8d 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -499,6 +499,8 @@
         <item>com.android.systemui</item>
     </string-array>
 
+    <integer name="ongoing_appops_dialog_max_apps">5</integer>
+
     <!-- Launcher package name for overlaying icons. -->
     <string name="launcher_overlayable_package" translatable="false">com.android.launcher3</string>
 
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index ea855eb..122fcb2 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -1137,6 +1137,23 @@
 
     <!-- How much into a DisplayCutout's bounds we can go, on each side -->
     <dimen name="display_cutout_margin_consumption">0px</dimen>
+
+    <!-- Height of the Ongoing App Ops chip -->
+    <dimen name="ongoing_appops_chip_height">32dp</dimen>
+    <!-- Padding between background of Ongoing App Ops chip and content -->
+    <dimen name="ongoing_appops_chip_bg_padding">8dp</dimen>
+    <!-- Side padding between background of Ongoing App Ops chip and content -->
+    <dimen name="ongoing_appops_chip_side_padding">8dp</dimen>
+    <!-- Margin between icons of Ongoing App Ops chip when QQS-->
+    <dimen name="ongoing_appops_chip_icon_margin_collapsed">0dp</dimen>
+    <!-- Margin between icons of Ongoing App Ops chip when QS-->
+    <dimen name="ongoing_appops_chip_icon_margin_expanded">2dp</dimen>
+    <!-- Icon size of Ongoing App Ops chip -->
+    <dimen name="ongoing_appops_chip_icon_size">@dimen/status_bar_icon_drawing_size</dimen>
+    <!-- Radius of Ongoing App Ops chip corners -->
+    <dimen name="ongoing_appops_chip_bg_corner_radius">16dp</dimen>
+
+
     <!-- How much each bubble is elevated. -->
     <dimen name="bubble_elevation">1dp</dimen>
     <!-- How much the bubble flyout text container is elevated. -->
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index d97aea7..8b6543a 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -2596,6 +2596,27 @@
          app for debugging. Will not be seen by users. [CHAR LIMIT=20] -->
     <string name="heap_dump_tile_name">Dump SysUI Heap</string>
 
+    <!-- Content description for ongoing privacy chip. Use with a single app [CHAR LIMIT=NONE]-->
+    <string name="ongoing_privacy_chip_content_single_app"><xliff:g id="app" example="Example App">%1$s</xliff:g> is using your <xliff:g id="types_list" example="camera, location">%2$s</xliff:g>.</string>
+
+    <!-- Content description for ongoing privacy chip. Use with multiple apps [CHAR LIMIT=NONE]-->
+    <string name="ongoing_privacy_chip_content_multiple_apps">Applications are using your <xliff:g id="types_list" example="camera, location">%s</xliff:g>.</string>
+
+    <!-- Separator for types. Include spaces before and after if needed [CHAR LIMIT=10] -->
+    <string name="ongoing_privacy_dialog_separator">,\u0020</string>
+
+    <!-- Separator for types, before last type. Include spaces before and after if needed [CHAR LIMIT=10] -->
+    <string name="ongoing_privacy_dialog_last_separator">\u0020and\u0020</string>
+
+    <!-- Text for camera app op [CHAR LIMIT=20]-->
+    <string name="privacy_type_camera">camera</string>
+
+    <!-- Text for location app op [CHAR LIMIT=20]-->
+    <string name="privacy_type_location">location</string>
+
+    <!-- Text for microphone app op [CHAR LIMIT=20]-->
+    <string name="privacy_type_microphone">microphone</string>
+
     <!-- Text for the quick setting tile for sensor privacy [CHAR LIMIT=30] -->
     <string name="sensor_privacy_mode">Sensors off</string>
 
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 8a36e7b..878947f 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -287,11 +287,11 @@
     private final Executor mBackgroundExecutor;
 
     /**
-     * Short delay before restarting biometric authentication after a successful try
-     * This should be slightly longer than the time between on<biometric>Authenticated
-     * (e.g. onFingerprintAuthenticated) and setKeyguardGoingAway(true).
+     * Short delay before restarting fingerprint authentication after a successful try. This should
+     * be slightly longer than the time between onFingerprintAuthenticated and
+     * setKeyguardGoingAway(true).
      */
-    private static final int BIOMETRIC_CONTINUE_DELAY_MS = 500;
+    private static final int FINGERPRINT_CONTINUE_DELAY_MS = 500;
 
     // If the HAL dies or is unable to authenticate, keyguard should retry after a short delay
     private int mHardwareFingerprintUnavailableRetryCount = 0;
@@ -599,7 +599,7 @@
         }
 
         mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_BIOMETRIC_AUTHENTICATION_CONTINUE),
-                BIOMETRIC_CONTINUE_DELAY_MS);
+                FINGERPRINT_CONTINUE_DELAY_MS);
 
         // Only authenticate fingerprint once when assistant is visible
         mAssistantVisible = false;
@@ -782,9 +782,6 @@
             }
         }
 
-        mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_BIOMETRIC_AUTHENTICATION_CONTINUE),
-                BIOMETRIC_CONTINUE_DELAY_MS);
-
         // Only authenticate face once when assistant is visible
         mAssistantVisible = false;
 
diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java
index 49a2328..58f8c07 100644
--- a/packages/SystemUI/src/com/android/systemui/Dependency.java
+++ b/packages/SystemUI/src/com/android/systemui/Dependency.java
@@ -54,6 +54,7 @@
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.power.EnhancedEstimates;
 import com.android.systemui.power.PowerUI;
+import com.android.systemui.privacy.PrivacyItemController;
 import com.android.systemui.recents.OverviewProxyService;
 import com.android.systemui.recents.Recents;
 import com.android.systemui.screenrecord.RecordingController;
@@ -130,9 +131,9 @@
 
 import javax.inject.Inject;
 import javax.inject.Named;
+import javax.inject.Singleton;
 
 import dagger.Lazy;
-import dagger.Subcomponent;
 
 /**
  * Class to handle ugly dependencies throughout sysui until we determine the
@@ -149,6 +150,7 @@
  * they have no clients they should not have any registered resources like bound
  * services, registered receivers, etc.
  */
+@Singleton
 public class Dependency {
     /**
      * Key for getting a the main looper.
@@ -294,6 +296,7 @@
     @Inject Lazy<SensorPrivacyManager> mSensorPrivacyManager;
     @Inject Lazy<AutoHideController> mAutoHideController;
     @Inject Lazy<ForegroundServiceNotificationListener> mForegroundServiceNotificationListener;
+    @Inject Lazy<PrivacyItemController> mPrivacyItemController;
     @Inject @Background Lazy<Looper> mBgLooper;
     @Inject @Background Lazy<Handler> mBgHandler;
     @Inject @Main Lazy<Looper> mMainLooper;
@@ -491,6 +494,7 @@
         mProviders.put(ForegroundServiceNotificationListener.class,
                 mForegroundServiceNotificationListener::get);
         mProviders.put(ClockManager.class, mClockManager::get);
+        mProviders.put(PrivacyItemController.class, mPrivacyItemController::get);
         mProviders.put(ActivityManagerWrapper.class, mActivityManagerWrapper::get);
         mProviders.put(DevicePolicyManagerWrapper.class, mDevicePolicyManagerWrapper::get);
         mProviders.put(PackageManagerWrapper.class, mPackageManagerWrapper::get);
@@ -519,7 +523,12 @@
         mProviders.put(RecordingController.class, mRecordingController::get);
         mProviders.put(Divider.class, mDivider::get);
 
-        sDependency = this;
+        Dependency.setInstance(this);
+    }
+
+    @VisibleForTesting
+    public static void setInstance(Dependency dependency) {
+        sDependency = dependency;
     }
 
     protected final <T> T getDependency(Class<T> cls) {
@@ -546,7 +555,7 @@
     }
 
     @VisibleForTesting
-    protected <T> T createDependency(Object cls) {
+    public <T> T createDependency(Object cls) {
         Preconditions.checkArgument(cls instanceof DependencyKey<?> || cls instanceof Class<?>);
 
         @SuppressWarnings("unchecked")
@@ -635,9 +644,4 @@
             return mDisplayName;
         }
     }
-
-    @Subcomponent
-    public interface DependencyInjector {
-        void createSystemUI(Dependency dependency);
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
index 5674fdd..1a15c0a 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
@@ -16,7 +16,6 @@
 
 package com.android.systemui;
 
-import android.annotation.NonNull;
 import android.content.Context;
 import android.content.res.Resources;
 import android.os.Handler;
@@ -30,7 +29,6 @@
 import com.android.keyguard.ViewMediatorCallback;
 import com.android.systemui.bubbles.BubbleController;
 import com.android.systemui.dagger.DaggerSystemUIRootComponent;
-import com.android.systemui.dagger.DependencyProvider;
 import com.android.systemui.dagger.SystemUIRootComponent;
 import com.android.systemui.keyguard.DismissCallbackRegistry;
 import com.android.systemui.plugins.FalsingManager;
@@ -48,9 +46,6 @@
 
 import java.util.concurrent.Executor;
 
-import dagger.Module;
-import dagger.Provides;
-
 /**
  * Class factory to provide customizable SystemUI components.
  */
@@ -97,24 +92,13 @@
 
         // Every other part of our codebase currently relies on Dependency, so we
         // really need to ensure the Dependency gets initialized early on.
-
-        Dependency dependency = new Dependency();
-        mRootComponent.createDependency().createSystemUI(dependency);
+        Dependency dependency = mRootComponent.createDependency();
         dependency.start();
     }
 
-    protected void initWithRootComponent(@NonNull SystemUIRootComponent rootComponent) {
-        if (mRootComponent != null) {
-            throw new RuntimeException("Root component can be set only once.");
-        }
-
-        mRootComponent = rootComponent;
-    }
-
     protected SystemUIRootComponent buildSystemUIRootComponent(Context context) {
         return DaggerSystemUIRootComponent.builder()
-                .dependencyProvider(new DependencyProvider())
-                .contextHolder(new ContextHolder(context))
+                .context(context)
                 .build();
     }
 
@@ -168,18 +152,4 @@
                 Dependency.get(DozeParameters.class),
                 Dependency.get(BubbleController.class));
     }
-
-    @Module
-    public static class ContextHolder {
-        private Context mContext;
-
-        public ContextHolder(Context context) {
-            mContext = context;
-        }
-
-        @Provides
-        public Context provideContext() {
-            return mContext;
-        }
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/DisplayIdIndexSupplier.java b/packages/SystemUI/src/com/android/systemui/accessibility/DisplayIdIndexSupplier.java
new file mode 100644
index 0000000..769a344
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/DisplayIdIndexSupplier.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.accessibility;
+
+import android.annotation.Nullable;
+import android.hardware.display.DisplayManager;
+import android.util.SparseArray;
+import android.view.Display;
+
+import androidx.annotation.NonNull;
+
+/**
+ * Supplies the instance with given display Id. It generates a new instance if the corresponding
+ * one is not existed. It should run in single thread to avoid race conditions.
+ *
+ * @param <T> the type of results supplied by {@link #createInstance(Display)}.
+ */
+abstract class DisplayIdIndexSupplier<T> {
+
+    private final SparseArray<T> mSparseArray = new SparseArray<>();
+    private final DisplayManager mDisplayManager;
+
+    /**
+     * @param displayManager DisplayManager
+     */
+    DisplayIdIndexSupplier(DisplayManager displayManager) {
+        mDisplayManager = displayManager;
+    }
+
+    /**
+     * @param displayId the logical display Id
+     * @return {@code null} if the given display id is invalid
+     */
+    @Nullable
+    public T get(int displayId) {
+        T instance = mSparseArray.get(displayId);
+        if (instance != null) {
+            return instance;
+        }
+        final Display display = mDisplayManager.getDisplay(displayId);
+        if (display == null) {
+            return null;
+        }
+        instance = createInstance(display);
+        mSparseArray.put(displayId, instance);
+        return instance;
+    }
+
+    @NonNull
+    protected abstract T createInstance(Display display);
+
+    /**
+     * Removes the instance with given display Id.
+     *
+     * @param displayId the logical display id
+     */
+    public void remove(int displayId) {
+        mSparseArray.remove(displayId);
+    }
+
+    /**
+     * Clears all elements.
+     */
+    public void clear() {
+        mSparseArray.clear();
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationModeSwitch.java b/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationModeSwitch.java
index 398a2c9..68a0a65 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationModeSwitch.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationModeSwitch.java
@@ -30,6 +30,7 @@
 /**
  * Shows/hides a {@link android.widget.ImageView} on the screen and changes the values of
  * {@link Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE} when the UI is toggled.
+ * The button UI would automatically be dismissed after displaying for a period of time.
  */
 class MagnificationModeSwitch {
 
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/ModeSwitchesController.java b/packages/SystemUI/src/com/android/systemui/accessibility/ModeSwitchesController.java
index e73ff13..ffc70bc 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/ModeSwitchesController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/ModeSwitchesController.java
@@ -19,31 +19,33 @@
 import android.annotation.MainThread;
 import android.content.Context;
 import android.hardware.display.DisplayManager;
-import android.util.Log;
-import android.util.SparseArray;
 import android.view.Display;
 
+import com.android.internal.annotations.VisibleForTesting;
+
 import javax.inject.Singleton;
 
 /**
- * Class to control magnification mode switch button. Shows the button UI when both full-screen
- * and window magnification mode are capable, and when the magnification scale is changed. And
- * the button UI would automatically be dismissed after displaying for a period of time.
+ * A class to control {@link MagnificationModeSwitch}. It should show the button UI with following
+ * conditions:
+ * <ol>
+ *   <li> Both full-screen and window magnification mode are capable.</li>
+ *   <li> The magnification scale is changed by a user.</li>
+ * <ol>
  */
 @Singleton
 public class ModeSwitchesController {
 
-    private static final String TAG = "ModeSwitchesController";
-
-    private final Context mContext;
-    private final DisplayManager mDisplayManager;
-
-    private final SparseArray<MagnificationModeSwitch> mDisplaysToSwitches =
-            new SparseArray<>();
+    private final SwitchSupplier mSwitchSupplier;
 
     public ModeSwitchesController(Context context) {
-        mContext = context;
-        mDisplayManager = mContext.getSystemService(DisplayManager.class);
+        mSwitchSupplier = new SwitchSupplier(context,
+                context.getSystemService(DisplayManager.class));
+    }
+
+    @VisibleForTesting
+    ModeSwitchesController(SwitchSupplier switchSupplier) {
+        mSwitchSupplier = switchSupplier;
     }
 
     /**
@@ -52,20 +54,17 @@
      *
      * @param displayId The logical display id
      * @param mode      The magnification mode
-     *
      * @see android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW
      * @see android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN
      */
     @MainThread
     void showButton(int displayId, int mode) {
-        if (mDisplaysToSwitches.get(displayId) == null) {
-            final MagnificationModeSwitch magnificationModeSwitch =
-                    createMagnificationSwitchController(displayId);
-            if (magnificationModeSwitch == null) {
-                return;
-            }
+        final MagnificationModeSwitch magnificationModeSwitch =
+                mSwitchSupplier.get(displayId);
+        if (magnificationModeSwitch == null) {
+            return;
         }
-        mDisplaysToSwitches.get(displayId).showButton(mode);
+        magnificationModeSwitch.showButton(mode);
     }
 
     /**
@@ -74,30 +73,34 @@
      * @param displayId The logical display id
      */
     void removeButton(int displayId) {
-        if (mDisplaysToSwitches.get(displayId) == null) {
+        final MagnificationModeSwitch magnificationModeSwitch =
+                mSwitchSupplier.get(displayId);
+        if (magnificationModeSwitch == null) {
             return;
         }
-        mDisplaysToSwitches.get(displayId).removeButton();
+        magnificationModeSwitch.removeButton();
     }
 
-    private MagnificationModeSwitch createMagnificationSwitchController(int displayId) {
-        if (mDisplayManager.getDisplay(displayId) == null) {
-            Log.w(TAG, "createMagnificationSwitchController displayId is invalid.");
-            return null;
+    @VisibleForTesting
+    static class SwitchSupplier extends DisplayIdIndexSupplier<MagnificationModeSwitch> {
+
+        private final Context mContext;
+
+        /**
+         * @param context        Context
+         * @param displayManager DisplayManager
+         */
+        SwitchSupplier(Context context, DisplayManager displayManager) {
+            super(displayManager);
+            mContext = context;
         }
-        final MagnificationModeSwitch
-                magnificationModeSwitch = new MagnificationModeSwitch(
-                getDisplayContext(displayId));
-        mDisplaysToSwitches.put(displayId, magnificationModeSwitch);
-        return magnificationModeSwitch;
-    }
 
-    private Context getDisplayContext(int displayId) {
-        final Display display = mDisplayManager.getDisplay(displayId);
-        final Context context = (displayId == Display.DEFAULT_DISPLAY)
-                ? mContext
-                : mContext.createDisplayContext(display);
-        return context;
+        @Override
+        protected MagnificationModeSwitch createInstance(Display display) {
+            final Context context = (display.getDisplayId() == Display.DEFAULT_DISPLAY)
+                    ? mContext
+                    : mContext.createDisplayContext(display);
+            return new MagnificationModeSwitch(context);
+        }
     }
-
 }
diff --git a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
index 5fd7b53..4df6660 100644
--- a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
@@ -18,6 +18,7 @@
 
 import android.app.AppOpsManager;
 import android.content.Context;
+import android.content.pm.PackageManager;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.UserHandle;
@@ -25,11 +26,14 @@
 import android.util.Log;
 import android.util.SparseArray;
 
+import androidx.annotation.WorkerThread;
+
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.systemui.Dumpable;
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dump.DumpManager;
+import com.android.systemui.util.Assert;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -62,6 +66,7 @@
     private H mBGHandler;
     private final List<AppOpsController.Callback> mCallbacks = new ArrayList<>();
     private final SparseArray<Set<Callback>> mCallbacksByCode = new SparseArray<>();
+    private final PermissionFlagsCache mFlagsCache;
     private boolean mListening;
 
     @GuardedBy("mActiveItems")
@@ -82,8 +87,11 @@
     public AppOpsControllerImpl(
             Context context,
             @Background Looper bgLooper,
-            DumpManager dumpManager) {
+            DumpManager dumpManager,
+            PermissionFlagsCache cache
+    ) {
         mAppOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
+        mFlagsCache = cache;
         mBGHandler = new H(bgLooper);
         final int numOps = OPS.length;
         for (int i = 0; i < numOps; i++) {
@@ -230,10 +238,66 @@
     }
 
     /**
+     * Does the app-op code refer to a user sensitive permission for the specified user id
+     * and package. Only user sensitive permission should be shown to the user by default.
+     *
+     * @param appOpCode The code of the app-op.
+     * @param uid The uid of the user.
+     * @param packageName The name of the package.
+     *
+     * @return {@code true} iff the app-op item is user sensitive
+     */
+    private boolean isUserSensitive(int appOpCode, int uid, String packageName) {
+        String permission = AppOpsManager.opToPermission(appOpCode);
+        if (permission == null) {
+            return false;
+        }
+        int permFlags = mFlagsCache.getPermissionFlags(permission,
+                packageName, uid);
+        return (permFlags & PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED) != 0;
+    }
+
+    /**
+     * Does the app-op item refer to an operation that should be shown to the user.
+     * Only specficic ops (like SYSTEM_ALERT_WINDOW) or ops that refer to user sensitive
+     * permission should be shown to the user by default.
+     *
+     * @param item The item
+     *
+     * @return {@code true} iff the app-op item should be shown to the user
+     */
+    private boolean isUserVisible(AppOpItem item) {
+        return isUserVisible(item.getCode(), item.getUid(), item.getPackageName());
+    }
+
+
+    /**
+     * Does the app-op, uid and package name, refer to an operation that should be shown to the
+     * user. Only specficic ops (like {@link AppOpsManager.OP_SYSTEM_ALERT_WINDOW}) or
+     * ops that refer to user sensitive permission should be shown to the user by default.
+     *
+     * @param item The item
+     *
+     * @return {@code true} iff the app-op for should be shown to the user
+     */
+    private boolean isUserVisible(int appOpCode, int uid, String packageName) {
+        // currently OP_SYSTEM_ALERT_WINDOW does not correspond to a platform permission
+        // which may be user senstive, so for now always show it to the user.
+        if (appOpCode == AppOpsManager.OP_SYSTEM_ALERT_WINDOW) {
+            return true;
+        }
+
+        return isUserSensitive(appOpCode, uid, packageName);
+    }
+
+    /**
      * Returns a copy of the list containing all the active AppOps that the controller tracks.
      *
+     * Call from a worker thread as it may perform long operations.
+     *
      * @return List of active AppOps information
      */
+    @WorkerThread
     public List<AppOpItem> getActiveAppOps() {
         return getActiveAppOpsForUser(UserHandle.USER_ALL);
     }
@@ -242,18 +306,23 @@
      * Returns a copy of the list containing all the active AppOps that the controller tracks, for
      * a given user id.
      *
+     * Call from a worker thread as it may perform long operations.
+     *
      * @param userId User id to track, can be {@link UserHandle#USER_ALL}
      *
      * @return List of active AppOps information for that user id
      */
+    @WorkerThread
     public List<AppOpItem> getActiveAppOpsForUser(int userId) {
+        Assert.isNotMainThread();
         List<AppOpItem> list = new ArrayList<>();
         synchronized (mActiveItems) {
             final int numActiveItems = mActiveItems.size();
             for (int i = 0; i < numActiveItems; i++) {
                 AppOpItem item = mActiveItems.get(i);
                 if ((userId == UserHandle.USER_ALL
-                        || UserHandle.getUserId(item.getUid()) == userId)) {
+                        || UserHandle.getUserId(item.getUid()) == userId)
+                        && isUserVisible(item)) {
                     list.add(item);
                 }
             }
@@ -263,7 +332,8 @@
             for (int i = 0; i < numNotedItems; i++) {
                 AppOpItem item = mNotedItems.get(i);
                 if ((userId == UserHandle.USER_ALL
-                        || UserHandle.getUserId(item.getUid()) == userId)) {
+                        || UserHandle.getUserId(item.getUid()) == userId)
+                        && isUserVisible(item)) {
                     list.add(item);
                 }
             }
@@ -311,7 +381,7 @@
     }
 
     private void notifySuscribers(int code, int uid, String packageName, boolean active) {
-        if (mCallbacksByCode.contains(code)) {
+        if (mCallbacksByCode.contains(code) && isUserVisible(code, uid, packageName)) {
             if (DEBUG) Log.d(TAG, "Notifying of change in package " + packageName);
             for (Callback cb: mCallbacksByCode.get(code)) {
                 cb.onActiveStateChanged(code, uid, packageName, active);
diff --git a/packages/SystemUI/src/com/android/systemui/appops/PermissionFlagsCache.kt b/packages/SystemUI/src/com/android/systemui/appops/PermissionFlagsCache.kt
new file mode 100644
index 0000000..45ed78f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/appops/PermissionFlagsCache.kt
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.appops
+
+import android.content.pm.PackageManager
+import android.os.UserHandle
+import androidx.annotation.WorkerThread
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.util.Assert
+import java.util.concurrent.Executor
+import javax.inject.Inject
+import javax.inject.Singleton
+
+private data class PermissionFlagKey(
+    val permission: String,
+    val packageName: String,
+    val uid: Int
+)
+
+/**
+ * Cache for PackageManager's PermissionFlags.
+ *
+ * After a specific `{permission, package, uid}` has been requested, updates to it will be tracked,
+ * and changes to the uid will trigger new requests (in the background).
+ */
+@Singleton
+class PermissionFlagsCache @Inject constructor(
+    private val packageManager: PackageManager,
+    @Background private val executor: Executor
+) : PackageManager.OnPermissionsChangedListener {
+
+    private val permissionFlagsCache =
+            mutableMapOf<Int, MutableMap<PermissionFlagKey, Int>>()
+    private var listening = false
+
+    override fun onPermissionsChanged(uid: Int) {
+        executor.execute {
+            // Only track those that we've seen before
+            val keys = permissionFlagsCache.get(uid)
+            if (keys != null) {
+                keys.mapValuesTo(keys) {
+                    getFlags(it.key)
+                }
+            }
+        }
+    }
+
+    /**
+     * Retrieve permission flags from cache or PackageManager. There parameters will be passed
+     * directly to [PackageManager].
+     *
+     * Calls to this method should be done from a background thread (though it will only be
+     * enforced if the cache is not hit).
+     */
+    @WorkerThread
+    fun getPermissionFlags(permission: String, packageName: String, uid: Int): Int {
+        if (!listening) {
+            listening = true
+            packageManager.addOnPermissionsChangeListener(this)
+        }
+        val key = PermissionFlagKey(permission, packageName, uid)
+        return permissionFlagsCache.getOrPut(uid, { mutableMapOf() }).get(key) ?: run {
+            getFlags(key).also {
+                Assert.isNotMainThread()
+                permissionFlagsCache.get(uid)?.put(key, it)
+            }
+        }
+    }
+
+    private fun getFlags(key: PermissionFlagKey): Int {
+        return packageManager.getPermissionFlags(key.permission, key.packageName,
+                UserHandle.getUserHandleForUid(key.uid))
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
index 980e4c0..361ea67 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
@@ -29,7 +29,6 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
-import android.content.pm.PackageManager;
 import android.content.res.Configuration;
 import android.hardware.biometrics.BiometricAuthenticator;
 import android.hardware.biometrics.BiometricConstants;
@@ -39,12 +38,10 @@
 import android.hardware.face.FaceManager;
 import android.hardware.fingerprint.FingerprintManager;
 import android.hardware.fingerprint.FingerprintSensorProperties;
-import android.hardware.fingerprint.IFingerprintService;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.RemoteException;
-import android.os.ServiceManager;
 import android.util.Log;
 import android.view.WindowManager;
 
@@ -247,6 +244,10 @@
         IActivityTaskManager getActivityTaskManager() {
             return ActivityTaskManager.getService();
         }
+
+        FingerprintManager getFingerprintManager(Context context) {
+            return context.getSystemService(FingerprintManager.class);
+        }
     }
 
     @Inject
@@ -273,7 +274,7 @@
         mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
         mActivityTaskManager = mInjector.getActivityTaskManager();
 
-        final FingerprintManager fpm = mContext.getSystemService(FingerprintManager.class);
+        final FingerprintManager fpm = mInjector.getFingerprintManager(mContext);
         if (fpm != null && fpm.isHardwareDetected()) {
             final List<FingerprintSensorProperties> fingerprintSensorProperties =
                     fpm.getSensorProperties();
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleDismissView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleDismissView.java
deleted file mode 100644
index 9db371e..0000000
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleDismissView.java
+++ /dev/null
@@ -1,148 +0,0 @@
-/*
- * 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.bubbles;
-
-import android.content.Context;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.animation.AccelerateDecelerateInterpolator;
-import android.widget.FrameLayout;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-
-import androidx.dynamicanimation.animation.DynamicAnimation;
-import androidx.dynamicanimation.animation.SpringAnimation;
-import androidx.dynamicanimation.animation.SpringForce;
-
-import com.android.systemui.R;
-
-/** Dismiss view that contains a scrim gradient, as well as a dismiss icon, text, and circle. */
-public class BubbleDismissView extends FrameLayout {
-    /** Duration for animations involving the dismiss target text/icon. */
-    private static final int DISMISS_TARGET_ANIMATION_BASE_DURATION = 150;
-    private static final float SCALE_FOR_POP = 1.2f;
-    private static final float SCALE_FOR_DISMISS = 0.9f;
-
-    private LinearLayout mDismissTarget;
-    private ImageView mDismissIcon;
-    private View mDismissCircle;
-
-    private SpringAnimation mDismissTargetAlphaSpring;
-    private SpringAnimation mDismissTargetVerticalSpring;
-
-    public BubbleDismissView(Context context) {
-        super(context);
-        setVisibility(GONE);
-
-        LayoutInflater.from(context).inflate(R.layout.bubble_dismiss_target, this, true);
-        mDismissTarget = findViewById(R.id.bubble_dismiss_icon_container);
-        mDismissIcon = findViewById(R.id.bubble_dismiss_close_icon);
-        mDismissCircle = findViewById(R.id.bubble_dismiss_circle);
-
-        // Set up the basic target area animations. These are very simple animations that don't need
-        // fancy interpolators.
-        final AccelerateDecelerateInterpolator interpolator =
-                new AccelerateDecelerateInterpolator();
-        mDismissIcon.animate()
-                .setDuration(DISMISS_TARGET_ANIMATION_BASE_DURATION)
-                .setInterpolator(interpolator);
-        mDismissCircle.animate()
-                .setDuration(DISMISS_TARGET_ANIMATION_BASE_DURATION / 2)
-                .setInterpolator(interpolator);
-
-        mDismissTargetAlphaSpring =
-                new SpringAnimation(mDismissTarget, DynamicAnimation.ALPHA)
-                        .setSpring(new SpringForce()
-                                .setStiffness(SpringForce.STIFFNESS_LOW)
-                                .setDampingRatio(SpringForce.DAMPING_RATIO_LOW_BOUNCY));
-        mDismissTargetVerticalSpring =
-                new SpringAnimation(mDismissTarget, DynamicAnimation.TRANSLATION_Y)
-                        .setSpring(new SpringForce()
-                                .setStiffness(SpringForce.STIFFNESS_MEDIUM)
-                                .setDampingRatio(SpringForce.DAMPING_RATIO_LOW_BOUNCY));
-
-        mDismissTargetAlphaSpring.addEndListener((anim, canceled, alpha, velocity) -> {
-            // Since DynamicAnimations end when they're 'nearly' done, we can't rely on alpha being
-            // exactly zero when this listener is triggered. However, if it's less than 50% we can
-            // safely assume it was animating out rather than in.
-            if (alpha < 0.5f) {
-                // If the alpha spring was animating the view out, set it to GONE when it's done.
-                setVisibility(INVISIBLE);
-            }
-        });
-    }
-
-    /** Springs in the dismiss target. */
-    void springIn() {
-        setVisibility(View.VISIBLE);
-
-        // Fade in the dismiss target icon.
-        mDismissIcon.animate()
-                .setDuration(50)
-                .scaleX(1f)
-                .scaleY(1f)
-                .alpha(1f);
-        mDismissTarget.setAlpha(0f);
-        mDismissTargetAlphaSpring.animateToFinalPosition(1f);
-
-        // Spring up the dismiss target.
-        mDismissTarget.setTranslationY(mDismissTarget.getHeight() / 2f);
-        mDismissTargetVerticalSpring.animateToFinalPosition(0);
-
-        mDismissCircle.setAlpha(0f);
-        mDismissCircle.setScaleX(SCALE_FOR_POP);
-        mDismissCircle.setScaleY(SCALE_FOR_POP);
-
-        // Fade in circle and reduce size.
-        mDismissCircle.animate()
-                .alpha(1f)
-                .scaleX(1f)
-                .scaleY(1f);
-    }
-
-    /** Springs out the dismiss target. */
-    void springOut() {
-        // Fade out the target icon.
-        mDismissIcon.animate()
-                .setDuration(50)
-                .scaleX(SCALE_FOR_DISMISS)
-                .scaleY(SCALE_FOR_DISMISS)
-                .alpha(0f);
-
-        // Fade out the target.
-        mDismissTargetAlphaSpring.animateToFinalPosition(0f);
-
-        // Spring the target down a bit.
-        mDismissTargetVerticalSpring.animateToFinalPosition(mDismissTarget.getHeight() / 2f);
-
-        // Pop out the circle.
-        mDismissCircle.animate()
-                .scaleX(SCALE_FOR_DISMISS)
-                .scaleY(SCALE_FOR_DISMISS)
-                .alpha(0f);
-    }
-
-    /** Returns the Y value of the center of the dismiss target. */
-    float getDismissTargetCenterY() {
-        return getTop() + mDismissTarget.getTop() + mDismissTarget.getHeight() / 2f;
-    }
-
-    /** Returns the dismiss target, which contains the text/icon and any added padding. */
-    View getDismissTarget() {
-        return mDismissTarget;
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleManageEducationView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleManageEducationView.java
deleted file mode 100644
index 86244ba..0000000
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleManageEducationView.java
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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.bubbles;
-
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.graphics.Color;
-import android.util.AttributeSet;
-import android.view.Gravity;
-import android.view.View;
-import android.widget.LinearLayout;
-import android.widget.TextView;
-
-import com.android.internal.util.ContrastColorUtil;
-import com.android.systemui.R;
-
-/**
- * Educational view to highlight the manage button that allows a user to configure the settings
- * for the bubble. Shown only the first time a user expands a bubble.
- */
-public class BubbleManageEducationView extends LinearLayout {
-
-    private View mManageView;
-    private TextView mTitleTextView;
-    private TextView mDescTextView;
-
-    public BubbleManageEducationView(Context context) {
-        this(context, null);
-    }
-
-    public BubbleManageEducationView(Context context, AttributeSet attrs) {
-        this(context, attrs, 0);
-    }
-
-    public BubbleManageEducationView(Context context, AttributeSet attrs, int defStyleAttr) {
-        this(context, attrs, defStyleAttr, 0);
-    }
-
-    public BubbleManageEducationView(Context context, AttributeSet attrs, int defStyleAttr,
-            int defStyleRes) {
-        super(context, attrs, defStyleAttr, defStyleRes);
-    }
-
-    @Override
-    protected void onFinishInflate() {
-        super.onFinishInflate();
-
-        mManageView = findViewById(R.id.manage_education_view);
-        mTitleTextView = findViewById(R.id.user_education_title);
-        mDescTextView = findViewById(R.id.user_education_description);
-
-        final TypedArray ta = mContext.obtainStyledAttributes(
-                new int[] {android.R.attr.colorAccent,
-                        android.R.attr.textColorPrimaryInverse});
-        final int bgColor = ta.getColor(0, Color.BLACK);
-        int textColor = ta.getColor(1, Color.WHITE);
-        ta.recycle();
-
-        textColor = ContrastColorUtil.ensureTextContrast(textColor, bgColor, true);
-        mTitleTextView.setTextColor(textColor);
-        mDescTextView.setTextColor(textColor);
-    }
-
-    /**
-     * Specifies the position for the manage view.
-     */
-    public void setManageViewPosition(int x, int y) {
-        mManageView.setTranslationX(x);
-        mManageView.setTranslationY(y);
-    }
-
-    /**
-     * @return the height of the view that shows the educational text and pointer.
-     */
-    public int getManageViewHeight() {
-        return mManageView.getHeight();
-    }
-
-    @Override
-    public void setLayoutDirection(int direction) {
-        super.setLayoutDirection(direction);
-        if (getResources().getConfiguration().getLayoutDirection() == View.LAYOUT_DIRECTION_RTL) {
-            mManageView.setBackgroundResource(R.drawable.bubble_stack_user_education_bg_rtl);
-            mTitleTextView.setGravity(Gravity.RIGHT);
-            mDescTextView.setGravity(Gravity.RIGHT);
-        } else {
-            mManageView.setBackgroundResource(R.drawable.bubble_stack_user_education_bg);
-            mTitleTextView.setGravity(Gravity.LEFT);
-            mDescTextView.setGravity(Gravity.LEFT);
-        }
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflow.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflow.java
deleted file mode 100644
index bb9d109..0000000
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflow.java
+++ /dev/null
@@ -1,179 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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.bubbles;
-
-import static android.view.Display.INVALID_DISPLAY;
-import static android.view.View.GONE;
-
-import static com.android.systemui.bubbles.BadgedImageView.DEFAULT_PATH_SIZE;
-
-import android.content.Context;
-import android.content.res.Configuration;
-import android.content.res.Resources;
-import android.graphics.Bitmap;
-import android.graphics.Matrix;
-import android.graphics.Path;
-import android.graphics.drawable.AdaptiveIconDrawable;
-import android.graphics.drawable.ColorDrawable;
-import android.graphics.drawable.InsetDrawable;
-import android.util.PathParser;
-import android.util.TypedValue;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.ImageView;
-
-import com.android.systemui.R;
-
-/**
- * Class for showing aged out bubbles.
- */
-public class BubbleOverflow implements BubbleViewProvider {
-    public static final String KEY = "Overflow";
-
-    private BadgedImageView mOverflowBtn;
-    private BubbleExpandedView mExpandedView;
-    private LayoutInflater mInflater;
-    private Context mContext;
-    private Bitmap mIcon;
-    private Path mPath;
-    private int mBitmapSize;
-    private int mIconBitmapSize;
-    private int mDotColor;
-
-    public BubbleOverflow(Context context) {
-        mContext = context;
-        mInflater = LayoutInflater.from(context);
-    }
-
-    void setUpOverflow(ViewGroup parentViewGroup, BubbleStackView stackView) {
-        updateDimensions();
-        mExpandedView = (BubbleExpandedView) mInflater.inflate(
-                R.layout.bubble_expanded_view, parentViewGroup /* root */,
-                false /* attachToRoot */);
-        mExpandedView.setOverflow(true);
-        mExpandedView.setStackView(stackView);
-        mExpandedView.applyThemeAttrs();
-        updateIcon(mContext, parentViewGroup);
-    }
-
-    void updateDimensions() {
-        mBitmapSize = mContext.getResources().getDimensionPixelSize(R.dimen.bubble_bitmap_size);
-        mIconBitmapSize = mContext.getResources().getDimensionPixelSize(
-                R.dimen.bubble_overflow_icon_bitmap_size);
-        if (mExpandedView != null) {
-            mExpandedView.updateDimensions();
-        }
-    }
-
-    void updateIcon(Context context, ViewGroup parentViewGroup) {
-        mContext = context;
-        mInflater = LayoutInflater.from(context);
-        mOverflowBtn = (BadgedImageView) mInflater.inflate(R.layout.bubble_overflow_button,
-                parentViewGroup /* root */,
-                false /* attachToRoot */);
-        mOverflowBtn.setContentDescription(mContext.getResources().getString(
-                R.string.bubble_overflow_button_content_description));
-        Resources res = mContext.getResources();
-
-        // Set color for button icon and dot
-        TypedValue typedValue = new TypedValue();
-        mContext.getTheme().resolveAttribute(android.R.attr.colorAccent, typedValue, true);
-        int colorAccent = mContext.getColor(typedValue.resourceId);
-        mOverflowBtn.getDrawable().setTint(colorAccent);
-        mDotColor = colorAccent;
-
-        // Set color for button and activity background
-        ColorDrawable bg = new ColorDrawable(res.getColor(R.color.bubbles_light));
-        final int mode = res.getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
-        if (mode == Configuration.UI_MODE_NIGHT_YES) {
-            bg = new ColorDrawable(res.getColor(R.color.bubbles_dark));
-        }
-
-        // Apply icon inset
-        InsetDrawable fg = new InsetDrawable(mOverflowBtn.getDrawable(),
-                mBitmapSize - mIconBitmapSize /* inset */);
-        AdaptiveIconDrawable adaptiveIconDrawable = new AdaptiveIconDrawable(bg, fg);
-
-        BubbleIconFactory iconFactory = new BubbleIconFactory(mContext);
-        mIcon = iconFactory.createBadgedIconBitmap(adaptiveIconDrawable,
-                null /* user */,
-                true /* shrinkNonAdaptiveIcons */).icon;
-
-        // Get path with dot location
-        float scale = iconFactory.getNormalizer().getScale(mOverflowBtn.getDrawable(),
-                null /* outBounds */, null /* path */, null /* outMaskShape */);
-        float radius = DEFAULT_PATH_SIZE / 2f;
-        mPath = PathParser.createPathFromPathData(
-                mContext.getResources().getString(com.android.internal.R.string.config_icon_mask));
-        Matrix matrix = new Matrix();
-        matrix.setScale(scale /* x scale */, scale /* y scale */, radius /* pivot x */,
-                radius /* pivot y */);
-        mPath.transform(matrix);
-
-        mOverflowBtn.setRenderedBubble(this);
-    }
-
-    void setVisible(int visible) {
-        mOverflowBtn.setVisibility(visible);
-    }
-
-    @Override
-    public BubbleExpandedView getExpandedView() {
-        return mExpandedView;
-    }
-
-    @Override
-    public int getDotColor() {
-        return mDotColor;
-    }
-
-    @Override
-    public Bitmap getBadgedImage() {
-        return mIcon;
-    }
-
-    @Override
-    public boolean showDot() {
-        return false;
-    }
-
-    @Override
-    public Path getDotPath() {
-        return mPath;
-    }
-
-    @Override
-    public void setContentVisibility(boolean visible) {
-        mExpandedView.setContentVisibility(visible);
-    }
-
-    @Override
-    public View getIconView() {
-        return mOverflowBtn;
-    }
-
-    @Override
-    public String getKey() {
-        return BubbleOverflow.KEY;
-    }
-
-    @Override
-    public int getDisplayId() {
-        return mExpandedView != null ? mExpandedView.getVirtualDisplayId() : INVALID_DISPLAY;
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflow.kt b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflow.kt
new file mode 100644
index 0000000..155b71b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflow.kt
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.bubbles
+
+import android.content.Context
+import android.content.res.Configuration
+import android.graphics.Bitmap
+import android.graphics.Matrix
+import android.graphics.Path
+import android.graphics.drawable.AdaptiveIconDrawable
+import android.graphics.drawable.ColorDrawable
+import android.graphics.drawable.InsetDrawable
+import android.util.PathParser
+import android.util.TypedValue
+import android.view.LayoutInflater
+import android.view.View
+import android.widget.FrameLayout
+import com.android.systemui.R
+
+class BubbleOverflow(
+    private val context: Context,
+    private val stack: BubbleStackView
+) : BubbleViewProvider {
+
+    private var bitmap: Bitmap? = null
+    private var dotPath: Path? = null
+    private var bitmapSize = 0
+    private var iconBitmapSize = 0
+    private var dotColor = 0
+
+    private val inflater: LayoutInflater = LayoutInflater.from(context)
+    private val expandedView: BubbleExpandedView = inflater
+        .inflate(R.layout.bubble_expanded_view, null /* root */, false /* attachToRoot */)
+            as BubbleExpandedView
+    private val overflowBtn: BadgedImageView = inflater
+        .inflate(R.layout.bubble_overflow_button, null /* root */, false /* attachToRoot */)
+            as BadgedImageView
+    init {
+        updateResources()
+        with(expandedView) {
+            setOverflow(true)
+            setStackView(stack)
+            applyThemeAttrs()
+        }
+        with(overflowBtn) {
+            setContentDescription(context.resources.getString(
+                R.string.bubble_overflow_button_content_description))
+            updateBtnTheme()
+        }
+    }
+
+    fun update() {
+        updateResources()
+        expandedView.applyThemeAttrs()
+        // Apply inset and new style to fresh icon drawable.
+        overflowBtn.setImageResource(R.drawable.ic_bubble_overflow_button)
+        updateBtnTheme()
+    }
+
+    fun updateResources() {
+        bitmapSize = context.resources.getDimensionPixelSize(R.dimen.bubble_bitmap_size)
+        iconBitmapSize = context.resources.getDimensionPixelSize(
+                R.dimen.bubble_overflow_icon_bitmap_size)
+        val bubbleSize = context.resources.getDimensionPixelSize(R.dimen.individual_bubble_size)
+        overflowBtn.setLayoutParams(FrameLayout.LayoutParams(bubbleSize, bubbleSize))
+        expandedView.updateDimensions()
+    }
+
+    fun updateBtnTheme() {
+        val res = context.resources
+
+        // Set overflow button accent color, dot color
+        val typedValue = TypedValue()
+        context.theme.resolveAttribute(android.R.attr.colorAccent, typedValue, true)
+
+        val colorAccent = res.getColor(typedValue.resourceId)
+        overflowBtn.getDrawable()?.setTint(colorAccent)
+        dotColor = colorAccent
+
+        // Set button and activity background color
+        val nightMode = (res.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK
+                == Configuration.UI_MODE_NIGHT_YES)
+        val bg = ColorDrawable(res.getColor(
+                if (nightMode) R.color.bubbles_dark else R.color.bubbles_light))
+
+        // Set button icon
+        val iconFactory = BubbleIconFactory(context)
+        val fg = InsetDrawable(overflowBtn.getDrawable(),
+                bitmapSize - iconBitmapSize /* inset */)
+        bitmap = iconFactory.createBadgedIconBitmap(AdaptiveIconDrawable(bg, fg),
+                null /* user */, true /* shrinkNonAdaptiveIcons */).icon
+
+        // Set dot path
+        dotPath = PathParser.createPathFromPathData(
+                res.getString(com.android.internal.R.string.config_icon_mask))
+        val scale = iconFactory.normalizer.getScale(overflowBtn.getDrawable(),
+                null /* outBounds */, null /* path */, null /* outMaskShape */)
+        val radius = BadgedImageView.DEFAULT_PATH_SIZE / 2f
+        val matrix = Matrix()
+        matrix.setScale(scale /* x scale */, scale /* y scale */, radius /* pivot x */,
+                radius /* pivot y */)
+        dotPath?.transform(matrix)
+        overflowBtn.setRenderedBubble(this)
+    }
+
+    fun setVisible(visible: Int) {
+        overflowBtn.visibility = visible
+    }
+
+    override fun getExpandedView(): BubbleExpandedView? {
+        return expandedView
+    }
+
+    override fun getDotColor(): Int {
+        return dotColor
+    }
+
+    override fun getBadgedImage(): Bitmap? {
+        return bitmap
+    }
+
+    override fun showDot(): Boolean {
+        return false
+    }
+
+    override fun getDotPath(): Path? {
+        return dotPath
+    }
+
+    override fun setContentVisibility(visible: Boolean) {
+        expandedView.setContentVisibility(visible)
+    }
+
+    override fun getIconView(): View? {
+        return overflowBtn
+    }
+
+    override fun getKey(): String {
+        return KEY
+    }
+
+    override fun getDisplayId(): Int {
+        return expandedView.virtualDisplayId
+    }
+
+    companion object {
+        @JvmField val KEY = "Overflow"
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
index f02945e..ea12c95 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
@@ -48,7 +48,6 @@
 import android.graphics.Rect;
 import android.graphics.RectF;
 import android.graphics.Region;
-import android.graphics.drawable.TransitionDrawable;
 import android.os.Bundle;
 import android.os.Handler;
 import android.provider.Settings;
@@ -95,7 +94,6 @@
 import com.android.systemui.shared.system.QuickStepContract;
 import com.android.systemui.shared.system.SysUiStatsLog;
 import com.android.systemui.statusbar.phone.CollapsedStatusBarFragment;
-import com.android.systemui.util.DismissCircleView;
 import com.android.systemui.util.FloatingContentCoordinator;
 import com.android.systemui.util.RelativeTouchListener;
 import com.android.systemui.util.animation.PhysicsAnimator;
@@ -118,7 +116,7 @@
     private static final String TAG = TAG_WITH_CLASS_NAME ? "BubbleStackView" : TAG_BUBBLES;
 
     /** Animation durations for bubble stack user education views. **/
-    private static final int ANIMATE_STACK_USER_EDUCATION_DURATION = 200;
+    static final int ANIMATE_STACK_USER_EDUCATION_DURATION = 200;
     private static final int ANIMATE_STACK_USER_EDUCATION_DURATION_SHORT = 40;
 
     /** How far the flyout needs to be dragged before it's dismissed regardless of velocity. */
@@ -139,9 +137,6 @@
     /** Percent to darken the bubbles when they're in the dismiss target. */
     private static final float DARKEN_PERCENT = 0.3f;
 
-    /** Duration of the dismiss scrim fading in/out. */
-    private static final int DISMISS_TRANSITION_DURATION_MS = 200;
-
     /** How long to wait, in milliseconds, before hiding the flyout. */
     @VisibleForTesting
     static final int FLYOUT_HIDE_AFTER = 5000;
@@ -300,7 +295,7 @@
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         pw.println("Stack view state:");
         pw.print("  gestureInProgress:       "); pw.println(mIsGestureInProgress);
-        pw.print("  showingDismiss:          "); pw.println(mShowingDismiss);
+        pw.print("  showingDismiss:          "); pw.println(mDismissView.isShowing());
         pw.print("  isExpansionAnimating:    "); pw.println(mIsExpansionAnimating);
         pw.print("  expandedContainerVis:    "); pw.println(mExpandedViewContainer.getVisibility());
         pw.print("  expandedContainerAlpha:  "); pw.println(mExpandedViewContainer.getAlpha());
@@ -347,7 +342,6 @@
     private boolean mViewUpdatedRequested = false;
     private boolean mIsExpansionAnimating = false;
     private boolean mIsBubbleSwitchAnimating = false;
-    private boolean mShowingDismiss = false;
 
     /** The view to desaturate/darken when magneted to the dismiss target. */
     @Nullable private View mDesaturateAndDarkenTargetView;
@@ -465,7 +459,7 @@
                     if (wasFlungOut) {
                         mExpandedAnimationController.snapBubbleBack(
                                 mExpandedAnimationController.getDraggedOutBubble(), velX, velY);
-                        hideDismissTarget();
+                        mDismissView.hide();
                     } else {
                         mExpandedAnimationController.onUnstuckFromTarget();
                     }
@@ -479,9 +473,9 @@
 
                     mExpandedAnimationController.dismissDraggedOutBubble(
                             mExpandedAnimationController.getDraggedOutBubble() /* bubble */,
-                            mDismissTargetContainer.getHeight() /* translationYBy */,
+                            mDismissView.getHeight() /* translationYBy */,
                             BubbleStackView.this::dismissMagnetizedObject /* after */);
-                    hideDismissTarget();
+                    mDismissView.hide();
                 }
             };
 
@@ -502,7 +496,7 @@
                     if (wasFlungOut) {
                         mStackAnimationController.flingStackThenSpringToEdge(
                                 mStackAnimationController.getStackPosition().x, velX, velY);
-                        hideDismissTarget();
+                        mDismissView.hide();
                     } else {
                         mStackAnimationController.onUnstuckFromTarget();
                     }
@@ -511,14 +505,14 @@
                 @Override
                 public void onReleasedInTarget(@NonNull MagnetizedObject.MagneticTarget target) {
                     mStackAnimationController.animateStackDismissal(
-                            mDismissTargetContainer.getHeight() /* translationYBy */,
+                            mDismissView.getHeight() /* translationYBy */,
                             () -> {
                                 resetDesaturationAndDarken();
                                 dismissMagnetizedObject();
                             }
                     );
 
-                    hideDismissTarget();
+                    mDismissView.hide();
                 }
             };
 
@@ -639,7 +633,7 @@
             }
 
             // Show the dismiss target, if we haven't already.
-            springInDismissTargetMaybe();
+            mDismissView.show();
 
             // First, see if the magnetized object consumes the event - if so, we shouldn't move the
             // bubble since it's stuck to the target.
@@ -681,7 +675,7 @@
                             SysUiStatsLog.BUBBLE_UICHANGED__ACTION__STACK_MOVED);
                 }
 
-                hideDismissTarget();
+                mDismissView.hide();
             }
 
             mIsDraggingStack = false;
@@ -743,12 +737,7 @@
         }
     };
 
-    private View mDismissTargetCircle;
-    private ViewGroup mDismissTargetContainer;
-    private PhysicsAnimator<View> mDismissTargetAnimator;
-    private PhysicsAnimator.SpringConfig mDismissTargetSpring = new PhysicsAnimator.SpringConfig(
-            SpringForce.STIFFNESS_LOW, SpringForce.DAMPING_RATIO_LOW_BOUNCY);
-
+    private DismissView mDismissView;
     private int mOrientation = Configuration.ORIENTATION_UNDEFINED;
 
     @Nullable
@@ -759,7 +748,7 @@
     private View mUserEducationView;
 
     private boolean mShouldShowManageEducation;
-    private BubbleManageEducationView mManageEducationView;
+    private ManageEducationView mManageEducationView;
     private boolean mAnimatingManageEducationAway;
 
     private ViewGroup mManageMenu;
@@ -866,34 +855,8 @@
                 .setDampingRatio(SpringForce.DAMPING_RATIO_LOW_BOUNCY));
         mFlyoutTransitionSpring.addEndListener(mAfterFlyoutTransitionSpring);
 
-        final int targetSize = res.getDimensionPixelSize(R.dimen.dismiss_circle_size);
-        mDismissTargetCircle = new DismissCircleView(context);
-        final FrameLayout.LayoutParams newParams =
-                new FrameLayout.LayoutParams(targetSize, targetSize);
-        newParams.gravity = Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL;
-        mDismissTargetCircle.setLayoutParams(newParams);
-        mDismissTargetAnimator = PhysicsAnimator.getInstance(mDismissTargetCircle);
-
-        mDismissTargetContainer = new FrameLayout(context);
-        mDismissTargetContainer.setLayoutParams(new FrameLayout.LayoutParams(
-                MATCH_PARENT,
-                getResources().getDimensionPixelSize(R.dimen.floating_dismiss_gradient_height),
-                Gravity.BOTTOM));
-
-        final int bottomMargin =
-                getResources().getDimensionPixelSize(R.dimen.floating_dismiss_bottom_margin);
-        mDismissTargetContainer.setPadding(0, 0, 0, bottomMargin);
-        mDismissTargetContainer.setClipToPadding(false);
-        mDismissTargetContainer.setClipChildren(false);
-        mDismissTargetContainer.addView(mDismissTargetCircle);
-        mDismissTargetContainer.setVisibility(View.INVISIBLE);
-        mDismissTargetContainer.setBackgroundResource(
-                R.drawable.floating_dismiss_gradient_transition);
-        addView(mDismissTargetContainer);
-
-        // Start translated down so the target springs up.
-        mDismissTargetCircle.setTranslationY(
-                getResources().getDimensionPixelSize(R.dimen.floating_dismiss_gradient_height));
+        mDismissView = new DismissView(context);
+        addView(mDismissView);
 
         final ContentResolver contentResolver = getContext().getContentResolver();
         final int dismissRadius = Settings.Secure.getInt(
@@ -901,13 +864,23 @@
 
         // Save the MagneticTarget instance for the newly set up view - we'll add this to the
         // MagnetizedObjects.
-        mMagneticTarget = new MagnetizedObject.MagneticTarget(mDismissTargetCircle, dismissRadius);
+        mMagneticTarget = new MagnetizedObject.MagneticTarget(
+                mDismissView.getCircle(), dismissRadius);
 
         setClipChildren(false);
         setFocusable(true);
         mBubbleContainer.bringToFront();
 
-        setUpOverflow();
+        mBubbleOverflow = new BubbleOverflow(getContext(), this);
+        mBubbleContainer.addView(mBubbleOverflow.getIconView(),
+                mBubbleContainer.getChildCount() /* index */,
+                new FrameLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
+                        ViewGroup.LayoutParams.WRAP_CONTENT));
+        updateOverflow();
+        mBubbleOverflow.getIconView().setOnClickListener((View v) -> {
+            setSelectedBubble(mBubbleOverflow);
+            showManageMenu(false);
+        });
 
         mOnImeVisibilityChanged = onImeVisibilityChanged;
         mHideCurrentInputMethodCallback = hideCurrentInputMethodCallback;
@@ -933,7 +906,7 @@
                 (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {
                     mExpandedAnimationController.updateResources(mOrientation, mDisplaySize);
                     mStackAnimationController.updateResources(mOrientation);
-                    mBubbleOverflow.updateDimensions();
+                    mBubbleOverflow.updateResources();
 
                     // Need to update the padding around the view
                     WindowInsets insets = getRootWindowInsets();
@@ -1162,12 +1135,9 @@
             Log.d(TAG, "shouldShowManageEducation: " + mShouldShowManageEducation);
         }
         if (mShouldShowManageEducation) {
-            mManageEducationView = (BubbleManageEducationView)
-                    mInflater.inflate(R.layout.bubbles_manage_button_education, this,
+            mManageEducationView = (ManageEducationView)
+                    mInflater.inflate(R.layout.bubbles_manage_button_education, this /* root */,
                             false /* attachToRoot */);
-            mManageEducationView.setVisibility(GONE);
-            mManageEducationView.setElevation(mBubbleElevation);
-            mManageEducationView.setLayoutDirection(View.LAYOUT_DIRECTION_LOCALE);
             addView(mManageEducationView);
         }
     }
@@ -1187,32 +1157,21 @@
         addView(mFlyout, new FrameLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT));
     }
 
-    private void setUpOverflow() {
-        int overflowBtnIndex = 0;
-        if (mBubbleOverflow == null) {
-            mBubbleOverflow = new BubbleOverflow(getContext());
-            mBubbleOverflow.setUpOverflow(mBubbleContainer, this);
-        } else {
-            mBubbleContainer.removeView(mBubbleOverflow.getIconView());
-            mBubbleOverflow.setUpOverflow(mBubbleContainer, this);
-            overflowBtnIndex = mBubbleContainer.getChildCount();
-        }
-        mBubbleContainer.addView(mBubbleOverflow.getIconView(), overflowBtnIndex,
-                new FrameLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT));
-        mBubbleOverflow.getIconView().setOnClickListener(v -> {
-            setSelectedBubble(mBubbleOverflow);
-            showManageMenu(false);
-        });
+    private void updateOverflow() {
+        mBubbleOverflow.update();
+        mBubbleContainer.reorderView(mBubbleOverflow.getIconView(),
+                mBubbleContainer.getChildCount() - 1 /* index */);
         updateOverflowVisibility();
     }
+
     /**
      * Handle theme changes.
      */
     public void onThemeChanged() {
         setUpFlyout();
-        setUpOverflow();
         setUpUserEducation();
         setUpManageMenu();
+        updateOverflow();
         updateExpandedViewTheme();
     }
 
@@ -1261,7 +1220,7 @@
 
     /** Respond to the display size change by recalculating view size and location. */
     public void onDisplaySizeChanged() {
-        setUpOverflow();
+        updateOverflow();
 
         WindowManager wm = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
         wm.getDefaultDisplay().getRealSize(mDisplaySize);
@@ -1279,12 +1238,7 @@
         }
         mExpandedAnimationController.updateResources(mOrientation, mDisplaySize);
         mStackAnimationController.updateResources(mOrientation);
-
-        final int targetSize = res.getDimensionPixelSize(R.dimen.dismiss_circle_size);
-        mDismissTargetCircle.getLayoutParams().width = targetSize;
-        mDismissTargetCircle.getLayoutParams().height = targetSize;
-        mDismissTargetCircle.requestLayout();
-
+        mDismissView.updateResources();
         mMagneticTarget.setMagneticFieldRadiusPx(mBubbleSize * 2);
     }
 
@@ -1796,28 +1750,8 @@
                 && mManageEducationView.getVisibility() != VISIBLE
                 && mIsExpanded
                 && mExpandedBubble.getExpandedView() != null) {
-            mManageEducationView.setAlpha(0);
-            mManageEducationView.setVisibility(VISIBLE);
-            mManageEducationView.post(() -> {
-                mExpandedBubble.getExpandedView().getManageButtonBoundsOnScreen(mTempRect);
-                final int viewHeight = mManageEducationView.getManageViewHeight();
-                final int inset = getResources().getDimensionPixelSize(
-                        R.dimen.bubbles_manage_education_top_inset);
-                mManageEducationView.bringToFront();
-                mManageEducationView.setManageViewPosition(0, mTempRect.top - viewHeight + inset);
-                mManageEducationView.animate()
-                        .setDuration(ANIMATE_STACK_USER_EDUCATION_DURATION)
-                        .setInterpolator(FAST_OUT_SLOW_IN).alpha(1);
-                mManageEducationView.findViewById(R.id.manage).setOnClickListener(view -> {
-                            mExpandedBubble.getExpandedView().findViewById(R.id.settings_button)
-                                    .performClick();
-                            maybeShowManageEducation(false);
-                        });
-                mManageEducationView.findViewById(R.id.got_it).setOnClickListener(view ->
-                        maybeShowManageEducation(false));
-                mManageEducationView.setOnClickListener(view ->
-                        maybeShowManageEducation(false));
-            });
+            mManageEducationView.show(mExpandedBubble.getExpandedView(), mTempRect,
+                    () -> maybeShowManageEducation(false) /* run on click */);
             Prefs.putBoolean(getContext(), HAS_SEEN_BUBBLES_MANAGE_EDUCATION, true);
         } else if (!show
                 && mManageEducationView.getVisibility() == VISIBLE
@@ -2362,48 +2296,6 @@
         }
     }
 
-    /** Animates in the dismiss target. */
-    private void springInDismissTargetMaybe() {
-        if (mShowingDismiss) {
-            return;
-        }
-
-        mShowingDismiss = true;
-
-        mDismissTargetContainer.bringToFront();
-        mDismissTargetContainer.setZ(Short.MAX_VALUE - 1);
-        mDismissTargetContainer.setVisibility(VISIBLE);
-
-        ((TransitionDrawable) mDismissTargetContainer.getBackground()).startTransition(
-                DISMISS_TRANSITION_DURATION_MS);
-
-        mDismissTargetAnimator.cancel();
-        mDismissTargetAnimator
-                .spring(DynamicAnimation.TRANSLATION_Y, 0f, mDismissTargetSpring)
-                .start();
-    }
-
-    /**
-     * Animates the dismiss target out, as well as the circle that encircles the bubbles, if they
-     * were dragged into the target and encircled.
-     */
-    private void hideDismissTarget() {
-        if (!mShowingDismiss) {
-            return;
-        }
-
-        mShowingDismiss = false;
-
-        ((TransitionDrawable) mDismissTargetContainer.getBackground()).reverseTransition(
-                DISMISS_TRANSITION_DURATION_MS);
-
-        mDismissTargetAnimator
-                .spring(DynamicAnimation.TRANSLATION_Y, mDismissTargetContainer.getHeight(),
-                        mDismissTargetSpring)
-                .withEndActions(() -> mDismissTargetContainer.setVisibility(View.INVISIBLE))
-                .start();
-    }
-
     /** Animates the flyout collapsed (to dot), or the reverse, starting with the given velocity. */
     private void animateFlyoutCollapsed(boolean collapsed, float velX) {
         final boolean onLeft = mStackAnimationController.isStackOnLeftSide();
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/DismissView.kt b/packages/SystemUI/src/com/android/systemui/bubbles/DismissView.kt
new file mode 100644
index 0000000..71faf4a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/DismissView.kt
@@ -0,0 +1,85 @@
+package com.android.systemui.bubbles
+
+import android.content.Context
+import android.graphics.drawable.TransitionDrawable
+import android.view.Gravity
+import android.view.View
+import android.view.ViewGroup
+import android.widget.FrameLayout
+import androidx.dynamicanimation.animation.DynamicAnimation
+import androidx.dynamicanimation.animation.SpringForce.DAMPING_RATIO_LOW_BOUNCY
+import androidx.dynamicanimation.animation.SpringForce.STIFFNESS_LOW
+import com.android.systemui.R
+import com.android.systemui.util.DismissCircleView
+import com.android.systemui.util.animation.PhysicsAnimator
+
+/*
+ * View that handles interactions between DismissCircleView and BubbleStackView.
+ */
+class DismissView(context: Context) : FrameLayout(context) {
+
+    var circle = DismissCircleView(context).apply {
+        val targetSize: Int = context.resources.getDimensionPixelSize(R.dimen.dismiss_circle_size)
+        val newParams = LayoutParams(targetSize, targetSize)
+        newParams.gravity = Gravity.BOTTOM or Gravity.CENTER_HORIZONTAL
+        setLayoutParams(newParams)
+        setTranslationY(
+            resources.getDimensionPixelSize(R.dimen.floating_dismiss_gradient_height).toFloat())
+    }
+
+    var isShowing = false
+    private val animator = PhysicsAnimator.getInstance(circle)
+    private val spring = PhysicsAnimator.SpringConfig(STIFFNESS_LOW, DAMPING_RATIO_LOW_BOUNCY);
+    private val DISMISS_SCRIM_FADE_MS = 200
+    init {
+        setLayoutParams(LayoutParams(
+            ViewGroup.LayoutParams.MATCH_PARENT,
+            resources.getDimensionPixelSize(R.dimen.floating_dismiss_gradient_height),
+            Gravity.BOTTOM))
+        setPadding(0, 0, 0, resources.getDimensionPixelSize(R.dimen.floating_dismiss_bottom_margin))
+        setClipToPadding(false)
+        setClipChildren(false)
+        setVisibility(View.INVISIBLE)
+        setBackgroundResource(
+            R.drawable.floating_dismiss_gradient_transition)
+        addView(circle)
+    }
+
+    /**
+     * Animates this view in.
+     */
+    fun show() {
+        if (isShowing) return
+        isShowing = true
+        bringToFront()
+        setZ(Short.MAX_VALUE - 1f)
+        setVisibility(View.VISIBLE)
+        (getBackground() as TransitionDrawable).startTransition(DISMISS_SCRIM_FADE_MS)
+        animator.cancel()
+        animator
+            .spring(DynamicAnimation.TRANSLATION_Y, 0f, spring)
+            .start()
+    }
+
+    /**
+     * Animates this view out, as well as the circle that encircles the bubbles, if they
+     * were dragged into the target and encircled.
+     */
+    fun hide() {
+        if (!isShowing) return
+        isShowing = false
+        (getBackground() as TransitionDrawable).reverseTransition(DISMISS_SCRIM_FADE_MS)
+        animator
+            .spring(DynamicAnimation.TRANSLATION_Y, height.toFloat(),
+                spring)
+            .withEndActions({ setVisibility(View.INVISIBLE) })
+            .start()
+    }
+
+    fun updateResources() {
+        val targetSize: Int = context.resources.getDimensionPixelSize(R.dimen.dismiss_circle_size)
+        circle.layoutParams.width = targetSize
+        circle.layoutParams.height = targetSize
+        circle.requestLayout()
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/ManageEducationView.kt b/packages/SystemUI/src/com/android/systemui/bubbles/ManageEducationView.kt
new file mode 100644
index 0000000..c58ab31
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/ManageEducationView.kt
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.bubbles
+
+import android.content.Context
+import android.graphics.Color
+import android.graphics.Rect
+import android.util.AttributeSet
+import android.view.Gravity
+import android.view.View
+import android.widget.Button
+import android.widget.LinearLayout
+import android.widget.TextView
+import com.android.internal.util.ContrastColorUtil
+import com.android.systemui.Interpolators
+import com.android.systemui.R
+
+/**
+ * Educational view to highlight the manage button that allows a user to configure the settings
+ * for the bubble. Shown only the first time a user expands a bubble.
+ */
+class ManageEducationView @JvmOverloads constructor(
+    context: Context?,
+    attrs: AttributeSet? = null,
+    defStyleAttr: Int = 0,
+    defStyleRes: Int = 0
+) : LinearLayout(context, attrs, defStyleAttr, defStyleRes) {
+
+    private val manageView by lazy { findViewById<View>(R.id.manage_education_view) }
+    private val manageButton by lazy { findViewById<Button>(R.id.manage) }
+    private val gotItButton by lazy { findViewById<Button>(R.id.got_it) }
+    private val titleTextView by lazy { findViewById<TextView>(R.id.user_education_title) }
+    private val descTextView by lazy { findViewById<TextView>(R.id.user_education_description) }
+    private var isInflated = false
+
+    init {
+        this.visibility = View.GONE
+        this.elevation = resources.getDimensionPixelSize(R.dimen.bubble_elevation).toFloat()
+        this.layoutDirection = View.LAYOUT_DIRECTION_LOCALE
+    }
+
+    override fun setLayoutDirection(direction: Int) {
+        super.setLayoutDirection(direction)
+        // setLayoutDirection runs before onFinishInflate
+        // so skip if views haven't inflated; otherwise we'll get NPEs
+        if (!isInflated) return
+        setDirection()
+    }
+
+    override fun onFinishInflate() {
+        super.onFinishInflate()
+        isInflated = true
+        setDirection()
+        setTextColor()
+    }
+
+    private fun setTextColor() {
+        val typedArray = mContext.obtainStyledAttributes(intArrayOf(android.R.attr.colorAccent,
+            android.R.attr.textColorPrimaryInverse))
+        val bgColor = typedArray.getColor(0 /* index */, Color.BLACK)
+        var textColor = typedArray.getColor(1 /* index */, Color.WHITE)
+        typedArray.recycle()
+        textColor = ContrastColorUtil.ensureTextContrast(textColor, bgColor, true)
+        titleTextView.setTextColor(textColor)
+        descTextView.setTextColor(textColor)
+    }
+
+    fun setDirection() {
+        manageView.setBackgroundResource(
+            if (resources.configuration.layoutDirection == View.LAYOUT_DIRECTION_RTL)
+                R.drawable.bubble_stack_user_education_bg_rtl
+            else R.drawable.bubble_stack_user_education_bg)
+        titleTextView.gravity = Gravity.START
+        descTextView.gravity = Gravity.START
+    }
+
+    fun show(expandedView: BubbleExpandedView, rect : Rect, hideMenu: Runnable) {
+        alpha = 0f
+        visibility = View.VISIBLE
+        post {
+            expandedView.getManageButtonBoundsOnScreen(rect)
+            with(hideMenu) {
+                manageButton
+                    .setOnClickListener {
+                        expandedView.findViewById<View>(R.id.settings_button).performClick()
+                        this.run()
+                    }
+                gotItButton.setOnClickListener { this.run() }
+                setOnClickListener { this.run() }
+            }
+            with(manageView) {
+                translationX = 0f
+                val inset = resources.getDimensionPixelSize(
+                    R.dimen.bubbles_manage_education_top_inset)
+                translationY = (rect.top - manageView.height + inset).toFloat()
+            }
+            bringToFront()
+            animate()
+                .setDuration(BubbleStackView.ANIMATE_STACK_USER_EDUCATION_DURATION.toLong())
+                .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
+                .alpha(1f)
+        }
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DefaultBroadcastReceiverBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/DefaultBroadcastReceiverBinder.java
index 56d0fa2..6e8d63b 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/DefaultBroadcastReceiverBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/DefaultBroadcastReceiverBinder.java
@@ -18,7 +18,9 @@
 
 import android.content.BroadcastReceiver;
 
-import com.android.systemui.screenshot.GlobalScreenshot.ActionProxyReceiver;
+import com.android.systemui.screenshot.ActionProxyReceiver;
+import com.android.systemui.screenshot.DeleteScreenshotReceiver;
+import com.android.systemui.screenshot.SmartActionsReceiver;
 
 import dagger.Binds;
 import dagger.Module;
@@ -30,10 +32,31 @@
  */
 @Module
 public abstract class DefaultBroadcastReceiverBinder {
-    /** */
+    /**
+     *
+     */
     @Binds
     @IntoMap
     @ClassKey(ActionProxyReceiver.class)
     public abstract BroadcastReceiver bindActionProxyReceiver(
             ActionProxyReceiver broadcastReceiver);
+
+    /**
+     *
+     */
+    @Binds
+    @IntoMap
+    @ClassKey(DeleteScreenshotReceiver.class)
+    public abstract BroadcastReceiver bindDeleteScreenshotReceiver(
+            DeleteScreenshotReceiver broadcastReceiver);
+
+    /**
+     *
+     */
+    @Binds
+    @IntoMap
+    @ClassKey(SmartActionsReceiver.class)
+    public abstract BroadcastReceiver bindSmartActionsReceiver(
+            SmartActionsReceiver broadcastReceiver);
+
 }
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index 4bd046e..fce545b 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -45,6 +45,7 @@
 import com.android.systemui.util.concurrency.ConcurrencyModule;
 import com.android.systemui.util.sensors.AsyncSensorManager;
 import com.android.systemui.util.sensors.SensorModule;
+import com.android.systemui.util.settings.SettingsUtilModule;
 import com.android.systemui.util.time.SystemClock;
 import com.android.systemui.util.time.SystemClockImpl;
 
@@ -65,7 +66,8 @@
             LogModule.class,
             PeopleHubModule.class,
             SensorModule.class,
-            SettingsModule.class
+            SettingsModule.class,
+            SettingsUtilModule.class
         },
         subcomponents = {StatusBarComponent.class,
                 NotificationRowComponent.class,
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIRootComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIRootComponent.java
index e5cc1384..900c11f 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIRootComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIRootComponent.java
@@ -19,12 +19,12 @@
 import static com.android.systemui.Dependency.ALLOW_NOTIFICATION_LONG_PRESS_NAME;
 
 import android.content.ContentProvider;
+import android.content.Context;
 
 import com.android.systemui.BootCompleteCacheImpl;
 import com.android.systemui.Dependency;
 import com.android.systemui.InitController;
 import com.android.systemui.SystemUIAppComponentFactory;
-import com.android.systemui.SystemUIFactory;
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.fragments.FragmentService;
 import com.android.systemui.keyguard.KeyguardSliceProvider;
@@ -37,6 +37,7 @@
 import javax.inject.Named;
 import javax.inject.Singleton;
 
+import dagger.BindsInstance;
 import dagger.Component;
 
 /**
@@ -50,13 +51,23 @@
         OneHandedModule.class,
         PipModule.class,
         SystemServicesModule.class,
-        SystemUIFactory.ContextHolder.class,
         SystemUIBinder.class,
         SystemUIModule.class,
         SystemUIDefaultModule.class})
 public interface SystemUIRootComponent {
 
     /**
+     * Builder for a SystemUIRootComponent.
+     */
+    @Component.Builder
+    interface Builder {
+        @BindsInstance
+        Builder context(Context context);
+
+        SystemUIRootComponent build();
+    }
+
+    /**
      * Provides a BootCompleteCache.
      */
     @Singleton
@@ -78,7 +89,7 @@
      * Main dependency providing module.
      */
     @Singleton
-    Dependency.DependencyInjector createDependency();
+    Dependency createDependency();
 
     /** */
     @Singleton
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java b/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java
index b9d23ad..1ef806c 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java
@@ -364,9 +364,6 @@
             Log.i(TAG, "Dropping pulse done because current state is already done: " + mState);
             return mState;
         }
-        if (requestedState == State.DOZE_AOD && mBatteryController.isAodPowerSave()) {
-            return State.DOZE;
-        }
         if (requestedState == State.DOZE_REQUEST_PULSE && !mState.canPulse()) {
             Log.i(TAG, "Dropping pulse request because current state can't pulse: " + mState);
             return mState;
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
index 37bdda8..524d9c8 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
@@ -21,7 +21,6 @@
 
 import android.annotation.AnyThread;
 import android.app.ActivityManager;
-import android.content.ContentResolver;
 import android.content.Context;
 import android.database.ContentObserver;
 import android.hardware.Sensor;
@@ -49,6 +48,7 @@
 import com.android.systemui.statusbar.phone.DozeParameters;
 import com.android.systemui.util.sensors.AsyncSensorManager;
 import com.android.systemui.util.sensors.ProximitySensor;
+import com.android.systemui.util.settings.SecureSettings;
 import com.android.systemui.util.wakelock.WakeLock;
 
 import java.io.PrintWriter;
@@ -64,10 +64,10 @@
 
     private final Context mContext;
     private final AsyncSensorManager mSensorManager;
-    private final ContentResolver mResolver;
     private final AmbientDisplayConfiguration mConfig;
     private final WakeLock mWakeLock;
     private final Consumer<Boolean> mProxCallback;
+    private final SecureSettings mSecureSettings;
     private final Callback mCallback;
     @VisibleForTesting
     protected TriggerSensor[] mSensors;
@@ -98,13 +98,13 @@
     DozeSensors(Context context, AsyncSensorManager sensorManager,
             DozeParameters dozeParameters, AmbientDisplayConfiguration config, WakeLock wakeLock,
             Callback callback, Consumer<Boolean> proxCallback, DozeLog dozeLog,
-            ProximitySensor proximitySensor) {
+            ProximitySensor proximitySensor, SecureSettings secureSettings) {
         mContext = context;
         mSensorManager = sensorManager;
         mConfig = config;
         mWakeLock = wakeLock;
         mProxCallback = proxCallback;
-        mResolver = mContext.getContentResolver();
+        mSecureSettings = secureSettings;
         mCallback = callback;
         mProximitySensor = proximitySensor;
 
@@ -241,7 +241,7 @@
         }
 
         if (!anyListening) {
-            mResolver.unregisterContentObserver(mSettingsObserver);
+            mSecureSettings.unregisterContentObserver(mSettingsObserver);
         } else if (!mSettingRegistered) {
             for (TriggerSensor s : mSensors) {
                 s.registerSettingsObserver(mSettingsObserver);
@@ -400,7 +400,7 @@
             } else if (TextUtils.isEmpty(mSetting)) {
                 return true;
             }
-            return Settings.Secure.getIntForUser(mResolver, mSetting, mSettingDefault ? 1 : 0,
+            return mSecureSettings.getIntForUser(mSetting, mSettingDefault ? 1 : 0,
                     UserHandle.USER_CURRENT) != 0;
         }
 
@@ -444,9 +444,8 @@
 
         public void registerSettingsObserver(ContentObserver settingsObserver) {
             if (mConfigured && !TextUtils.isEmpty(mSetting)) {
-                mResolver.registerContentObserver(
-                        Settings.Secure.getUriFor(mSetting), false /* descendants */,
-                        mSettingsObserver, UserHandle.USER_ALL);
+                mSecureSettings.registerContentObserverForUser(
+                        mSetting, mSettingsObserver, UserHandle.USER_ALL);
             }
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
index abbbd17..e38dce0 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
@@ -46,6 +46,7 @@
 import com.android.systemui.util.Assert;
 import com.android.systemui.util.sensors.AsyncSensorManager;
 import com.android.systemui.util.sensors.ProximitySensor;
+import com.android.systemui.util.settings.SecureSettings;
 import com.android.systemui.util.wakelock.WakeLock;
 
 import java.io.PrintWriter;
@@ -163,7 +164,8 @@
             DozeParameters dozeParameters, AsyncSensorManager sensorManager,
             WakeLock wakeLock, DockManager dockManager,
             ProximitySensor proximitySensor, ProximitySensor.ProximityCheck proxCheck,
-            DozeLog dozeLog, BroadcastDispatcher broadcastDispatcher) {
+            DozeLog dozeLog, BroadcastDispatcher broadcastDispatcher,
+            SecureSettings secureSettings) {
         mContext = context;
         mDozeHost = dozeHost;
         mConfig = config;
@@ -172,7 +174,8 @@
         mWakeLock = wakeLock;
         mAllowPulseTriggers = true;
         mDozeSensors = new DozeSensors(context, mSensorManager, dozeParameters,
-                config, wakeLock, this::onSensor, this::onProximityFar, dozeLog, proximitySensor);
+                config, wakeLock, this::onSensor, this::onProximityFar, dozeLog, proximitySensor,
+                secureSettings);
         mUiModeManager = mContext.getSystemService(UiModeManager.class);
         mDockManager = dockManager;
         mProxCheck = proxCheck;
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataFilter.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataFilter.kt
index 662831e..24ca970 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaDataFilter.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataFilter.kt
@@ -27,6 +27,7 @@
 import javax.inject.Singleton
 
 private const val TAG = "MediaDataFilter"
+private const val DEBUG = true
 
 /**
  * Filters data updates from [MediaDataCombineLatest] based on the current user ID, and handles user
@@ -98,7 +99,7 @@
         // are up to date
         mediaEntries.clear()
         keyCopy.forEach {
-            Log.d(TAG, "Removing $it after user change")
+            if (DEBUG) Log.d(TAG, "Removing $it after user change")
             listenersCopy.forEach { listener ->
                 listener.onMediaDataRemoved(it)
             }
@@ -106,7 +107,7 @@
 
         dataSource.getData().forEach { (key, data) ->
             if (lockscreenUserManager.isCurrentProfile(data.userId)) {
-                Log.d(TAG, "Re-adding $key after user change")
+                if (DEBUG) Log.d(TAG, "Re-adding $key after user change")
                 mediaEntries.put(key, data)
                 listenersCopy.forEach { listener ->
                     listener.onMediaDataLoaded(key, null, data)
@@ -119,6 +120,7 @@
      * Invoked when the user has dismissed the media carousel
      */
     fun onSwipeToDismiss() {
+        if (DEBUG) Log.d(TAG, "Media carousel swiped away")
         val mediaKeys = mediaEntries.keys.toSet()
         mediaKeys.forEach {
             mediaDataManager.setTimedOut(it, timedOut = true)
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
index b327773..d82150f2 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
@@ -63,6 +63,7 @@
 )
 
 private const val TAG = "MediaDataManager"
+private const val DEBUG = true
 private const val DEFAULT_LUMINOSITY = 0.25f
 private const val LUMINOSITY_THRESHOLD = 0.05f
 private const val SATURATION_MULTIPLIER = 0.8f
@@ -253,7 +254,7 @@
     fun removeListener(listener: Listener) = listeners.remove(listener)
 
     /**
-     * Called whenever the player has been paused or stopped for a while.
+     * Called whenever the player has been paused or stopped for a while, or swiped from QQS.
      * This will make the player not active anymore, hiding it from QQS and Keyguard.
      * @see MediaData.active
      */
@@ -263,6 +264,7 @@
                 return
             }
             it.active = !timedOut
+            if (DEBUG) Log.d(TAG, "Updating $token timedOut: $timedOut")
             onMediaDataLoaded(token, token, it)
         }
     }
@@ -283,7 +285,9 @@
             return
         }
 
-        Log.d(TAG, "adding track for $userId from browser: $desc")
+        if (DEBUG) {
+            Log.d(TAG, "adding track for $userId from browser: $desc")
+        }
 
         // Album art
         var artworkBitmap = desc.iconBitmap
@@ -383,7 +387,7 @@
         if (actions != null) {
             for ((index, action) in actions.withIndex()) {
                 if (action.getIcon() == null) {
-                    Log.i(TAG, "No icon for action $index ${action.title}")
+                    if (DEBUG) Log.i(TAG, "No icon for action $index ${action.title}")
                     actionsToShowCollapsed.remove(index)
                     continue
                 }
@@ -427,7 +431,7 @@
             if (!TextUtils.isEmpty(uriString)) {
                 val albumArt = loadBitmapFromUri(Uri.parse(uriString))
                 if (albumArt != null) {
-                    Log.d(TAG, "loaded art from $uri")
+                    if (DEBUG) Log.d(TAG, "loaded art from $uri")
                     return albumArt
                 }
             }
@@ -514,7 +518,7 @@
         Assert.isMainThread()
         val removed = mediaEntries.remove(key)
         if (useMediaResumption && removed?.resumeAction != null) {
-            Log.d(TAG, "Not removing $key because resumable")
+            if (DEBUG) Log.d(TAG, "Not removing $key because resumable")
             // Move to resume key (aka package name) if that key doesn't already exist.
             val resumeAction = getResumeMediaAction(removed.resumeAction!!)
             val updated = removed.copy(token = null, actions = listOf(resumeAction),
diff --git a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedUI.java b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedUI.java
index 0f4e6be..9239435 100644
--- a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedUI.java
+++ b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedUI.java
@@ -29,7 +29,9 @@
 import android.os.Looper;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.os.SystemProperties;
 import android.provider.Settings;
+import android.util.Log;
 
 import androidx.annotation.VisibleForTesting;
 
@@ -56,6 +58,7 @@
     private static final String TAG = "OneHandedUI";
     private static final String ONE_HANDED_MODE_GESTURAL_OVERLAY =
             "com.android.internal.systemui.onehanded.gestural";
+    private static final String SUPPORT_ONE_HANDED_MODE = "ro.support_one_handed_mode";
 
     private final OneHandedManagerImpl mOneHandedManager;
     private final CommandQueue mCommandQueue;
@@ -136,10 +139,18 @@
             ScreenLifecycle screenLifecycle) {
         super(context);
 
+        if (!SystemProperties.getBoolean(SUPPORT_ONE_HANDED_MODE, false)) {
+            Log.i(TAG, "Device config SUPPORT_ONE_HANDED_MODE off");
+            mCommandQueue = null;
+            mOneHandedManager = null;
+            mOverlayManager = null;
+            mSettingUtil = null;
+            mTimeoutHandler = null;
+            mScreenLifecycle = null;
+            return;
+        }
+
         mCommandQueue = commandQueue;
-        /* TODO(b/154290458) define a boolean system properties "support_one_handed_mode"
-            boolean supportOneHanded = SystemProperties.getBoolean("support_one_handed_mode");
-            if (!supportOneHanded) return; */
         mOneHandedManager = oneHandedManager;
         mSettingUtil = settingsUtil;
         mTimeoutHandler = OneHandedTimeoutHandler.get();
@@ -150,9 +161,9 @@
 
     @Override
     public void start() {
-        /* TODO(b/154290458) define a boolean system properties "support_one_handed_mode"
-            boolean supportOneHanded = SystemProperties.getBoolean("support_one_handed_mode");
-            if (!supportOneHanded) return; */
+        if (!SystemProperties.getBoolean(SUPPORT_ONE_HANDED_MODE, false)) {
+            return;
+        }
         mCommandQueue.addCallback(this);
         setupKeyguardUpdateMonitor();
         setupScreenObserver();
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
index eb73e13..582cd04 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
@@ -78,7 +78,7 @@
     private final DisplayInfo mTmpDisplayInfo = new DisplayInfo();
     private final Rect mTmpInsetBounds = new Rect();
     private final Rect mTmpNormalBounds = new Rect();
-    private final Rect mReentryBounds = new Rect();
+    protected final Rect mReentryBounds = new Rect();
 
     private PipBoundsHandler mPipBoundsHandler;
     private InputConsumerController mInputConsumerController;
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt b/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt
new file mode 100644
index 0000000..870e714
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.systemui.privacy
+
+import android.content.Context
+import android.util.AttributeSet
+import android.view.Gravity
+import android.view.ViewGroup
+import android.widget.FrameLayout
+import android.widget.ImageView
+import android.widget.LinearLayout
+import com.android.systemui.R
+
+class OngoingPrivacyChip @JvmOverloads constructor(
+    context: Context,
+    attrs: AttributeSet? = null,
+    defStyleAttrs: Int = 0,
+    defStyleRes: Int = 0
+) : FrameLayout(context, attrs, defStyleAttrs, defStyleRes) {
+
+    private val iconMarginExpanded = context.resources.getDimensionPixelSize(
+                    R.dimen.ongoing_appops_chip_icon_margin_expanded)
+    private val iconMarginCollapsed = context.resources.getDimensionPixelSize(
+                    R.dimen.ongoing_appops_chip_icon_margin_collapsed)
+    private val iconSize =
+            context.resources.getDimensionPixelSize(R.dimen.ongoing_appops_chip_icon_size)
+    private val iconColor = context.resources.getColor(
+            R.color.status_bar_clock_color, context.theme)
+    private val sidePadding =
+            context.resources.getDimensionPixelSize(R.dimen.ongoing_appops_chip_side_padding)
+    private val backgroundDrawable = context.getDrawable(R.drawable.privacy_chip_bg)
+    private lateinit var iconsContainer: LinearLayout
+    private lateinit var back: FrameLayout
+    var expanded = false
+        set(value) {
+            if (value != field) {
+                field = value
+                updateView(PrivacyChipBuilder(context, privacyList))
+            }
+        }
+
+    var privacyList = emptyList<PrivacyItem>()
+        set(value) {
+            field = value
+            updateView(PrivacyChipBuilder(context, field))
+        }
+
+    override fun onFinishInflate() {
+        super.onFinishInflate()
+
+        back = requireViewById(R.id.background)
+        iconsContainer = requireViewById(R.id.icons_container)
+    }
+
+    // Should only be called if the builder icons or app changed
+    private fun updateView(builder: PrivacyChipBuilder) {
+        back.background = if (expanded) backgroundDrawable else null
+        val padding = if (expanded) sidePadding else 0
+        back.setPaddingRelative(padding, 0, padding, 0)
+        fun setIcons(chipBuilder: PrivacyChipBuilder, iconsContainer: ViewGroup) {
+            iconsContainer.removeAllViews()
+            chipBuilder.generateIcons().forEachIndexed { i, it ->
+                it.mutate()
+                it.setTint(iconColor)
+                val image = ImageView(context).apply {
+                    setImageDrawable(it)
+                    scaleType = ImageView.ScaleType.CENTER_INSIDE
+                }
+                iconsContainer.addView(image, iconSize, iconSize)
+                if (i != 0) {
+                    val lp = image.layoutParams as MarginLayoutParams
+                    lp.marginStart = if (expanded) iconMarginExpanded else iconMarginCollapsed
+                    image.layoutParams = lp
+                }
+            }
+        }
+
+        if (!privacyList.isEmpty()) {
+            generateContentDescription(builder)
+            setIcons(builder, iconsContainer)
+            val lp = iconsContainer.layoutParams as FrameLayout.LayoutParams
+            lp.gravity = Gravity.CENTER_VERTICAL or
+                    (if (expanded) Gravity.CENTER_HORIZONTAL else Gravity.END)
+            iconsContainer.layoutParams = lp
+        } else {
+            iconsContainer.removeAllViews()
+        }
+        requestLayout()
+    }
+
+    private fun generateContentDescription(builder: PrivacyChipBuilder) {
+        val typesText = builder.joinTypes()
+        contentDescription = context.getString(
+                R.string.ongoing_privacy_chip_content_multiple_apps, typesText)
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyChipBuilder.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyChipBuilder.kt
new file mode 100644
index 0000000..1d2e747
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyChipBuilder.kt
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.systemui.privacy
+
+import android.content.Context
+import com.android.systemui.R
+
+class PrivacyChipBuilder(private val context: Context, itemsList: List<PrivacyItem>) {
+
+    val appsAndTypes: List<Pair<PrivacyApplication, List<PrivacyType>>>
+    val types: List<PrivacyType>
+    private val separator = context.getString(R.string.ongoing_privacy_dialog_separator)
+    private val lastSeparator = context.getString(R.string.ongoing_privacy_dialog_last_separator)
+
+    init {
+        appsAndTypes = itemsList.groupBy({ it.application }, { it.privacyType })
+                .toList()
+                .sortedWith(compareBy({ -it.second.size }, // Sort by number of AppOps
+                        { it.second.min() })) // Sort by "smallest" AppOpp (Location is largest)
+        types = itemsList.map { it.privacyType }.distinct().sorted()
+    }
+
+    fun generateIcons() = types.map { it.getIcon(context) }
+
+    private fun <T> List<T>.joinWithAnd(): StringBuilder {
+        return subList(0, size - 1).joinTo(StringBuilder(), separator = separator).apply {
+            append(lastSeparator)
+            append(this@joinWithAnd.last())
+        }
+    }
+
+    fun joinTypes(): String {
+        return when (types.size) {
+            0 -> ""
+            1 -> types[0].getName(context)
+            else -> types.map { it.getName(context) }.joinWithAnd().toString()
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyChipEvent.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyChipEvent.kt
new file mode 100644
index 0000000..1f24fde
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyChipEvent.kt
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.privacy
+
+import com.android.internal.logging.UiEvent
+import com.android.internal.logging.UiEventLogger
+
+enum class PrivacyChipEvent(private val _id: Int) : UiEventLogger.UiEventEnum {
+    @UiEvent(doc = "Privacy chip is viewed by the user. Logged at most once per time QS is visible")
+    ONGOING_INDICATORS_CHIP_VIEW(601),
+
+    @UiEvent(doc = "Privacy chip is clicked")
+    ONGOING_INDICATORS_CHIP_CLICK(602);
+
+    override fun getId() = _id
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItem.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItem.kt
new file mode 100644
index 0000000..3da1363
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItem.kt
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.systemui.privacy
+
+import android.content.Context
+import com.android.systemui.R
+
+typealias Privacy = PrivacyType
+
+enum class PrivacyType(val nameId: Int, val iconId: Int) {
+    // This is uses the icons used by the corresponding permission groups in the AndroidManifest
+    TYPE_CAMERA(R.string.privacy_type_camera,
+            com.android.internal.R.drawable.perm_group_camera),
+    TYPE_MICROPHONE(R.string.privacy_type_microphone,
+            com.android.internal.R.drawable.perm_group_microphone),
+    TYPE_LOCATION(R.string.privacy_type_location,
+            com.android.internal.R.drawable.perm_group_location);
+
+    fun getName(context: Context) = context.resources.getString(nameId)
+
+    fun getIcon(context: Context) = context.resources.getDrawable(iconId, context.theme)
+}
+
+data class PrivacyItem(val privacyType: PrivacyType, val application: PrivacyApplication)
+
+data class PrivacyApplication(val packageName: String, val uid: Int)
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt
new file mode 100644
index 0000000..d5a14f7
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt
@@ -0,0 +1,296 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.privacy
+
+import android.app.ActivityManager
+import android.app.AppOpsManager
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import android.content.IntentFilter
+import android.os.UserHandle
+import android.os.UserManager
+import android.provider.DeviceConfig
+import com.android.internal.annotations.VisibleForTesting
+import com.android.internal.config.sysui.SystemUiDeviceConfigFlags
+import com.android.systemui.Dumpable
+import com.android.systemui.appops.AppOpItem
+import com.android.systemui.appops.AppOpsController
+import com.android.systemui.broadcast.BroadcastDispatcher
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.util.DeviceConfigProxy
+import com.android.systemui.util.concurrency.DelayableExecutor
+import java.io.FileDescriptor
+import java.io.PrintWriter
+import java.lang.ref.WeakReference
+import java.util.concurrent.Executor
+import javax.inject.Inject
+import javax.inject.Singleton
+
+@Singleton
+class PrivacyItemController @Inject constructor(
+    context: Context,
+    private val appOpsController: AppOpsController,
+    @Main uiExecutor: DelayableExecutor,
+    @Background private val bgExecutor: Executor,
+    private val broadcastDispatcher: BroadcastDispatcher,
+    private val deviceConfigProxy: DeviceConfigProxy,
+    private val userManager: UserManager,
+    dumpManager: DumpManager
+) : Dumpable {
+
+    @VisibleForTesting
+    internal companion object {
+        val OPS = intArrayOf(AppOpsManager.OP_CAMERA,
+                AppOpsManager.OP_RECORD_AUDIO,
+                AppOpsManager.OP_COARSE_LOCATION,
+                AppOpsManager.OP_FINE_LOCATION)
+        val intentFilter = IntentFilter().apply {
+            addAction(Intent.ACTION_USER_SWITCHED)
+            addAction(Intent.ACTION_MANAGED_PROFILE_AVAILABLE)
+            addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE)
+        }
+        const val TAG = "PrivacyItemController"
+    }
+
+    @VisibleForTesting
+    internal var privacyList = emptyList<PrivacyItem>()
+        @Synchronized get() = field.toList() // Returns a shallow copy of the list
+        @Synchronized set
+
+    private fun isPermissionsHubEnabled(): Boolean {
+        return deviceConfigProxy.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
+                SystemUiDeviceConfigFlags.PROPERTY_PERMISSIONS_HUB_ENABLED, false)
+    }
+
+    private var currentUserIds = emptyList<Int>()
+    private var listening = false
+    private val callbacks = mutableListOf<WeakReference<Callback>>()
+    private val internalUiExecutor = MyExecutor(uiExecutor)
+
+    private val notifyChanges = Runnable {
+        val list = privacyList
+        callbacks.forEach { it.get()?.onPrivacyItemsChanged(list) }
+    }
+
+    private val updateListAndNotifyChanges = Runnable {
+        updatePrivacyList()
+        uiExecutor.execute(notifyChanges)
+    }
+
+    var indicatorsAvailable = isPermissionsHubEnabled()
+        private set
+    @VisibleForTesting
+    internal val devicePropertiesChangedListener =
+            object : DeviceConfig.OnPropertiesChangedListener {
+        override fun onPropertiesChanged(properties: DeviceConfig.Properties) {
+            if (DeviceConfig.NAMESPACE_PRIVACY.equals(properties.getNamespace()) &&
+                    properties.getKeyset().contains(
+                    SystemUiDeviceConfigFlags.PROPERTY_PERMISSIONS_HUB_ENABLED)) {
+                val flag = properties.getBoolean(
+                        SystemUiDeviceConfigFlags.PROPERTY_PERMISSIONS_HUB_ENABLED, false)
+                if (indicatorsAvailable != flag) {
+                    // This is happening already in the UI executor, so we can iterate in the
+                    indicatorsAvailable = flag
+                    callbacks.forEach { it.get()?.onFlagChanged(flag) }
+                }
+
+                internalUiExecutor.updateListeningState()
+            }
+        }
+    }
+
+    private val cb = object : AppOpsController.Callback {
+        override fun onActiveStateChanged(
+            code: Int,
+            uid: Int,
+            packageName: String,
+            active: Boolean
+        ) {
+            val userId = UserHandle.getUserId(uid)
+            if (userId in currentUserIds) {
+                update(false)
+            }
+        }
+    }
+
+    @VisibleForTesting
+    internal var userSwitcherReceiver = Receiver()
+        set(value) {
+            unregisterReceiver()
+            field = value
+            if (listening) registerReceiver()
+        }
+
+    init {
+        deviceConfigProxy.addOnPropertiesChangedListener(
+                DeviceConfig.NAMESPACE_PRIVACY,
+                uiExecutor,
+                devicePropertiesChangedListener)
+        dumpManager.registerDumpable(TAG, this)
+    }
+
+    private fun unregisterReceiver() {
+        broadcastDispatcher.unregisterReceiver(userSwitcherReceiver)
+    }
+
+    private fun registerReceiver() {
+        broadcastDispatcher.registerReceiver(userSwitcherReceiver, intentFilter,
+                null /* handler */, UserHandle.ALL)
+    }
+
+    private fun update(updateUsers: Boolean) {
+        bgExecutor.execute {
+            if (updateUsers) {
+                val currentUser = ActivityManager.getCurrentUser()
+                currentUserIds = userManager.getProfiles(currentUser).map { it.id }
+            }
+            updateListAndNotifyChanges.run()
+        }
+    }
+
+    /**
+     * Updates listening status based on whether there are callbacks and the indicators are enabled
+     *
+     * This is only called from private (add/remove)Callback and from the config listener, all in
+     * main thread.
+     */
+    private fun setListeningState() {
+        val listen = !callbacks.isEmpty() and indicatorsAvailable
+        if (listening == listen) return
+        listening = listen
+        if (listening) {
+            appOpsController.addCallback(OPS, cb)
+            registerReceiver()
+            update(true)
+        } else {
+            appOpsController.removeCallback(OPS, cb)
+            unregisterReceiver()
+            // Make sure that we remove all indicators and notify listeners if we are not
+            // listening anymore due to indicators being disabled
+            update(false)
+        }
+    }
+
+    private fun addCallback(callback: WeakReference<Callback>) {
+        callbacks.add(callback)
+        if (callbacks.isNotEmpty() && !listening) {
+            internalUiExecutor.updateListeningState()
+        }
+        // Notify this callback if we didn't set to listening
+        else if (listening) {
+            internalUiExecutor.execute(NotifyChangesToCallback(callback.get(), privacyList))
+        }
+    }
+
+    private fun removeCallback(callback: WeakReference<Callback>) {
+        // Removes also if the callback is null
+        callbacks.removeIf { it.get()?.equals(callback.get()) ?: true }
+        if (callbacks.isEmpty()) {
+            internalUiExecutor.updateListeningState()
+        }
+    }
+
+    fun addCallback(callback: Callback) {
+        addCallback(WeakReference(callback))
+    }
+
+    fun removeCallback(callback: Callback) {
+        removeCallback(WeakReference(callback))
+    }
+
+    private fun updatePrivacyList() {
+        if (!listening) {
+            privacyList = emptyList()
+            return
+        }
+        val list = currentUserIds.flatMap { appOpsController.getActiveAppOpsForUser(it) }
+                .mapNotNull { toPrivacyItem(it) }.distinct()
+        privacyList = list
+    }
+
+    private fun toPrivacyItem(appOpItem: AppOpItem): PrivacyItem? {
+        val type: PrivacyType = when (appOpItem.code) {
+            AppOpsManager.OP_CAMERA -> PrivacyType.TYPE_CAMERA
+            AppOpsManager.OP_COARSE_LOCATION -> PrivacyType.TYPE_LOCATION
+            AppOpsManager.OP_FINE_LOCATION -> PrivacyType.TYPE_LOCATION
+            AppOpsManager.OP_RECORD_AUDIO -> PrivacyType.TYPE_MICROPHONE
+            else -> return null
+        }
+        val app = PrivacyApplication(appOpItem.packageName, appOpItem.uid)
+        return PrivacyItem(type, app)
+    }
+
+    interface Callback {
+        fun onPrivacyItemsChanged(privacyItems: List<PrivacyItem>)
+        @JvmDefault
+        fun onFlagChanged(flag: Boolean) {}
+    }
+
+    internal inner class Receiver : BroadcastReceiver() {
+        override fun onReceive(context: Context, intent: Intent) {
+            if (intentFilter.hasAction(intent.action)) {
+                update(true)
+            }
+        }
+    }
+
+    private class NotifyChangesToCallback(
+        private val callback: Callback?,
+        private val list: List<PrivacyItem>
+    ) : Runnable {
+        override fun run() {
+            callback?.onPrivacyItemsChanged(list)
+        }
+    }
+
+    override fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<out String>) {
+        pw.println("PrivacyItemController state:")
+        pw.println("  Listening: $listening")
+        pw.println("  Current user ids: $currentUserIds")
+        pw.println("  Privacy Items:")
+        privacyList.forEach {
+            pw.print("    ")
+            pw.println(it.toString())
+        }
+        pw.println("  Callbacks:")
+        callbacks.forEach {
+            it.get()?.let {
+                pw.print("    ")
+                pw.println(it.toString())
+            }
+        }
+    }
+
+    private inner class MyExecutor(
+        private val delegate: DelayableExecutor
+    ) : Executor {
+
+        private var listeningCanceller: Runnable? = null
+
+        override fun execute(command: Runnable) {
+            delegate.execute(command)
+        }
+
+        fun updateListeningState() {
+            listeningCanceller?.run()
+            listeningCanceller = delegate.executeDelayed({ setListeningState() }, 0L)
+        }
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
index b07b1a9..2dc82dd 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
@@ -31,6 +31,7 @@
 import android.graphics.Rect;
 import android.media.AudioManager;
 import android.os.Handler;
+import android.os.Looper;
 import android.provider.AlarmClock;
 import android.provider.Settings;
 import android.service.notification.ZenModeConfig;
@@ -46,7 +47,9 @@
 import android.view.WindowInsets;
 import android.widget.FrameLayout;
 import android.widget.ImageView;
+import android.widget.LinearLayout;
 import android.widget.RelativeLayout;
+import android.widget.Space;
 import android.widget.TextView;
 
 import androidx.annotation.NonNull;
@@ -55,6 +58,7 @@
 import androidx.lifecycle.LifecycleOwner;
 import androidx.lifecycle.LifecycleRegistry;
 
+import com.android.internal.logging.UiEventLogger;
 import com.android.settingslib.Utils;
 import com.android.systemui.BatteryMeterView;
 import com.android.systemui.DualToneHandler;
@@ -63,6 +67,10 @@
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.plugins.DarkIconDispatcher;
 import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
+import com.android.systemui.privacy.OngoingPrivacyChip;
+import com.android.systemui.privacy.PrivacyChipEvent;
+import com.android.systemui.privacy.PrivacyItem;
+import com.android.systemui.privacy.PrivacyItemController;
 import com.android.systemui.qs.QSDetail.Callback;
 import com.android.systemui.qs.carrier.QSCarrierGroup;
 import com.android.systemui.statusbar.CommandQueue;
@@ -101,7 +109,6 @@
     private static final int TOOLTIP_NOT_YET_SHOWN_COUNT = 0;
     public static final int MAX_TOOLTIP_SHOWN_COUNT = 2;
 
-    private final Handler mHandler = new Handler();
     private final NextAlarmController mAlarmController;
     private final ZenModeController mZenController;
     private final StatusBarIconController mStatusBarIconController;
@@ -140,9 +147,14 @@
     private View mRingerContainer;
     private Clock mClockView;
     private DateView mDateView;
+    private OngoingPrivacyChip mPrivacyChip;
+    private Space mSpace;
     private BatteryMeterView mBatteryRemainingIcon;
     private RingerModeTracker mRingerModeTracker;
+    private boolean mPermissionsHubEnabled;
 
+    private PrivacyItemController mPrivacyItemController;
+    private final UiEventLogger mUiEventLogger;
     // Used for RingerModeTracker
     private final LifecycleRegistry mLifecycle = new LifecycleRegistry(this);
 
@@ -156,22 +168,43 @@
     private int mCutOutPaddingRight;
     private float mExpandedHeaderAlpha = 1.0f;
     private float mKeyguardExpansionFraction;
+    private boolean mPrivacyChipLogged = false;
+
+    private PrivacyItemController.Callback mPICCallback = new PrivacyItemController.Callback() {
+        @Override
+        public void onPrivacyItemsChanged(List<PrivacyItem> privacyItems) {
+            mPrivacyChip.setPrivacyList(privacyItems);
+            setChipVisibility(!privacyItems.isEmpty());
+        }
+
+        @Override
+        public void onFlagChanged(boolean flag) {
+            if (mPermissionsHubEnabled != flag) {
+                StatusIconContainer iconContainer = requireViewById(R.id.statusIcons);
+                iconContainer.setIgnoredSlots(getIgnoredIconSlots());
+                setChipVisibility(!mPrivacyChip.getPrivacyList().isEmpty());
+            }
+        }
+    };
 
     @Inject
     public QuickStatusBarHeader(@Named(VIEW_CONTEXT) Context context, AttributeSet attrs,
             NextAlarmController nextAlarmController, ZenModeController zenModeController,
             StatusBarIconController statusBarIconController,
-            ActivityStarter activityStarter,
-            CommandQueue commandQueue, RingerModeTracker ringerModeTracker) {
+            ActivityStarter activityStarter, PrivacyItemController privacyItemController,
+            CommandQueue commandQueue, RingerModeTracker ringerModeTracker,
+            UiEventLogger uiEventLogger) {
         super(context, attrs);
         mAlarmController = nextAlarmController;
         mZenController = zenModeController;
         mStatusBarIconController = statusBarIconController;
         mActivityStarter = activityStarter;
+        mPrivacyItemController = privacyItemController;
         mDualToneHandler = new DualToneHandler(
                 new ContextThemeWrapper(context, R.style.QSHeaderTheme));
         mCommandQueue = commandQueue;
         mRingerModeTracker = ringerModeTracker;
+        mUiEventLogger = uiEventLogger;
     }
 
     @Override
@@ -198,8 +231,11 @@
         mRingerModeTextView = findViewById(R.id.ringer_mode_text);
         mRingerContainer = findViewById(R.id.ringer_container);
         mRingerContainer.setOnClickListener(this::onClick);
+        mPrivacyChip = findViewById(R.id.privacy_chip);
+        mPrivacyChip.setOnClickListener(this::onClick);
         mCarrierGroup = findViewById(R.id.carrier_group);
 
+
         updateResources();
 
         Rect tintArea = new Rect(0, 0, 0, 0);
@@ -219,6 +255,7 @@
         mClockView = findViewById(R.id.clock);
         mClockView.setOnClickListener(this);
         mDateView = findViewById(R.id.date);
+        mSpace = findViewById(R.id.space);
 
         // Tint for the battery icons are handled in setupHost()
         mBatteryRemainingIcon = findViewById(R.id.batteryRemainingIcon);
@@ -229,6 +266,8 @@
         mBatteryRemainingIcon.setPercentShowMode(BatteryMeterView.MODE_ESTIMATE);
         mRingerModeTextView.setSelected(true);
         mNextAlarmTextView.setSelected(true);
+
+        mPermissionsHubEnabled = mPrivacyItemController.getIndicatorsAvailable();
     }
 
     public QuickQSPanel getHeaderQsPanel() {
@@ -241,6 +280,10 @@
                 com.android.internal.R.string.status_bar_camera));
         ignored.add(mContext.getResources().getString(
                 com.android.internal.R.string.status_bar_microphone));
+        if (mPermissionsHubEnabled) {
+            ignored.add(mContext.getResources().getString(
+                    com.android.internal.R.string.status_bar_location));
+        }
 
         return ignored;
     }
@@ -256,6 +299,20 @@
         }
     }
 
+    private void setChipVisibility(boolean chipVisible) {
+        if (chipVisible && mPermissionsHubEnabled) {
+            mPrivacyChip.setVisibility(View.VISIBLE);
+            // Makes sure that the chip is logged as viewed at most once each time QS is opened
+            // mListening makes sure that the callback didn't return after the user closed QS
+            if (!mPrivacyChipLogged && mListening) {
+                mPrivacyChipLogged = true;
+                mUiEventLogger.log(PrivacyChipEvent.ONGOING_INDICATORS_CHIP_VIEW);
+            }
+        } else {
+            mPrivacyChip.setVisibility(View.GONE);
+        }
+    }
+
     private boolean updateRingerStatus() {
         boolean isOriginalVisible = mRingerModeTextView.getVisibility() == View.VISIBLE;
         CharSequence originalRingerText = mRingerModeTextView.getText();
@@ -363,6 +420,7 @@
 
         updateStatusIconAlphaAnimator();
         updateHeaderTextContainerAlphaAnimator();
+        updatePrivacyChipAlphaAnimator();
     }
 
     private void updateStatusIconAlphaAnimator() {
@@ -377,6 +435,12 @@
                 .build();
     }
 
+    private void updatePrivacyChipAlphaAnimator() {
+        mPrivacyChipAlphaAnimator = new TouchAnimator.Builder()
+                .addFloat(mPrivacyChip, "alpha", 1, 0, 1)
+                .build();
+    }
+
     public void setExpanded(boolean expanded) {
         if (mExpanded == expanded) return;
         mExpanded = expanded;
@@ -415,6 +479,10 @@
                 mHeaderTextContainerView.setVisibility(INVISIBLE);
             }
         }
+        if (mPrivacyChipAlphaAnimator != null) {
+            mPrivacyChip.setExpanded(expansionFraction > 0.5);
+            mPrivacyChipAlphaAnimator.setPosition(keyguardExpansionFraction);
+        }
         if (expansionFraction < 1 && expansionFraction > 0.99) {
             if (mHeaderQsPanel.switchTileLayout()) {
                 updateResources();
@@ -453,6 +521,31 @@
         Pair<Integer, Integer> padding =
                 StatusBarWindowView.paddingNeededForCutoutAndRoundedCorner(
                         cutout, cornerCutoutPadding, -1);
+        if (padding == null) {
+            mSystemIconsView.setPaddingRelative(
+                    getResources().getDimensionPixelSize(R.dimen.status_bar_padding_start), 0,
+                    getResources().getDimensionPixelSize(R.dimen.status_bar_padding_end), 0);
+        } else {
+            mSystemIconsView.setPadding(padding.first, 0, padding.second, 0);
+
+        }
+        LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) mSpace.getLayoutParams();
+        boolean cornerCutout = cornerCutoutPadding != null
+                && (cornerCutoutPadding.first == 0 || cornerCutoutPadding.second == 0);
+        if (cutout != null) {
+            Rect topCutout = cutout.getBoundingRectTop();
+            if (topCutout.isEmpty() || cornerCutout) {
+                mHasTopCutout = false;
+                lp.width = 0;
+                mSpace.setVisibility(View.GONE);
+            } else {
+                mHasTopCutout = true;
+                lp.width = topCutout.width();
+                mSpace.setVisibility(View.VISIBLE);
+            }
+        }
+        mSpace.setLayoutParams(lp);
+        setChipVisibility(mPrivacyChip.getVisibility() == View.VISIBLE);
         mCutOutPaddingLeft = padding.first;
         mCutOutPaddingRight = padding.second;
         mWaterfallTopInset = cutout == null ? 0 : cutout.getWaterfallInsets().top;
@@ -513,10 +606,15 @@
             mZenController.addCallback(this);
             mAlarmController.addCallback(this);
             mLifecycle.setCurrentState(Lifecycle.State.RESUMED);
+            // Get the most up to date info
+            mPermissionsHubEnabled = mPrivacyItemController.getIndicatorsAvailable();
+            mPrivacyItemController.addCallback(mPICCallback);
         } else {
             mZenController.removeCallback(this);
             mAlarmController.removeCallback(this);
             mLifecycle.setCurrentState(Lifecycle.State.CREATED);
+            mPrivacyItemController.removeCallback(mPICCallback);
+            mPrivacyChipLogged = false;
         }
     }
 
@@ -534,6 +632,15 @@
                 mActivityStarter.postStartActivityDismissingKeyguard(new Intent(
                         AlarmClock.ACTION_SHOW_ALARMS), 0);
             }
+        } else if (v == mPrivacyChip) {
+            // If the privacy chip is visible, it means there were some indicators
+            Handler mUiHandler = new Handler(Looper.getMainLooper());
+            mUiEventLogger.log(PrivacyChipEvent.ONGOING_INDICATORS_CHIP_CLICK);
+            mUiHandler.post(() -> {
+                mActivityStarter.postStartActivityDismissingKeyguard(
+                        new Intent(Intent.ACTION_REVIEW_ONGOING_PERMISSION_USAGE), 0);
+                mHost.collapsePanels();
+            });
         } else if (v == mRingerContainer && mRingerContainer.isVisibleToUser()) {
             mActivityStarter.postStartActivityDismissingKeyguard(new Intent(
                     Settings.ACTION_SOUND_SETTINGS), 0);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index 9115b48..d03082e 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -809,7 +809,9 @@
 
     @Override
     public void addCallback(OverviewProxyListener listener) {
-        mConnectionCallbacks.add(listener);
+        if (!mConnectionCallbacks.contains(listener)) {
+            mConnectionCallbacks.add(listener);
+        }
         listener.onConnectionChanged(mOverviewProxy != null);
         listener.onNavBarButtonAlphaChanged(mNavBarButtonAlpha, false);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ActionProxyReceiver.java b/packages/SystemUI/src/com/android/systemui/screenshot/ActionProxyReceiver.java
new file mode 100644
index 0000000..3fd7f945
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ActionProxyReceiver.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.screenshot;
+
+import static com.android.systemui.screenshot.GlobalScreenshot.ACTION_TYPE_EDIT;
+import static com.android.systemui.screenshot.GlobalScreenshot.ACTION_TYPE_SHARE;
+import static com.android.systemui.screenshot.GlobalScreenshot.EXTRA_ACTION_INTENT;
+import static com.android.systemui.screenshot.GlobalScreenshot.EXTRA_DISALLOW_ENTER_PIP;
+import static com.android.systemui.screenshot.GlobalScreenshot.EXTRA_ID;
+import static com.android.systemui.screenshot.GlobalScreenshot.EXTRA_SMART_ACTIONS_ENABLED;
+import static com.android.systemui.statusbar.phone.StatusBar.SYSTEM_DIALOG_REASON_SCREENSHOT;
+
+import android.app.ActivityOptions;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.util.Log;
+
+import com.android.systemui.shared.system.ActivityManagerWrapper;
+import com.android.systemui.statusbar.phone.StatusBar;
+
+import java.util.Optional;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+import javax.inject.Inject;
+
+/**
+ * Receiver to proxy the share or edit intent, used to clean up the notification and send
+ * appropriate signals to the system (ie. to dismiss the keyguard if necessary).
+ */
+public class ActionProxyReceiver extends BroadcastReceiver {
+    private static final String TAG = "ActionProxyReceiver";
+
+    private static final int CLOSE_WINDOWS_TIMEOUT_MILLIS = 3000;
+    private final StatusBar mStatusBar;
+    private final ActivityManagerWrapper mActivityManagerWrapper;
+    private final ScreenshotSmartActions mScreenshotSmartActions;
+
+    @Inject
+    public ActionProxyReceiver(Optional<StatusBar> statusBar,
+            ActivityManagerWrapper activityManagerWrapper,
+            ScreenshotSmartActions screenshotSmartActions) {
+        mStatusBar = statusBar.orElse(null);
+        mActivityManagerWrapper = activityManagerWrapper;
+        mScreenshotSmartActions = screenshotSmartActions;
+    }
+
+    @Override
+    public void onReceive(Context context, final Intent intent) {
+        Runnable startActivityRunnable = () -> {
+            try {
+                mActivityManagerWrapper.closeSystemWindows(
+                        SYSTEM_DIALOG_REASON_SCREENSHOT).get(
+                        CLOSE_WINDOWS_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
+            } catch (TimeoutException | InterruptedException | ExecutionException e) {
+                Log.e(TAG, "Unable to share screenshot", e);
+                return;
+            }
+
+            PendingIntent actionIntent = intent.getParcelableExtra(EXTRA_ACTION_INTENT);
+            ActivityOptions opts = ActivityOptions.makeBasic();
+            opts.setDisallowEnterPictureInPictureWhileLaunching(
+                    intent.getBooleanExtra(EXTRA_DISALLOW_ENTER_PIP, false));
+            try {
+                actionIntent.send(context, 0, null, null, null, null, opts.toBundle());
+            } catch (PendingIntent.CanceledException e) {
+                Log.e(TAG, "Pending intent canceled", e);
+            }
+
+        };
+
+        if (mStatusBar != null) {
+            mStatusBar.executeRunnableDismissingKeyguard(startActivityRunnable, null,
+                    true /* dismissShade */, true /* afterKeyguardGone */,
+                    true /* deferred */);
+        } else {
+            startActivityRunnable.run();
+        }
+
+        if (intent.getBooleanExtra(EXTRA_SMART_ACTIONS_ENABLED, false)) {
+            String actionType = Intent.ACTION_EDIT.equals(intent.getAction())
+                    ? ACTION_TYPE_EDIT
+                    : ACTION_TYPE_SHARE;
+            mScreenshotSmartActions.notifyScreenshotAction(
+                    context, intent.getStringExtra(EXTRA_ID), actionType, false);
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/DeleteImageInBackgroundTask.java b/packages/SystemUI/src/com/android/systemui/screenshot/DeleteImageInBackgroundTask.java
deleted file mode 100644
index 8c48655..0000000
--- a/packages/SystemUI/src/com/android/systemui/screenshot/DeleteImageInBackgroundTask.java
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * 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.screenshot;
-
-import android.content.ContentResolver;
-import android.content.Context;
-import android.net.Uri;
-import android.os.AsyncTask;
-
-/**
- * An AsyncTask that deletes an image from the media store in the background.
- */
-class DeleteImageInBackgroundTask extends AsyncTask<Uri, Void, Void> {
-    private Context mContext;
-
-    DeleteImageInBackgroundTask(Context context) {
-        mContext = context;
-    }
-
-    @Override
-    protected Void doInBackground(Uri... params) {
-        if (params.length != 1) return null;
-
-        Uri screenshotUri = params[0];
-        ContentResolver resolver = mContext.getContentResolver();
-        resolver.delete(screenshotUri, null, null);
-        return null;
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/DeleteScreenshotReceiver.java b/packages/SystemUI/src/com/android/systemui/screenshot/DeleteScreenshotReceiver.java
new file mode 100644
index 0000000..9028bb5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/DeleteScreenshotReceiver.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.screenshot;
+
+import static com.android.systemui.screenshot.GlobalScreenshot.ACTION_TYPE_DELETE;
+import static com.android.systemui.screenshot.GlobalScreenshot.EXTRA_ID;
+import static com.android.systemui.screenshot.GlobalScreenshot.EXTRA_SMART_ACTIONS_ENABLED;
+import static com.android.systemui.screenshot.GlobalScreenshot.SCREENSHOT_URI_ID;
+
+import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+
+import com.android.systemui.dagger.qualifiers.Background;
+
+import java.util.concurrent.Executor;
+
+import javax.inject.Inject;
+
+/**
+ * Removes the file at a provided URI.
+ */
+public class DeleteScreenshotReceiver extends BroadcastReceiver {
+
+    private final ScreenshotSmartActions mScreenshotSmartActions;
+    private final Executor mBackgroundExecutor;
+
+    @Inject
+    public DeleteScreenshotReceiver(ScreenshotSmartActions screenshotSmartActions,
+            @Background Executor backgroundExecutor) {
+        mScreenshotSmartActions = screenshotSmartActions;
+        mBackgroundExecutor = backgroundExecutor;
+    }
+
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        if (!intent.hasExtra(SCREENSHOT_URI_ID)) {
+            return;
+        }
+
+        // And delete the image from the media store
+        final Uri uri = Uri.parse(intent.getStringExtra(SCREENSHOT_URI_ID));
+        mBackgroundExecutor.execute(() -> {
+            ContentResolver resolver = context.getContentResolver();
+            resolver.delete(uri, null, null);
+        });
+        if (intent.getBooleanExtra(EXTRA_SMART_ACTIONS_ENABLED, false)) {
+            mScreenshotSmartActions.notifyScreenshotAction(
+                    context, intent.getStringExtra(EXTRA_ID), ACTION_TYPE_DELETE, false);
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
index 4a4aa79..c535230 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
@@ -21,8 +21,6 @@
 import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
 import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
 
-import static com.android.systemui.statusbar.phone.StatusBar.SYSTEM_DIALOG_REASON_SCREENSHOT;
-
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.AnimatorSet;
@@ -30,13 +28,10 @@
 import android.annotation.Nullable;
 import android.annotation.SuppressLint;
 import android.app.ActivityManager;
-import android.app.ActivityOptions;
 import android.app.Notification;
 import android.app.PendingIntent;
-import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.Context;
-import android.content.Intent;
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.graphics.Bitmap;
@@ -62,7 +57,6 @@
 import android.util.DisplayMetrics;
 import android.util.Log;
 import android.util.MathUtils;
-import android.util.Slog;
 import android.view.Display;
 import android.view.KeyEvent;
 import android.view.LayoutInflater;
@@ -87,23 +81,15 @@
 import com.android.internal.logging.UiEventLogger;
 import com.android.systemui.R;
 import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.shared.system.QuickStepContract;
-import com.android.systemui.statusbar.phone.StatusBar;
 
 import java.util.ArrayList;
 import java.util.List;
-import java.util.Optional;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
 import java.util.function.Consumer;
 
 import javax.inject.Inject;
 import javax.inject.Singleton;
 
-import dagger.Lazy;
-
 /**
  * Class for handling device screen shots
  */
@@ -192,6 +178,7 @@
     private final UiEventLogger mUiEventLogger;
 
     private final Context mContext;
+    private final ScreenshotSmartActions mScreenshotSmartActions;
     private final WindowManager mWindowManager;
     private final WindowManager.LayoutParams mWindowLayoutParams;
     private final Display mDisplay;
@@ -247,9 +234,11 @@
     @Inject
     public GlobalScreenshot(
             Context context, @Main Resources resources,
+            ScreenshotSmartActions screenshotSmartActions,
             ScreenshotNotificationsController screenshotNotificationsController,
             UiEventLogger uiEventLogger) {
         mContext = context;
+        mScreenshotSmartActions = screenshotSmartActions;
         mNotificationsController = screenshotNotificationsController;
         mUiEventLogger = uiEventLogger;
 
@@ -504,7 +493,7 @@
         });
         mScreenshotLayout.setOnKeyListener((v, keyCode, event) -> {
             if (keyCode == KeyEvent.KEYCODE_BACK) {
-                dismissScreenshot("back pressed", true);
+                dismissScreenshot("back pressed", false);
                 return true;
             }
             return false;
@@ -704,7 +693,7 @@
             });
         }
 
-        mSaveInBgTask = new SaveImageInBackgroundTask(mContext, data);
+        mSaveInBgTask = new SaveImageInBackgroundTask(mContext, mScreenshotSmartActions, data);
         mSaveInBgTask.execute();
     }
 
@@ -1024,6 +1013,7 @@
         mScreenshotPreview.setLayerType(View.LAYER_TYPE_NONE, null);
         mScreenshotPreview.setContentDescription(
                 mContext.getResources().getString(R.string.screenshot_preview_description));
+        mScreenshotPreview.setOnClickListener(null);
         mScreenshotLayout.setAlpha(1);
         mDismissButton.setTranslationY(0);
         mActionsContainer.setTranslationY(0);
@@ -1116,119 +1106,4 @@
             return insetDrawable;
         }
     }
-
-    /**
-     * Receiver to proxy the share or edit intent, used to clean up the notification and send
-     * appropriate signals to the system (ie. to dismiss the keyguard if necessary).
-     */
-    public static class ActionProxyReceiver extends BroadcastReceiver {
-        static final int CLOSE_WINDOWS_TIMEOUT_MILLIS = 3000;
-        private final StatusBar mStatusBar;
-
-        @Inject
-        public ActionProxyReceiver(Optional<Lazy<StatusBar>> statusBarLazy) {
-            Lazy<StatusBar> statusBar = statusBarLazy.orElse(null);
-            mStatusBar = statusBar != null ? statusBar.get() : null;
-        }
-
-        @Override
-        public void onReceive(Context context, final Intent intent) {
-            Runnable startActivityRunnable = () -> {
-                try {
-                    ActivityManagerWrapper.getInstance().closeSystemWindows(
-                            SYSTEM_DIALOG_REASON_SCREENSHOT).get(
-                            CLOSE_WINDOWS_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
-                } catch (TimeoutException | InterruptedException | ExecutionException e) {
-                    Slog.e(TAG, "Unable to share screenshot", e);
-                    return;
-                }
-
-                PendingIntent actionIntent = intent.getParcelableExtra(EXTRA_ACTION_INTENT);
-                if (intent.getBooleanExtra(EXTRA_CANCEL_NOTIFICATION, false)) {
-                    ScreenshotNotificationsController.cancelScreenshotNotification(context);
-                }
-                ActivityOptions opts = ActivityOptions.makeBasic();
-                opts.setDisallowEnterPictureInPictureWhileLaunching(
-                        intent.getBooleanExtra(EXTRA_DISALLOW_ENTER_PIP, false));
-                try {
-                    actionIntent.send(context, 0, null, null, null, null, opts.toBundle());
-                } catch (PendingIntent.CanceledException e) {
-                    Log.e(TAG, "Pending intent canceled", e);
-                }
-
-            };
-
-            if (mStatusBar != null) {
-                mStatusBar.executeRunnableDismissingKeyguard(startActivityRunnable, null,
-                        true /* dismissShade */, true /* afterKeyguardGone */,
-                        true /* deferred */);
-            } else {
-                startActivityRunnable.run();
-            }
-
-            if (intent.getBooleanExtra(EXTRA_SMART_ACTIONS_ENABLED, false)) {
-                String actionType = Intent.ACTION_EDIT.equals(intent.getAction())
-                        ? ACTION_TYPE_EDIT
-                        : ACTION_TYPE_SHARE;
-                ScreenshotSmartActions.notifyScreenshotAction(
-                        context, intent.getStringExtra(EXTRA_ID), actionType, false);
-            }
-        }
-    }
-
-    /**
-     * Removes the notification for a screenshot after a share target is chosen.
-     */
-    public static class TargetChosenReceiver extends BroadcastReceiver {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            // Clear the notification only after the user has chosen a share action
-            ScreenshotNotificationsController.cancelScreenshotNotification(context);
-        }
-    }
-
-    /**
-     * Removes the last screenshot.
-     */
-    public static class DeleteScreenshotReceiver extends BroadcastReceiver {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            if (!intent.hasExtra(SCREENSHOT_URI_ID)) {
-                return;
-            }
-
-            // Clear the notification when the image is deleted
-            ScreenshotNotificationsController.cancelScreenshotNotification(context);
-
-            // And delete the image from the media store
-            final Uri uri = Uri.parse(intent.getStringExtra(SCREENSHOT_URI_ID));
-            new DeleteImageInBackgroundTask(context).execute(uri);
-            if (intent.getBooleanExtra(EXTRA_SMART_ACTIONS_ENABLED, false)) {
-                ScreenshotSmartActions.notifyScreenshotAction(
-                        context, intent.getStringExtra(EXTRA_ID), ACTION_TYPE_DELETE, false);
-            }
-        }
-    }
-
-    /**
-     * Executes the smart action tapped by the user in the notification.
-     */
-    public static class SmartActionsReceiver extends BroadcastReceiver {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            PendingIntent pendingIntent = intent.getParcelableExtra(EXTRA_ACTION_INTENT);
-            String actionType = intent.getStringExtra(EXTRA_ACTION_TYPE);
-            Slog.d(TAG, "Executing smart action [" + actionType + "]:" + pendingIntent.getIntent());
-            ActivityOptions opts = ActivityOptions.makeBasic();
-
-            try {
-                pendingIntent.send(context, 0, null, null, null, null, opts.toBundle());
-            } catch (PendingIntent.CanceledException e) {
-                Log.e(TAG, "Pending intent canceled", e);
-            }
-
-            ScreenshotSmartActions.notifyScreenshotAction(
-                    context, intent.getStringExtra(EXTRA_ID), actionType, true);
-        }
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java b/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java
index 468b9b1..df1d789 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java
@@ -81,6 +81,7 @@
     private static final String SCREENSHOT_SHARE_SUBJECT_TEMPLATE = "Screenshot (%s)";
 
     private final Context mContext;
+    private final ScreenshotSmartActions mScreenshotSmartActions;
     private final GlobalScreenshot.SaveImageInBackgroundData mParams;
     private final GlobalScreenshot.SavedImageData mImageData;
     private final String mImageFileName;
@@ -90,8 +91,10 @@
     private final boolean mSmartActionsEnabled;
     private final Random mRandom = new Random();
 
-    SaveImageInBackgroundTask(Context context, GlobalScreenshot.SaveImageInBackgroundData data) {
+    SaveImageInBackgroundTask(Context context, ScreenshotSmartActions screenshotSmartActions,
+            GlobalScreenshot.SaveImageInBackgroundData data) {
         mContext = context;
+        mScreenshotSmartActions = screenshotSmartActions;
         mImageData = new GlobalScreenshot.SavedImageData();
 
         // Prepare all the output metadata
@@ -141,7 +144,7 @@
             final Uri uri = resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
 
             CompletableFuture<List<Notification.Action>> smartActionsFuture =
-                    ScreenshotSmartActions.getSmartActionsFuture(
+                    mScreenshotSmartActions.getSmartActionsFuture(
                             mScreenshotId, uri, image, mSmartActionsProvider,
                             mSmartActionsEnabled, getUserHandle(mContext));
 
@@ -199,7 +202,7 @@
                         SystemUiDeviceConfigFlags.SCREENSHOT_NOTIFICATION_SMART_ACTIONS_TIMEOUT_MS,
                         1000);
                 smartActions.addAll(buildSmartActions(
-                        ScreenshotSmartActions.getSmartActions(
+                        mScreenshotSmartActions.getSmartActions(
                                 mScreenshotId, smartActionsFuture, timeoutMs,
                                 mSmartActionsProvider),
                         mContext));
@@ -274,11 +277,8 @@
         // by setting the (otherwise unused) request code to the current user id.
         int requestCode = context.getUserId();
 
-        PendingIntent chooserAction = PendingIntent.getBroadcast(context, requestCode,
-                new Intent(context, GlobalScreenshot.TargetChosenReceiver.class),
-                PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT);
         Intent sharingChooserIntent =
-                Intent.createChooser(sharingIntent, null, chooserAction.getIntentSender())
+                Intent.createChooser(sharingIntent, null)
                         .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK)
                         .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
 
@@ -288,7 +288,7 @@
 
         // Create a share action for the notification
         PendingIntent shareAction = PendingIntent.getBroadcastAsUser(context, requestCode,
-                new Intent(context, GlobalScreenshot.ActionProxyReceiver.class)
+                new Intent(context, ActionProxyReceiver.class)
                         .putExtra(GlobalScreenshot.EXTRA_ACTION_INTENT, pendingIntent)
                         .putExtra(GlobalScreenshot.EXTRA_DISALLOW_ENTER_PIP, true)
                         .putExtra(GlobalScreenshot.EXTRA_ID, mScreenshotId)
@@ -333,10 +333,8 @@
 
         // Create a edit action
         PendingIntent editAction = PendingIntent.getBroadcastAsUser(context, requestCode,
-                new Intent(context, GlobalScreenshot.ActionProxyReceiver.class)
+                new Intent(context, ActionProxyReceiver.class)
                         .putExtra(GlobalScreenshot.EXTRA_ACTION_INTENT, pendingIntent)
-                        .putExtra(GlobalScreenshot.EXTRA_CANCEL_NOTIFICATION,
-                                editIntent.getComponent() != null)
                         .putExtra(GlobalScreenshot.EXTRA_ID, mScreenshotId)
                         .putExtra(GlobalScreenshot.EXTRA_SMART_ACTIONS_ENABLED,
                                 mSmartActionsEnabled)
@@ -358,7 +356,7 @@
 
         // Create a delete action for the notification
         PendingIntent deleteAction = PendingIntent.getBroadcast(context, requestCode,
-                new Intent(context, GlobalScreenshot.DeleteScreenshotReceiver.class)
+                new Intent(context, DeleteScreenshotReceiver.class)
                         .putExtra(GlobalScreenshot.SCREENSHOT_URI_ID, uri.toString())
                         .putExtra(GlobalScreenshot.EXTRA_ID, mScreenshotId)
                         .putExtra(GlobalScreenshot.EXTRA_SMART_ACTIONS_ENABLED,
@@ -398,7 +396,7 @@
             String actionType = extras.getString(
                     ScreenshotNotificationSmartActionsProvider.ACTION_TYPE,
                     ScreenshotNotificationSmartActionsProvider.DEFAULT_ACTION_TYPE);
-            Intent intent = new Intent(context, GlobalScreenshot.SmartActionsReceiver.class)
+            Intent intent = new Intent(context, SmartActionsReceiver.class)
                     .putExtra(GlobalScreenshot.EXTRA_ACTION_INTENT, action.actionIntent)
                     .addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
             addIntentExtras(mScreenshotId, intent, actionType, mSmartActionsEnabled);
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSmartActions.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSmartActions.java
index 442b373..633cdd6 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSmartActions.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSmartActions.java
@@ -39,14 +39,21 @@
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeoutException;
 
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
 /**
  * Collects the static functions for retrieving and acting on smart actions.
  */
+@Singleton
 public class ScreenshotSmartActions {
     private static final String TAG = "ScreenshotSmartActions";
 
+    @Inject
+    public ScreenshotSmartActions() {}
+
     @VisibleForTesting
-    static CompletableFuture<List<Notification.Action>> getSmartActionsFuture(
+    CompletableFuture<List<Notification.Action>> getSmartActionsFuture(
             String screenshotId, Uri screenshotUri, Bitmap image,
             ScreenshotNotificationSmartActionsProvider smartActionsProvider,
             boolean smartActionsEnabled, UserHandle userHandle) {
@@ -86,7 +93,7 @@
     }
 
     @VisibleForTesting
-    static List<Notification.Action> getSmartActions(String screenshotId,
+    List<Notification.Action> getSmartActions(String screenshotId,
             CompletableFuture<List<Notification.Action>> smartActionsFuture, int timeoutMs,
             ScreenshotNotificationSmartActionsProvider smartActionsProvider) {
         long startTimeMs = SystemClock.uptimeMillis();
@@ -116,7 +123,7 @@
         }
     }
 
-    static void notifyScreenshotOp(String screenshotId,
+    void notifyScreenshotOp(String screenshotId,
             ScreenshotNotificationSmartActionsProvider smartActionsProvider,
             ScreenshotNotificationSmartActionsProvider.ScreenshotOp op,
             ScreenshotNotificationSmartActionsProvider.ScreenshotOpStatus status, long durationMs) {
@@ -127,7 +134,7 @@
         }
     }
 
-    static void notifyScreenshotAction(Context context, String screenshotId, String action,
+    void notifyScreenshotAction(Context context, String screenshotId, String action,
             boolean isSmartAction) {
         try {
             ScreenshotNotificationSmartActionsProvider provider =
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/SmartActionsReceiver.java b/packages/SystemUI/src/com/android/systemui/screenshot/SmartActionsReceiver.java
new file mode 100644
index 0000000..217235b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/SmartActionsReceiver.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.screenshot;
+
+import static com.android.systemui.screenshot.GlobalScreenshot.EXTRA_ACTION_INTENT;
+import static com.android.systemui.screenshot.GlobalScreenshot.EXTRA_ACTION_TYPE;
+import static com.android.systemui.screenshot.GlobalScreenshot.EXTRA_ID;
+
+import android.app.ActivityOptions;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.util.Log;
+import android.util.Slog;
+
+import javax.inject.Inject;
+
+
+/**
+ * Executes the smart action tapped by the user in the notification.
+ */
+public class SmartActionsReceiver extends BroadcastReceiver {
+    private static final String TAG = "SmartActionsReceiver";
+
+    private final ScreenshotSmartActions mScreenshotSmartActions;
+
+    @Inject
+    SmartActionsReceiver(ScreenshotSmartActions screenshotSmartActions) {
+        mScreenshotSmartActions = screenshotSmartActions;
+    }
+
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        PendingIntent pendingIntent = intent.getParcelableExtra(EXTRA_ACTION_INTENT);
+        String actionType = intent.getStringExtra(EXTRA_ACTION_TYPE);
+        Slog.d(TAG, "Executing smart action [" + actionType + "]:" + pendingIntent.getIntent());
+        ActivityOptions opts = ActivityOptions.makeBasic();
+
+        try {
+            pendingIntent.send(context, 0, null, null, null, null, opts.toBundle());
+        } catch (PendingIntent.CanceledException e) {
+            Log.e(TAG, "Pending intent canceled", e);
+        }
+
+        mScreenshotSmartActions.notifyScreenshotAction(
+                context, intent.getStringExtra(EXTRA_ID), actionType, true);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
index 6f143da..a043f0f 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
@@ -61,7 +61,7 @@
         @Override
         public void onReceive(Context context, Intent intent) {
             if (ACTION_CLOSE_SYSTEM_DIALOGS.equals(intent.getAction()) && mScreenshot != null) {
-                mScreenshot.dismissScreenshot("close system dialogs", true);
+                mScreenshot.dismissScreenshot("close system dialogs", false);
             }
         }
     };
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NavigationBarController.java b/packages/SystemUI/src/com/android/systemui/statusbar/NavigationBarController.java
index 8c24c54..2638d28 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NavigationBarController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NavigationBarController.java
@@ -154,7 +154,7 @@
                             Dependency.get(IWindowManager.class));
             navBar.setAutoHideController(autoHideController);
             navBar.restoreAppearanceAndTransientState();
-            mNavigationBars.append(displayId, navBar);
+            mNavigationBars.put(displayId, navBar);
 
             if (result != null) {
                 navBar.setImeWindowStatus(display.getDisplayId(), result.mImeToken,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
index 9abc660..aba9e10 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
@@ -24,18 +24,24 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.Notification;
+import android.content.Context;
+import android.os.RemoteException;
+import android.os.ServiceManager;
 import android.os.SystemClock;
 import android.service.notification.NotificationListenerService;
 import android.service.notification.NotificationListenerService.Ranking;
 import android.service.notification.NotificationListenerService.RankingMap;
+import android.service.notification.NotificationStats;
 import android.service.notification.StatusBarNotification;
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.Log;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.statusbar.IStatusBarService;
 import com.android.internal.statusbar.NotificationVisibility;
 import com.android.systemui.Dumpable;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.FeatureFlags;
 import com.android.systemui.statusbar.NotificationLifetimeExtender;
 import com.android.systemui.statusbar.NotificationListener;
@@ -52,6 +58,7 @@
 import com.android.systemui.statusbar.notification.dagger.NotificationsModule;
 import com.android.systemui.statusbar.notification.logging.NotificationLogger;
 import com.android.systemui.statusbar.phone.NotificationGroupManager;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
 import com.android.systemui.util.Assert;
 import com.android.systemui.util.leak.LeakDetector;
 
@@ -127,6 +134,8 @@
 
     private final NotificationEntryManagerLogger mLogger;
 
+    private final IStatusBarService mStatusBarService;
+
     // Lazily retrieved dependencies
     private final Lazy<NotificationRowBinder> mNotificationRowBinderLazy;
     private final Lazy<NotificationRemoteInputManager> mRemoteInputManagerLazy;
@@ -138,6 +147,8 @@
     private final NotificationRankingManager mRankingManager;
     private final FeatureFlags mFeatureFlags;
     private final ForegroundServiceDismissalFeatureController mFgsFeatureController;
+    private final HeadsUpManager mHeadsUpManager;
+    private final StatusBarStateController mStatusBarStateController;
 
     private NotificationPresenter mPresenter;
     private RankingMap mLatestRankingMap;
@@ -201,7 +212,10 @@
             Lazy<NotificationRowBinder> notificationRowBinderLazy,
             Lazy<NotificationRemoteInputManager> notificationRemoteInputManagerLazy,
             LeakDetector leakDetector,
-            ForegroundServiceDismissalFeatureController fgsFeatureController) {
+            ForegroundServiceDismissalFeatureController fgsFeatureController,
+            HeadsUpManager headsUpManager,
+            StatusBarStateController statusBarStateController
+    ) {
         mLogger = logger;
         mGroupManager = groupManager;
         mRankingManager = rankingManager;
@@ -211,6 +225,11 @@
         mRemoteInputManagerLazy = notificationRemoteInputManagerLazy;
         mLeakDetector = leakDetector;
         mFgsFeatureController = fgsFeatureController;
+        mHeadsUpManager = headsUpManager;
+        mStatusBarStateController = statusBarStateController;
+
+        mStatusBarService = IStatusBarService.Stub.asInterface(
+                ServiceManager.checkService(Context.STATUS_BAR_SERVICE));
     }
 
     /** Once called, the NEM will start processing notification events from system server. */
@@ -496,6 +515,9 @@
                 removedByUser |= entryDismissed;
 
                 mLogger.logNotifRemoved(entry.getKey(), removedByUser);
+                if (removedByUser && visibility != null) {
+                    sendNotificationRemovalToServer(entry.getKey(), entry.getSbn(), visibility);
+                }
                 for (NotificationEntryListener listener : mNotificationEntryListeners) {
                     listener.onEntryRemoved(entry, visibility, removedByUser, reason);
                 }
@@ -511,6 +533,36 @@
         }
     }
 
+    private void sendNotificationRemovalToServer(
+            String key,
+            StatusBarNotification notification,
+            NotificationVisibility nv) {
+        final String pkg = notification.getPackageName();
+        final String tag = notification.getTag();
+        final int id = notification.getId();
+        final int userId = notification.getUser().getIdentifier();
+        try {
+            int dismissalSurface = NotificationStats.DISMISSAL_SHADE;
+            if (mHeadsUpManager.isAlerting(key)) {
+                dismissalSurface = NotificationStats.DISMISSAL_PEEK;
+            } else if (mStatusBarStateController.isDozing()) {
+                dismissalSurface = NotificationStats.DISMISSAL_AOD;
+            }
+            int dismissalSentiment = NotificationStats.DISMISS_SENTIMENT_NEUTRAL;
+            mStatusBarService.onNotificationClear(
+                    pkg,
+                    tag,
+                    id,
+                    userId,
+                    notification.getKey(),
+                    dismissalSurface,
+                    dismissalSentiment,
+                    nv);
+        } catch (RemoteException ex) {
+            // system process is dead if we're here.
+        }
+    }
+
     /**
      * Ensures that the group children are cancelled immediately when the group summary is cancelled
      * instead of waiting for the notification manager to send all cancels. Otherwise this could
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt
index 2747696..f982cf0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt
@@ -382,7 +382,7 @@
     }
 
     private fun shouldAnimateVisibility() =
-            dozeParameters.getAlwaysOn() && !dozeParameters.getDisplayNeedsBlanking()
+            dozeParameters.alwaysOn && !dozeParameters.displayNeedsBlanking
 
     interface WakeUpListener {
         /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
index d2c202c..d661b5e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
@@ -59,6 +59,7 @@
 import com.android.systemui.statusbar.notification.row.PriorityOnboardingDialogController;
 import com.android.systemui.statusbar.phone.NotificationGroupManager;
 import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
 import com.android.systemui.util.leak.LeakDetector;
 
 import java.util.concurrent.Executor;
@@ -88,7 +89,9 @@
             Lazy<NotificationRowBinder> notificationRowBinderLazy,
             Lazy<NotificationRemoteInputManager> notificationRemoteInputManagerLazy,
             LeakDetector leakDetector,
-            ForegroundServiceDismissalFeatureController fgsFeatureController) {
+            ForegroundServiceDismissalFeatureController fgsFeatureController,
+            HeadsUpManager headsUpManager,
+            StatusBarStateController statusBarStateController) {
         return new NotificationEntryManager(
                 logger,
                 groupManager,
@@ -98,7 +101,9 @@
                 notificationRowBinderLazy,
                 notificationRemoteInputManagerLazy,
                 leakDetector,
-                fgsFeatureController);
+                fgsFeatureController,
+                headsUpManager,
+                statusBarStateController);
     }
 
     /** Provides an instance of {@link NotificationGutsManager} */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
index bd0d0b3..4441270 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
@@ -21,7 +21,6 @@
 import android.os.ServiceManager;
 import android.os.SystemClock;
 import android.service.notification.NotificationListenerService;
-import android.service.notification.NotificationStats;
 import android.service.notification.StatusBarNotification;
 import android.util.ArrayMap;
 import android.util.ArraySet;
@@ -44,7 +43,6 @@
 import com.android.systemui.statusbar.notification.dagger.NotificationsModule;
 import com.android.systemui.statusbar.notification.stack.ExpandableViewState;
 import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
-import com.android.systemui.statusbar.policy.HeadsUpManager;
 
 import java.util.Collection;
 import java.util.Collections;
@@ -74,7 +72,6 @@
     private final Executor mUiBgExecutor;
     private final NotificationEntryManager mEntryManager;
     private final NotificationPanelLogger mNotificationPanelLogger;
-    private HeadsUpManager mHeadsUpManager;
     private final ExpansionStateLogger mExpansionStateLogger;
 
     protected Handler mHandler = new Handler();
@@ -226,9 +223,6 @@
                     NotificationVisibility visibility,
                     boolean removedByUser,
                     int reason) {
-                if (removedByUser && visibility != null) {
-                    logNotificationClear(entry.getKey(), entry.getSbn(), visibility);
-                }
                 mExpansionStateLogger.onEntryRemoved(entry.getKey());
             }
 
@@ -250,10 +244,6 @@
         mListContainer = listContainer;
     }
 
-    public void setHeadsUpManager(HeadsUpManager headsUpManager) {
-        mHeadsUpManager = headsUpManager;
-    }
-
     public void stopNotificationLogging() {
         if (mLogging) {
             mLogging = false;
@@ -296,30 +286,6 @@
         }
     }
 
-    // TODO: This method has side effects, it is NOT just logging that a notification
-    // was cleared, it also actually removes the notification
-    private void logNotificationClear(String key, StatusBarNotification notification,
-            NotificationVisibility nv) {
-        final String pkg = notification.getPackageName();
-        final String tag = notification.getTag();
-        final int id = notification.getId();
-        final int userId = notification.getUserId();
-        try {
-            int dismissalSurface = NotificationStats.DISMISSAL_SHADE;
-            if (mHeadsUpManager.isAlerting(key)) {
-                dismissalSurface = NotificationStats.DISMISSAL_PEEK;
-            } else if (mListContainer.hasPulsingNotifications()) {
-                dismissalSurface = NotificationStats.DISMISSAL_AOD;
-            }
-            int dismissalSentiment = NotificationStats.DISMISS_SENTIMENT_NEUTRAL;
-            mBarService.onNotificationClear(pkg, tag, id, userId, notification.getKey(),
-                    dismissalSurface,
-                    dismissalSentiment, nv);
-        } catch (RemoteException ex) {
-            // system process is dead if we're here.
-        }
-    }
-
     /**
      * Logs Notification inflation error
      */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt
index 91cc896..ff7793d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt
@@ -329,6 +329,7 @@
         // shade.
         for (i in parent.childCount - 1 downTo -1) {
             val child: View? = parent.getChildAt(i)
+
             child?.let {
                 logShadeChild(i, child)
                 // If this child is a header, update the tracked positions
@@ -342,7 +343,8 @@
                 }
             }
 
-            val row = child as? ExpandableNotificationRow
+            val row = (child as? ExpandableNotificationRow)
+                    ?.takeUnless { it.visibility == View.GONE }
 
             // Is there a section discontinuity? This usually occurs due to HUNs
             inIncomingSection = inIncomingSection || nextBucket?.let { next ->
@@ -389,7 +391,7 @@
 
             // Offset the target to account for the current position of the people header.
             peopleState?.targetPosition = peopleState?.currentPosition?.let { current ->
-                peopleState?.targetPosition?.let { target ->
+                peopleState.targetPosition?.let { target ->
                     if (current < target) target - 1 else target
                 }
             }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
index f5ea1c8..5fab4be 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
@@ -28,6 +28,7 @@
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.doze.AlwaysOnDisplayPolicy;
 import com.android.systemui.doze.DozeScreenState;
+import com.android.systemui.statusbar.policy.BatteryController;
 import com.android.systemui.tuner.TunerService;
 
 import java.io.PrintWriter;
@@ -52,6 +53,7 @@
 
     private final AlwaysOnDisplayPolicy mAlwaysOnPolicy;
     private final Resources mResources;
+    private final BatteryController mBatteryController;
 
     private boolean mDozeAlwaysOn;
     private boolean mControlScreenOffAnimation;
@@ -62,10 +64,12 @@
             AmbientDisplayConfiguration ambientDisplayConfiguration,
             AlwaysOnDisplayPolicy alwaysOnDisplayPolicy,
             PowerManager powerManager,
+            BatteryController batteryController,
             TunerService tunerService) {
         mResources = resources;
         mAmbientDisplayConfiguration = ambientDisplayConfiguration;
         mAlwaysOnPolicy = alwaysOnDisplayPolicy;
+        mBatteryController = batteryController;
 
         mControlScreenOffAnimation = !getDisplayNeedsBlanking();
         mPowerManager = powerManager;
@@ -164,7 +168,7 @@
      * @return {@code true} if enabled and available.
      */
     public boolean getAlwaysOn() {
-        return mDozeAlwaysOn;
+        return mDozeAlwaysOn && !mBatteryController.isAodPowerSave();
     }
 
     /**
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 a35aca5..9606318 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java
@@ -121,6 +121,7 @@
 
     private final Context mContext;
     private final OverviewProxyService mOverviewProxyService;
+    private final SysUiState mSysUiState;
     private final Runnable mStateChangeCallback;
 
     private final PluginManager mPluginManager;
@@ -197,14 +198,22 @@
                 }
             };
 
+    private final SysUiState.SysUiStateCallback mSysUiStateCallback =
+            new SysUiState.SysUiStateCallback() {
+        @Override
+        public void onSystemUiStateChanged(int sysUiFlags) {
+            mSysUiFlags = sysUiFlags;
+        }
+    };
+
     public EdgeBackGestureHandler(Context context, OverviewProxyService overviewProxyService,
-            SysUiState sysUiFlagContainer, PluginManager pluginManager,
-            Runnable stateChangeCallback) {
+            SysUiState sysUiState, PluginManager pluginManager, Runnable stateChangeCallback) {
         super(Dependency.get(BroadcastDispatcher.class));
         mContext = context;
         mDisplayId = context.getDisplayId();
         mMainExecutor = context.getMainExecutor();
         mOverviewProxyService = overviewProxyService;
+        mSysUiState = sysUiState;
         mPluginManager = pluginManager;
         mStateChangeCallback = stateChangeCallback;
         ComponentName recentsComponentName = ComponentName.unflattenFromString(
@@ -238,7 +247,6 @@
                 mContext.getMainThreadHandler(), mContext, this::onNavigationSettingsChanged);
 
         updateCurrentUserResources();
-        sysUiFlagContainer.addCallback(sysUiFlags -> mSysUiFlags = sysUiFlags);
     }
 
     public void updateCurrentUserResources() {
@@ -287,6 +295,7 @@
         mIsAttached = true;
         Dependency.get(ProtoTracer.class).add(this);
         mOverviewProxyService.addCallback(mQuickSwitchListener);
+        mSysUiState.addCallback(mSysUiStateCallback);
         updateIsEnabled();
         startTracking();
     }
@@ -298,6 +307,7 @@
         mIsAttached = false;
         Dependency.get(ProtoTracer.class).remove(this);
         mOverviewProxyService.removeCallback(mQuickSwitchListener);
+        mSysUiState.removeCallback(mSysUiStateCallback);
         updateIsEnabled();
         stopTracking();
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
index b7733cc..f43fa64 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
@@ -120,6 +120,7 @@
 import com.android.systemui.statusbar.AutoHideUiElement;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.CommandQueue.Callbacks;
+import com.android.systemui.statusbar.NavigationBarController;
 import com.android.systemui.statusbar.NotificationRemoteInputManager;
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
@@ -131,6 +132,7 @@
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
+import java.lang.ref.WeakReference;
 import java.util.List;
 import java.util.Locale;
 import java.util.Optional;
@@ -354,6 +356,7 @@
             // If the button will actually become visible and the navbar is about to hide,
             // tell the statusbar to keep it around for longer
             mAutoHideController.touchAutoHide();
+            mNavigationBarView.notifyActiveTouchRegions();
         }
     };
 
@@ -550,6 +553,9 @@
             mOrientationHandle.getViewTreeObserver().removeOnGlobalLayoutListener(
                     mOrientationHandleGlobalLayoutListener);
         }
+        mHandler.removeCallbacks(mAutoDim);
+        mNavigationBarView = null;
+        mOrientationHandle = null;
     }
 
     @Override
@@ -1458,11 +1464,11 @@
         if (DEBUG) Log.v(TAG, "addNavigationBar: about to add " + navigationBarView);
         if (navigationBarView == null) return null;
 
-        final NavigationBarFragment fragment = FragmentHostManager.get(navigationBarView)
-                .create(NavigationBarFragment.class);
         navigationBarView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
             @Override
             public void onViewAttachedToWindow(View v) {
+                final NavigationBarFragment fragment =
+                        FragmentHostManager.get(v).create(NavigationBarFragment.class);
                 final FragmentHostManager fragmentHost = FragmentHostManager.get(v);
                 fragmentHost.getFragmentManager().beginTransaction()
                         .replace(R.id.navigation_bar_frame, fragment, TAG)
@@ -1472,6 +1478,8 @@
 
             @Override
             public void onViewDetachedFromWindow(View v) {
+                final FragmentHostManager fragmentHost = FragmentHostManager.get(v);
+                fragmentHost.removeTagListener(TAG, listener);
                 FragmentHostManager.removeAndDestroy(v);
                 navigationBarView.removeOnAttachStateChangeListener(this);
             }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
index 7936e53..84512ac 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -115,12 +115,8 @@
     int mNavigationIconHints = 0;
     private int mNavBarMode;
 
-    private Rect mHomeButtonBounds = new Rect();
-    private Rect mBackButtonBounds = new Rect();
-    private Rect mRecentsButtonBounds = new Rect();
-    private Rect mRotationButtonBounds = new Rect();
     private final Region mActiveRegion = new Region();
-    private int[] mTmpPosition = new int[2];
+    private Rect mTmpBounds = new Rect();
 
     private KeyButtonDrawable mBackIcon;
     private KeyButtonDrawable mHomeDefaultIcon;
@@ -712,6 +708,7 @@
         getHomeButton().setVisibility(disableHome       ? View.INVISIBLE : View.VISIBLE);
         getRecentsButton().setVisibility(disableRecent  ? View.INVISIBLE : View.VISIBLE);
         getHomeHandle().setVisibility(disableHomeHandle ? View.INVISIBLE : View.VISIBLE);
+        notifyActiveTouchRegions();
     }
 
     @VisibleForTesting
@@ -929,42 +926,30 @@
     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
         super.onLayout(changed, left, top, right, bottom);
 
-        mActiveRegion.setEmpty();
-        updateButtonLocation(getBackButton(), mBackButtonBounds, true);
-        updateButtonLocation(getHomeButton(), mHomeButtonBounds, false);
-        updateButtonLocation(getRecentsButton(), mRecentsButtonBounds, false);
-        updateButtonLocation(getRotateSuggestionButton(), mRotationButtonBounds, true);
-        // TODO: Handle button visibility changes
-        mOverviewProxyService.onActiveNavBarRegionChanges(mActiveRegion);
+        notifyActiveTouchRegions();
         mRecentsOnboarding.setNavBarHeight(getMeasuredHeight());
     }
 
-    private void updateButtonLocation(ButtonDispatcher button, Rect buttonBounds,
-            boolean isActive) {
+    /**
+     * Notifies the overview service of the active touch regions.
+     */
+    public void notifyActiveTouchRegions() {
+        mActiveRegion.setEmpty();
+        updateButtonLocation(getBackButton());
+        updateButtonLocation(getHomeButton());
+        updateButtonLocation(getRecentsButton());
+        updateButtonLocation(getRotateSuggestionButton());
+        mOverviewProxyService.onActiveNavBarRegionChanges(mActiveRegion);
+    }
+
+    private void updateButtonLocation(ButtonDispatcher button) {
         View view = button.getCurrentView();
-        if (view == null) {
-            buttonBounds.setEmpty();
+        if (view == null || !button.isVisible()) {
             return;
         }
-        // Temporarily reset the translation back to origin to get the position in window
-        final float posX = view.getTranslationX();
-        final float posY = view.getTranslationY();
-        view.setTranslationX(0);
-        view.setTranslationY(0);
 
-        if (isActive) {
-            view.getLocationOnScreen(mTmpPosition);
-            buttonBounds.set(mTmpPosition[0], mTmpPosition[1],
-                    mTmpPosition[0] + view.getMeasuredWidth(),
-                    mTmpPosition[1] + view.getMeasuredHeight());
-            mActiveRegion.op(buttonBounds, Op.UNION);
-        }
-        view.getLocationInWindow(mTmpPosition);
-        buttonBounds.set(mTmpPosition[0], mTmpPosition[1],
-                mTmpPosition[0] + view.getMeasuredWidth(),
-                mTmpPosition[1] + view.getMeasuredHeight());
-        view.setTranslationX(posX);
-        view.setTranslationY(posY);
+        view.getBoundsOnScreen(mTmpBounds);
+        mActiveRegion.op(mTmpBounds, Op.UNION);
     }
 
     private void updateOrientationViews() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
index 5bb8fab..b2cfcea 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
@@ -47,6 +47,9 @@
 import com.android.systemui.dagger.qualifiers.DisplayId;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.dagger.qualifiers.UiBackground;
+import com.android.systemui.privacy.PrivacyItem;
+import com.android.systemui.privacy.PrivacyItemController;
+import com.android.systemui.privacy.PrivacyType;
 import com.android.systemui.qs.tiles.DndTile;
 import com.android.systemui.qs.tiles.RotationLockTile;
 import com.android.systemui.screenrecord.RecordingController;
@@ -70,6 +73,9 @@
 import com.android.systemui.util.RingerModeTracker;
 import com.android.systemui.util.time.DateFormatUtil;
 
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.List;
 import java.util.Locale;
 import java.util.concurrent.Executor;
 
@@ -87,13 +93,13 @@
                 ZenModeController.Callback,
                 DeviceProvisionedListener,
                 KeyguardStateController.Callback,
+                PrivacyItemController.Callback,
                 LocationController.LocationChangeCallback,
                 RecordingController.RecordingStateChangeCallback {
     private static final String TAG = "PhoneStatusBarPolicy";
     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
-    static final int LOCATION_STATUS_ICON_ID =
-            com.android.internal.R.drawable.perm_group_location;
+    static final int LOCATION_STATUS_ICON_ID = PrivacyType.TYPE_LOCATION.getIconId();
 
     private final String mSlotCast;
     private final String mSlotHotspot;
@@ -107,6 +113,8 @@
     private final String mSlotHeadset;
     private final String mSlotDataSaver;
     private final String mSlotLocation;
+    private final String mSlotMicrophone;
+    private final String mSlotCamera;
     private final String mSlotSensorsOff;
     private final String mSlotScreenRecord;
     private final int mDisplayId;
@@ -132,6 +140,7 @@
     private final DeviceProvisionedController mProvisionedController;
     private final KeyguardStateController mKeyguardStateController;
     private final LocationController mLocationController;
+    private final PrivacyItemController mPrivacyItemController;
     private final Executor mUiBgExecutor;
     private final SensorPrivacyController mSensorPrivacyController;
     private final RecordingController mRecordingController;
@@ -162,7 +171,8 @@
             RecordingController recordingController,
             @Nullable TelecomManager telecomManager, @DisplayId int displayId,
             @Main SharedPreferences sharedPreferences, DateFormatUtil dateFormatUtil,
-            RingerModeTracker ringerModeTracker) {
+            RingerModeTracker ringerModeTracker,
+            PrivacyItemController privacyItemController) {
         mIconController = iconController;
         mCommandQueue = commandQueue;
         mBroadcastDispatcher = broadcastDispatcher;
@@ -181,6 +191,7 @@
         mProvisionedController = deviceProvisionedController;
         mKeyguardStateController = keyguardStateController;
         mLocationController = locationController;
+        mPrivacyItemController = privacyItemController;
         mSensorPrivacyController = sensorPrivacyController;
         mRecordingController = recordingController;
         mUiBgExecutor = uiBgExecutor;
@@ -200,6 +211,8 @@
         mSlotHeadset = resources.getString(com.android.internal.R.string.status_bar_headset);
         mSlotDataSaver = resources.getString(com.android.internal.R.string.status_bar_data_saver);
         mSlotLocation = resources.getString(com.android.internal.R.string.status_bar_location);
+        mSlotMicrophone = resources.getString(com.android.internal.R.string.status_bar_microphone);
+        mSlotCamera = resources.getString(com.android.internal.R.string.status_bar_camera);
         mSlotSensorsOff = resources.getString(com.android.internal.R.string.status_bar_sensors_off);
         mSlotScreenRecord = resources.getString(
                 com.android.internal.R.string.status_bar_screen_record);
@@ -271,6 +284,13 @@
                 mResources.getString(R.string.accessibility_data_saver_on));
         mIconController.setIconVisibility(mSlotDataSaver, false);
 
+        // privacy items
+        mIconController.setIcon(mSlotMicrophone, PrivacyType.TYPE_MICROPHONE.getIconId(),
+                mResources.getString(PrivacyType.TYPE_MICROPHONE.getNameId()));
+        mIconController.setIconVisibility(mSlotMicrophone, false);
+        mIconController.setIcon(mSlotCamera, PrivacyType.TYPE_CAMERA.getIconId(),
+                mResources.getString(PrivacyType.TYPE_CAMERA.getNameId()));
+        mIconController.setIconVisibility(mSlotCamera, false);
         mIconController.setIcon(mSlotLocation, LOCATION_STATUS_ICON_ID,
                 mResources.getString(R.string.accessibility_location_active));
         mIconController.setIconVisibility(mSlotLocation, false);
@@ -294,6 +314,7 @@
         mNextAlarmController.addCallback(mNextAlarmCallback);
         mDataSaver.addCallback(this);
         mKeyguardStateController.addCallback(this);
+        mPrivacyItemController.addCallback(this);
         mSensorPrivacyController.addCallback(mSensorPrivacyListener);
         mLocationController.addCallback(this);
         mRecordingController.addCallback(this);
@@ -609,9 +630,44 @@
         mIconController.setIconVisibility(mSlotDataSaver, isDataSaving);
     }
 
+    @Override  // PrivacyItemController.Callback
+    public void onPrivacyItemsChanged(List<PrivacyItem> privacyItems) {
+        updatePrivacyItems(privacyItems);
+    }
+
+    private void updatePrivacyItems(List<PrivacyItem> items) {
+        boolean showCamera = false;
+        boolean showMicrophone = false;
+        boolean showLocation = false;
+        for (PrivacyItem item : items) {
+            if (item == null /* b/124234367 */) {
+                Log.e(TAG, "updatePrivacyItems - null item found");
+                StringWriter out = new StringWriter();
+                mPrivacyItemController.dump(null, new PrintWriter(out), null);
+                // Throw so we can look into this
+                throw new NullPointerException(out.toString());
+            }
+            switch (item.getPrivacyType()) {
+                case TYPE_CAMERA:
+                    showCamera = true;
+                    break;
+                case TYPE_LOCATION:
+                    showLocation = true;
+                    break;
+                case TYPE_MICROPHONE:
+                    showMicrophone = true;
+                    break;
+            }
+        }
+
+        mIconController.setIconVisibility(mSlotCamera, showCamera);
+        mIconController.setIconVisibility(mSlotMicrophone, showMicrophone);
+        mIconController.setIconVisibility(mSlotLocation, showLocation);
+    }
+
     @Override
     public void onLocationActiveChanged(boolean active) {
-        updateLocation();
+        if (!mPrivacyItemController.getIndicatorsAvailable()) updateLocation();
     }
 
     // Updates the status view based on the current state of location requests.
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 c5acd9b..c5571e8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -18,9 +18,6 @@
 
 import static android.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT;
 import static android.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
-import static android.app.StatusBarManager.DISABLE2_SYSTEM_ICONS;
-import static android.app.StatusBarManager.DISABLE_CLOCK;
-import static android.app.StatusBarManager.DISABLE_NOTIFICATION_ICONS;
 import static android.app.StatusBarManager.WINDOW_STATE_HIDDEN;
 import static android.app.StatusBarManager.WINDOW_STATE_SHOWING;
 import static android.app.StatusBarManager.WindowType;
@@ -1101,7 +1098,6 @@
         mHeadsUpManager.addListener(mNotificationPanelViewController.getOnHeadsUpChangedListener());
         mHeadsUpManager.addListener(mVisualStabilityManager);
         mNotificationPanelViewController.setHeadsUpManager(mHeadsUpManager);
-        mNotificationLogger.setHeadsUpManager(mHeadsUpManager);
 
         createNavigationBar(result);
 
@@ -4175,7 +4171,6 @@
     @Override
     public void setTopAppHidesStatusBar(boolean topAppHidesStatusBar) {
         mTopHidesStatusBar = topAppHidesStatusBar;
-        updateStatusBarIcons(topAppHidesStatusBar);
         if (!topAppHidesStatusBar && mWereIconsJustHidden) {
             // Immediately update the icon hidden state, since that should only apply if we're
             // staying fullscreen.
@@ -4185,17 +4180,6 @@
         updateHideIconsForBouncer(true /* animate */);
     }
 
-    private void updateStatusBarIcons(boolean topAppHidesStatusBar) {
-        int flags1 = StatusBarManager.DISABLE_NONE;
-        int flags2 = StatusBarManager.DISABLE2_NONE;
-        if (topAppHidesStatusBar) {
-            flags1 = DISABLE_NOTIFICATION_ICONS | DISABLE_CLOCK;
-            flags2 = DISABLE2_SYSTEM_ICONS;
-        }
-
-        mCommandQueue.disable(mDisplayId, flags1, flags2, false);
-    }
-
     protected void toggleKeyboardShortcuts(int deviceId) {
         KeyboardShortcuts.toggle(mContext, deviceId);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
index 6dd96f92..9560195 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
@@ -23,6 +23,7 @@
 import android.content.IntentFilter;
 import android.content.res.TypedArray;
 import android.graphics.Rect;
+import android.icu.text.DateTimePatternGenerator;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Parcelable;
@@ -53,8 +54,6 @@
 import com.android.systemui.tuner.TunerService;
 import com.android.systemui.tuner.TunerService.Tunable;
 
-import libcore.icu.LocaleData;
-
 import java.text.SimpleDateFormat;
 import java.util.Calendar;
 import java.util.Locale;
@@ -391,15 +390,16 @@
     private final CharSequence getSmallTime() {
         Context context = getContext();
         boolean is24 = DateFormat.is24HourFormat(context, mCurrentUserId);
-        LocaleData d = LocaleData.get(context.getResources().getConfiguration().locale);
+        DateTimePatternGenerator dtpg = DateTimePatternGenerator.getInstance(
+                context.getResources().getConfiguration().locale);
 
         final char MAGIC1 = '\uEF00';
         final char MAGIC2 = '\uEF01';
 
         SimpleDateFormat sdf;
         String format = mShowSeconds
-                ? is24 ? d.timeFormat_Hms : d.timeFormat_hms
-                : is24 ? d.timeFormat_Hm : d.timeFormat_hm;
+                ? is24 ? dtpg.getBestPattern("Hms") : dtpg.getBestPattern("hms")
+                : is24 ? dtpg.getBestPattern("Hm") : dtpg.getBestPattern("hm");
         if (!format.equals(mClockFormatString)) {
             mContentDescriptionFormat = new SimpleDateFormat(format);
             /*
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java
index 251693e..adfc14e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java
@@ -39,6 +39,7 @@
 import com.android.systemui.appops.AppOpItem;
 import com.android.systemui.appops.AppOpsController;
 import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.util.Utils;
 
@@ -65,8 +66,8 @@
 
     @Inject
     public LocationControllerImpl(Context context, AppOpsController appOpsController,
-            @Main Looper mainLooper, BroadcastDispatcher broadcastDispatcher,
-            BootCompleteCache bootCompleteCache) {
+            @Main Looper mainLooper, @Background Handler backgroundHandler,
+            BroadcastDispatcher broadcastDispatcher, BootCompleteCache bootCompleteCache) {
         mContext = context;
         mAppOpsController = appOpsController;
         mBootCompleteCache = bootCompleteCache;
@@ -80,7 +81,7 @@
         mAppOpsController.addCallback(new int[]{OP_MONITOR_HIGH_POWER_LOCATION}, this);
 
         // Examine the current location state and initialize the status view.
-        updateActiveLocationRequests();
+        backgroundHandler.post(this::updateActiveLocationRequests);
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIRootComponent.java b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIRootComponent.java
index 2c8297c..dce38c1 100644
--- a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIRootComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIRootComponent.java
@@ -16,8 +16,6 @@
 
 package com.android.systemui.tv;
 
-import android.content.Context;
-
 import com.android.systemui.dagger.DefaultComponentBinder;
 import com.android.systemui.dagger.DependencyBinder;
 import com.android.systemui.dagger.DependencyProvider;
@@ -30,7 +28,6 @@
 
 import javax.inject.Singleton;
 
-import dagger.BindsInstance;
 import dagger.Component;
 
 /**
@@ -52,9 +49,7 @@
      * Component Builder interface. This allows to bind Context instance in the component
      */
     @Component.Builder
-    interface Builder {
-        @BindsInstance Builder context(Context context);
-
+    interface Builder extends SystemUIRootComponent.Builder {
         TvSystemUIRootComponent build();
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/usb/UsbDebuggingActivity.java b/packages/SystemUI/src/com/android/systemui/usb/UsbDebuggingActivity.java
index 7561af7..b1241b1 100644
--- a/packages/SystemUI/src/com/android/systemui/usb/UsbDebuggingActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/usb/UsbDebuggingActivity.java
@@ -70,6 +70,8 @@
 
         if (SystemProperties.getInt("service.adb.tcp.port", 0) == 0) {
             mDisconnectedReceiver = new UsbDisconnectedReceiver(this);
+            IntentFilter filter = new IntentFilter(UsbManager.ACTION_USB_STATE);
+            mBroadcastDispatcher.registerReceiver(mDisconnectedReceiver, filter);
         }
 
         Intent intent = getIntent();
@@ -119,6 +121,7 @@
             }
             boolean connected = intent.getBooleanExtra(UsbManager.USB_CONNECTED, false);
             if (!connected) {
+                Log.d(TAG, "USB disconnected, notifying service");
                 notifyService(false);
                 mActivity.finish();
             }
@@ -126,29 +129,20 @@
     }
 
     @Override
-    public void onStart() {
-        super.onStart();
-        if (mDisconnectedReceiver != null) {
-            IntentFilter filter = new IntentFilter(UsbManager.ACTION_USB_STATE);
-            mBroadcastDispatcher.registerReceiver(mDisconnectedReceiver, filter);
-        }
-    }
-
-    @Override
-    protected void onStop() {
+    protected void onDestroy() {
         if (mDisconnectedReceiver != null) {
             mBroadcastDispatcher.unregisterReceiver(mDisconnectedReceiver);
         }
-        super.onStop();
-    }
-
-    @Override
-    protected void onDestroy() {
-        // If the ADB service has not yet been notified due to this dialog being closed in some
-        // other way then notify the service to deny the connection to ensure system_server sends
-        // a response to adbd.
-        if (!mServiceNotified) {
-            notifyService(false);
+        // Only notify the service if the activity is finishing; if onDestroy has been called due to
+        // a configuration change then allow the user to still authorize the connection the next
+        // time the activity is in the foreground.
+        if (isFinishing()) {
+            // If the ADB service has not yet been notified due to this dialog being closed in some
+            // other way then notify the service to deny the connection to ensure system_server
+            // sends a response to adbd.
+            if (!mServiceNotified) {
+                notifyService(false);
+            }
         }
         super.onDestroy();
     }
diff --git a/core/java/android/app/IRequestFinishCallback.aidl b/packages/SystemUI/src/com/android/systemui/util/settings/GlobalSettings.java
similarity index 66%
rename from core/java/android/app/IRequestFinishCallback.aidl
rename to packages/SystemUI/src/com/android/systemui/util/settings/GlobalSettings.java
index 3270565..84ab66b 100644
--- a/core/java/android/app/IRequestFinishCallback.aidl
+++ b/packages/SystemUI/src/com/android/systemui/util/settings/GlobalSettings.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2019 The Android Open Source Project
+ * Copyright (C) 2020 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,14 +14,12 @@
  * limitations under the License.
  */
 
-package android.app;
+package com.android.systemui.util.settings;
 
 /**
- * This callback allows ActivityTaskManager to ask the calling Activity
- * to finish in response to a call to onBackPressedOnTaskRoot.
+ * Public interface that can be injected to interact with Settings.Global.
  *
- * {@hide}
+ * See {@link SettingsProxy} for details.
  */
-oneway interface IRequestFinishCallback {
-    void requestFinish();
+public interface GlobalSettings extends SettingsProxy {
 }
diff --git a/packages/SystemUI/src/com/android/systemui/util/settings/GlobalSettingsImpl.java b/packages/SystemUI/src/com/android/systemui/util/settings/GlobalSettingsImpl.java
new file mode 100644
index 0000000..1a30b0a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/settings/GlobalSettingsImpl.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.util.settings;
+
+import android.content.ContentResolver;
+import android.net.Uri;
+import android.provider.Settings;
+
+import javax.inject.Inject;
+
+class GlobalSettingsImpl implements GlobalSettings {
+    private final ContentResolver mContentResolver;
+
+    @Inject
+    GlobalSettingsImpl(ContentResolver contentResolver) {
+        mContentResolver = contentResolver;
+    }
+
+    @Override
+    public ContentResolver getContentResolver() {
+        return mContentResolver;
+    }
+
+    @Override
+    public Uri getUriFor(String name) {
+        return Settings.Global.getUriFor(name);
+    }
+
+    @Override
+    public String getStringForUser(String name, int userHandle) {
+        return Settings.Global.getStringForUser(mContentResolver, name, userHandle);
+    }
+
+    @Override
+    public boolean putString(String name, String value, boolean overrideableByRestore) {
+        throw new UnsupportedOperationException(
+                "This method only exists publicly for Settings.System and Settings.Secure");
+    }
+
+    @Override
+    public boolean putStringForUser(String name, String value, int userHandle) {
+        return Settings.Global.putStringForUser(mContentResolver, name, value, userHandle);
+    }
+
+    @Override
+    public boolean putStringForUser(String name, String value, String tag, boolean makeDefault,
+            int userHandle, boolean overrideableByRestore) {
+        return Settings.Global.putStringForUser(
+                mContentResolver, name, value, tag, makeDefault, userHandle, overrideableByRestore);
+    }
+
+    @Override
+    public boolean putString(String name, String value, String tag, boolean makeDefault) {
+        return Settings.Global.putString(mContentResolver, name, value, tag, makeDefault);
+    }
+}
diff --git a/core/java/android/app/IRequestFinishCallback.aidl b/packages/SystemUI/src/com/android/systemui/util/settings/SecureSettings.java
similarity index 66%
copy from core/java/android/app/IRequestFinishCallback.aidl
copy to packages/SystemUI/src/com/android/systemui/util/settings/SecureSettings.java
index 3270565..798033e 100644
--- a/core/java/android/app/IRequestFinishCallback.aidl
+++ b/packages/SystemUI/src/com/android/systemui/util/settings/SecureSettings.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2019 The Android Open Source Project
+ * Copyright (C) 2020 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,14 +14,13 @@
  * limitations under the License.
  */
 
-package android.app;
+package com.android.systemui.util.settings;
 
 /**
- * This callback allows ActivityTaskManager to ask the calling Activity
- * to finish in response to a call to onBackPressedOnTaskRoot.
+ * Public interface that can be injected to interact with Settings.Secure.
  *
- * {@hide}
+ * See {@link SettingsProxy} for details.
  */
-oneway interface IRequestFinishCallback {
-    void requestFinish();
+
+public interface SecureSettings extends SettingsProxy {
 }
diff --git a/packages/SystemUI/src/com/android/systemui/util/settings/SecureSettingsImpl.java b/packages/SystemUI/src/com/android/systemui/util/settings/SecureSettingsImpl.java
new file mode 100644
index 0000000..020c234
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/settings/SecureSettingsImpl.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.util.settings;
+
+import android.content.ContentResolver;
+import android.net.Uri;
+import android.provider.Settings;
+
+import javax.inject.Inject;
+
+class SecureSettingsImpl implements SecureSettings {
+    private final ContentResolver mContentResolver;
+
+    @Inject
+    SecureSettingsImpl(ContentResolver contentResolver) {
+        mContentResolver = contentResolver;
+    }
+
+    @Override
+    public ContentResolver getContentResolver() {
+        return mContentResolver;
+    }
+
+    @Override
+    public Uri getUriFor(String name) {
+        return Settings.Secure.getUriFor(name);
+    }
+
+    @Override
+    public String getStringForUser(String name, int userHandle) {
+        return Settings.Secure.getStringForUser(mContentResolver, name, userHandle);
+    }
+
+    @Override
+    public boolean putString(String name, String value, boolean overrideableByRestore) {
+        return Settings.Secure.putString(mContentResolver, name, value, overrideableByRestore);
+    }
+
+    @Override
+    public boolean putStringForUser(String name, String value, int userHandle) {
+        return Settings.Secure.putStringForUser(mContentResolver, name, value, userHandle);
+    }
+
+    @Override
+    public boolean putStringForUser(String name, String value, String tag, boolean makeDefault,
+            int userHandle, boolean overrideableByRestore) {
+        return Settings.Secure.putStringForUser(
+                mContentResolver, name, value, tag, makeDefault, userHandle, overrideableByRestore);
+    }
+
+    @Override
+    public boolean putString(String name, String value, String tag, boolean makeDefault) {
+        return Settings.Secure.putString(mContentResolver, name, value, tag, makeDefault);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/util/settings/SettingsProxy.java b/packages/SystemUI/src/com/android/systemui/util/settings/SettingsProxy.java
new file mode 100644
index 0000000..5c37f79
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/settings/SettingsProxy.java
@@ -0,0 +1,412 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.util.settings;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.content.ContentResolver;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.provider.Settings;
+
+/**
+ * Used to interact with Settings.Secure, Settings.Global, and Settings.System.
+ *
+ * This interface can be implemented to give instance method (instead of static method) versions
+ * of Settings.Secure, Settings.Global, and Settings.System. It can be injected into class
+ * constructors and then faked or mocked as needed in tests.
+ *
+ * You can ask for {@link SecureSettings}, {@link GlobalSettings}, or {@link SystemSettings} to be
+ * injected as needed.
+ *
+ * This class also provides {@link #registerContentObserver(String, ContentObserver)} methods,
+ * normally found on {@link ContentResolver} instances, unifying setting related actions in one
+ * place.
+ */
+public interface SettingsProxy {
+
+    /**
+     * Returns the {@link ContentResolver} this instance was constructed with.
+     */
+    ContentResolver getContentResolver();
+
+    /**
+     * Returns the user id for the associated {@link ContentResolver}.
+     */
+    default int getUserId() {
+        return getContentResolver().getUserId();
+    }
+
+    /**
+     * Construct the content URI for a particular name/value pair,
+     * useful for monitoring changes with a ContentObserver.
+     * @param name to look up in the table
+     * @return the corresponding content URI, or null if not present
+     */
+    Uri getUriFor(String name);
+
+    /**
+     * Convenience wrapper around
+     * {@link ContentResolver#registerContentObserver(Uri, boolean, ContentObserver)}.'
+     *
+     * Implicitly calls {@link #getUriFor(String)} on the passed in name.
+     */
+    default void registerContentObserver(String name, ContentObserver settingsObserver) {
+        registerContentObserverForUser(name, settingsObserver, getUserId());
+    }
+
+    /**
+     * Convenience wrapper around
+     * {@link ContentResolver#registerContentObserver(Uri, boolean, ContentObserver, int)}
+     *
+     * Implicitly calls {@link #getUriFor(String)} on the passed in name.
+     */
+    default void registerContentObserverForUser(
+            String name, ContentObserver settingsObserver, int userHandle) {
+        getContentResolver().registerContentObserver(
+                getUriFor(name), false, settingsObserver, userHandle);
+    }
+
+    /** See {@link ContentResolver#unregisterContentObserver(ContentObserver)}. */
+    default void unregisterContentObserver(ContentObserver settingsObserver) {
+        getContentResolver().unregisterContentObserver(settingsObserver);
+    }
+
+    /**
+     * Look up a name in the database.
+     * @param name to look up in the table
+     * @return the corresponding value, or null if not present
+     */
+    default String getString(String name) {
+        return getStringForUser(name, getUserId());
+    }
+
+    /**See {@link #getString(String)}. */
+    String getStringForUser(String name, int userHandle);
+
+    /**
+     * Store a name/value pair into the database. Values written by this method will be
+     * overridden if a restore happens in the future.
+     *
+     * @param name to store
+     * @param value to associate with the name
+     * @return true if the value was set, false on database errors
+     */
+    boolean putString(String name, String value, boolean overrideableByRestore);
+
+    /**
+     * Store a name/value pair into the database.
+     * @param name to store
+     * @param value to associate with the name
+     * @return true if the value was set, false on database errors
+     */
+    default boolean putString(String name, String value) {
+        return putStringForUser(name, value, getUserId());
+    }
+
+    /** See {@link #putString(String, String)}. */
+    boolean putStringForUser(String name, String value, int userHandle);
+
+    /** See {@link #putString(String, String)}. */
+    boolean putStringForUser(@NonNull String name, @Nullable String value, @Nullable String tag,
+            boolean makeDefault, @UserIdInt int userHandle, boolean overrideableByRestore);
+
+    /**
+     * Store a name/value pair into the database.
+     * <p>
+     * The method takes an optional tag to associate with the setting
+     * which can be used to clear only settings made by your package and
+     * associated with this tag by passing the tag to {@link
+     * #resetToDefaults(String)}. Anyone can override
+     * the current tag. Also if another package changes the setting
+     * then the tag will be set to the one specified in the set call
+     * which can be null. Also any of the settings setters that do not
+     * take a tag as an argument effectively clears the tag.
+     * </p><p>
+     * For example, if you set settings A and B with tags T1 and T2 and
+     * another app changes setting A (potentially to the same value), it
+     * can assign to it a tag T3 (note that now the package that changed
+     * the setting is not yours). Now if you reset your changes for T1 and
+     * T2 only setting B will be reset and A not (as it was changed by
+     * another package) but since A did not change you are in the desired
+     * initial state. Now if the other app changes the value of A (assuming
+     * you registered an observer in the beginning) you would detect that
+     * the setting was changed by another app and handle this appropriately
+     * (ignore, set back to some value, etc).
+     * </p><p>
+     * Also the method takes an argument whether to make the value the
+     * default for this setting. If the system already specified a default
+     * value, then the one passed in here will <strong>not</strong>
+     * be set as the default.
+     * </p>
+     *
+     * @param name to store.
+     * @param value to associate with the name.
+     * @param tag to associate with the setting.
+     * @param makeDefault whether to make the value the default one.
+     * @return true if the value was set, false on database errors.
+     *
+     * @see #resetToDefaults(String)
+     *
+     */
+    boolean putString(@NonNull String name, @Nullable String value, @Nullable String tag,
+            boolean makeDefault);
+
+    /**
+     * Convenience function for retrieving a single secure settings value
+     * as an integer.  Note that internally setting values are always
+     * stored as strings; this function converts the string to an integer
+     * for you.  The default value will be returned if the setting is
+     * not defined or not an integer.
+     *
+     * @param name The name of the setting to retrieve.
+     * @param def Value to return if the setting is not defined.
+     *
+     * @return The setting's current value, or 'def' if it is not defined
+     * or not a valid integer.
+     */
+    default int getInt(String name, int def) {
+        return getIntForUser(name, def, getUserId());
+    }
+
+    /** See {@link #getInt(String, int)}. */
+    default int getIntForUser(String name, int def, int userHandle) {
+        String v = getStringForUser(name, userHandle);
+        try {
+            return v != null ? Integer.parseInt(v) : def;
+        } catch (NumberFormatException e) {
+            return def;
+        }
+    }
+
+    /**
+     * Convenience function for retrieving a single secure settings value
+     * as an integer.  Note that internally setting values are always
+     * stored as strings; this function converts the string to an integer
+     * for you.
+     * <p>
+     * This version does not take a default value.  If the setting has not
+     * been set, or the string value is not a number,
+     * it throws {@link Settings.SettingNotFoundException}.
+     *
+     * @param name The name of the setting to retrieve.
+     *
+     * @throws Settings.SettingNotFoundException Thrown if a setting by the given
+     * name can't be found or the setting value is not an integer.
+     *
+     * @return The setting's current value.
+     */
+    default int getInt(String name) throws Settings.SettingNotFoundException {
+        return getIntForUser(name, getUserId());
+    }
+
+    /** See {@link #getInt(String)}. */
+    default int getIntForUser(String name, int userHandle)
+            throws Settings.SettingNotFoundException {
+        String v = getStringForUser(name, userHandle);
+        try {
+            return Integer.parseInt(v);
+        } catch (NumberFormatException e) {
+            throw new Settings.SettingNotFoundException(name);
+        }
+    }
+
+    /**
+     * Convenience function for updating a single settings value as an
+     * integer. This will either create a new entry in the table if the
+     * given name does not exist, or modify the value of the existing row
+     * with that name.  Note that internally setting values are always
+     * stored as strings, so this function converts the given value to a
+     * string before storing it.
+     *
+     * @param name The name of the setting to modify.
+     * @param value The new value for the setting.
+     * @return true if the value was set, false on database errors
+     */
+    default boolean putInt(String name, int value) {
+        return putIntForUser(name, value, getUserId());
+    }
+    /** See {@link #putInt(String, int)}. */
+    default boolean putIntForUser(String name, int value, int userHandle) {
+        return putStringForUser(name, Integer.toString(value), userHandle);
+    }
+
+    /**
+     * Convenience function for retrieving a single secure settings value
+     * as a {@code long}.  Note that internally setting values are always
+     * stored as strings; this function converts the string to a {@code long}
+     * for you.  The default value will be returned if the setting is
+     * not defined or not a {@code long}.
+     *
+     * @param name The name of the setting to retrieve.
+     * @param def Value to return if the setting is not defined.
+     *
+     * @return The setting's current value, or 'def' if it is not defined
+     * or not a valid {@code long}.
+     */
+    default long getLong(String name, long def) {
+        return getLongForUser(name, def, getUserId());
+    }
+
+    /** See {@link #getLong(String, long)}. */
+    default long getLongForUser(String name, long def, int userHandle) {
+        String valString = getStringForUser(name, userHandle);
+        long value;
+        try {
+            value = valString != null ? Long.parseLong(valString) : def;
+        } catch (NumberFormatException e) {
+            value = def;
+        }
+        return value;
+    }
+
+    /**
+     * Convenience function for retrieving a single secure settings value
+     * as a {@code long}.  Note that internally setting values are always
+     * stored as strings; this function converts the string to a {@code long}
+     * for you.
+     * <p>
+     * This version does not take a default value.  If the setting has not
+     * been set, or the string value is not a number,
+     * it throws {@link Settings.SettingNotFoundException}.
+     *
+     * @param name The name of the setting to retrieve.
+     *
+     * @return The setting's current value.
+     * @throws Settings.SettingNotFoundException Thrown if a setting by the given
+     * name can't be found or the setting value is not an integer.
+     */
+    default long getLong(String name) throws Settings.SettingNotFoundException {
+        return getLongForUser(name, getUserId());
+    }
+
+    /** See {@link #getLong(String)}. */
+    default long getLongForUser(String name, int userHandle)
+            throws Settings.SettingNotFoundException {
+        String valString = getStringForUser(name, userHandle);
+        try {
+            return Long.parseLong(valString);
+        } catch (NumberFormatException e) {
+            throw new Settings.SettingNotFoundException(name);
+        }
+    }
+
+    /**
+     * Convenience function for updating a secure settings value as a long
+     * integer. This will either create a new entry in the table if the
+     * given name does not exist, or modify the value of the existing row
+     * with that name.  Note that internally setting values are always
+     * stored as strings, so this function converts the given value to a
+     * string before storing it.
+     *
+     * @param name The name of the setting to modify.
+     * @param value The new value for the setting.
+     * @return true if the value was set, false on database errors
+     */
+    default boolean putLong(String name, long value) {
+        return putLongForUser(name, value, getUserId());
+    }
+
+    /** See {@link #putLong(String, long)}. */
+    default boolean putLongForUser(String name, long value, int userHandle) {
+        return putStringForUser(name, Long.toString(value), userHandle);
+    }
+
+    /**
+     * Convenience function for retrieving a single secure settings value
+     * as a floating point number.  Note that internally setting values are
+     * always stored as strings; this function converts the string to an
+     * float for you. The default value will be returned if the setting
+     * is not defined or not a valid float.
+     *
+     * @param name The name of the setting to retrieve.
+     * @param def Value to return if the setting is not defined.
+     *
+     * @return The setting's current value, or 'def' if it is not defined
+     * or not a valid float.
+     */
+    default float getFloat(String name, float def) {
+        return getFloatForUser(name, def, getUserId());
+    }
+
+    /** See {@link #getFloat(String)}. */
+    default float getFloatForUser(String name, float def, int userHandle) {
+        String v = getStringForUser(name, userHandle);
+        try {
+            return v != null ? Float.parseFloat(v) : def;
+        } catch (NumberFormatException e) {
+            return def;
+        }
+    }
+
+    /**
+     * Convenience function for retrieving a single secure settings value
+     * as a float.  Note that internally setting values are always
+     * stored as strings; this function converts the string to a float
+     * for you.
+     * <p>
+     * This version does not take a default value.  If the setting has not
+     * been set, or the string value is not a number,
+     * it throws {@link Settings.SettingNotFoundException}.
+     *
+     * @param name The name of the setting to retrieve.
+     *
+     * @throws Settings.SettingNotFoundException Thrown if a setting by the given
+     * name can't be found or the setting value is not a float.
+     *
+     * @return The setting's current value.
+     */
+    default float getFloat(String name) throws Settings.SettingNotFoundException {
+        return getFloatForUser(name, getUserId());
+    }
+
+    /** See {@link #getFloat(String, float)}. */
+    default float getFloatForUser(String name, int userHandle)
+            throws Settings.SettingNotFoundException {
+        String v = getStringForUser(name, userHandle);
+        if (v == null) {
+            throw new Settings.SettingNotFoundException(name);
+        }
+        try {
+            return Float.parseFloat(v);
+        } catch (NumberFormatException e) {
+            throw new Settings.SettingNotFoundException(name);
+        }
+    }
+
+    /**
+     * Convenience function for updating a single settings value as a
+     * floating point number. This will either create a new entry in the
+     * table if the given name does not exist, or modify the value of the
+     * existing row with that name.  Note that internally setting values
+     * are always stored as strings, so this function converts the given
+     * value to a string before storing it.
+     *
+     * @param name The name of the setting to modify.
+     * @param value The new value for the setting.
+     * @return true if the value was set, false on database errors
+     */
+    default boolean putFloat(String name, float value) {
+        return putFloatForUser(name, value, getUserId());
+    }
+
+    /** See {@link #putFloat(String, float)} */
+    default boolean putFloatForUser(String name, float value, int userHandle) {
+        return putStringForUser(name, Float.toString(value), userHandle);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/util/settings/SettingsUtilModule.java b/packages/SystemUI/src/com/android/systemui/util/settings/SettingsUtilModule.java
new file mode 100644
index 0000000..f36c335e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/settings/SettingsUtilModule.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.util.settings;
+
+import dagger.Binds;
+import dagger.Module;
+
+/**
+ * Dagger Module for classes within com.android.systemui.util.settings.
+ */
+@Module
+public interface SettingsUtilModule {
+
+    /** Bind SecureSettingsImpl to SecureSettings. */
+    @Binds
+    SecureSettings bindsSecureSettings(SecureSettingsImpl impl);
+
+    /** Bind SystemSettingsImpl to SystemSettings. */
+    @Binds
+    SystemSettings bindsSystemSettings(SystemSettingsImpl impl);
+
+    /** Bind GlobalSettingsImpl to GlobalSettings. */
+    @Binds
+    GlobalSettings bindsGlobalSettings(GlobalSettingsImpl impl);
+}
diff --git a/core/java/android/app/IRequestFinishCallback.aidl b/packages/SystemUI/src/com/android/systemui/util/settings/SystemSettings.java
similarity index 66%
copy from core/java/android/app/IRequestFinishCallback.aidl
copy to packages/SystemUI/src/com/android/systemui/util/settings/SystemSettings.java
index 3270565..d57d749 100644
--- a/core/java/android/app/IRequestFinishCallback.aidl
+++ b/packages/SystemUI/src/com/android/systemui/util/settings/SystemSettings.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2019 The Android Open Source Project
+ * Copyright (C) 2020 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,14 +14,12 @@
  * limitations under the License.
  */
 
-package android.app;
+package com.android.systemui.util.settings;
 
 /**
- * This callback allows ActivityTaskManager to ask the calling Activity
- * to finish in response to a call to onBackPressedOnTaskRoot.
+ * Public interface that can be injected to interact with Settings.System.
  *
- * {@hide}
+ * See {@link SettingsProxy} for details.
  */
-oneway interface IRequestFinishCallback {
-    void requestFinish();
+public interface SystemSettings extends SettingsProxy {
 }
diff --git a/packages/SystemUI/src/com/android/systemui/util/settings/SystemSettingsImpl.java b/packages/SystemUI/src/com/android/systemui/util/settings/SystemSettingsImpl.java
new file mode 100644
index 0000000..0dbb76f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/settings/SystemSettingsImpl.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.util.settings;
+
+import android.content.ContentResolver;
+import android.net.Uri;
+import android.provider.Settings;
+
+import javax.inject.Inject;
+
+class SystemSettingsImpl implements SystemSettings {
+    private final ContentResolver mContentResolver;
+
+    @Inject
+    SystemSettingsImpl(ContentResolver contentResolver) {
+        mContentResolver = contentResolver;
+    }
+
+    @Override
+    public ContentResolver getContentResolver() {
+        return mContentResolver;
+    }
+
+    @Override
+    public Uri getUriFor(String name) {
+        return Settings.System.getUriFor(name);
+    }
+
+    @Override
+    public String getStringForUser(String name, int userHandle) {
+        return Settings.System.getStringForUser(mContentResolver, name, userHandle);
+    }
+
+    @Override
+    public boolean putString(String name, String value, boolean overrideableByRestore) {
+        return Settings.System.putString(mContentResolver, name, value, overrideableByRestore);
+    }
+
+    @Override
+    public boolean putStringForUser(String name, String value, int userHandle) {
+        return Settings.System.putStringForUser(mContentResolver, name, value, userHandle);
+    }
+
+    @Override
+    public boolean putStringForUser(String name, String value, String tag, boolean makeDefault,
+            int userHandle, boolean overrideableByRestore) {
+        throw new UnsupportedOperationException(
+                "This method only exists publicly for Settings.Secure and Settings.Global");
+    }
+
+    @Override
+    public boolean putString(String name, String value, String tag, boolean makeDefault) {
+        throw new UnsupportedOperationException(
+                "This method only exists publicly for Settings.Secure and Settings.Global");
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WindowManagerShellModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WindowManagerShellModule.java
index bba5ff5..fbc1676 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WindowManagerShellModule.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WindowManagerShellModule.java
@@ -52,16 +52,16 @@
 
     @Singleton
     @Provides
-    static SystemWindows provideSystemWindows(Context context, DisplayController displayController,
+    static SystemWindows provideSystemWindows(DisplayController displayController,
             IWindowManager wmService) {
-        return new SystemWindows(context, displayController, wmService);
+        return new SystemWindows(displayController, wmService);
     }
 
     @Singleton
     @Provides
     static DisplayImeController provideDisplayImeController(
-            SystemWindows syswin, DisplayController displayController,
+            IWindowManager wmService, DisplayController displayController,
             @Main Handler mainHandler, TransactionPool transactionPool) {
-        return new DisplayImeController(syswin, displayController, mainHandler, transactionPool);
+        return new DisplayImeController(wmService, displayController, mainHandler, transactionPool);
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/DependencyTest.java b/packages/SystemUI/tests/src/com/android/systemui/DependencyTest.java
index 475ddc1..35be496 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/DependencyTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/DependencyTest.java
@@ -46,9 +46,8 @@
     @Test
     public void testInitDependency() {
         Dependency.clearDependencies();
-        Dependency dependency = new Dependency();
-        SystemUIFactory
-                .getInstance().getRootComponent().createDependency().createSystemUI(dependency);
+        Dependency dependency =
+                SystemUIFactory.getInstance().getRootComponent().createDependency();
         dependency.start();
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/SysuiBaseFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/SysuiBaseFragmentTest.java
index cf778504..3687b4c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/SysuiBaseFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/SysuiBaseFragmentTest.java
@@ -54,7 +54,10 @@
     @Before
     public void SysuiSetup() {
         SystemUIFactory.createFromConfig(mContext);
-        mDependency = new TestableDependency(mContext);
+        mDependency = new TestableDependency(
+                SystemUIFactory.getInstance().getRootComponent().createDependency());
+        Dependency.setInstance(mDependency);
+
         // TODO: Figure out another way to give reference to a SysuiTestableContext.
         mSysuiContext = (SysuiTestableContext) mContext;
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java
index b6cc2ee..08e2784 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java
@@ -73,7 +73,9 @@
     @Before
     public void SysuiSetup() throws Exception {
         SystemUIFactory.createFromConfig(mContext);
-        mDependency = new TestableDependency(mContext);
+        mDependency = new TestableDependency(
+                SystemUIFactory.getInstance().getRootComponent().createDependency());
+        Dependency.setInstance(mDependency);
         mFakeBroadcastDispatcher = new FakeBroadcastDispatcher(mContext, mock(Looper.class),
                 mock(Executor.class), mock(DumpManager.class),
                 mock(BroadcastDispatcherLogger.class));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/TestableDependency.java b/packages/SystemUI/tests/src/com/android/systemui/TestableDependency.java
index a7f4fa5..ee52c78 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/TestableDependency.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/TestableDependency.java
@@ -16,7 +16,6 @@
 
 import static org.mockito.Mockito.mock;
 
-import android.content.Context;
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.Log;
@@ -26,13 +25,10 @@
 
     private final ArrayMap<Object, Object> mObjs = new ArrayMap<>();
     private final ArraySet<Object> mInstantiatedObjects = new ArraySet<>();
+    private final Dependency mParent;
 
-    public TestableDependency(Context context) {
-        SystemUIFactory.createFromConfig(context);
-        SystemUIFactory.getInstance().getRootComponent()
-                .createDependency()
-                .createSystemUI(this);
-        start();
+    public TestableDependency(Dependency parent) {
+        mParent = parent;
     }
 
     public <T> T injectMockDependency(Class<T> cls) {
@@ -53,11 +49,11 @@
     }
 
     @Override
-    protected <T> T createDependency(Object key) {
+    public <T> T createDependency(Object key) {
         if (mObjs.containsKey(key)) return (T) mObjs.get(key);
 
         mInstantiatedObjects.add(key);
-        return super.createDependency(key);
+        return mParent.createDependency(key);
     }
 
     @Override
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/DisplayIdIndexSupplierTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/DisplayIdIndexSupplierTest.java
new file mode 100644
index 0000000..9cb4fb3
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/DisplayIdIndexSupplierTest.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.accessibility;
+
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
+
+import android.hardware.display.DisplayManager;
+import android.testing.AndroidTestingRunner;
+import android.view.Display;
+
+import androidx.annotation.NonNull;
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class DisplayIdIndexSupplierTest extends SysuiTestCase {
+
+    private DisplayIdIndexSupplier<Object> mDisplayIdIndexSupplier;
+
+    @Before
+    public void setUp() throws Exception {
+        mDisplayIdIndexSupplier = new DisplayIdIndexSupplier(
+                mContext.getSystemService(DisplayManager.class)) {
+
+            @NonNull
+            @Override
+            protected Object createInstance(Display display) {
+                return new Object();
+            }
+        };
+    }
+
+    @Test
+    public void get_instanceIsNotNull() {
+        Object object = mDisplayIdIndexSupplier.get(Display.DEFAULT_DISPLAY);
+        assertNotNull(object);
+    }
+
+    @Test
+    public void get_removeExistedObject_newObject() {
+        Object object = mDisplayIdIndexSupplier.get(Display.DEFAULT_DISPLAY);
+        mDisplayIdIndexSupplier.remove(Display.DEFAULT_DISPLAY);
+
+        Object newObject = mDisplayIdIndexSupplier.get(Display.DEFAULT_DISPLAY);
+
+        assertNotEquals(object, newObject);
+    }
+
+    @Test
+    public void get_clearAllObjects_newObject() {
+        Object object = mDisplayIdIndexSupplier.get(Display.DEFAULT_DISPLAY);
+        mDisplayIdIndexSupplier.clear();
+
+        Object newObject = mDisplayIdIndexSupplier.get(Display.DEFAULT_DISPLAY);
+
+        assertNotEquals(object, newObject);
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/ModeSwitchesControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/ModeSwitchesControllerTest.java
index d6d2fcd..6948279 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/ModeSwitchesControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/ModeSwitchesControllerTest.java
@@ -16,20 +16,13 @@
 
 package com.android.systemui.accessibility;
 
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.mock;
+import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
-import android.content.Context;
 import android.provider.Settings;
 import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper;
 import android.view.Display;
-import android.view.View;
-import android.view.WindowManager;
-import android.view.WindowMetrics;
 
 import androidx.test.filters.SmallTest;
 
@@ -38,45 +31,43 @@
 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)
-@TestableLooper.RunWithLooper
 /** Tests the ModeSwitchesController. */
 public class ModeSwitchesControllerTest extends SysuiTestCase {
 
-    private WindowManager mWindowManager;
+    @Mock
+    private ModeSwitchesController.SwitchSupplier mSupplier;
+    @Mock
+    private MagnificationModeSwitch mModeSwitch;
     private ModeSwitchesController mModeSwitchesController;
 
+
     @Before
     public void setUp() {
-        mWindowManager = mock(WindowManager.class);
-        Display display = mContext.getSystemService(WindowManager.class).getDefaultDisplay();
-        when(mWindowManager.getDefaultDisplay()).thenReturn(display);
-        WindowMetrics metrics = mContext.getSystemService(WindowManager.class)
-                .getMaximumWindowMetrics();
-        when(mWindowManager.getMaximumWindowMetrics()).thenReturn(metrics);
-        mContext.addMockSystemService(Context.WINDOW_SERVICE, mWindowManager);
-        mModeSwitchesController = new ModeSwitchesController(mContext);
+        MockitoAnnotations.initMocks(this);
+        when(mSupplier.get(anyInt())).thenReturn(mModeSwitch);
+        mModeSwitchesController = new ModeSwitchesController(mSupplier);
     }
 
     @Test
     public void testShowButton() {
         mModeSwitchesController.showButton(Display.DEFAULT_DISPLAY,
                 Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW);
-        verify(mWindowManager).addView(any(), any());
+
+        verify(mModeSwitch).showButton(Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW);
     }
 
     @Test
     public void testRemoveButton() {
         mModeSwitchesController.showButton(Display.DEFAULT_DISPLAY,
                 Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW);
-        ArgumentCaptor<View> captor = ArgumentCaptor.forClass(View.class);
-        verify(mWindowManager).addView(captor.capture(), any(WindowManager.LayoutParams.class));
 
         mModeSwitchesController.removeButton(Display.DEFAULT_DISPLAY);
 
-        verify(mWindowManager).removeView(eq(captor.getValue()));
+        verify(mModeSwitch).removeButton();
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java
index e0049d1..4fdc06e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java
@@ -26,11 +26,14 @@
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
 
 import android.app.AppOpsManager;
+import android.content.pm.PackageManager;
 import android.os.Looper;
 import android.os.UserHandle;
 import android.testing.AndroidTestingRunner;
@@ -56,6 +59,7 @@
     private static final String TEST_PACKAGE_NAME = "test";
     private static final int TEST_UID = UserHandle.getUid(0, 0);
     private static final int TEST_UID_OTHER = UserHandle.getUid(1, 0);
+    private static final int TEST_UID_NON_USER_SENSITIVE = UserHandle.getUid(2, 0);
 
     @Mock
     private AppOpsManager mAppOpsManager;
@@ -65,6 +69,10 @@
     private AppOpsControllerImpl.H mMockHandler;
     @Mock
     private DumpManager mDumpManager;
+    @Mock
+    private PermissionFlagsCache mFlagsCache;
+    @Mock
+    private PackageManager mPackageManager;
 
     private AppOpsControllerImpl mController;
     private TestableLooper mTestableLooper;
@@ -76,8 +84,22 @@
 
         getContext().addMockSystemService(AppOpsManager.class, mAppOpsManager);
 
-        mController =
-                new AppOpsControllerImpl(mContext, mTestableLooper.getLooper(), mDumpManager);
+        // All permissions of TEST_UID and TEST_UID_OTHER are user sensitive. None of
+        // TEST_UID_NON_USER_SENSITIVE are user sensitive.
+        getContext().setMockPackageManager(mPackageManager);
+        when(mFlagsCache.getPermissionFlags(anyString(), anyString(), eq(TEST_UID))).thenReturn(
+                PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED);
+        when(mFlagsCache.getPermissionFlags(anyString(), anyString(), eq(TEST_UID_OTHER)))
+                .thenReturn(PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED);
+        when(mFlagsCache.getPermissionFlags(anyString(), anyString(),
+                eq(TEST_UID_NON_USER_SENSITIVE))).thenReturn(0);
+
+        mController = new AppOpsControllerImpl(
+                mContext,
+                mTestableLooper.getLooper(),
+                mDumpManager,
+                mFlagsCache
+        );
     }
 
     @Test
@@ -173,6 +195,26 @@
     }
 
     @Test
+    public void nonUserSensitiveOpsAreIgnored() {
+        mController.onOpActiveChanged(AppOpsManager.OP_RECORD_AUDIO,
+                TEST_UID_NON_USER_SENSITIVE, TEST_PACKAGE_NAME, true);
+        assertEquals(0, mController.getActiveAppOpsForUser(
+                UserHandle.getUserId(TEST_UID_NON_USER_SENSITIVE)).size());
+    }
+
+    @Test
+    public void nonUserSensitiveOpsNotNotified() {
+        mController.addCallback(new int[]{AppOpsManager.OP_RECORD_AUDIO}, mCallback);
+        mController.onOpActiveChanged(AppOpsManager.OP_RECORD_AUDIO,
+                TEST_UID_NON_USER_SENSITIVE, TEST_PACKAGE_NAME, true);
+
+        mTestableLooper.processAllMessages();
+
+        verify(mCallback, never())
+                .onActiveStateChanged(anyInt(), anyInt(), anyString(), anyBoolean());
+    }
+
+    @Test
     public void opNotedScheduledForRemoval() {
         mController.setBGHandler(mMockHandler);
         mController.onOpNoted(AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/appops/PermissionFlagsCacheTest.kt b/packages/SystemUI/tests/src/com/android/systemui/appops/PermissionFlagsCacheTest.kt
new file mode 100644
index 0000000..0fb0ce0
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/appops/PermissionFlagsCacheTest.kt
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.appops
+
+import android.content.pm.PackageManager
+import android.os.UserHandle
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.util.concurrency.FakeExecutor
+import com.android.systemui.util.time.FakeSystemClock
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertNotEquals
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.any
+import org.mockito.ArgumentMatchers.anyString
+import org.mockito.Mock
+import org.mockito.Mockito.`when`
+import org.mockito.Mockito.never
+import org.mockito.Mockito.times
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class PermissionFlagsCacheTest : SysuiTestCase() {
+
+    companion object {
+        const val TEST_PERMISSION = "test_permission"
+        const val TEST_PACKAGE = "test_package"
+        const val TEST_UID1 = 1000
+        const val TEST_UID2 = UserHandle.PER_USER_RANGE + 1000
+    }
+
+    @Mock
+    private lateinit var packageManager: PackageManager
+
+    private lateinit var executor: FakeExecutor
+    private lateinit var flagsCache: PermissionFlagsCache
+
+    @Before
+    fun setUp() {
+        MockitoAnnotations.initMocks(this)
+
+        executor = FakeExecutor(FakeSystemClock())
+
+        flagsCache = PermissionFlagsCache(packageManager, executor)
+        executor.runAllReady()
+    }
+
+    @Test
+    fun testNotListeningByDefault() {
+        verify(packageManager, never()).addOnPermissionsChangeListener(any())
+    }
+
+    @Test
+    fun testGetCorrectFlags() {
+        `when`(packageManager.getPermissionFlags(anyString(), anyString(), any())).thenReturn(0)
+        `when`(packageManager.getPermissionFlags(
+                TEST_PERMISSION,
+                TEST_PACKAGE,
+                UserHandle.getUserHandleForUid(TEST_UID1))
+        ).thenReturn(1)
+
+        assertEquals(1, flagsCache.getPermissionFlags(TEST_PERMISSION, TEST_PACKAGE, TEST_UID1))
+        assertNotEquals(1, flagsCache.getPermissionFlags(TEST_PERMISSION, TEST_PACKAGE, TEST_UID2))
+    }
+
+    @Test
+    fun testFlagIsCached() {
+        flagsCache.getPermissionFlags(TEST_PERMISSION, TEST_PACKAGE, TEST_UID1)
+
+        flagsCache.getPermissionFlags(TEST_PERMISSION, TEST_PACKAGE, TEST_UID1)
+
+        verify(packageManager, times(1)).getPermissionFlags(
+                TEST_PERMISSION,
+                TEST_PACKAGE,
+                UserHandle.getUserHandleForUid(TEST_UID1)
+        )
+    }
+
+    @Test
+    fun testListeningAfterFirstRequest() {
+        flagsCache.getPermissionFlags(TEST_PERMISSION, TEST_PACKAGE, TEST_UID1)
+
+        verify(packageManager).addOnPermissionsChangeListener(any())
+    }
+
+    @Test
+    fun testListeningOnlyOnce() {
+        flagsCache.getPermissionFlags(TEST_PERMISSION, TEST_PACKAGE, TEST_UID1)
+
+        flagsCache.getPermissionFlags(TEST_PERMISSION, TEST_PACKAGE, TEST_UID2)
+
+        verify(packageManager, times(1)).addOnPermissionsChangeListener(any())
+    }
+
+    @Test
+    fun testUpdateFlag() {
+        assertEquals(0, flagsCache.getPermissionFlags(TEST_PERMISSION, TEST_PACKAGE, TEST_UID1))
+
+        `when`(packageManager.getPermissionFlags(
+                TEST_PERMISSION,
+                TEST_PACKAGE,
+                UserHandle.getUserHandleForUid(TEST_UID1))
+        ).thenReturn(1)
+
+        flagsCache.onPermissionsChanged(TEST_UID1)
+
+        executor.runAllReady()
+
+        assertEquals(1, flagsCache.getPermissionFlags(TEST_PERMISSION, TEST_PACKAGE, TEST_UID1))
+    }
+
+    @Test
+    fun testUpdateFlag_notUpdatedIfUidHasNotBeenRequestedBefore() {
+        flagsCache.getPermissionFlags(TEST_PERMISSION, TEST_PACKAGE, TEST_UID1)
+
+        flagsCache.onPermissionsChanged(TEST_UID2)
+
+        executor.runAllReady()
+
+        verify(packageManager, never()).getPermissionFlags(
+                TEST_PERMISSION,
+                TEST_PACKAGE,
+                UserHandle.getUserHandleForUid(TEST_UID2)
+        )
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
index e8a0c73..d4a94c5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
@@ -45,6 +45,7 @@
 import android.hardware.biometrics.IBiometricSysuiReceiver;
 import android.hardware.biometrics.PromptInfo;
 import android.hardware.face.FaceManager;
+import android.hardware.fingerprint.FingerprintManager;
 import android.os.Bundle;
 import android.test.suitebuilder.annotation.SmallTest;
 import android.testing.AndroidTestingRunner;
@@ -530,6 +531,11 @@
         IActivityTaskManager getActivityTaskManager() {
             return mock(IActivityTaskManager.class);
         }
+
+        @Override
+        FingerprintManager getFingerprintManager(Context context) {
+            return mock(FingerprintManager.class);
+        }
     }
 }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java
index 7ebead8..4f0ff42 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java
@@ -47,6 +47,7 @@
 import com.android.systemui.statusbar.phone.DozeParameters;
 import com.android.systemui.util.sensors.AsyncSensorManager;
 import com.android.systemui.util.sensors.ProximitySensor;
+import com.android.systemui.util.settings.FakeSettings;
 import com.android.systemui.util.wakelock.WakeLock;
 
 import org.junit.Before;
@@ -84,6 +85,7 @@
     private DozeLog mDozeLog;
     @Mock
     private ProximitySensor mProximitySensor;
+    private FakeSettings mFakeSettings = new FakeSettings();
     private SensorManagerPlugin.SensorEventListener mWakeLockScreenListener;
     private TestableLooper mTestableLooper;
     private DozeSensors mDozeSensors;
@@ -154,7 +156,7 @@
         TestableDozeSensors() {
             super(getContext(), mSensorManager, mDozeParameters,
                     mAmbientDisplayConfiguration, mWakeLock, mCallback, mProxCallback, mDozeLog,
-                    mProximitySensor);
+                    mProximitySensor, mFakeSettings);
             for (TriggerSensor sensor : mSensors) {
                 if (sensor instanceof PluginSensor
                         && ((PluginSensor) sensor).mPluginSensor.getType()
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 d3af835..1ed5871 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
@@ -47,6 +47,7 @@
 import com.android.systemui.util.sensors.FakeSensorManager;
 import com.android.systemui.util.sensors.FakeThresholdSensor;
 import com.android.systemui.util.sensors.ProximitySensor;
+import com.android.systemui.util.settings.FakeSettings;
 import com.android.systemui.util.time.FakeSystemClock;
 import com.android.systemui.util.wakelock.WakeLock;
 import com.android.systemui.util.wakelock.WakeLockFake;
@@ -99,7 +100,7 @@
 
         mTriggers = new DozeTriggers(mContext, mHost, mAlarmManager, config, parameters,
                 asyncSensorManager, wakeLock, mDockManager, mProximitySensor,
-                mProximityCheck, mock(DozeLog.class), mBroadcastDispatcher);
+                mProximityCheck, mock(DozeLog.class), mBroadcastDispatcher, new FakeSettings());
         mTriggers.setDozeMachine(mMachine);
         waitForSensorManager();
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedUITest.java b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedUITest.java
index 7a234a4..ffedb07 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedUITest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedUITest.java
@@ -20,6 +20,7 @@
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 
+import android.os.SystemProperties;
 import android.provider.Settings;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
@@ -42,6 +43,9 @@
 @RunWith(AndroidTestingRunner.class)
 @TestableLooper.RunWithLooper
 public class OneHandedUITest extends OneHandedTestCase {
+    private static final String SUPPORT_ONE_HANDED_MODE = "ro.support_one_handed_mode";
+
+    boolean mIsSupportOneHandedMode;
     CommandQueue mCommandQueue;
     KeyguardUpdateMonitor mKeyguardUpdateMonitor;
     OneHandedUI mOneHandedUI;
@@ -58,6 +62,7 @@
     @Before
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
+        mIsSupportOneHandedMode = SystemProperties.getBoolean(SUPPORT_ONE_HANDED_MODE, false);
         mCommandQueue = new CommandQueue(mContext);
         mScreenLifecycle = new ScreenLifecycle();
         mOneHandedUI = new OneHandedUI(mContext,
@@ -72,6 +77,10 @@
 
     @Test
     public void testStartOneHanded() {
+        // Bypass test if device not support one-handed mode
+        if (!mIsSupportOneHandedMode) {
+            return;
+        }
         mOneHandedUI.startOneHanded();
 
         verify(mMockOneHandedManagerImpl, times(1)).startOneHanded();
@@ -79,6 +88,10 @@
 
     @Test
     public void testStopOneHanded() {
+        // Bypass test if device not support one-handed mode
+        if (!mIsSupportOneHandedMode) {
+            return;
+        }
         mOneHandedUI.stopOneHanded();
 
         verify(mMockOneHandedManagerImpl, times(1)).stopOneHanded();
@@ -86,6 +99,10 @@
 
     @Test
     public void testRegisterSettingsObserver_forEnabled() {
+        // Bypass test if device not support one-handed mode
+        if (!mIsSupportOneHandedMode) {
+            return;
+        }
         final String key = Settings.Secure.ONE_HANDED_MODE_ENABLED;
 
         verify(mMockSettingsUtil, times(1)).registerSettingsKeyObserver(key, any(), any());
@@ -93,6 +110,10 @@
 
     @Test
     public void testRegisterSettingsObserver_forTimeout() {
+        // Bypass test if device not support one-handed mode
+        if (!mIsSupportOneHandedMode) {
+            return;
+        }
         final String key = Settings.Secure.ONE_HANDED_MODE_TIMEOUT;
 
         verify(mMockSettingsUtil, times(1)).registerSettingsKeyObserver(key, any(), any());
@@ -100,6 +121,10 @@
 
     @Test
     public void testRegisterSettingsObserver_forTapAppExit() {
+        // Bypass test if device not support one-handed mode
+        if (!mIsSupportOneHandedMode) {
+            return;
+        }
         final String key = Settings.Secure.TAPS_APP_TO_EXIT;
 
         verify(mMockSettingsUtil, times(1)).registerSettingsKeyObserver(key, any(), any());
@@ -107,6 +132,10 @@
 
     @Test
     public void tesSettingsObserver_updateTapAppToExit() {
+        // Bypass test if device not support one-handed mode
+        if (!mIsSupportOneHandedMode) {
+            return;
+        }
         Settings.Secure.putInt(mContext.getContentResolver(),
                 Settings.Secure.TAPS_APP_TO_EXIT, 1);
 
@@ -115,6 +144,10 @@
 
     @Test
     public void tesSettingsObserver_updateEnabled() {
+        // Bypass test if device not support one-handed mode
+        if (!mIsSupportOneHandedMode) {
+            return;
+        }
         Settings.Secure.putInt(mContext.getContentResolver(),
                 Settings.Secure.ONE_HANDED_MODE_ENABLED, 1);
 
@@ -123,6 +156,10 @@
 
     @Test
     public void tesSettingsObserver_updateTimeout() {
+        // Bypass test if device not support one-handed mode
+        if (!mIsSupportOneHandedMode) {
+            return;
+        }
         Settings.Secure.putInt(mContext.getContentResolver(),
                 Settings.Secure.ONE_HANDED_MODE_TIMEOUT,
                 OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_MEDIUM_IN_SECONDS);
@@ -134,6 +171,10 @@
     @Ignore("Clarifying do not receive callback")
     @Test
     public void testKeyguardBouncerShowing_shouldStopOneHanded() {
+        // Bypass test if device not support one-handed mode
+        if (!mIsSupportOneHandedMode) {
+            return;
+        }
         mKeyguardUpdateMonitor.sendKeyguardBouncerChanged(true);
 
         verify(mMockOneHandedManagerImpl, times(1)).stopOneHanded();
@@ -141,6 +182,10 @@
 
     @Test
     public void testScreenTurningOff_shouldStopOneHanded() {
+        // Bypass test if device not support one-handed mode
+        if (!mIsSupportOneHandedMode) {
+            return;
+        }
         mScreenLifecycle.dispatchScreenTurningOff();
 
         verify(mMockOneHandedManagerImpl, times(1)).stopOneHanded();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyChipBuilderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyChipBuilderTest.kt
new file mode 100644
index 0000000..dcee5a716
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyChipBuilderTest.kt
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.privacy
+
+import androidx.test.filters.SmallTest
+import androidx.test.runner.AndroidJUnit4
+import com.android.systemui.SysuiTestCase
+import org.junit.Assert.assertEquals
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+class PrivacyChipBuilderTest : SysuiTestCase() {
+
+    companion object {
+        val TEST_UID = 1
+    }
+
+    @Test
+    fun testGenerateAppsList() {
+        val bar2 = PrivacyItem(Privacy.TYPE_CAMERA, PrivacyApplication(
+                "Bar", TEST_UID))
+        val bar3 = PrivacyItem(Privacy.TYPE_LOCATION, PrivacyApplication(
+                "Bar", TEST_UID))
+        val foo0 = PrivacyItem(Privacy.TYPE_MICROPHONE, PrivacyApplication(
+                "Foo", TEST_UID))
+        val baz1 = PrivacyItem(Privacy.TYPE_CAMERA, PrivacyApplication(
+                "Baz", TEST_UID))
+
+        val items = listOf(bar2, foo0, baz1, bar3)
+
+        val textBuilder = PrivacyChipBuilder(context, items)
+
+        val list = textBuilder.appsAndTypes
+        assertEquals(3, list.size)
+        val appsList = list.map { it.first }
+        val typesList = list.map { it.second }
+        // List is sorted by number of types and then by types
+        assertEquals(listOf("Bar", "Baz", "Foo"), appsList.map { it.packageName })
+        assertEquals(listOf(Privacy.TYPE_CAMERA, Privacy.TYPE_LOCATION), typesList[0])
+        assertEquals(listOf(Privacy.TYPE_CAMERA), typesList[1])
+        assertEquals(listOf(Privacy.TYPE_MICROPHONE), typesList[2])
+    }
+
+    @Test
+    fun testOrder() {
+        // We want location to always go last, so it will go in the "+ other apps"
+        val appCamera = PrivacyItem(PrivacyType.TYPE_CAMERA,
+                PrivacyApplication("Camera", TEST_UID))
+        val appMicrophone =
+                PrivacyItem(PrivacyType.TYPE_MICROPHONE,
+                        PrivacyApplication("Microphone", TEST_UID))
+        val appLocation =
+                PrivacyItem(PrivacyType.TYPE_LOCATION,
+                        PrivacyApplication("Location", TEST_UID))
+
+        val items = listOf(appLocation, appMicrophone, appCamera)
+        val textBuilder = PrivacyChipBuilder(context, items)
+        val appList = textBuilder.appsAndTypes.map { it.first }.map { it.packageName }
+        assertEquals(listOf("Camera", "Microphone", "Location"), appList)
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt
new file mode 100644
index 0000000..dddc350
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt
@@ -0,0 +1,290 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.privacy
+
+import android.app.ActivityManager
+import android.app.AppOpsManager
+import android.content.Context
+import android.content.Intent
+import android.content.pm.UserInfo
+import android.os.UserHandle
+import android.os.UserManager
+import android.provider.DeviceConfig
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper.RunWithLooper
+import androidx.test.filters.SmallTest
+import com.android.internal.config.sysui.SystemUiDeviceConfigFlags
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.appops.AppOpItem
+import com.android.systemui.appops.AppOpsController
+import com.android.systemui.broadcast.BroadcastDispatcher
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.util.DeviceConfigProxy
+import com.android.systemui.util.DeviceConfigProxyFake
+import com.android.systemui.util.concurrency.FakeExecutor
+import com.android.systemui.util.time.FakeSystemClock
+import org.hamcrest.Matchers.hasItem
+import org.hamcrest.Matchers.not
+import org.hamcrest.Matchers.nullValue
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertThat
+import org.junit.Assert.assertTrue
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.ArgumentMatchers.anyList
+import org.mockito.Captor
+import org.mockito.Mock
+import org.mockito.Mockito
+import org.mockito.Mockito.atLeastOnce
+import org.mockito.Mockito.doReturn
+import org.mockito.Mockito.mock
+import org.mockito.Mockito.never
+import org.mockito.Mockito.reset
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.verifyNoMoreInteractions
+import org.mockito.MockitoAnnotations
+
+@RunWith(AndroidTestingRunner::class)
+@SmallTest
+@RunWithLooper
+class PrivacyItemControllerTest : SysuiTestCase() {
+
+    companion object {
+        val CURRENT_USER_ID = ActivityManager.getCurrentUser()
+        val TEST_UID = CURRENT_USER_ID * UserHandle.PER_USER_RANGE
+        const val SYSTEM_UID = 1000
+        const val TEST_PACKAGE_NAME = "test"
+        const val DEVICE_SERVICES_STRING = "Device services"
+        const val TAG = "PrivacyItemControllerTest"
+        fun <T> capture(argumentCaptor: ArgumentCaptor<T>): T = argumentCaptor.capture()
+        fun <T> eq(value: T): T = Mockito.eq(value) ?: value
+        fun <T> any(): T = Mockito.any<T>()
+    }
+
+    @Mock
+    private lateinit var appOpsController: AppOpsController
+    @Mock
+    private lateinit var callback: PrivacyItemController.Callback
+    @Mock
+    private lateinit var userManager: UserManager
+    @Mock
+    private lateinit var broadcastDispatcher: BroadcastDispatcher
+    @Mock
+    private lateinit var dumpManager: DumpManager
+    @Captor
+    private lateinit var argCaptor: ArgumentCaptor<List<PrivacyItem>>
+    @Captor
+    private lateinit var argCaptorCallback: ArgumentCaptor<AppOpsController.Callback>
+
+    private lateinit var privacyItemController: PrivacyItemController
+    private lateinit var executor: FakeExecutor
+    private lateinit var deviceConfigProxy: DeviceConfigProxy
+
+    fun PrivacyItemController(context: Context): PrivacyItemController {
+        return PrivacyItemController(
+                context,
+                appOpsController,
+                executor,
+                executor,
+                broadcastDispatcher,
+                deviceConfigProxy,
+                userManager,
+                dumpManager
+        )
+    }
+
+    @Before
+    fun setup() {
+        MockitoAnnotations.initMocks(this)
+        executor = FakeExecutor(FakeSystemClock())
+        deviceConfigProxy = DeviceConfigProxyFake()
+
+        appOpsController = mDependency.injectMockDependency(AppOpsController::class.java)
+
+        deviceConfigProxy.setProperty(DeviceConfig.NAMESPACE_PRIVACY,
+                SystemUiDeviceConfigFlags.PROPERTY_PERMISSIONS_HUB_ENABLED,
+                "true", false)
+
+        doReturn(listOf(object : UserInfo() {
+            init {
+                id = CURRENT_USER_ID
+            }
+        })).`when`(userManager).getProfiles(anyInt())
+
+        privacyItemController = PrivacyItemController(mContext)
+    }
+
+    @Test
+    fun testSetListeningTrueByAddingCallback() {
+        privacyItemController.addCallback(callback)
+        executor.runAllReady()
+        verify(appOpsController).addCallback(eq(PrivacyItemController.OPS),
+                any())
+        verify(callback).onPrivacyItemsChanged(anyList())
+    }
+
+    @Test
+    fun testSetListeningFalseByRemovingLastCallback() {
+        privacyItemController.addCallback(callback)
+        executor.runAllReady()
+        verify(appOpsController, never()).removeCallback(any(),
+                any())
+        privacyItemController.removeCallback(callback)
+        executor.runAllReady()
+        verify(appOpsController).removeCallback(eq(PrivacyItemController.OPS),
+                any())
+        verify(callback).onPrivacyItemsChanged(emptyList())
+    }
+
+    @Test
+    fun testDistinctItems() {
+        doReturn(listOf(AppOpItem(AppOpsManager.OP_CAMERA, TEST_UID, "", 0),
+                AppOpItem(AppOpsManager.OP_CAMERA, TEST_UID, "", 1)))
+                .`when`(appOpsController).getActiveAppOpsForUser(anyInt())
+
+        privacyItemController.addCallback(callback)
+        executor.runAllReady()
+        verify(callback).onPrivacyItemsChanged(capture(argCaptor))
+        assertEquals(1, argCaptor.value.size)
+    }
+
+    @Test
+    fun testRegisterReceiver_allUsers() {
+        privacyItemController.addCallback(callback)
+        executor.runAllReady()
+        verify(broadcastDispatcher, atLeastOnce()).registerReceiver(
+                eq(privacyItemController.userSwitcherReceiver), any(), eq(null), eq(UserHandle.ALL))
+        verify(broadcastDispatcher, never())
+                .unregisterReceiver(eq(privacyItemController.userSwitcherReceiver))
+    }
+
+    @Test
+    fun testReceiver_ACTION_USER_FOREGROUND() {
+        privacyItemController.userSwitcherReceiver.onReceive(context,
+                Intent(Intent.ACTION_USER_SWITCHED))
+        executor.runAllReady()
+        verify(userManager).getProfiles(anyInt())
+    }
+
+    @Test
+    fun testReceiver_ACTION_MANAGED_PROFILE_ADDED() {
+        privacyItemController.userSwitcherReceiver.onReceive(context,
+                Intent(Intent.ACTION_MANAGED_PROFILE_AVAILABLE))
+        executor.runAllReady()
+        verify(userManager).getProfiles(anyInt())
+    }
+
+    @Test
+    fun testReceiver_ACTION_MANAGED_PROFILE_REMOVED() {
+        privacyItemController.userSwitcherReceiver.onReceive(context,
+                Intent(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE))
+        executor.runAllReady()
+        verify(userManager).getProfiles(anyInt())
+    }
+
+    @Test
+    fun testAddMultipleCallbacks() {
+        val otherCallback = mock(PrivacyItemController.Callback::class.java)
+        privacyItemController.addCallback(callback)
+        executor.runAllReady()
+        verify(callback).onPrivacyItemsChanged(anyList())
+
+        privacyItemController.addCallback(otherCallback)
+        executor.runAllReady()
+        verify(otherCallback).onPrivacyItemsChanged(anyList())
+        // Adding a callback should not unnecessarily call previous ones
+        verifyNoMoreInteractions(callback)
+    }
+
+    @Test
+    fun testMultipleCallbacksAreUpdated() {
+        doReturn(emptyList<AppOpItem>()).`when`(appOpsController).getActiveAppOpsForUser(anyInt())
+
+        val otherCallback = mock(PrivacyItemController.Callback::class.java)
+        privacyItemController.addCallback(callback)
+        privacyItemController.addCallback(otherCallback)
+        executor.runAllReady()
+        reset(callback)
+        reset(otherCallback)
+
+        verify(appOpsController).addCallback(any(), capture(argCaptorCallback))
+        argCaptorCallback.value.onActiveStateChanged(0, TEST_UID, "", true)
+        executor.runAllReady()
+        verify(callback).onPrivacyItemsChanged(anyList())
+        verify(otherCallback).onPrivacyItemsChanged(anyList())
+    }
+
+    @Test
+    fun testRemoveCallback() {
+        doReturn(emptyList<AppOpItem>()).`when`(appOpsController).getActiveAppOpsForUser(anyInt())
+        val otherCallback = mock(PrivacyItemController.Callback::class.java)
+        privacyItemController.addCallback(callback)
+        privacyItemController.addCallback(otherCallback)
+        executor.runAllReady()
+        executor.runAllReady()
+        reset(callback)
+        reset(otherCallback)
+
+        verify(appOpsController).addCallback(any(), capture(argCaptorCallback))
+        privacyItemController.removeCallback(callback)
+        argCaptorCallback.value.onActiveStateChanged(0, TEST_UID, "", true)
+        executor.runAllReady()
+        verify(callback, never()).onPrivacyItemsChanged(anyList())
+        verify(otherCallback).onPrivacyItemsChanged(anyList())
+    }
+
+    @Test
+    fun testListShouldNotHaveNull() {
+        doReturn(listOf(AppOpItem(AppOpsManager.OP_ACTIVATE_VPN, TEST_UID, "", 0),
+                        AppOpItem(AppOpsManager.OP_COARSE_LOCATION, TEST_UID, "", 0)))
+                .`when`(appOpsController).getActiveAppOpsForUser(anyInt())
+        privacyItemController.addCallback(callback)
+        executor.runAllReady()
+        executor.runAllReady()
+
+        verify(callback).onPrivacyItemsChanged(capture(argCaptor))
+        assertEquals(1, argCaptor.value.size)
+        assertThat(argCaptor.value, not(hasItem(nullValue())))
+    }
+
+    @Test
+    fun testListShouldBeCopy() {
+        val list = listOf(PrivacyItem(PrivacyType.TYPE_CAMERA,
+                PrivacyApplication("", TEST_UID)))
+        privacyItemController.privacyList = list
+        val privacyList = privacyItemController.privacyList
+        assertEquals(list, privacyList)
+        assertTrue(list !== privacyList)
+    }
+
+    @Test
+    fun testNotListeningWhenIndicatorsDisabled() {
+        deviceConfigProxy.setProperty(
+                DeviceConfig.NAMESPACE_PRIVACY,
+                SystemUiDeviceConfigFlags.PROPERTY_PERMISSIONS_HUB_ENABLED,
+                "false",
+                false
+        )
+        privacyItemController.addCallback(callback)
+        executor.runAllReady()
+        verify(appOpsController, never()).addCallback(eq(PrivacyItemController.OPS),
+                any())
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ActionProxyReceiverTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ActionProxyReceiverTest.java
new file mode 100644
index 0000000..4aaafbd
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ActionProxyReceiverTest.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.screenshot;
+
+import static com.android.systemui.screenshot.GlobalScreenshot.ACTION_TYPE_SHARE;
+import static com.android.systemui.screenshot.GlobalScreenshot.EXTRA_ID;
+import static com.android.systemui.screenshot.GlobalScreenshot.EXTRA_SMART_ACTIONS_ENABLED;
+import static com.android.systemui.statusbar.phone.StatusBar.SYSTEM_DIALOG_REASON_SCREENSHOT;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.isNull;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.testing.AndroidTestingRunner;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.shared.system.ActivityManagerWrapper;
+import com.android.systemui.statusbar.phone.StatusBar;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.mockito.stubbing.Answer;
+
+import java.util.Optional;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+@RunWith(AndroidTestingRunner.class)
+@SmallTest
+public class ActionProxyReceiverTest extends SysuiTestCase {
+
+    @Mock
+    private StatusBar mMockStatusBar;
+    @Mock
+    private ActivityManagerWrapper mMockActivityManagerWrapper;
+    @Mock
+    private Future mMockFuture;
+    @Mock
+    private ScreenshotSmartActions mMockScreenshotSmartActions;
+    @Mock
+    private PendingIntent mMockPendingIntent;
+
+    private Intent mIntent;
+
+    @Before
+    public void setup() throws InterruptedException, ExecutionException, TimeoutException {
+        MockitoAnnotations.initMocks(this);
+        mIntent = new Intent(mContext, ActionProxyReceiver.class)
+                .putExtra(GlobalScreenshot.EXTRA_ACTION_INTENT, mMockPendingIntent);
+
+        when(mMockActivityManagerWrapper.closeSystemWindows(anyString())).thenReturn(mMockFuture);
+        when(mMockFuture.get(anyLong(), any(TimeUnit.class))).thenReturn(null);
+    }
+
+    @Test
+    public void testPendingIntentSentWithoutStatusBar() throws PendingIntent.CanceledException {
+        ActionProxyReceiver actionProxyReceiver = constructActionProxyReceiver(false);
+
+        actionProxyReceiver.onReceive(mContext, mIntent);
+
+        verify(mMockActivityManagerWrapper).closeSystemWindows(SYSTEM_DIALOG_REASON_SCREENSHOT);
+        verify(mMockStatusBar, never()).executeRunnableDismissingKeyguard(
+                any(Runnable.class), any(Runnable.class), anyBoolean(), anyBoolean(), anyBoolean());
+        verify(mMockPendingIntent).send(
+                eq(mContext), anyInt(), isNull(), isNull(), isNull(), isNull(), any(Bundle.class));
+    }
+
+    @Test
+    public void testPendingIntentSentWithStatusBar() throws PendingIntent.CanceledException {
+        ActionProxyReceiver actionProxyReceiver = constructActionProxyReceiver(true);
+        // ensure that the pending intent call is passed through
+        doAnswer((Answer<Object>) invocation -> {
+            ((Runnable) invocation.getArgument(0)).run();
+            return null;
+        }).when(mMockStatusBar).executeRunnableDismissingKeyguard(
+                any(Runnable.class), isNull(), anyBoolean(), anyBoolean(), anyBoolean());
+
+        actionProxyReceiver.onReceive(mContext, mIntent);
+
+        verify(mMockActivityManagerWrapper).closeSystemWindows(SYSTEM_DIALOG_REASON_SCREENSHOT);
+        verify(mMockStatusBar).executeRunnableDismissingKeyguard(
+                any(Runnable.class), isNull(), eq(true), eq(true), eq(true));
+        verify(mMockPendingIntent).send(
+                eq(mContext), anyInt(), isNull(), isNull(), isNull(), isNull(), any(Bundle.class));
+    }
+
+    @Test
+    public void testSmartActionsNotNotifiedByDefault() {
+        ActionProxyReceiver actionProxyReceiver = constructActionProxyReceiver(true);
+
+        actionProxyReceiver.onReceive(mContext, mIntent);
+
+        verify(mMockScreenshotSmartActions, never())
+                .notifyScreenshotAction(any(Context.class), anyString(), anyString(), anyBoolean());
+    }
+
+    @Test
+    public void testSmartActionsNotifiedIfEnabled() {
+        ActionProxyReceiver actionProxyReceiver = constructActionProxyReceiver(true);
+        mIntent.putExtra(EXTRA_SMART_ACTIONS_ENABLED, true);
+        String testId = "testID";
+        mIntent.putExtra(EXTRA_ID, testId);
+
+        actionProxyReceiver.onReceive(mContext, mIntent);
+
+        verify(mMockScreenshotSmartActions).notifyScreenshotAction(
+                mContext, testId, ACTION_TYPE_SHARE, false);
+    }
+
+    private ActionProxyReceiver constructActionProxyReceiver(boolean withStatusBar) {
+        if (withStatusBar) {
+            return new ActionProxyReceiver(
+                    Optional.of(mMockStatusBar), mMockActivityManagerWrapper,
+                    mMockScreenshotSmartActions);
+        } else {
+            return new ActionProxyReceiver(
+                    Optional.empty(), mMockActivityManagerWrapper, mMockScreenshotSmartActions);
+        }
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/DeleteScreenshotReceiverTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/DeleteScreenshotReceiverTest.java
new file mode 100644
index 0000000..b924913
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/DeleteScreenshotReceiverTest.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.screenshot;
+
+import static com.android.systemui.screenshot.GlobalScreenshot.ACTION_TYPE_DELETE;
+import static com.android.systemui.screenshot.GlobalScreenshot.EXTRA_ID;
+import static com.android.systemui.screenshot.GlobalScreenshot.EXTRA_SMART_ACTIONS_ENABLED;
+import static com.android.systemui.screenshot.GlobalScreenshot.SCREENSHOT_URI_ID;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertNotNull;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.Intent;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Environment;
+import android.provider.MediaStore;
+import android.testing.AndroidTestingRunner;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.util.concurrency.FakeExecutor;
+import com.android.systemui.util.time.FakeSystemClock;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.io.File;
+import java.util.concurrent.Executor;
+
+@RunWith(AndroidTestingRunner.class)
+@SmallTest
+public class DeleteScreenshotReceiverTest extends SysuiTestCase {
+
+    @Mock
+    private ScreenshotSmartActions mMockScreenshotSmartActions;
+    @Mock
+    private Executor mMockExecutor;
+
+    private DeleteScreenshotReceiver mDeleteScreenshotReceiver;
+    private FakeExecutor mFakeExecutor = new FakeExecutor(new FakeSystemClock());
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+        mDeleteScreenshotReceiver =
+                new DeleteScreenshotReceiver(mMockScreenshotSmartActions, mMockExecutor);
+    }
+
+    @Test
+    public void testNoUriProvided() {
+        Intent intent = new Intent(mContext, DeleteScreenshotReceiver.class);
+
+        mDeleteScreenshotReceiver.onReceive(mContext, intent);
+
+        verify(mMockExecutor, never()).execute(any(Runnable.class));
+        verify(mMockScreenshotSmartActions, never()).notifyScreenshotAction(
+                any(Context.class), any(String.class), any(String.class), anyBoolean());
+    }
+
+    @Test
+    public void testFileDeleted() {
+        DeleteScreenshotReceiver deleteScreenshotReceiver =
+                new DeleteScreenshotReceiver(mMockScreenshotSmartActions, mFakeExecutor);
+        ContentResolver contentResolver = mContext.getContentResolver();
+        final Uri testUri = contentResolver.insert(
+                MediaStore.Images.Media.EXTERNAL_CONTENT_URI, getFakeContentValues());
+        assertNotNull(testUri);
+
+        try {
+            Cursor cursor =
+                    contentResolver.query(testUri, null, null, null, null);
+            assertEquals(1, cursor.getCount());
+            Intent intent = new Intent(mContext, DeleteScreenshotReceiver.class)
+                    .putExtra(SCREENSHOT_URI_ID, testUri.toString());
+
+            deleteScreenshotReceiver.onReceive(mContext, intent);
+            int runCount = mFakeExecutor.runAllReady();
+
+            assertEquals(1, runCount);
+            cursor =
+                    contentResolver.query(testUri, null, null, null, null);
+            assertEquals(0, cursor.getCount());
+        } finally {
+            contentResolver.delete(testUri, null, null);
+        }
+
+        // ensure smart actions not called by default
+        verify(mMockScreenshotSmartActions, never()).notifyScreenshotAction(
+                any(Context.class), any(String.class), any(String.class), anyBoolean());
+    }
+
+    @Test
+    public void testNotifyScreenshotAction() {
+        Intent intent = new Intent(mContext, DeleteScreenshotReceiver.class);
+        String uriString = "testUri";
+        String testId = "testID";
+        intent.putExtra(SCREENSHOT_URI_ID, uriString);
+        intent.putExtra(EXTRA_ID, testId);
+        intent.putExtra(EXTRA_SMART_ACTIONS_ENABLED, true);
+
+        mDeleteScreenshotReceiver.onReceive(mContext, intent);
+
+        verify(mMockExecutor).execute(any(Runnable.class));
+        verify(mMockScreenshotSmartActions).notifyScreenshotAction(
+                mContext, testId, ACTION_TYPE_DELETE, false);
+    }
+
+    private static ContentValues getFakeContentValues() {
+        final ContentValues values = new ContentValues();
+        values.put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_PICTURES
+                + File.separator + Environment.DIRECTORY_SCREENSHOTS);
+        values.put(MediaStore.MediaColumns.DISPLAY_NAME, "test_screenshot");
+        values.put(MediaStore.MediaColumns.MIME_TYPE, "image/png");
+        values.put(MediaStore.MediaColumns.DATE_ADDED, 0);
+        values.put(MediaStore.MediaColumns.DATE_MODIFIED, 0);
+        return values;
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsTest.java
index d3b3399..184329ec 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsTest.java
@@ -61,12 +61,14 @@
  */
 public class ScreenshotNotificationSmartActionsTest extends SysuiTestCase {
     private ScreenshotNotificationSmartActionsProvider mSmartActionsProvider;
+    private ScreenshotSmartActions mScreenshotSmartActions;
     private Handler mHandler;
 
     @Before
     public void setup() {
         mSmartActionsProvider = mock(
                 ScreenshotNotificationSmartActionsProvider.class);
+        mScreenshotSmartActions = new ScreenshotSmartActions();
         mHandler = mock(Handler.class);
     }
 
@@ -82,7 +84,7 @@
         when(smartActionsProvider.getActions(any(), any(), any(), any(), any()))
             .thenThrow(RuntimeException.class);
         CompletableFuture<List<Notification.Action>> smartActionsFuture =
-                ScreenshotSmartActions.getSmartActionsFuture(
+                mScreenshotSmartActions.getSmartActionsFuture(
                         "", Uri.parse("content://authority/data"), bitmap, smartActionsProvider,
                         true, UserHandle.getUserHandleForUid(UserHandle.myUserId()));
         assertNotNull(smartActionsFuture);
@@ -100,7 +102,7 @@
         int timeoutMs = 1000;
         when(smartActionsFuture.get(timeoutMs, TimeUnit.MILLISECONDS)).thenThrow(
                 RuntimeException.class);
-        List<Notification.Action> actions = ScreenshotSmartActions.getSmartActions(
+        List<Notification.Action> actions = mScreenshotSmartActions.getSmartActions(
                 "", smartActionsFuture, timeoutMs, mSmartActionsProvider);
         assertEquals(Collections.emptyList(), actions);
     }
@@ -111,7 +113,7 @@
             throws Exception {
         doThrow(RuntimeException.class).when(mSmartActionsProvider).notifyOp(any(), any(), any(),
                 anyLong());
-        ScreenshotSmartActions.notifyScreenshotOp(null, mSmartActionsProvider, null, null, -1);
+        mScreenshotSmartActions.notifyScreenshotOp(null, mSmartActionsProvider, null, null, -1);
     }
 
     // Tests for a non-hardware bitmap, ScreenshotNotificationSmartActionsProvider is never invoked
@@ -122,7 +124,7 @@
         Bitmap bitmap = mock(Bitmap.class);
         when(bitmap.getConfig()).thenReturn(Bitmap.Config.RGB_565);
         CompletableFuture<List<Notification.Action>> smartActionsFuture =
-                ScreenshotSmartActions.getSmartActionsFuture(
+                mScreenshotSmartActions.getSmartActionsFuture(
                         "", Uri.parse("content://autority/data"), bitmap, mSmartActionsProvider,
                         true, UserHandle.getUserHandleForUid(UserHandle.myUserId()));
         verify(mSmartActionsProvider, never()).getActions(any(), any(), any(), any(), any());
@@ -136,7 +138,7 @@
     public void testScreenshotNotificationSmartActionsProviderInvokedOnce() {
         Bitmap bitmap = mock(Bitmap.class);
         when(bitmap.getConfig()).thenReturn(Bitmap.Config.HARDWARE);
-        ScreenshotSmartActions.getSmartActionsFuture(
+        mScreenshotSmartActions.getSmartActionsFuture(
                 "", Uri.parse("content://autority/data"), bitmap, mSmartActionsProvider, true,
                 UserHandle.getUserHandleForUid(UserHandle.myUserId()));
         verify(mSmartActionsProvider, times(1)).getActions(any(), any(), any(), any(), any());
@@ -152,7 +154,7 @@
                 SystemUIFactory.getInstance().createScreenshotNotificationSmartActionsProvider(
                         mContext, null, mHandler);
         CompletableFuture<List<Notification.Action>> smartActionsFuture =
-                ScreenshotSmartActions.getSmartActionsFuture("", null, bitmap,
+                mScreenshotSmartActions.getSmartActionsFuture("", null, bitmap,
                         actionsProvider,
                         true, UserHandle.getUserHandleForUid(UserHandle.myUserId()));
         assertNotNull(smartActionsFuture);
@@ -172,7 +174,8 @@
         data.image = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
         data.finisher = null;
         data.mActionsReadyListener = null;
-        SaveImageInBackgroundTask task = new SaveImageInBackgroundTask(mContext, data);
+        SaveImageInBackgroundTask task =
+                new SaveImageInBackgroundTask(mContext, mScreenshotSmartActions, data);
 
         Notification.Action shareAction = task.createShareAction(mContext, mContext.getResources(),
                 Uri.parse("Screenshot_123.png"));
@@ -198,7 +201,8 @@
         data.image = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
         data.finisher = null;
         data.mActionsReadyListener = null;
-        SaveImageInBackgroundTask task = new SaveImageInBackgroundTask(mContext, data);
+        SaveImageInBackgroundTask task =
+                new SaveImageInBackgroundTask(mContext, mScreenshotSmartActions, data);
 
         Notification.Action editAction = task.createEditAction(mContext, mContext.getResources(),
                 Uri.parse("Screenshot_123.png"));
@@ -224,7 +228,8 @@
         data.image = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
         data.finisher = null;
         data.mActionsReadyListener = null;
-        SaveImageInBackgroundTask task = new SaveImageInBackgroundTask(mContext, data);
+        SaveImageInBackgroundTask task =
+                new SaveImageInBackgroundTask(mContext, mScreenshotSmartActions, data);
 
         Notification.Action deleteAction = task.createDeleteAction(mContext,
                 mContext.getResources(),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/SmartActionsReceiverTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/SmartActionsReceiverTest.java
new file mode 100644
index 0000000..ce6f073
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/SmartActionsReceiverTest.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.screenshot;
+
+import static com.android.systemui.screenshot.GlobalScreenshot.EXTRA_ACTION_TYPE;
+import static com.android.systemui.screenshot.GlobalScreenshot.EXTRA_ID;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.isNull;
+import static org.mockito.Mockito.verify;
+
+import android.app.PendingIntent;
+import android.content.Intent;
+import android.os.Bundle;
+import android.testing.AndroidTestingRunner;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@RunWith(AndroidTestingRunner.class)
+@SmallTest
+public class SmartActionsReceiverTest extends SysuiTestCase {
+
+    @Mock
+    private ScreenshotSmartActions mMockScreenshotSmartActions;
+    @Mock
+    private PendingIntent mMockPendingIntent;
+
+    private SmartActionsReceiver mSmartActionsReceiver;
+    private Intent mIntent;
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+        mSmartActionsReceiver = new SmartActionsReceiver(mMockScreenshotSmartActions);
+        mIntent = new Intent(mContext, SmartActionsReceiver.class)
+                .putExtra(GlobalScreenshot.EXTRA_ACTION_INTENT, mMockPendingIntent);
+    }
+
+    @Test
+    public void testSmartActionIntent() throws PendingIntent.CanceledException {
+        String testId = "testID";
+        String testActionType = "testActionType";
+        mIntent.putExtra(EXTRA_ID, testId);
+        mIntent.putExtra(EXTRA_ACTION_TYPE, testActionType);
+
+        mSmartActionsReceiver.onReceive(mContext, mIntent);
+
+        verify(mMockPendingIntent).send(
+                eq(mContext), eq(0), isNull(), isNull(), isNull(), isNull(), any(Bundle.class));
+        verify(mMockScreenshotSmartActions).notifyScreenshotAction(
+                mContext, testId, testActionType, true);
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
index aefea57..8a49326 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
@@ -61,6 +61,7 @@
 import com.android.systemui.Dependency;
 import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.FeatureFlags;
 import com.android.systemui.statusbar.NotificationLifetimeExtender;
 import com.android.systemui.statusbar.NotificationMediaManager;
@@ -200,7 +201,9 @@
                 () -> mNotificationRowBinder,
                 () -> mRemoteInputManager,
                 mLeakDetector,
-                mock(ForegroundServiceDismissalFeatureController.class)
+                mock(ForegroundServiceDismissalFeatureController.class),
+                mock(HeadsUpManager.class),
+                mock(StatusBarStateController.class)
         );
         mEntryManager.setUpWithPresenter(mPresenter);
         mEntryManager.addNotificationEntryListener(mEntryListener);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java
index c5374b2..601df2c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java
@@ -184,7 +184,9 @@
                 () -> mRowBinder,
                 () -> mRemoteInputManager,
                 mLeakDetector,
-                mock(ForegroundServiceDismissalFeatureController.class)
+                mock(ForegroundServiceDismissalFeatureController.class),
+                mock(HeadsUpManager.class),
+                mock(StatusBarStateController.class)
         );
 
         NotifRemoteViewCache cache = new NotifRemoteViewCacheImpl(mEntryManager);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java
index 243503d..7ca2478 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java
@@ -380,7 +380,7 @@
 
         setupMockStack(
                 PEOPLE_HEADER,
-                ALERTING.headsUp(),
+                ALERTING,
                 PERSON,
                 ALERTING_HEADER,
                 GENTLE_HEADER,
@@ -403,9 +403,9 @@
         enablePeopleFiltering();
 
         setupMockStack(
-                PERSON.headsUp(),
+                PERSON,
                 INCOMING_HEADER,
-                ALERTING.headsUp(),
+                ALERTING,
                 PEOPLE_HEADER,
                 PERSON
         );
@@ -425,7 +425,7 @@
         enablePeopleFiltering();
 
         setupMockStack(
-                PERSON.headsUp(),
+                PERSON,
                 PEOPLE_HEADER,
                 PERSON
         );
@@ -443,8 +443,8 @@
         enablePeopleFiltering();
 
         setupMockStack(
-                ALERTING.headsUp(),
-                PERSON.headsUp()
+                ALERTING,
+                PERSON
         );
         mSectionsManager.updateSectionBoundaries();
         verifyMockStack(
@@ -461,7 +461,7 @@
 
         setupMockStack(
                 INCOMING_HEADER,
-                ALERTING.headsUp(),
+                ALERTING,
                 PEOPLE_HEADER,
                 FSN,
                 PERSON,
@@ -502,9 +502,9 @@
     public void testMediaControls_AddWhenEnterKeyguardWithHeadsUp() {
         enableMediaControls();
 
-        // GIVEN a stack that doesn't include media controls but includes HEADS_UP
+        // GIVEN a stack that doesn't include media
         setupMockStack(
-                ALERTING.headsUp(),
+                ALERTING,
                 ALERTING,
                 GENTLE_HEADER,
                 GENTLE);
@@ -584,6 +584,27 @@
         );
     }
 
+    @Test
+    public void testIgnoreGoneView() {
+        enablePeopleFiltering();
+
+        setupMockStack(
+                PERSON.gone(),
+                ALERTING,
+                GENTLE
+        );
+
+        mSectionsManager.updateSectionBoundaries();
+
+        verifyMockStack(
+                ChildType.ALERTING_HEADER,
+                ChildType.PERSON,
+                ChildType.ALERTING,
+                ChildType.GENTLE_HEADER,
+                ChildType.GENTLE
+        );
+    }
+
     private void enablePeopleFiltering() {
         when(mSectionsFeatureManager.isFilteringEnabled()).thenReturn(true);
     }
@@ -619,16 +640,16 @@
                     child = mSectionsManager.getSilentHeaderView();
                     break;
                 case FSN:
-                    child = mockNotification(BUCKET_FOREGROUND_SERVICE, entry.mIsHeadsUp);
+                    child = mockNotification(BUCKET_FOREGROUND_SERVICE, entry.mIsGone);
                     break;
                 case PERSON:
-                    child = mockNotification(BUCKET_PEOPLE, entry.mIsHeadsUp);
+                    child = mockNotification(BUCKET_PEOPLE, entry.mIsGone);
                     break;
                 case ALERTING:
-                    child = mockNotification(BUCKET_ALERTING, entry.mIsHeadsUp);
+                    child = mockNotification(BUCKET_ALERTING, entry.mIsGone);
                     break;
                 case GENTLE:
-                    child = mockNotification(BUCKET_SILENT, entry.mIsHeadsUp);
+                    child = mockNotification(BUCKET_SILENT, entry.mIsGone);
                     break;
                 case OTHER:
                     child = mock(View.class);
@@ -643,7 +664,7 @@
         }
     }
 
-    private View mockNotification(int bucket, boolean headsUp) {
+    private View mockNotification(int bucket, boolean isGone) {
         ExpandableNotificationRow notifRow =
                 mock(ExpandableNotificationRow.class, RETURNS_DEEP_STUBS);
         when(notifRow.getVisibility()).thenReturn(View.VISIBLE);
@@ -659,8 +680,7 @@
             return null;
         }).when(mockEntry).setBucket(anyInt());
 
-        when(notifRow.isHeadsUp()).thenReturn(headsUp);
-        when(mockEntry.isRowHeadsUp()).thenReturn(headsUp);
+        when(notifRow.getVisibility()).thenReturn(isGone ? View.GONE : View.VISIBLE);
         return notifRow;
     }
 
@@ -767,16 +787,16 @@
                     child = mSectionsManager.getSilentHeaderView();
                     break;
                 case FSN:
-                    child = mockNotification(BUCKET_FOREGROUND_SERVICE, entry.mIsHeadsUp);
+                    child = mockNotification(BUCKET_FOREGROUND_SERVICE, entry.mIsGone);
                     break;
                 case PERSON:
-                    child = mockNotification(BUCKET_PEOPLE, entry.mIsHeadsUp);
+                    child = mockNotification(BUCKET_PEOPLE, entry.mIsGone);
                     break;
                 case ALERTING:
-                    child = mockNotification(BUCKET_ALERTING, entry.mIsHeadsUp);
+                    child = mockNotification(BUCKET_ALERTING, entry.mIsGone);
                     break;
                 case GENTLE:
-                    child = mockNotification(BUCKET_SILENT, entry.mIsHeadsUp);
+                    child = mockNotification(BUCKET_SILENT, entry.mIsGone);
                     break;
                 case OTHER:
                     child = mock(View.class);
@@ -796,36 +816,25 @@
     private static final StackEntry ALERTING_HEADER = new StackEntry(ChildType.ALERTING_HEADER);
     private static final StackEntry GENTLE_HEADER = new StackEntry(ChildType.GENTLE_HEADER);
     private static final StackEntry FSN = new StackEntry(ChildType.FSN);
-    private static final StackEntry.Hunnable PERSON = new StackEntry.Hunnable(ChildType.PERSON);
-    private static final StackEntry.Hunnable ALERTING = new StackEntry.Hunnable(ChildType.ALERTING);
+    private static final StackEntry PERSON = new StackEntry(ChildType.PERSON);
+    private static final StackEntry ALERTING = new StackEntry(ChildType.ALERTING);
     private static final StackEntry GENTLE = new StackEntry(ChildType.GENTLE);
 
     private static class StackEntry {
         final ChildType mChildType;
-        final boolean mIsHeadsUp;
+        final boolean mIsGone;
 
         StackEntry(ChildType childType) {
             this(childType, false);
         }
 
-        StackEntry(ChildType childType, boolean isHeadsUp) {
+        StackEntry(ChildType childType, boolean isGone) {
             mChildType = childType;
-            mIsHeadsUp = isHeadsUp;
+            mIsGone = isGone;
         }
 
-        static class Hunnable extends StackEntry {
-
-            Hunnable(ChildType childType) {
-                super(childType, false);
-            }
-
-            Hunnable(ChildType childType, boolean isHeadsUp) {
-                super(childType, isHeadsUp);
-            }
-
-            public Hunnable headsUp() {
-                return new Hunnable(mChildType, true);
-            }
+        public StackEntry gone() {
+            return new StackEntry(mChildType, true);
         }
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
index b286f94..6d411333 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
@@ -55,6 +55,7 @@
 import com.android.systemui.classifier.FalsingManagerFake;
 import com.android.systemui.media.KeyguardMediaController;
 import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.EmptyShadeView;
 import com.android.systemui.statusbar.FeatureFlags;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
@@ -92,6 +93,7 @@
 import com.android.systemui.statusbar.phone.ScrimController;
 import com.android.systemui.statusbar.phone.ShadeController;
 import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
 import com.android.systemui.statusbar.policy.ZenModeController;
 import com.android.systemui.util.leak.LeakDetector;
 
@@ -190,7 +192,9 @@
                 () -> mock(NotificationRowBinder.class),
                 () -> mRemoteInputManager,
                 mock(LeakDetector.class),
-                mock(ForegroundServiceDismissalFeatureController.class)
+                mock(ForegroundServiceDismissalFeatureController.class),
+                mock(HeadsUpManager.class),
+                mock(StatusBarStateController.class)
         );
         mEntryManager.setUpWithPresenter(mock(NotificationPresenter.class));
         when(mFeatureFlags.isNewNotifPipelineRenderingEnabled()).thenReturn(false);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeParametersTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeParametersTest.java
index debc840..fa253e6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeParametersTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeParametersTest.java
@@ -16,13 +16,18 @@
 
 package com.android.systemui.statusbar.phone;
 
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.eq;
 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.hardware.display.AmbientDisplayConfiguration;
 import android.os.PowerManager;
+import android.provider.Settings;
 import android.test.suitebuilder.annotation.SmallTest;
 
 import androidx.test.runner.AndroidJUnit4;
@@ -30,6 +35,7 @@
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.doze.AlwaysOnDisplayPolicy;
 import com.android.systemui.doze.DozeScreenState;
+import com.android.systemui.statusbar.policy.BatteryController;
 import com.android.systemui.tuner.TunerService;
 
 import org.junit.Assert;
@@ -50,6 +56,7 @@
     @Mock private AlwaysOnDisplayPolicy mAlwaysOnDisplayPolicy;
     @Mock private PowerManager mPowerManager;
     @Mock private TunerService mTunerService;
+    @Mock private BatteryController mBatteryController;
 
     @Before
     public void setup() {
@@ -59,11 +66,12 @@
             mAmbientDisplayConfiguration,
             mAlwaysOnDisplayPolicy,
             mPowerManager,
+            mBatteryController,
             mTunerService
         );
     }
     @Test
-    public void test_setControlScreenOffAnimation_setsDozeAfterScreenOff_false() {
+    public void testSetControlScreenOffAnimation_setsDozeAfterScreenOff_false() {
         mDozeParameters.setControlScreenOffAnimation(true);
         reset(mPowerManager);
         mDozeParameters.setControlScreenOffAnimation(false);
@@ -71,7 +79,7 @@
     }
 
     @Test
-    public void test_setControlScreenOffAnimation_setsDozeAfterScreenOff_true() {
+    public void testSetControlScreenOffAnimation_setsDozeAfterScreenOff_true() {
         mDozeParameters.setControlScreenOffAnimation(false);
         reset(mPowerManager);
         mDozeParameters.setControlScreenOffAnimation(true);
@@ -79,11 +87,28 @@
     }
 
     @Test
-    public void test_getWallpaperAodDuration_when_shouldControlScreenOff() {
+    public void testGetWallpaperAodDuration_when_shouldControlScreenOff() {
         mDozeParameters.setControlScreenOffAnimation(true);
         Assert.assertEquals(
                 "wallpaper hides faster when controlling screen off",
                 mDozeParameters.getWallpaperAodDuration(),
                 DozeScreenState.ENTER_DOZE_HIDE_WALLPAPER_DELAY);
     }
+
+    @Test
+    public void testGetAlwaysOn() {
+        when(mAmbientDisplayConfiguration.alwaysOnEnabled(anyInt())).thenReturn(true);
+        mDozeParameters.onTuningChanged(Settings.Secure.DOZE_ALWAYS_ON, "1");
+
+        assertThat(mDozeParameters.getAlwaysOn()).isTrue();
+    }
+
+    @Test
+    public void testGetAlwaysOn_whenBatterySaver() {
+        when(mBatteryController.isAodPowerSave()).thenReturn(true);
+        when(mAmbientDisplayConfiguration.alwaysOnEnabled(anyInt())).thenReturn(true);
+        mDozeParameters.onTuningChanged(Settings.Secure.DOZE_ALWAYS_ON, "1");
+
+        assertThat(mDozeParameters.getAlwaysOn()).isFalse();
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/LocationControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/LocationControllerImplTest.java
index 4d6922c..0c2361a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/LocationControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/LocationControllerImplTest.java
@@ -24,6 +24,7 @@
 import android.app.AppOpsManager;
 import android.content.Intent;
 import android.location.LocationManager;
+import android.os.Handler;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 import android.testing.TestableLooper.RunWithLooper;
@@ -60,8 +61,11 @@
         mLocationController = spy(new LocationControllerImpl(mContext,
                 mAppOpsController,
                 mTestableLooper.getLooper(),
+                new Handler(mTestableLooper.getLooper()),
                 mock(BroadcastDispatcher.class),
                 mock(BootCompleteCache.class)));
+
+        mTestableLooper.processAllMessages();
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/settings/FakeSettings.java b/packages/SystemUI/tests/src/com/android/systemui/util/settings/FakeSettings.java
new file mode 100644
index 0000000..8cb5f3e
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/settings/FakeSettings.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.util.settings;
+
+import android.content.ContentResolver;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.UserHandle;
+import android.util.Pair;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class FakeSettings implements SecureSettings, GlobalSettings, SystemSettings {
+    private final Map<SettingsKey, String> mValues = new HashMap<>();
+    private final Map<SettingsKey, List<ContentObserver>> mContentObservers =
+            new HashMap<>();
+
+    public static final Uri CONTENT_URI = Uri.parse("content://settings/fake");
+
+    public FakeSettings() {
+    }
+
+    public FakeSettings(String initialKey, String initialValue) {
+        putString(initialKey, initialValue);
+    }
+
+    public FakeSettings(Map<String, String> initialValues) {
+        for (Map.Entry<String, String> kv : initialValues.entrySet()) {
+            putString(kv.getKey(), kv.getValue());
+        }
+    }
+
+    @Override
+    public ContentResolver getContentResolver() {
+        return null;
+    }
+
+    @Override
+    public void registerContentObserverForUser(String name, ContentObserver settingsObserver,
+            int userHandle) {
+        SettingsKey key = new SettingsKey(userHandle, name);
+        mContentObservers.putIfAbsent(key, new ArrayList<>());
+        List<ContentObserver> observers = mContentObservers.get(key);
+        observers.add(settingsObserver);
+    }
+
+    @Override
+    public void unregisterContentObserver(ContentObserver settingsObserver) {
+        for (SettingsKey key : mContentObservers.keySet()) {
+            List<ContentObserver> observers = mContentObservers.get(key);
+            observers.remove(settingsObserver);
+        }
+    }
+
+    @Override
+    public Uri getUriFor(String name) {
+        return Uri.withAppendedPath(CONTENT_URI, name);
+    }
+
+    @Override
+    public int getUserId() {
+        return UserHandle.USER_CURRENT;
+    }
+
+    @Override
+    public String getString(String name) {
+        return getStringForUser(name, getUserId());
+    }
+
+    @Override
+    public String getStringForUser(String name, int userHandle) {
+        return mValues.get(new SettingsKey(userHandle, name));
+    }
+
+    @Override
+    public boolean putString(String name, String value, boolean overrideableByRestore) {
+        return putStringForUser(name, value, null, false, getUserId(), overrideableByRestore);
+    }
+
+    @Override
+    public boolean putString(String name, String value) {
+        return putString(name, value, false);
+    }
+
+    @Override
+    public boolean putStringForUser(String name, String value, int userHandle) {
+        return putStringForUser(name, value, null, false, userHandle, false);
+    }
+
+    @Override
+    public boolean putStringForUser(String name, String value, String tag, boolean makeDefault,
+            int userHandle, boolean overrideableByRestore) {
+        SettingsKey key = new SettingsKey(userHandle, name);
+        mValues.put(key, value);
+
+        Uri uri = getUriFor(name);
+        for (ContentObserver observer : mContentObservers.getOrDefault(key, new ArrayList<>())) {
+            observer.dispatchChange(false, List.of(uri), userHandle);
+        }
+        return true;
+    }
+
+    @Override
+    public boolean putString(String name, String value, String tag, boolean makeDefault) {
+        return putString(name, value);
+    }
+
+    private static class SettingsKey extends Pair<Integer, String> {
+        SettingsKey(Integer first, String second) {
+            super(first, second);
+        }
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/settings/FakeSettingsTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/settings/FakeSettingsTest.java
new file mode 100644
index 0000000..0d560f2
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/settings/FakeSettingsTest.java
@@ -0,0 +1,101 @@
+/*
+ * 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.util.settings;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+
+import android.database.ContentObserver;
+import android.provider.Settings;
+import android.testing.AndroidTestingRunner;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.Collection;
+import java.util.Map;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class FakeSettingsTest extends SysuiTestCase {
+    @Mock
+    ContentObserver mContentObserver;
+
+    private FakeSettings mFakeSettings;
+
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+
+        mFakeSettings = new FakeSettings();
+    }
+
+    /**
+     * Test FakeExecutor that receives non-delayed items to execute.
+     */
+    @Test
+    public void testPutAndGet() throws Settings.SettingNotFoundException {
+        mFakeSettings.putInt("foobar", 1);
+        assertThat(mFakeSettings.getInt("foobar")).isEqualTo(1);
+    }
+
+    @Test
+    public void testInitialize() {
+        mFakeSettings = new FakeSettings("abra", "cadabra");
+        assertThat(mFakeSettings.getString("abra")).isEqualTo("cadabra");
+    }
+
+    @Test
+    public void testInitializeWithMap() {
+        mFakeSettings = new FakeSettings(Map.of("one fish", "two fish", "red fish", "blue fish"));
+        assertThat(mFakeSettings.getString("red fish")).isEqualTo("blue fish");
+        assertThat(mFakeSettings.getString("one fish")).isEqualTo("two fish");
+    }
+
+    @Test
+    public void testRegisterContentObserver() {
+        mFakeSettings.registerContentObserver("cat", mContentObserver);
+
+        mFakeSettings.putString("cat", "hat");
+
+        verify(mContentObserver).dispatchChange(anyBoolean(), any(Collection.class), anyInt());
+    }
+
+    @Test
+    public void testUnregisterContentObserver() {
+        mFakeSettings.registerContentObserver("cat", mContentObserver);
+        mFakeSettings.unregisterContentObserver(mContentObserver);
+
+        mFakeSettings.putString("cat", "hat");
+
+        verify(mContentObserver, never()).dispatchChange(
+                anyBoolean(), any(Collection.class), anyInt());
+    }
+}
diff --git a/services/core/java/com/android/server/GestureLauncherService.java b/services/core/java/com/android/server/GestureLauncherService.java
index 7f6dc14..c87dcd7 100644
--- a/services/core/java/com/android/server/GestureLauncherService.java
+++ b/services/core/java/com/android/server/GestureLauncherService.java
@@ -75,6 +75,16 @@
      */
     @VisibleForTesting static final long POWER_SHORT_TAP_SEQUENCE_MAX_INTERVAL_MS = 500;
 
+    /**
+     * Number of taps required to launch panic ui.
+     */
+    private static final int PANIC_POWER_TAP_COUNT_THRESHOLD = 5;
+
+    /**
+     * Number of taps required to launch camera shortcut.
+     */
+    private static final int CAMERA_POWER_TAP_COUNT_THRESHOLD = 2;
+
     /** The listener that receives the gesture event. */
     private final GestureEventListener mGestureListener = new GestureEventListener();
     private final CameraLiftTriggerEventListener mCameraLiftTriggerListener =
@@ -135,6 +145,7 @@
 
     private long mLastPowerDown;
     private int mPowerButtonConsecutiveTaps;
+    private int mPowerButtonSlowConsecutiveTaps;
 
     public GestureLauncherService(Context context) {
         this(context, new MetricsLogger());
@@ -350,9 +361,8 @@
      * Whether to enable panic button gesture.
      */
     public static boolean isPanicButtonGestureEnabled(Context context, int userId) {
-        return isCameraLaunchEnabled(context.getResources())
-                && (Settings.Secure.getIntForUser(context.getContentResolver(),
-                Settings.Secure.PANIC_GESTURE_ENABLED, 0, userId) != 0);
+        return Settings.Secure.getIntForUser(context.getContentResolver(),
+                Settings.Secure.PANIC_GESTURE_ENABLED, 0, userId) != 0;
     }
 
     /**
@@ -384,6 +394,13 @@
                 isCameraLiftTriggerEnabled(resources);
     }
 
+    /**
+     * Attempts to intercept power key down event by detecting certain gesture patterns
+     *
+     * @param interactive true if the event's policy contains {@code FLAG_INTERACTIVE}
+     * @param outLaunched true if some action is taken as part of the key intercept (eg, app launch)
+     * @return true if the key down event is intercepted
+     */
     public boolean interceptPowerKeyDown(KeyEvent event, boolean interactive,
             MutableBoolean outLaunched) {
         if (event.isLongPress()) {
@@ -392,41 +409,60 @@
             // taps or consecutive taps, so we want to ignore the long press event.
             return false;
         }
-        boolean launched = false;
+        boolean launchCamera = false;
+        boolean launchPanic = false;
         boolean intercept = false;
         long powerTapInterval;
         synchronized (this) {
             powerTapInterval = event.getEventTime() - mLastPowerDown;
-            if (mCameraDoubleTapPowerEnabled
-                    && powerTapInterval < CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS) {
-                launched = true;
-                intercept = interactive;
-                mPowerButtonConsecutiveTaps++;
-            } else if (powerTapInterval < POWER_SHORT_TAP_SEQUENCE_MAX_INTERVAL_MS) {
-                mPowerButtonConsecutiveTaps++;
-            } else {
-                mPowerButtonConsecutiveTaps = 1;
-            }
             mLastPowerDown = event.getEventTime();
+            if (powerTapInterval >= POWER_SHORT_TAP_SEQUENCE_MAX_INTERVAL_MS) {
+                // Tap too slow, reset consecutive tap counts.
+                mPowerButtonConsecutiveTaps = 1;
+                mPowerButtonSlowConsecutiveTaps = 1;
+            } else if (powerTapInterval >= CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS) {
+                // Tap too slow for shortcuts
+                mPowerButtonConsecutiveTaps = 1;
+                mPowerButtonSlowConsecutiveTaps++;
+            } else {
+                // Fast consecutive tap
+                mPowerButtonConsecutiveTaps++;
+                mPowerButtonSlowConsecutiveTaps++;
+            }
+            if (mPanicButtonGestureEnabled
+                    && mPowerButtonConsecutiveTaps == PANIC_POWER_TAP_COUNT_THRESHOLD) {
+                launchPanic = true;
+                intercept = interactive;
+            } else if (mCameraDoubleTapPowerEnabled
+                    && powerTapInterval < CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS
+                    && mPowerButtonConsecutiveTaps == CAMERA_POWER_TAP_COUNT_THRESHOLD) {
+                launchCamera = true;
+                intercept = interactive;
+            }
         }
-        if (DBG && mPowerButtonConsecutiveTaps > 1) {
-            Slog.i(TAG, Long.valueOf(mPowerButtonConsecutiveTaps) +
-                    " consecutive power button taps detected");
+        if (mPowerButtonConsecutiveTaps > 1 || mPowerButtonSlowConsecutiveTaps > 1) {
+            Slog.i(TAG, Long.valueOf(mPowerButtonConsecutiveTaps)
+                    + " consecutive power button taps detected, "
+                    + Long.valueOf(mPowerButtonSlowConsecutiveTaps)
+                    + " consecutive slow power button taps detected");
         }
-        if (launched) {
+        if (launchCamera) {
             Slog.i(TAG, "Power button double tap gesture detected, launching camera. Interval="
                     + powerTapInterval + "ms");
-            launched = handleCameraGesture(false /* useWakelock */,
+            launchCamera = handleCameraGesture(false /* useWakelock */,
                     StatusBarManager.CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP);
-            if (launched) {
+            if (launchCamera) {
                 mMetricsLogger.action(MetricsEvent.ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE,
                         (int) powerTapInterval);
             }
+        } else if (launchPanic) {
+            Slog.i(TAG, "Panic gesture detected, launching panic.");
         }
-        mMetricsLogger.histogram("power_consecutive_short_tap_count", mPowerButtonConsecutiveTaps);
+        mMetricsLogger.histogram("power_consecutive_short_tap_count",
+                mPowerButtonSlowConsecutiveTaps);
         mMetricsLogger.histogram("power_double_tap_interval", (int) powerTapInterval);
-        outLaunched.value = launched;
-        return intercept && launched;
+        outLaunched.value = launchCamera || launchPanic;
+        return intercept && (launchCamera || launchPanic);
     }
 
     /**
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 816d663..1520dd3 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -3238,6 +3238,12 @@
 
     @Override
     public void lockUserKey(int userId) {
+        //  Do not lock user 0 data for headless system user
+        if (userId == UserHandle.USER_SYSTEM
+                && UserManager.isHeadlessSystemUserMode()) {
+            throw new IllegalArgumentException("Headless system user data cannot be locked..");
+        }
+
         enforcePermission(android.Manifest.permission.STORAGE_INTERNAL);
 
         try {
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index 676b767..a4c6c87 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -63,7 +63,6 @@
 import android.telephony.CellSignalStrengthNr;
 import android.telephony.CellSignalStrengthTdscdma;
 import android.telephony.CellSignalStrengthWcdma;
-import android.telephony.DataFailCause;
 import android.telephony.DisconnectCause;
 import android.telephony.LocationAccessPolicy;
 import android.telephony.PhoneCapability;
@@ -1800,11 +1799,9 @@
             if (validatePhoneId(phoneId)) {
                 mPreciseDataConnectionStates.get(phoneId).put(
                         apnType,
-                        new PreciseDataConnectionState(
-                                TelephonyManager.DATA_UNKNOWN,
-                                TelephonyManager.NETWORK_TYPE_UNKNOWN,
-                                apnType, null, null,
-                                DataFailCause.NONE, null));
+                        new PreciseDataConnectionState.Builder()
+                                .setApnTypes(apnType)
+                                .build());
                 for (Record r : mRecords) {
                     if (r.matchPhoneStateListenerEvent(
                             PhoneStateListener.LISTEN_PRECISE_DATA_CONNECTION_STATE)
@@ -1986,11 +1983,10 @@
             if (validatePhoneId(phoneId)) {
                 mPreciseDataConnectionStates.get(phoneId).put(
                         apnType,
-                        new PreciseDataConnectionState(
-                                TelephonyManager.DATA_UNKNOWN,
-                                TelephonyManager.NETWORK_TYPE_UNKNOWN,
-                                apnType, null, null,
-                                failCause, null));
+                        new PreciseDataConnectionState.Builder()
+                                .setApnTypes(apnType)
+                                .setFailCause(failCause)
+                                .build());
                 for (Record r : mRecords) {
                     if (r.matchPhoneStateListenerEvent(
                             PhoneStateListener.LISTEN_PRECISE_DATA_CONNECTION_STATE)
diff --git a/services/core/java/com/android/server/UiModeManagerService.java b/services/core/java/com/android/server/UiModeManagerService.java
index be080e5..915189c 100644
--- a/services/core/java/com/android/server/UiModeManagerService.java
+++ b/services/core/java/com/android/server/UiModeManagerService.java
@@ -81,6 +81,7 @@
 import java.util.Map;
 import java.util.Set;
 
+import static android.app.UiModeManager.DEFAULT_PRIORITY;
 import static android.app.UiModeManager.MODE_NIGHT_AUTO;
 import static android.app.UiModeManager.MODE_NIGHT_CUSTOM;
 import static android.app.UiModeManager.MODE_NIGHT_YES;
@@ -1446,6 +1447,8 @@
             pw.println("    Print this help text.");
             pw.println("  night [yes|no|auto|custom]");
             pw.println("    Set or read night mode.");
+            pw.println("  car [yes|no]");
+            pw.println("    Set or read car mode.");
             pw.println("  time [start|end] <ISO time>");
             pw.println("    Set custom start/end schedule time"
                     + " (night mode must be set to custom to apply).");
@@ -1461,6 +1464,8 @@
                 switch (cmd) {
                     case "night":
                         return handleNightMode();
+                    case "car":
+                        return handleCarMode();
                     case "time":
                         return handleCustomTime();
                     default:
@@ -1558,6 +1563,34 @@
                     return -1;
             }
         }
+
+        private int handleCarMode() throws RemoteException {
+            final PrintWriter err = getErrPrintWriter();
+            final String modeStr = getNextArg();
+            if (modeStr == null) {
+                printCurrentCarMode();
+                return 0;
+            }
+
+            if (modeStr.equals("yes")) {
+                mInterface.enableCarMode(0 /* flags */, DEFAULT_PRIORITY, "" /* package */);
+                printCurrentCarMode();
+                return 0;
+            } else if (modeStr.equals("no")) {
+                mInterface.disableCarMode(0 /* flags */);
+                printCurrentCarMode();
+                return 0;
+            } else {
+                err.println("Error: mode must be 'yes', or 'no'");
+                return -1;
+            }
+        }
+
+        private void printCurrentCarMode() throws RemoteException {
+            final PrintWriter pw = getOutPrintWriter();
+            final int currMode = mInterface.getCurrentModeType();
+            pw.println("Car mode: " + (currMode == Configuration.UI_MODE_TYPE_CAR ? "yes" : "no"));
+        }
     }
 
     public final class LocalService extends UiModeManagerInternal {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 32f7d1d..555bf7d 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -291,6 +291,7 @@
 import android.util.ArraySet;
 import android.util.DebugUtils;
 import android.util.EventLog;
+import android.util.IntArray;
 import android.util.Log;
 import android.util.Pair;
 import android.util.PrintWriterPrinter;
@@ -310,6 +311,7 @@
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.app.IAppOpsActiveCallback;
 import com.android.internal.app.IAppOpsCallback;
 import com.android.internal.app.IAppOpsService;
 import com.android.internal.app.ProcessMap;
@@ -657,6 +659,14 @@
     int mUidChangeDispatchCount;
 
     /**
+     * Uids of apps with current active camera sessions.  Access synchronized on
+     * the IntArray instance itself, and no other locks must be acquired while that
+     * one is held.
+     */
+    @GuardedBy("mActiveCameraUids")
+    final IntArray mActiveCameraUids = new IntArray(4);
+
+    /**
      * Helper class which strips out priority and proto arguments then calls the dump function with
      * the appropriate arguments. If priority arguments are omitted, function calls the legacy
      * dump command.
@@ -2053,7 +2063,10 @@
                     }
                     if (proc != null) {
                         long startTime = SystemClock.currentThreadTimeMillis();
-                        long pss = Debug.getPss(pid, tmp, null);
+                        // skip background PSS calculation of apps that are capturing
+                        // camera imagery
+                        final boolean usingCamera = isCameraActiveForUid(proc.uid);
+                        long pss = usingCamera ? 0 : Debug.getPss(pid, tmp, null);
                         long endTime = SystemClock.currentThreadTimeMillis();
                         synchronized (ActivityManagerService.this) {
                             if (pss != 0 && proc.thread != null && proc.setProcState == procState
@@ -2066,6 +2079,7 @@
                                 ProcessList.abortNextPssTime(proc.procStateMemTracker);
                                 if (DEBUG_PSS) Slog.d(TAG_PSS, "Skipped pss collection of " + pid +
                                         ": " + (proc.thread == null ? "NO_THREAD " : "") +
+                                        (usingCamera ? "CAMERA " : "") +
                                         (proc.pid != pid ? "PID_CHANGED " : "") +
                                         " initState=" + procState + " curState=" +
                                         proc.setProcState + " " +
@@ -2155,6 +2169,14 @@
                         }
                     }
                 });
+
+        final int[] cameraOp = {AppOpsManager.OP_CAMERA};
+        mAppOpsService.startWatchingActive(cameraOp, new IAppOpsActiveCallback.Stub() {
+            @Override
+            public void opActiveChanged(int op, int uid, String packageName, boolean active) {
+                cameraActiveChanged(uid, active);
+            }
+        });
     }
 
     public void setWindowManager(WindowManagerService wm) {
@@ -18273,6 +18295,27 @@
         }
     }
 
+    final void cameraActiveChanged(@UserIdInt int uid, boolean active) {
+        synchronized (mActiveCameraUids) {
+            final int curIndex = mActiveCameraUids.indexOf(uid);
+            if (active) {
+                if (curIndex < 0) {
+                    mActiveCameraUids.add(uid);
+                }
+            } else {
+                if (curIndex >= 0) {
+                    mActiveCameraUids.remove(curIndex);
+                }
+            }
+        }
+    }
+
+    final boolean isCameraActiveForUid(@UserIdInt int uid) {
+        synchronized (mActiveCameraUids) {
+            return mActiveCameraUids.indexOf(uid) >= 0;
+        }
+    }
+
     @GuardedBy("this")
     final void doStopUidLocked(int uid, final UidRecord uidRec) {
         mServices.stopInBackgroundLocked(uid);
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index b6ad1a5..1038069 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -2050,7 +2050,9 @@
             final int pid = precedence.pid;
             long now = System.currentTimeMillis();
             final long end = now + PROC_KILL_TIMEOUT;
+            final int oldPolicy = StrictMode.getThreadPolicyMask();
             try {
+                StrictMode.setThreadPolicyMask(0);
                 Process.waitForProcessDeath(pid, PROC_KILL_TIMEOUT);
                 // It's killed successfully, but we'd make sure the cleanup work is done.
                 synchronized (precedence) {
@@ -2069,9 +2071,11 @@
                     }
                 }
             } catch (Exception e) {
-                // It's still alive...
+                // It's still alive... maybe blocked at uninterruptible sleep ?
                 Slog.wtf(TAG, precedence.toString() + " refused to die, but we need to launch "
-                        + app);
+                        + app, e);
+            } finally {
+                StrictMode.setThreadPolicyMask(oldPolicy);
             }
         }
         try {
@@ -2416,7 +2420,15 @@
             ProcessList.killProcessGroup(app.uid, app.pid);
             checkSlow(startTime, "startProcess: done killing old proc");
 
-            Slog.wtf(TAG_PROCESSES, app.toString() + " is attached to a previous process");
+            if (!app.killed || mService.mLastMemoryLevel <= ProcessStats.ADJ_MEM_FACTOR_NORMAL
+                    || app.setProcState < ActivityManager.PROCESS_STATE_CACHED_EMPTY
+                    || app.lastCachedPss < getCachedRestoreThresholdKb()) {
+                // Throw a wtf if it's not killed, or killed but not because the system was in
+                // memory pressure + the app was in "cch-empty" and used large amount of memory
+                Slog.wtf(TAG_PROCESSES, app.toString() + " is attached to a previous process");
+            } else {
+                Slog.w(TAG_PROCESSES, app.toString() + " is attached to a previous process");
+            }
             // We are not going to re-use the ProcessRecord, as we haven't dealt with the cleanup
             // routine of it yet, but we'd set it as the precedence of the new process.
             precedence = app;
@@ -2819,7 +2831,15 @@
             // We are re-adding a persistent process.  Whatevs!  Just leave it there.
             Slog.w(TAG, "Re-adding persistent process " + proc);
         } else if (old != null) {
-            Slog.wtf(TAG, "Already have existing proc " + old + " when adding " + proc);
+            if (old.killed) {
+                // The old process has been killed, we probably haven't had
+                // a chance to clean up the old record, just log a warning
+                Slog.w(TAG, "Existing proc " + old + " was killed "
+                        + (SystemClock.uptimeMillis() - old.mKillTime)
+                        + "ms ago when adding " + proc);
+            } else {
+                Slog.wtf(TAG, "Already have existing proc " + old + " when adding " + proc);
+            }
         }
         UidRecord uidRec = mActiveUids.get(proc.uid);
         if (uidRec == null) {
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index 598b70e7..cd4302b 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -350,6 +350,8 @@
 
     boolean mReachable; // Whether or not this process is reachable from given process
 
+    long mKillTime; // The timestamp in uptime when this process was killed.
+
     void setStartParams(int startUid, HostingRecord hostingRecord, String seInfo,
             long startTime) {
         this.startUid = startUid;
@@ -916,6 +918,7 @@
             if (!mPersistent) {
                 killed = true;
                 killedByAm = true;
+                mKillTime = SystemClock.uptimeMillis();
             }
             Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
         }
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index 9562158..1bf761f 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -1891,8 +1891,7 @@
         if (callingUid != 0 && callingUid != SYSTEM_UID) {
             final boolean allow;
             final boolean isSameProfileGroup = isSameProfileGroup(callingUserId, targetUserId);
-            if (mInjector.isCallerRecents(callingUid)
-                    && isSameProfileGroup(callingUserId, targetUserId)) {
+            if (mInjector.isCallerRecents(callingUid) && isSameProfileGroup) {
                 // If the caller is Recents and the caller has ownership of the profile group,
                 // we then allow it to access its profiles.
                 allow = true;
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
index 3ff6ec1..86e6a32 100755
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
@@ -244,7 +244,7 @@
         if (dest != mAddress && dest != Constants.ADDR_BROADCAST) {
             return false;
         }
-        // Cache incoming message. Note that it caches only white-listed one.
+        // Cache incoming message if it is included in the list of cacheable opcodes.
         mCecMessageCache.cacheMessage(message);
         return onMessage(message);
     }
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
index aed94fc..64d70d6 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
@@ -225,7 +225,7 @@
             if (SystemProperties.getBoolean(Constants.PROPERTY_KEEP_AWAKE, true)) {
                 mWakeLock = new SystemWakeLock();
             } else {
-                // Create a dummy lock object that doesn't do anything about wake lock,
+                // Create a stub lock object that doesn't do anything about wake lock,
                 // hence allows the device to go to sleep even if it's the active source.
                 mWakeLock = new ActiveWakeLock() {
                     @Override
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
index 2c0ddaf..804cc92 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
@@ -1667,6 +1667,7 @@
         if (avr == null) {
             return;
         }
+        setArcStatus(false);
 
         // Seq #44.
         removeAction(RequestArcInitiationAction.class);
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index 30d7d54..a8a9a36 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -2290,7 +2290,7 @@
             pw.println("mHdmiControlEnabled: " + mHdmiControlEnabled);
             pw.println("mMhlInputChangeEnabled: " + mMhlInputChangeEnabled);
             pw.println("mSystemAudioActivated: " + isSystemAudioActivated());
-            pw.println("mHdmiCecVolumeControlEnabled " + mHdmiCecVolumeControlEnabled);
+            pw.println("mHdmiCecVolumeControlEnabled: " + mHdmiCecVolumeControlEnabled);
             pw.decreaseIndent();
 
             pw.println("mMhlController: ");
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 0154fe0..254285d 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -2942,7 +2942,7 @@
                 vis = 0;
             }
             if (!mCurPerceptible) {
-                vis = 0;
+                vis &= ~InputMethodService.IME_VISIBLE;
             }
             // mImeWindowVis should be updated before calling shouldShowImeSwitcherLocked().
             final boolean needsToShowImeSwitcher = shouldShowImeSwitcherLocked(vis);
diff --git a/services/core/java/com/android/server/location/LocationManagerService.java b/services/core/java/com/android/server/location/LocationManagerService.java
index f69c823..d933c10 100644
--- a/services/core/java/com/android/server/location/LocationManagerService.java
+++ b/services/core/java/com/android/server/location/LocationManagerService.java
@@ -17,6 +17,9 @@
 package com.android.server.location;
 
 import static android.Manifest.permission.ACCESS_FINE_LOCATION;
+import static android.app.AppOpsManager.OP_MOCK_LOCATION;
+import static android.app.AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION;
+import static android.app.AppOpsManager.OP_MONITOR_LOCATION;
 import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE;
 import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
@@ -120,10 +123,16 @@
 import com.android.server.location.util.AppOpsHelper;
 import com.android.server.location.util.Injector;
 import com.android.server.location.util.LocationAttributionHelper;
+import com.android.server.location.util.LocationPermissionsHelper;
+import com.android.server.location.util.LocationPowerSaveModeHelper;
 import com.android.server.location.util.LocationUsageLogger;
+import com.android.server.location.util.ScreenInteractiveHelper;
 import com.android.server.location.util.SettingsHelper;
 import com.android.server.location.util.SystemAppForegroundHelper;
 import com.android.server.location.util.SystemAppOpsHelper;
+import com.android.server.location.util.SystemLocationPermissionsHelper;
+import com.android.server.location.util.SystemLocationPowerSaveModeHelper;
+import com.android.server.location.util.SystemScreenInteractiveHelper;
 import com.android.server.location.util.SystemSettingsHelper;
 import com.android.server.location.util.SystemUserInfoHelper;
 import com.android.server.location.util.UserInfoHelper;
@@ -173,7 +182,7 @@
             publishBinderService(Context.LOCATION_SERVICE, mService);
 
             // client caching behavior is only enabled after seeing the first invalidate
-            invalidateLocalLocationEnabledCaches();
+            LocationManager.invalidateLocalLocationEnabledCaches();
             // disable caching for our own process
             Objects.requireNonNull(mService.mContext.getSystemService(LocationManager.class))
                     .disableLocalLocationEnabledCaches();
@@ -486,7 +495,7 @@
 
     private void onLocationModeChanged(int userId) {
         boolean enabled = mSettingsHelper.isLocationEnabled(userId);
-        invalidateLocalLocationEnabledCaches();
+        LocationManager.invalidateLocalLocationEnabledCaches();
 
         if (D) {
             Log.d(TAG, "[u" + userId + "] location enabled = " + enabled);
@@ -1232,19 +1241,20 @@
             if (!currentlyMonitoring) {
                 if (allowMonitoring) {
                     if (!highPower) {
-                        return mAppOpsHelper.startLocationMonitoring(mCallerIdentity);
+                        return mAppOpsHelper.startOpNoThrow(OP_MONITOR_LOCATION, mCallerIdentity);
                     } else {
-                        return mAppOpsHelper.startHighPowerLocationMonitoring(mCallerIdentity);
+                        return mAppOpsHelper.startOpNoThrow(OP_MONITOR_HIGH_POWER_LOCATION,
+                                mCallerIdentity);
                     }
                 }
             } else {
-                if (!allowMonitoring || !mAppOpsHelper.checkLocationAccess(mCallerIdentity,
+                if (!allowMonitoring || !mAppOpsHelper.checkOpNoThrow(LocationPermissions.asAppOp(
                         LocationPermissions.getPermissionLevel(mContext, mCallerIdentity.getUid(),
-                                mCallerIdentity.getPid()))) {
+                                mCallerIdentity.getPid())), mCallerIdentity)) {
                     if (!highPower) {
-                        mAppOpsHelper.stopLocationMonitoring(mCallerIdentity);
+                        mAppOpsHelper.finishOp(OP_MONITOR_LOCATION, mCallerIdentity);
                     } else {
-                        mAppOpsHelper.stopHighPowerLocationMonitoring(mCallerIdentity);
+                        mAppOpsHelper.finishOp(OP_MONITOR_HIGH_POWER_LOCATION, mCallerIdentity);
                     }
                     return false;
                 }
@@ -1589,8 +1599,9 @@
                     continue;
                 }
 
-                if (!mAppOpsHelper.checkLocationAccess(identity,
-                        record.mRequest.isCoarse() ? PERMISSION_COARSE : PERMISSION_FINE)) {
+                if (!mAppOpsHelper.checkOpNoThrow(LocationPermissions.asAppOp(
+                        record.mRequest.isCoarse() ? PERMISSION_COARSE : PERMISSION_FINE),
+                        identity)) {
                     continue;
                 }
                 final boolean isBatterySaverDisablingLocation = shouldThrottleRequests
@@ -2118,7 +2129,8 @@
             }
 
             // appops check should always be right before delivery
-            if (!mAppOpsHelper.noteLocationAccess(identity, permissionLevel)) {
+            if (!mAppOpsHelper.noteOpNoThrow(LocationPermissions.asAppOp(permissionLevel),
+                    identity)) {
                 return null;
             }
 
@@ -2179,7 +2191,8 @@
 
             if (locationAgeMs < MAX_CURRENT_LOCATION_AGE_MS) {
                 // appops check should always be right before delivery
-                if (mAppOpsHelper.noteLocationAccess(identity, permissionLevel)) {
+                if (mAppOpsHelper.noteOpNoThrow(LocationPermissions.asAppOp(permissionLevel),
+                        identity)) {
                     transport.deliverResult(lastLocation);
                 } else {
                     transport.deliverResult(null);
@@ -2329,7 +2342,7 @@
     }
 
     @Override
-    public boolean sendExtraCommand(String provider, String command, Bundle extras) {
+    public void sendExtraCommand(String provider, String command, Bundle extras) {
         LocationPermissions.enforceCallingOrSelfLocationPermission(mContext, PERMISSION_COARSE);
         mContext.enforceCallingOrSelfPermission(
                 permission.ACCESS_LOCATION_EXTRA_COMMANDS, null);
@@ -2350,8 +2363,6 @@
                 LocationStatsEnums.USAGE_ENDED,
                 LocationStatsEnums.API_SEND_EXTRA_COMMAND,
                 provider);
-
-        return true;
     }
 
     @Override
@@ -2553,7 +2564,8 @@
                 r.mLastFixBroadcast = location;
 
                 // appops check should always be right before delivery
-                if (!mAppOpsHelper.noteLocationAccess(receiver.mCallerIdentity, permissionLevel)) {
+                if (!mAppOpsHelper.noteOpNoThrow(LocationPermissions.asAppOp(permissionLevel),
+                        receiver.mCallerIdentity)) {
                     continue;
                 }
 
@@ -2644,7 +2656,7 @@
         // unsafe is ok because app ops will verify the package name
         CallerIdentity identity = CallerIdentity.fromBinderUnsafe(packageName,
                 attributionTag);
-        if (!mAppOpsHelper.noteMockLocationAccess(identity)) {
+        if (!mAppOpsHelper.noteOp(OP_MOCK_LOCATION, identity)) {
             return;
         }
 
@@ -2664,7 +2676,7 @@
         // unsafe is ok because app ops will verify the package name
         CallerIdentity identity = CallerIdentity.fromBinderUnsafe(packageName,
                 attributionTag);
-        if (!mAppOpsHelper.noteMockLocationAccess(identity)) {
+        if (!mAppOpsHelper.noteOp(OP_MOCK_LOCATION, identity)) {
             return;
         }
 
@@ -2687,7 +2699,7 @@
         // unsafe is ok because app ops will verify the package name
         CallerIdentity identity = CallerIdentity.fromBinderUnsafe(packageName,
                 attributionTag);
-        if (!mAppOpsHelper.noteMockLocationAccess(identity)) {
+        if (!mAppOpsHelper.noteOp(OP_MOCK_LOCATION, identity)) {
             return;
         }
 
@@ -2708,7 +2720,7 @@
         // unsafe is ok because app ops will verify the package name
         CallerIdentity identity = CallerIdentity.fromBinderUnsafe(packageName,
                 attributionTag);
-        if (!mAppOpsHelper.noteMockLocationAccess(identity)) {
+        if (!mAppOpsHelper.noteOp(OP_MOCK_LOCATION, identity)) {
             return;
         }
 
@@ -2926,24 +2938,36 @@
 
         private final UserInfoHelper mUserInfoHelper;
         private final SystemAppOpsHelper mAppOpsHelper;
+        private final SystemLocationPermissionsHelper mLocationPermissionsHelper;
         private final SystemSettingsHelper mSettingsHelper;
         private final SystemAppForegroundHelper mAppForegroundHelper;
-        private final LocationUsageLogger mLocationUsageLogger;
+        private final SystemLocationPowerSaveModeHelper mLocationPowerSaveModeHelper;
+        private final SystemScreenInteractiveHelper mScreenInteractiveHelper;
         private final LocationAttributionHelper mLocationAttributionHelper;
+        private final LocationUsageLogger mLocationUsageLogger;
+        private final LocationRequestStatistics mLocationRequestStatistics;
 
         SystemInjector(Context context, UserInfoHelper userInfoHelper) {
             mUserInfoHelper = userInfoHelper;
             mAppOpsHelper = new SystemAppOpsHelper(context);
+            mLocationPermissionsHelper = new SystemLocationPermissionsHelper(context,
+                    mAppOpsHelper);
             mSettingsHelper = new SystemSettingsHelper(context);
             mAppForegroundHelper = new SystemAppForegroundHelper(context);
-            mLocationUsageLogger = new LocationUsageLogger();
+            mLocationPowerSaveModeHelper = new SystemLocationPowerSaveModeHelper(context);
+            mScreenInteractiveHelper = new SystemScreenInteractiveHelper(context);
             mLocationAttributionHelper = new LocationAttributionHelper(mAppOpsHelper);
+            mLocationUsageLogger = new LocationUsageLogger();
+            mLocationRequestStatistics = new LocationRequestStatistics();
         }
 
         void onSystemReady() {
             mAppOpsHelper.onSystemReady();
+            mLocationPermissionsHelper.onSystemReady();
             mSettingsHelper.onSystemReady();
             mAppForegroundHelper.onSystemReady();
+            mLocationPowerSaveModeHelper.onSystemReady();
+            mScreenInteractiveHelper.onSystemReady();
         }
 
         @Override
@@ -2957,6 +2981,11 @@
         }
 
         @Override
+        public LocationPermissionsHelper getLocationPermissionsHelper() {
+            return mLocationPermissionsHelper;
+        }
+
+        @Override
         public SettingsHelper getSettingsHelper() {
             return mSettingsHelper;
         }
@@ -2972,8 +3001,23 @@
         }
 
         @Override
+        public LocationPowerSaveModeHelper getLocationPowerSaveModeHelper() {
+            return mLocationPowerSaveModeHelper;
+        }
+
+        @Override
+        public ScreenInteractiveHelper getScreenInteractiveHelper() {
+            return mScreenInteractiveHelper;
+        }
+
+        @Override
         public LocationAttributionHelper getLocationAttributionHelper() {
             return mLocationAttributionHelper;
         }
+
+        @Override
+        public LocationRequestStatistics getLocationRequestStatistics() {
+            return mLocationRequestStatistics;
+        }
     }
 }
diff --git a/services/core/java/com/android/server/location/PassiveProvider.java b/services/core/java/com/android/server/location/PassiveProvider.java
index f37992a..f6896b8 100644
--- a/services/core/java/com/android/server/location/PassiveProvider.java
+++ b/services/core/java/com/android/server/location/PassiveProvider.java
@@ -50,14 +50,10 @@
             Criteria.POWER_LOW,
             Criteria.ACCURACY_COARSE);
 
-    private volatile boolean mReportLocation;
-
     public PassiveProvider(Context context) {
         // using a direct executor is ok because this class has no locks that could deadlock
         super(DIRECT_EXECUTOR, CallerIdentity.fromContext(context));
 
-        mReportLocation = false;
-
         setProperties(PROPERTIES);
         setAllowed(true);
     }
@@ -66,15 +62,11 @@
      * Pass a location into the passive provider.
      */
     public void updateLocation(Location location) {
-        if (mReportLocation) {
-            reportLocation(location);
-        }
+        reportLocation(location);
     }
 
     @Override
-    public void onSetRequest(ProviderRequest request) {
-        mReportLocation = request.reportLocation;
-    }
+    public void onSetRequest(ProviderRequest request) {}
 
     @Override
     protected void onExtraCommand(int uid, int pid, String command, Bundle extras) {}
diff --git a/services/core/java/com/android/server/location/geofence/GeofenceManager.java b/services/core/java/com/android/server/location/geofence/GeofenceManager.java
index c855a12..2d9734e 100644
--- a/services/core/java/com/android/server/location/geofence/GeofenceManager.java
+++ b/services/core/java/com/android/server/location/geofence/GeofenceManager.java
@@ -16,7 +16,6 @@
 
 package com.android.server.location.geofence;
 
-import static android.Manifest.permission;
 import static android.location.LocationManager.KEY_PROXIMITY_ENTERING;
 
 import static com.android.internal.util.ConcurrentUtils.DIRECT_EXECUTOR;
@@ -44,8 +43,8 @@
 import com.android.server.location.LocationPermissions;
 import com.android.server.location.listeners.ListenerMultiplexer;
 import com.android.server.location.listeners.PendingIntentListenerRegistration;
-import com.android.server.location.util.AppOpsHelper;
 import com.android.server.location.util.Injector;
+import com.android.server.location.util.LocationPermissionsHelper;
 import com.android.server.location.util.LocationUsageLogger;
 import com.android.server.location.util.SettingsHelper;
 import com.android.server.location.util.UserInfoHelper;
@@ -86,7 +85,7 @@
 
         // we store these values because we don't trust the listeners not to give us dupes, not to
         // spam us, and because checking the values may be more expensive
-        private boolean mAppOpsAllowed;
+        private boolean mPermitted;
 
         private @Nullable Location mCachedLocation;
         private float mCachedLocationDistanceM;
@@ -101,7 +100,7 @@
 
             mWakeLock = Objects.requireNonNull(mContext.getSystemService(PowerManager.class))
                     .newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
-                    TAG + ":" + identity.getPackageName());
+                            TAG + ":" + identity.getPackageName());
             mWakeLock.setReferenceCounted(true);
             mWakeLock.setWorkSource(identity.addToWorkSource(null));
         }
@@ -114,7 +113,8 @@
         @Override
         protected void onPendingIntentListenerRegister() {
             mGeofenceState = STATE_UNKNOWN;
-            mAppOpsAllowed = mAppOpsHelper.checkLocationAccess(getIdentity(), PERMISSION_FINE);
+            mPermitted = mLocationPermissionsHelper.hasLocationPermissions(PERMISSION_FINE,
+                    getIdentity());
         }
 
         @Override
@@ -127,18 +127,32 @@
             }
         }
 
-        boolean isAppOpsAllowed() {
-            return mAppOpsAllowed;
+        boolean isPermitted() {
+            return mPermitted;
         }
 
-        boolean onAppOpsChanged(String packageName) {
+        boolean onLocationPermissionsChanged(String packageName) {
             if (getIdentity().getPackageName().equals(packageName)) {
-                boolean appOpsAllowed = mAppOpsHelper.checkLocationAccess(getIdentity(),
-                        PERMISSION_FINE);
-                if (appOpsAllowed != mAppOpsAllowed) {
-                    mAppOpsAllowed = appOpsAllowed;
-                    return true;
-                }
+                return onLocationPermissionsChanged();
+            }
+
+            return false;
+        }
+
+        boolean onLocationPermissionsChanged(int uid) {
+            if (getIdentity().getUid() == uid) {
+                return onLocationPermissionsChanged();
+            }
+
+            return false;
+        }
+
+        private boolean onLocationPermissionsChanged() {
+            boolean permitted = mLocationPermissionsHelper.hasLocationPermissions(PERMISSION_FINE,
+                    getIdentity());
+            if (permitted != mPermitted) {
+                mPermitted = permitted;
+                return true;
             }
 
             return false;
@@ -186,10 +200,10 @@
 
             mWakeLock.acquire(WAKELOCK_TIMEOUT_MS);
             try {
-                pendingIntent.send(mContext, 0, intent,
-                        (pI, i, rC, rD, rE) -> mWakeLock.release(),
-                        null, permission.ACCESS_FINE_LOCATION,
-                        PendingIntentUtils.createDontSendToRestrictedAppsBundle(null));
+                // send() only enforces permissions for broadcast intents, but since clients can
+                // select any kind of pending intent we do not rely on send() to enforce permissions
+                pendingIntent.send(mContext, 0, intent, (pI, i, rC, rD, rE) -> mWakeLock.release(),
+                        null, null, PendingIntentUtils.createDontSendToRestrictedAppsBundle(null));
             } catch (PendingIntent.CanceledException e) {
                 mWakeLock.release();
                 removeRegistration(new GeofenceKey(pendingIntent, getRequest()), this);
@@ -202,7 +216,7 @@
             builder.append(getIdentity());
 
             ArraySet<String> flags = new ArraySet<>(1);
-            if (!mAppOpsAllowed) {
+            if (!mPermitted) {
                 flags.add("na");
             }
             if (!flags.isEmpty()) {
@@ -224,10 +238,22 @@
     private final SettingsHelper.UserSettingChangedListener
             mLocationPackageBlacklistChangedListener =
             this::onLocationPackageBlacklistChanged;
-    private final AppOpsHelper.LocationAppOpListener mAppOpsChangedListener = this::onAppOpsChanged;
+    private final LocationPermissionsHelper.LocationPermissionsListener
+            mLocationPermissionsListener =
+            new LocationPermissionsHelper.LocationPermissionsListener() {
+                @Override
+                public void onLocationPermissionsChanged(String packageName) {
+                    GeofenceManager.this.onLocationPermissionsChanged(packageName);
+                }
+
+                @Override
+                public void onLocationPermissionsChanged(int uid) {
+                    GeofenceManager.this.onLocationPermissionsChanged(uid);
+                }
+            };
 
     protected final UserInfoHelper mUserInfoHelper;
-    protected final AppOpsHelper mAppOpsHelper;
+    protected final LocationPermissionsHelper mLocationPermissionsHelper;
     protected final SettingsHelper mSettingsHelper;
     protected final LocationUsageLogger mLocationUsageLogger;
 
@@ -241,7 +267,7 @@
         mContext = context.createAttributionContext(ATTRIBUTION_TAG);
         mUserInfoHelper = injector.getUserInfoHelper();
         mSettingsHelper = injector.getSettingsHelper();
-        mAppOpsHelper = injector.getAppOpsHelper();
+        mLocationPermissionsHelper = injector.getLocationPermissionsHelper();
         mLocationUsageLogger = injector.getLocationUsageLogger();
     }
 
@@ -281,7 +307,7 @@
     @Override
     protected boolean isActive(GeofenceRegistration registration) {
         CallerIdentity identity = registration.getIdentity();
-        return registration.isAppOpsAllowed()
+        return registration.isPermitted()
                 && mUserInfoHelper.isCurrentUserId(identity.getUserId())
                 && mSettingsHelper.isLocationEnabled(identity.getUserId())
                 && !mSettingsHelper.isLocationPackageBlacklisted(identity.getUserId(),
@@ -294,7 +320,7 @@
         mSettingsHelper.addOnLocationEnabledChangedListener(mLocationEnabledChangedListener);
         mSettingsHelper.addOnLocationPackageBlacklistChangedListener(
                 mLocationPackageBlacklistChangedListener);
-        mAppOpsHelper.addListener(mAppOpsChangedListener);
+        mLocationPermissionsHelper.addListener(mLocationPermissionsListener);
     }
 
     @Override
@@ -303,7 +329,7 @@
         mSettingsHelper.removeOnLocationEnabledChangedListener(mLocationEnabledChangedListener);
         mSettingsHelper.removeOnLocationPackageBlacklistChangedListener(
                 mLocationPackageBlacklistChangedListener);
-        mAppOpsHelper.removeListener(mAppOpsChangedListener);
+        mLocationPermissionsHelper.removeListener(mLocationPermissionsListener);
     }
 
     @Override
@@ -434,7 +460,11 @@
         updateRegistrations(registration -> registration.getIdentity().getUserId() == userId);
     }
 
-    private void onAppOpsChanged(String packageName) {
-        updateRegistrations(registration -> registration.onAppOpsChanged(packageName));
+    private void onLocationPermissionsChanged(String packageName) {
+        updateRegistrations(registration -> registration.onLocationPermissionsChanged(packageName));
+    }
+
+    private void onLocationPermissionsChanged(int uid) {
+        updateRegistrations(registration -> registration.onLocationPermissionsChanged(uid));
     }
 }
diff --git a/services/core/java/com/android/server/location/gnss/GnssListenerMultiplexer.java b/services/core/java/com/android/server/location/gnss/GnssListenerMultiplexer.java
index 53e660a..0b7968b 100644
--- a/services/core/java/com/android/server/location/gnss/GnssListenerMultiplexer.java
+++ b/services/core/java/com/android/server/location/gnss/GnssListenerMultiplexer.java
@@ -31,8 +31,8 @@
 import com.android.server.location.listeners.BinderListenerRegistration;
 import com.android.server.location.listeners.ListenerMultiplexer;
 import com.android.server.location.util.AppForegroundHelper;
-import com.android.server.location.util.AppOpsHelper;
 import com.android.server.location.util.Injector;
+import com.android.server.location.util.LocationPermissionsHelper;
 import com.android.server.location.util.SettingsHelper;
 import com.android.server.location.util.UserInfoHelper;
 import com.android.server.location.util.UserInfoHelper.UserListener;
@@ -65,7 +65,7 @@
         // we store these values because we don't trust the listeners not to give us dupes, not to
         // spam us, and because checking the values may be more expensive
         private boolean mForeground;
-        private boolean mAppOpsAllowed;
+        private boolean mPermitted;
 
         protected GnssListenerRegistration(@Nullable TRequest request,
                 CallerIdentity callerIdentity, TListener listener) {
@@ -84,24 +84,39 @@
             return mForeground;
         }
 
-        boolean isAppOpsAllowed() {
-            return mAppOpsAllowed;
+        boolean isPermitted() {
+            return mPermitted;
         }
 
         @Override
         protected void onBinderListenerRegister() {
-            mAppOpsAllowed = mAppOpsHelper.checkLocationAccess(getIdentity(), PERMISSION_FINE);
+            mPermitted = mLocationPermissionsHelper.hasLocationPermissions(PERMISSION_FINE,
+                    getIdentity());
             mForeground = mAppForegroundHelper.isAppForeground(getIdentity().getUid());
         }
 
-        boolean onAppOpsChanged(String packageName) {
+        boolean onLocationPermissionsChanged(String packageName) {
             if (getIdentity().getPackageName().equals(packageName)) {
-                boolean appOpsAllowed = mAppOpsHelper.checkLocationAccess(getIdentity(),
-                        PERMISSION_FINE);
-                if (appOpsAllowed != mAppOpsAllowed) {
-                    mAppOpsAllowed = appOpsAllowed;
-                    return true;
-                }
+                return onLocationPermissionsChanged();
+            }
+
+            return false;
+        }
+
+        boolean onLocationPermissionsChanged(int uid) {
+            if (getIdentity().getUid() == uid) {
+                return onLocationPermissionsChanged();
+            }
+
+            return false;
+        }
+
+        private boolean onLocationPermissionsChanged() {
+            boolean permitted = mLocationPermissionsHelper.hasLocationPermissions(PERMISSION_FINE,
+                    getIdentity());
+            if (permitted != mPermitted) {
+                mPermitted = permitted;
+                return true;
             }
 
             return false;
@@ -125,7 +140,7 @@
             if (!mForeground) {
                 flags.add("bg");
             }
-            if (!mAppOpsAllowed) {
+            if (!mPermitted) {
                 flags.add("na");
             }
             if (!flags.isEmpty()) {
@@ -141,7 +156,7 @@
 
     protected final UserInfoHelper mUserInfoHelper;
     protected final SettingsHelper mSettingsHelper;
-    protected final AppOpsHelper mAppOpsHelper;
+    protected final LocationPermissionsHelper mLocationPermissionsHelper;
     protected final AppForegroundHelper mAppForegroundHelper;
     protected final LocationManagerInternal mLocationManagerInternal;
 
@@ -154,14 +169,26 @@
     private final SettingsHelper.UserSettingChangedListener
             mLocationPackageBlacklistChangedListener =
             this::onLocationPackageBlacklistChanged;
-    private final AppOpsHelper.LocationAppOpListener mAppOpsChangedListener = this::onAppOpsChanged;
+    private final LocationPermissionsHelper.LocationPermissionsListener
+            mLocationPermissionsListener =
+            new LocationPermissionsHelper.LocationPermissionsListener() {
+                @Override
+                public void onLocationPermissionsChanged(String packageName) {
+                    GnssListenerMultiplexer.this.onLocationPermissionsChanged(packageName);
+                }
+
+                @Override
+                public void onLocationPermissionsChanged(int uid) {
+                    GnssListenerMultiplexer.this.onLocationPermissionsChanged(uid);
+                }
+            };
     private final AppForegroundHelper.AppForegroundListener mAppForegroundChangedListener =
             this::onAppForegroundChanged;
 
     protected GnssListenerMultiplexer(Injector injector) {
         mUserInfoHelper = injector.getUserInfoHelper();
         mSettingsHelper = injector.getSettingsHelper();
-        mAppOpsHelper = injector.getAppOpsHelper();
+        mLocationPermissionsHelper = injector.getLocationPermissionsHelper();
         mAppForegroundHelper = injector.getAppForegroundHelper();
         mLocationManagerInternal = Objects.requireNonNull(
                 LocalServices.getService(LocationManagerInternal.class));
@@ -208,7 +235,7 @@
         CallerIdentity identity = registration.getIdentity();
         // TODO: this should be checking if the gps provider is enabled, not if location is enabled,
         //  but this is the same for now.
-        return registration.isAppOpsAllowed()
+        return registration.isPermitted()
                 && (registration.isForeground() || isBackgroundRestrictionExempt(identity))
                 && mUserInfoHelper.isCurrentUserId(identity.getUserId())
                 && mSettingsHelper.isLocationEnabled(identity.getUserId())
@@ -241,7 +268,7 @@
                 mBackgroundThrottlePackageWhitelistChangedListener);
         mSettingsHelper.addOnLocationPackageBlacklistChangedListener(
                 mLocationPackageBlacklistChangedListener);
-        mAppOpsHelper.addListener(mAppOpsChangedListener);
+        mLocationPermissionsHelper.addListener(mLocationPermissionsListener);
         mAppForegroundHelper.addListener(mAppForegroundChangedListener);
     }
 
@@ -257,7 +284,7 @@
                 mBackgroundThrottlePackageWhitelistChangedListener);
         mSettingsHelper.removeOnLocationPackageBlacklistChangedListener(
                 mLocationPackageBlacklistChangedListener);
-        mAppOpsHelper.removeListener(mAppOpsChangedListener);
+        mLocationPermissionsHelper.removeListener(mLocationPermissionsListener);
         mAppForegroundHelper.removeListener(mAppForegroundChangedListener);
     }
 
@@ -279,8 +306,12 @@
         updateRegistrations(registration -> registration.getIdentity().getUserId() == userId);
     }
 
-    private void onAppOpsChanged(String packageName) {
-        updateRegistrations(registration -> registration.onAppOpsChanged(packageName));
+    private void onLocationPermissionsChanged(String packageName) {
+        updateRegistrations(registration -> registration.onLocationPermissionsChanged(packageName));
+    }
+
+    private void onLocationPermissionsChanged(int uid) {
+        updateRegistrations(registration -> registration.onLocationPermissionsChanged(uid));
     }
 
     private void onAppForegroundChanged(int uid, boolean foreground) {
diff --git a/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java b/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
index 8aaf4bf6..8004ec7 100644
--- a/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
@@ -2008,9 +2008,7 @@
     private final class FusedLocationListener extends LocationChangeListener {
         @Override
         public void onLocationChanged(Location location) {
-            if (LocationManager.FUSED_PROVIDER.equals(location.getProvider())) {
-                injectBestLocation(location);
-            }
+            injectBestLocation(location);
         }
     }
 
diff --git a/services/core/java/com/android/server/location/gnss/GnssManagerService.java b/services/core/java/com/android/server/location/gnss/GnssManagerService.java
index 58e725c..8e81f29 100644
--- a/services/core/java/com/android/server/location/gnss/GnssManagerService.java
+++ b/services/core/java/com/android/server/location/gnss/GnssManagerService.java
@@ -18,10 +18,9 @@
 
 import static android.location.LocationManager.GPS_PROVIDER;
 
-import static com.android.server.location.LocationPermissions.PERMISSION_FINE;
-
 import android.Manifest;
 import android.annotation.Nullable;
+import android.app.AppOpsManager;
 import android.content.Context;
 import android.location.GnssAntennaInfo;
 import android.location.GnssMeasurementCorrections;
@@ -47,10 +46,8 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.Preconditions;
 import com.android.server.LocalServices;
-import com.android.server.location.util.AppForegroundHelper;
 import com.android.server.location.util.AppOpsHelper;
 import com.android.server.location.util.Injector;
-import com.android.server.location.util.SettingsHelper;
 
 import java.io.FileDescriptor;
 import java.util.List;
@@ -68,9 +65,7 @@
     }
 
     private final Context mContext;
-    private final SettingsHelper mSettingsHelper;
     private final AppOpsHelper mAppOpsHelper;
-    private final AppForegroundHelper mAppForegroundHelper;
     private final LocationManagerInternal mLocationManagerInternal;
 
     private final GnssLocationProvider mGnssLocationProvider;
@@ -109,9 +104,7 @@
         GnssNative.initialize();
 
         mContext = context.createAttributionContext(ATTRIBUTION_ID);
-        mSettingsHelper = injector.getSettingsHelper();
         mAppOpsHelper = injector.getAppOpsHelper();
-        mAppForegroundHelper = injector.getAppForegroundHelper();
         mLocationManagerInternal = LocalServices.getService(LocationManagerInternal.class);
 
         if (gnssLocationProvider == null) {
@@ -192,9 +185,10 @@
     public boolean startGnssBatch(long periodNanos, boolean wakeOnFifoFull, String packageName,
             String attributionTag) {
         mContext.enforceCallingOrSelfPermission(Manifest.permission.LOCATION_HARDWARE, null);
+        mContext.enforceCallingOrSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION, null);
 
         CallerIdentity identity = CallerIdentity.fromBinder(mContext, packageName, attributionTag);
-        if (!mAppOpsHelper.checkLocationAccess(identity, PERMISSION_FINE)) {
+        if (!mAppOpsHelper.checkOpNoThrow(AppOpsManager.OP_FINE_LOCATION, identity)) {
             return false;
         }
 
diff --git a/services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java b/services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java
index 9227a17..0815d46 100644
--- a/services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java
+++ b/services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java
@@ -16,10 +16,10 @@
 
 package com.android.server.location.gnss;
 
-import static com.android.server.location.LocationPermissions.PERMISSION_FINE;
 import static com.android.server.location.gnss.GnssManagerService.D;
 import static com.android.server.location.gnss.GnssManagerService.TAG;
 
+import android.app.AppOpsManager;
 import android.location.GnssMeasurementsEvent;
 import android.location.GnssRequest;
 import android.location.IGnssMeasurementsListener;
@@ -30,6 +30,7 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.Preconditions;
+import com.android.server.location.util.AppOpsHelper;
 import com.android.server.location.util.Injector;
 import com.android.server.location.util.LocationUsageLogger;
 import com.android.server.location.util.SettingsHelper;
@@ -47,6 +48,7 @@
         GnssListenerMultiplexer<GnssRequest, IGnssMeasurementsListener, Boolean> implements
         SettingsHelper.GlobalSettingChangedListener {
 
+    private final AppOpsHelper mAppOpsHelper;
     private final LocationUsageLogger mLogger;
     private final GnssMeasurementProviderNative mNative;
 
@@ -57,6 +59,7 @@
     @VisibleForTesting
     public GnssMeasurementsProvider(Injector injector, GnssMeasurementProviderNative aNative) {
         super(injector);
+        mAppOpsHelper = injector.getAppOpsHelper();
         mLogger = injector.getLocationUsageLogger();
         mNative = aNative;
     }
@@ -163,7 +166,8 @@
      */
     public void onMeasurementsAvailable(GnssMeasurementsEvent event) {
         deliverToListeners(registration -> {
-            if (mAppOpsHelper.noteLocationAccess(registration.getIdentity(), PERMISSION_FINE)) {
+            if (mAppOpsHelper.noteOpNoThrow(AppOpsManager.OP_FINE_LOCATION,
+                    registration.getIdentity())) {
                 return listener -> listener.onGnssMeasurementsReceived(event);
             } else {
                 return null;
diff --git a/services/core/java/com/android/server/location/gnss/GnssNavigationMessageProvider.java b/services/core/java/com/android/server/location/gnss/GnssNavigationMessageProvider.java
index a07fbe4..7dcffc6 100644
--- a/services/core/java/com/android/server/location/gnss/GnssNavigationMessageProvider.java
+++ b/services/core/java/com/android/server/location/gnss/GnssNavigationMessageProvider.java
@@ -16,10 +16,10 @@
 
 package com.android.server.location.gnss;
 
-import static com.android.server.location.LocationPermissions.PERMISSION_FINE;
 import static com.android.server.location.gnss.GnssManagerService.D;
 import static com.android.server.location.gnss.GnssManagerService.TAG;
 
+import android.app.AppOpsManager;
 import android.location.GnssNavigationMessage;
 import android.location.IGnssNavigationMessageListener;
 import android.location.util.identity.CallerIdentity;
@@ -27,6 +27,7 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.Preconditions;
+import com.android.server.location.util.AppOpsHelper;
 import com.android.server.location.util.Injector;
 
 /**
@@ -39,6 +40,7 @@
 public class GnssNavigationMessageProvider extends
         GnssListenerMultiplexer<Void, IGnssNavigationMessageListener, Void> {
 
+    private final AppOpsHelper mAppOpsHelper;
     private final GnssNavigationMessageProviderNative mNative;
 
     public GnssNavigationMessageProvider(Injector injector) {
@@ -49,6 +51,7 @@
     public GnssNavigationMessageProvider(Injector injector,
             GnssNavigationMessageProviderNative aNative) {
         super(injector);
+        mAppOpsHelper = injector.getAppOpsHelper();
         mNative = aNative;
     }
 
@@ -90,7 +93,8 @@
      */
     public void onNavigationMessageAvailable(GnssNavigationMessage event) {
         deliverToListeners(registration -> {
-            if (mAppOpsHelper.noteLocationAccess(registration.getIdentity(), PERMISSION_FINE)) {
+            if (mAppOpsHelper.noteOpNoThrow(AppOpsManager.OP_FINE_LOCATION,
+                    registration.getIdentity())) {
                 return listener -> listener.onGnssNavigationMessageReceived(event);
             } else {
                 return null;
diff --git a/services/core/java/com/android/server/location/gnss/GnssStatusProvider.java b/services/core/java/com/android/server/location/gnss/GnssStatusProvider.java
index d33b058..19f7927 100644
--- a/services/core/java/com/android/server/location/gnss/GnssStatusProvider.java
+++ b/services/core/java/com/android/server/location/gnss/GnssStatusProvider.java
@@ -16,10 +16,10 @@
 
 package com.android.server.location.gnss;
 
-import static com.android.server.location.LocationPermissions.PERMISSION_FINE;
 import static com.android.server.location.gnss.GnssManagerService.D;
 import static com.android.server.location.gnss.GnssManagerService.TAG;
 
+import android.app.AppOpsManager;
 import android.location.GnssStatus;
 import android.location.IGnssStatusListener;
 import android.location.util.identity.CallerIdentity;
@@ -27,6 +27,7 @@
 import android.stats.location.LocationStatsEnums;
 import android.util.Log;
 
+import com.android.server.location.util.AppOpsHelper;
 import com.android.server.location.util.Injector;
 import com.android.server.location.util.LocationUsageLogger;
 
@@ -35,10 +36,12 @@
  */
 public class GnssStatusProvider extends GnssListenerMultiplexer<Void, IGnssStatusListener, Void> {
 
+    private final AppOpsHelper mAppOpsHelper;
     private final LocationUsageLogger mLogger;
 
     public GnssStatusProvider(Injector injector) {
         super(injector);
+        mAppOpsHelper = injector.getAppOpsHelper();
         mLogger = injector.getLocationUsageLogger();
     }
 
@@ -113,7 +116,8 @@
      */
     public void onSvStatusChanged(GnssStatus gnssStatus) {
         deliverToListeners(registration -> {
-            if (mAppOpsHelper.noteLocationAccess(registration.getIdentity(), PERMISSION_FINE)) {
+            if (mAppOpsHelper.noteOpNoThrow(AppOpsManager.OP_FINE_LOCATION,
+                    registration.getIdentity())) {
                 return listener -> listener.onSvStatusChanged(gnssStatus);
             } else {
                 return null;
@@ -126,7 +130,8 @@
      */
     public void onNmeaReceived(long timestamp, String nmea) {
         deliverToListeners(registration -> {
-            if (mAppOpsHelper.noteLocationAccess(registration.getIdentity(), PERMISSION_FINE)) {
+            if (mAppOpsHelper.noteOpNoThrow(AppOpsManager.OP_FINE_LOCATION,
+                    registration.getIdentity())) {
                 return listener -> listener.onNmeaReceived(timestamp, nmea);
             } else {
                 return null;
diff --git a/services/core/java/com/android/server/location/listeners/ListenerMultiplexer.java b/services/core/java/com/android/server/location/listeners/ListenerMultiplexer.java
index f5889ce..528cf8a 100644
--- a/services/core/java/com/android/server/location/listeners/ListenerMultiplexer.java
+++ b/services/core/java/com/android/server/location/listeners/ListenerMultiplexer.java
@@ -21,6 +21,7 @@
 import android.os.Binder;
 import android.os.Build;
 import android.util.ArrayMap;
+import android.util.ArraySet;
 import android.util.IndentingPrintWriter;
 import android.util.Pair;
 
@@ -40,8 +41,8 @@
  * A base class to multiplex client listener registrations within system server. Registrations are
  * divided into two categories, active registrations and inactive registrations, as defined by
  * {@link #isActive(ListenerRegistration)}. If a registration's active state changes,
- * {@link #updateRegistrations(Predicate)} or {@link #updateRegistration(Object, Predicate)} must be
- * invoked and return true for any registration whose active state may have changed.
+ * {@link #updateRegistrations(Predicate)} must be invoked and return true for any registration
+ * whose active state may have changed.
  *
  * Callbacks invoked for various changes will always be ordered according to this lifecycle list:
  *
@@ -217,7 +218,6 @@
                     mRegistrations.put(key, registration);
                 }
 
-
                 if (wasEmpty) {
                     onRegister();
                 }
@@ -268,7 +268,8 @@
             try (UpdateServiceBuffer ignored1 = mUpdateServiceBuffer.acquire();
                  ReentrancyGuard ignored2 = mReentrancyGuard.acquire()) {
 
-                for (int i = 0; i < mRegistrations.size(); i++) {
+                final int size = mRegistrations.size();
+                for (int i = 0; i < size; i++) {
                     TKey key = mRegistrations.keyAt(i);
                     if (predicate.test(key)) {
                         removeRegistration(key, mRegistrations.valueAt(i));
@@ -287,7 +288,7 @@
      * completely at some later time.
      */
     protected final void removeRegistration(@NonNull Object key,
-            @NonNull ListenerRegistration registration) {
+            @NonNull ListenerRegistration<?, ?> registration) {
         synchronized (mRegistrations) {
             int index = mRegistrations.indexOfKey(key);
             if (index < 0) {
@@ -353,7 +354,8 @@
             }
 
             ArrayList<TRegistration> actives = new ArrayList<>(mRegistrations.size());
-            for (int i = 0; i < mRegistrations.size(); i++) {
+            final int size = mRegistrations.size();
+            for (int i = 0; i < size; i++) {
                 TRegistration registration = mRegistrations.valueAt(i);
                 if (registration.isActive()) {
                     actives.add(registration);
@@ -395,7 +397,8 @@
     protected final void updateService(Predicate<TRegistration> predicate) {
         synchronized (mRegistrations) {
             boolean updateService = false;
-            for (int i = 0; i < mRegistrations.size(); i++) {
+            final int size = mRegistrations.size();
+            for (int i = 0; i < size; i++) {
                 TRegistration registration = mRegistrations.valueAt(i);
                 if (predicate.test(registration) && registration.isActive()) {
                     updateService = true;
@@ -434,7 +437,8 @@
             try (UpdateServiceBuffer ignored1 = mUpdateServiceBuffer.acquire();
                  ReentrancyGuard ignored2 = mReentrancyGuard.acquire()) {
 
-                for (int i = 0; i < mRegistrations.size(); i++) {
+                final int size = mRegistrations.size();
+                for (int i = 0; i < size; i++) {
                     TRegistration registration = mRegistrations.valueAt(i);
                     if (predicate.test(registration)) {
                         onRegistrationActiveChanged(registration);
@@ -446,33 +450,6 @@
         }
     }
 
-    /**
-     * Evaluates the predicate on the registration with the given key. The predicate should return
-     * true if the active state of the registration may have changed as a result. Any
-     * {@link #updateService()} invocations made while this method is executing will be deferred
-     * until after the method is complete so as to avoid redundant work.
-     */
-    protected final void updateRegistration(TKey key, @NonNull Predicate<TRegistration> predicate) {
-        synchronized (mRegistrations) {
-            // since updating a registration can invoke a variety of callbacks, we need to ensure
-            // those callbacks themselves do not re-enter, as this could lead to out-of-order
-            // callbacks. note that try-with-resources ordering is meaningful here as well. we want
-            // to close the reentrancy guard first, as this may generate additional service updates,
-            // then close the update service buffer.
-            long identity = Binder.clearCallingIdentity();
-            try (UpdateServiceBuffer ignored1 = mUpdateServiceBuffer.acquire();
-                 ReentrancyGuard ignored2 = mReentrancyGuard.acquire()) {
-
-                TRegistration registration = mRegistrations.get(key);
-                if (registration != null && predicate.test(registration)) {
-                    onRegistrationActiveChanged(registration);
-                }
-            } finally {
-                Binder.restoreCallingIdentity(identity);
-            }
-        }
-    }
-
     @GuardedBy("mRegistrations")
     private void onRegistrationActiveChanged(TRegistration registration) {
         if (Build.IS_DEBUGGABLE) {
@@ -511,7 +488,8 @@
         synchronized (mRegistrations) {
             long identity = Binder.clearCallingIdentity();
             try (ReentrancyGuard ignored = mReentrancyGuard.acquire()) {
-                for (int i = 0; i < mRegistrations.size(); i++) {
+                final int size = mRegistrations.size();
+                for (int i = 0; i < size; i++) {
                     TRegistration registration = mRegistrations.valueAt(i);
                     if (registration.isActive()) {
                         ListenerOperation<TListener> operation = function.apply(registration);
@@ -537,7 +515,8 @@
         synchronized (mRegistrations) {
             long identity = Binder.clearCallingIdentity();
             try (ReentrancyGuard ignored = mReentrancyGuard.acquire()) {
-                for (int i = 0; i < mRegistrations.size(); i++) {
+                final int size = mRegistrations.size();
+                for (int i = 0; i < size; i++) {
                     TRegistration registration = mRegistrations.valueAt(i);
                     if (registration.isActive()) {
                         execute(registration, operation);
@@ -571,7 +550,8 @@
                 ipw.println("listeners:");
 
                 ipw.increaseIndent();
-                for (int i = 0; i < mRegistrations.size(); i++) {
+                final int size = mRegistrations.size();
+                for (int i = 0; i < size; i++) {
                     TRegistration registration = mRegistrations.valueAt(i);
                     ipw.print(registration);
                     if (!registration.isActive()) {
@@ -612,23 +592,33 @@
      */
     private final class ReentrancyGuard implements AutoCloseable {
 
+        @GuardedBy("mRegistrations")
         private int mGuardCount;
-        private @Nullable ArrayList<Pair<Object, ListenerRegistration>> mScheduledRemovals;
+        @GuardedBy("mRegistrations")
+        private @Nullable ArraySet<Pair<Object, ListenerRegistration<?, ?>>> mScheduledRemovals;
 
         ReentrancyGuard() {
             mGuardCount = 0;
             mScheduledRemovals = null;
         }
 
+        @GuardedBy("mRegistrations")
         boolean isReentrant() {
+            if (Build.IS_DEBUGGABLE) {
+                Preconditions.checkState(Thread.holdsLock(mRegistrations));
+            }
             return mGuardCount != 0;
         }
 
-        void markForRemoval(Object key, ListenerRegistration registration) {
+        @GuardedBy("mRegistrations")
+        void markForRemoval(Object key, ListenerRegistration<?, ?> registration) {
+            if (Build.IS_DEBUGGABLE) {
+                Preconditions.checkState(Thread.holdsLock(mRegistrations));
+            }
             Preconditions.checkState(isReentrant());
 
             if (mScheduledRemovals == null) {
-                mScheduledRemovals = new ArrayList<>(mRegistrations.size());
+                mScheduledRemovals = new ArraySet<>(mRegistrations.size());
             }
             mScheduledRemovals.add(new Pair<>(key, registration));
         }
@@ -640,7 +630,7 @@
 
         @Override
         public void close() {
-            ArrayList<Pair<Object, ListenerRegistration>> scheduledRemovals = null;
+            ArraySet<Pair<Object, ListenerRegistration<?, ?>>> scheduledRemovals = null;
 
             Preconditions.checkState(mGuardCount > 0);
             if (--mGuardCount == 0) {
@@ -650,8 +640,10 @@
 
             if (scheduledRemovals != null) {
                 try (UpdateServiceBuffer ignored = mUpdateServiceBuffer.acquire()) {
-                    for (int i = 0; i < scheduledRemovals.size(); i++) {
-                        Pair<Object, ListenerRegistration> pair = scheduledRemovals.get(i);
+                    final int size = scheduledRemovals.size();
+                    for (int i = 0; i < size; i++) {
+                        Pair<Object, ListenerRegistration<?, ?>> pair = scheduledRemovals.valueAt(
+                                i);
                         removeRegistration(pair.first, pair.second);
                     }
                 }
diff --git a/services/core/java/com/android/server/location/listeners/RemovableListenerRegistration.java b/services/core/java/com/android/server/location/listeners/RemovableListenerRegistration.java
index e529a7d..6a815ea 100644
--- a/services/core/java/com/android/server/location/listeners/RemovableListenerRegistration.java
+++ b/services/core/java/com/android/server/location/listeners/RemovableListenerRegistration.java
@@ -61,7 +61,7 @@
      * Removes this registration. Does nothing if invoked before {@link #onRegister(Object)} or
      * after {@link #onUnregister()}. It is safe to invoke this from within either function.
      */
-    public void remove() {
+    public final void remove() {
         Object key = mKey;
         if (key != null) {
             getOwner().removeRegistration(key, this);
diff --git a/services/core/java/com/android/server/location/util/AppOpsHelper.java b/services/core/java/com/android/server/location/util/AppOpsHelper.java
index 3e42f27..1578289 100644
--- a/services/core/java/com/android/server/location/util/AppOpsHelper.java
+++ b/services/core/java/com/android/server/location/util/AppOpsHelper.java
@@ -16,15 +16,8 @@
 
 package com.android.server.location.util;
 
-import static android.app.AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION;
-import static android.app.AppOpsManager.OP_MONITOR_LOCATION;
-
-import android.app.AppOpsManager;
 import android.location.util.identity.CallerIdentity;
 
-import com.android.server.location.LocationPermissions;
-import com.android.server.location.LocationPermissions.PermissionLevel;
-
 import java.util.concurrent.CopyOnWriteArrayList;
 
 /**
@@ -70,84 +63,27 @@
     }
 
     /**
-     * Checks if the given identity may have locations delivered without noting that a location is
-     * being delivered. This is a looser guarantee than
-     * {@link #noteLocationAccess(CallerIdentity, int)}, and this function does not validate package
-     * arguments and so should not be used with unvalidated arguments or before actually delivering
-     * locations.
-     *
-     * @see AppOpsManager#checkOpNoThrow(int, int, String)
+     * Starts the given appop.
      */
-    public final boolean checkLocationAccess(CallerIdentity callerIdentity,
-            @PermissionLevel int permissionLevel) {
-        if (permissionLevel == LocationPermissions.PERMISSION_NONE) {
-            return false;
-        }
-
-        return checkOpNoThrow(LocationPermissions.asAppOp(permissionLevel), callerIdentity);
-    }
+    public abstract boolean startOpNoThrow(int appOp, CallerIdentity callerIdentity);
 
     /**
-     * Notes location access to the given identity, ie, location delivery. This method should be
-     * called right before a location is delivered, and if it returns false, the location should not
-     * be delivered.
+     * Finishes the given appop.
      */
-    public final boolean noteLocationAccess(CallerIdentity identity,
-            @PermissionLevel int permissionLevel) {
-        if (permissionLevel == LocationPermissions.PERMISSION_NONE) {
-            return false;
-        }
-
-        return noteOpNoThrow(LocationPermissions.asAppOp(permissionLevel), identity);
-    }
+    public abstract void finishOp(int appOp, CallerIdentity callerIdentity);
 
     /**
-     * Notifies app ops that the given identity is using location at normal/low power levels. If
-     * this function returns false, do not later call
-     * {@link #stopLocationMonitoring(CallerIdentity)}.
+     * Checks the given appop.
      */
-    public final boolean startLocationMonitoring(CallerIdentity identity) {
-        return startOpNoThrow(OP_MONITOR_LOCATION, identity);
-    }
+    public abstract boolean checkOpNoThrow(int appOp, CallerIdentity callerIdentity);
 
     /**
-     * Notifies app ops that the given identity is no longer using location at normal/low power
-     * levels.
+     * Notes the given appop (and may throw a security exception).
      */
-    public final void stopLocationMonitoring(CallerIdentity identity) {
-        finishOp(OP_MONITOR_LOCATION, identity);
-    }
+    public abstract boolean noteOp(int appOp, CallerIdentity callerIdentity);
 
     /**
-     * Notifies app ops that the given identity is using location at high levels. If this function
-     * returns false, do not later call {@link #stopLocationMonitoring(CallerIdentity)}.
+     * Notes the given appop.
      */
-    public final boolean startHighPowerLocationMonitoring(CallerIdentity identity) {
-        return startOpNoThrow(OP_MONITOR_HIGH_POWER_LOCATION, identity);
-    }
-
-    /**
-     * Notifies app ops that the given identity is no longer using location at high power levels.
-     */
-    public final void stopHighPowerLocationMonitoring(CallerIdentity identity) {
-        finishOp(OP_MONITOR_HIGH_POWER_LOCATION, identity);
-    }
-
-    /**
-     * Notes access to any mock location APIs. If this call returns false, access to the APIs should
-     * silently fail.
-     */
-    public final boolean noteMockLocationAccess(CallerIdentity callerIdentity) {
-        return noteOp(AppOpsManager.OP_MOCK_LOCATION, callerIdentity);
-    }
-
-    protected abstract boolean startOpNoThrow(int appOp, CallerIdentity callerIdentity);
-
-    protected abstract void finishOp(int appOp, CallerIdentity callerIdentity);
-
-    protected abstract boolean checkOpNoThrow(int appOp, CallerIdentity callerIdentity);
-
-    protected abstract boolean noteOp(int appOp, CallerIdentity callerIdentity);
-
-    protected abstract boolean noteOpNoThrow(int appOp, CallerIdentity callerIdentity);
+    public abstract boolean noteOpNoThrow(int appOp, CallerIdentity callerIdentity);
 }
diff --git a/services/core/java/com/android/server/location/util/Injector.java b/services/core/java/com/android/server/location/util/Injector.java
index e16df5d..379b303 100644
--- a/services/core/java/com/android/server/location/util/Injector.java
+++ b/services/core/java/com/android/server/location/util/Injector.java
@@ -17,6 +17,7 @@
 package com.android.server.location.util;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.location.LocationRequestStatistics;
 
 /**
  * Injects various location dependencies so that they may be controlled by tests.
@@ -30,15 +31,27 @@
     /** Returns an AppOpsHelper. */
     AppOpsHelper getAppOpsHelper();
 
+    /** Returns a LocationPermissionsHelper. */
+    LocationPermissionsHelper getLocationPermissionsHelper();
+
     /** Returns a SettingsHelper. */
     SettingsHelper getSettingsHelper();
 
     /** Returns an AppForegroundHelper. */
     AppForegroundHelper getAppForegroundHelper();
 
-    /** Returns a LocationUsageLogger. */
-    LocationUsageLogger getLocationUsageLogger();
+    /** Returns a LocationPowerSaveModeHelper. */
+    LocationPowerSaveModeHelper getLocationPowerSaveModeHelper();
+
+    /** Returns a ScreenInteractiveHelper. */
+    ScreenInteractiveHelper getScreenInteractiveHelper();
 
     /** Returns a LocationAttributionHelper. */
     LocationAttributionHelper getLocationAttributionHelper();
+
+    /** Returns a LocationUsageLogger. */
+    LocationUsageLogger getLocationUsageLogger();
+
+    /** Returns a LocationRequestStatistics. */
+    LocationRequestStatistics getLocationRequestStatistics();
 }
diff --git a/services/core/java/com/android/server/location/util/LocationAttributionHelper.java b/services/core/java/com/android/server/location/util/LocationAttributionHelper.java
index 8fe0941..bc3ac0f 100644
--- a/services/core/java/com/android/server/location/util/LocationAttributionHelper.java
+++ b/services/core/java/com/android/server/location/util/LocationAttributionHelper.java
@@ -16,9 +16,16 @@
 
 package com.android.server.location.util;
 
+import static android.app.AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION;
+import static android.app.AppOpsManager.OP_MONITOR_LOCATION;
+
+import static com.android.server.location.LocationManagerService.D;
+import static com.android.server.location.LocationManagerService.TAG;
+
 import android.location.util.identity.CallerIdentity;
 import android.util.ArrayMap;
 import android.util.ArraySet;
+import android.util.Log;
 
 import com.android.internal.annotations.GuardedBy;
 
@@ -83,7 +90,7 @@
                 i -> new ArraySet<>());
         boolean empty = keySet.isEmpty();
         if (keySet.add(new ProviderListener(provider, key)) && empty) {
-            if (!mAppOpsHelper.startLocationMonitoring(identity)) {
+            if (!mAppOpsHelper.startOpNoThrow(OP_MONITOR_LOCATION, identity)) {
                 mAttributions.remove(identity);
             }
         }
@@ -99,7 +106,7 @@
         if (keySet != null && keySet.remove(new ProviderListener(provider, key))
                 && keySet.isEmpty()) {
             mAttributions.remove(identity);
-            mAppOpsHelper.stopLocationMonitoring(identity);
+            mAppOpsHelper.finishOp(OP_MONITOR_LOCATION, identity);
         }
     }
 
@@ -113,14 +120,18 @@
                 i -> new ArraySet<>());
         boolean empty = keySet.isEmpty();
         if (keySet.add(new ProviderListener(provider, key)) && empty) {
-            if (!mAppOpsHelper.startHighPowerLocationMonitoring(identity)) {
+            if (mAppOpsHelper.startOpNoThrow(OP_MONITOR_HIGH_POWER_LOCATION, identity)) {
+                if (D) {
+                    Log.v(TAG, "starting high power location attribution for " + identity);
+                }
+            } else {
                 mHighPowerAttributions.remove(identity);
             }
         }
     }
 
     /**
-     * Report high power  location usage has stopped for the given caller on the given provider,
+     * Report high power location usage has stopped for the given caller on the given provider,
      * with a unique key.
      */
     public synchronized void reportHighPowerLocationStop(CallerIdentity identity, String provider,
@@ -128,8 +139,11 @@
         Set<ProviderListener> keySet = mHighPowerAttributions.get(identity);
         if (keySet != null && keySet.remove(new ProviderListener(provider, key))
                 && keySet.isEmpty()) {
+            if (D) {
+                Log.v(TAG, "stopping high power location attribution for " + identity);
+            }
             mHighPowerAttributions.remove(identity);
-            mAppOpsHelper.stopHighPowerLocationMonitoring(identity);
+            mAppOpsHelper.finishOp(OP_MONITOR_HIGH_POWER_LOCATION, identity);
         }
     }
 }
diff --git a/services/core/java/com/android/server/location/util/LocationPermissionsHelper.java b/services/core/java/com/android/server/location/util/LocationPermissionsHelper.java
new file mode 100644
index 0000000..daf5679
--- /dev/null
+++ b/services/core/java/com/android/server/location/util/LocationPermissionsHelper.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.location.util;
+
+import static com.android.server.location.LocationPermissions.PERMISSION_NONE;
+
+import android.location.util.identity.CallerIdentity;
+
+import com.android.server.location.LocationPermissions;
+import com.android.server.location.LocationPermissions.PermissionLevel;
+
+import java.util.concurrent.CopyOnWriteArrayList;
+
+/**
+ * Provides helpers and listeners for appops.
+ */
+public abstract class LocationPermissionsHelper {
+
+    /**
+     * Listener for current user changes.
+     */
+    public interface LocationPermissionsListener {
+
+        /**
+         * Called when something has changed about location permissions for the given package.
+         */
+        void onLocationPermissionsChanged(String packageName);
+
+        /**
+         * Called when something has changed about location permissions for the given uid.
+         */
+        void onLocationPermissionsChanged(int uid);
+    }
+
+    private final CopyOnWriteArrayList<LocationPermissionsListener> mListeners;
+    private final AppOpsHelper mAppOps;
+
+    public LocationPermissionsHelper(AppOpsHelper appOps) {
+        mListeners = new CopyOnWriteArrayList<>();
+        mAppOps = appOps;
+
+        mAppOps.addListener(this::onAppOpsChanged);
+    }
+
+    protected final void notifyLocationPermissionsChanged(String packageName) {
+        for (LocationPermissionsListener listener : mListeners) {
+            listener.onLocationPermissionsChanged(packageName);
+        }
+    }
+
+    protected final void notifyLocationPermissionsChanged(int uid) {
+        for (LocationPermissionsListener listener : mListeners) {
+            listener.onLocationPermissionsChanged(uid);
+        }
+    }
+
+    private void onAppOpsChanged(String packageName) {
+        notifyLocationPermissionsChanged(packageName);
+    }
+
+    /**
+     * Adds a listener for location permissions events. Callbacks occur on an unspecified thread.
+     */
+    public final void addListener(LocationPermissionsListener listener) {
+        mListeners.add(listener);
+    }
+
+    /**
+     * Removes a listener for location permissions events.
+     */
+    public final void removeListener(LocationPermissionsListener listener) {
+        mListeners.remove(listener);
+    }
+
+    /**
+     * Returns true if the given identity may access location at the given permissions level, taking
+     * into account both permissions and appops.
+     */
+    public final boolean hasLocationPermissions(@PermissionLevel int permissionLevel,
+            CallerIdentity identity) {
+        if (permissionLevel == PERMISSION_NONE) {
+            return false;
+        }
+
+        if (!hasPermission(LocationPermissions.asPermission(permissionLevel), identity)) {
+            return false;
+        }
+
+        return mAppOps.checkOpNoThrow(permissionLevel, identity);
+    }
+
+    protected abstract boolean hasPermission(String permission, CallerIdentity callerIdentity);
+}
diff --git a/services/core/java/com/android/server/location/util/LocationPowerSaveModeHelper.java b/services/core/java/com/android/server/location/util/LocationPowerSaveModeHelper.java
new file mode 100644
index 0000000..a9a8c50
--- /dev/null
+++ b/services/core/java/com/android/server/location/util/LocationPowerSaveModeHelper.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.location.util;
+
+import android.os.PowerManager.LocationPowerSaveMode;
+
+import java.util.concurrent.CopyOnWriteArrayList;
+
+/**
+ * Provides accessors and listeners for location power save mode.
+ */
+public abstract class LocationPowerSaveModeHelper {
+
+    /**
+     * Listener for location power save mode changes.
+     */
+    public interface LocationPowerSaveModeChangedListener {
+        /**
+         * Called when the location power save mode changes.
+         */
+        void onLocationPowerSaveModeChanged(@LocationPowerSaveMode int locationPowerSaveMode);
+    }
+
+    private final CopyOnWriteArrayList<LocationPowerSaveModeChangedListener> mListeners;
+
+    public LocationPowerSaveModeHelper() {
+        mListeners = new CopyOnWriteArrayList<>();
+    }
+
+    /**
+     * Add a listener for changes to location power save mode. Callbacks occur on an unspecified
+     * thread.
+     */
+    public final void addListener(LocationPowerSaveModeChangedListener listener) {
+        mListeners.add(listener);
+    }
+
+    /**
+     * Removes a listener for changes to location power save mode.
+     */
+    public final void removeListener(LocationPowerSaveModeChangedListener listener) {
+        mListeners.remove(listener);
+    }
+
+    protected final void notifyLocationPowerSaveModeChanged(
+            @LocationPowerSaveMode int locationPowerSaveMode) {
+        for (LocationPowerSaveModeChangedListener listener : mListeners) {
+            listener.onLocationPowerSaveModeChanged(locationPowerSaveMode);
+        }
+    }
+
+    /**
+     * Returns the current location power save mode.
+     */
+    @LocationPowerSaveMode
+    public abstract int getLocationPowerSaveMode();
+}
diff --git a/services/core/java/com/android/server/location/util/ScreenInteractiveHelper.java b/services/core/java/com/android/server/location/util/ScreenInteractiveHelper.java
new file mode 100644
index 0000000..d47bce3
--- /dev/null
+++ b/services/core/java/com/android/server/location/util/ScreenInteractiveHelper.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.location.util;
+
+import java.util.concurrent.CopyOnWriteArrayList;
+
+/**
+ * Provides accessors and listeners for screen interactive state (screen on/off).
+ */
+public abstract class ScreenInteractiveHelper {
+
+    /**
+     * Listener for screen interactive changes.
+     */
+    public interface ScreenInteractiveChangedListener {
+        /**
+         * Called when the screen interative state changes.
+         */
+        void onScreenInteractiveChanged(boolean isInteractive);
+    }
+
+    private final CopyOnWriteArrayList<ScreenInteractiveChangedListener> mListeners;
+
+    public ScreenInteractiveHelper() {
+        mListeners = new CopyOnWriteArrayList<>();
+    }
+
+    /**
+     * Add a listener for changes to screen interactive state. Callbacks occur on an unspecified
+     * thread.
+     */
+    public final void addListener(ScreenInteractiveChangedListener listener) {
+        mListeners.add(listener);
+    }
+
+    /**
+     * Removes a listener for changes to screen interactive state.
+     */
+    public final void removeListener(ScreenInteractiveChangedListener listener) {
+        mListeners.remove(listener);
+    }
+
+    protected final void notifyScreenInteractiveChanged(boolean interactive) {
+        for (ScreenInteractiveChangedListener listener : mListeners) {
+            listener.onScreenInteractiveChanged(interactive);
+        }
+    }
+
+    /**
+     * Returns true if the screen is currently interactive, and false otherwise.
+     */
+    public abstract boolean isInteractive();
+}
diff --git a/services/core/java/com/android/server/location/util/SystemAppOpsHelper.java b/services/core/java/com/android/server/location/util/SystemAppOpsHelper.java
index a953836..cfb7697 100644
--- a/services/core/java/com/android/server/location/util/SystemAppOpsHelper.java
+++ b/services/core/java/com/android/server/location/util/SystemAppOpsHelper.java
@@ -57,7 +57,7 @@
     }
 
     @Override
-    protected boolean startOpNoThrow(int appOp, CallerIdentity callerIdentity) {
+    public boolean startOpNoThrow(int appOp, CallerIdentity callerIdentity) {
         Preconditions.checkState(mAppOps != null);
 
         long identity = Binder.clearCallingIdentity();
@@ -75,7 +75,7 @@
     }
 
     @Override
-    protected void finishOp(int appOp, CallerIdentity callerIdentity) {
+    public void finishOp(int appOp, CallerIdentity callerIdentity) {
         Preconditions.checkState(mAppOps != null);
 
         long identity = Binder.clearCallingIdentity();
@@ -91,7 +91,7 @@
     }
 
     @Override
-    protected boolean checkOpNoThrow(int appOp, CallerIdentity callerIdentity) {
+    public boolean checkOpNoThrow(int appOp, CallerIdentity callerIdentity) {
         Preconditions.checkState(mAppOps != null);
 
         long identity = Binder.clearCallingIdentity();
@@ -106,7 +106,7 @@
     }
 
     @Override
-    protected boolean noteOp(int appOp, CallerIdentity callerIdentity) {
+    public boolean noteOp(int appOp, CallerIdentity callerIdentity) {
         Preconditions.checkState(mAppOps != null);
 
         long identity = Binder.clearCallingIdentity();
@@ -123,7 +123,7 @@
     }
 
     @Override
-    protected boolean noteOpNoThrow(int appOp, CallerIdentity callerIdentity) {
+    public boolean noteOpNoThrow(int appOp, CallerIdentity callerIdentity) {
         Preconditions.checkState(mAppOps != null);
 
         long identity = Binder.clearCallingIdentity();
diff --git a/services/core/java/com/android/server/location/util/SystemLocationPermissionsHelper.java b/services/core/java/com/android/server/location/util/SystemLocationPermissionsHelper.java
new file mode 100644
index 0000000..b9c0dde
--- /dev/null
+++ b/services/core/java/com/android/server/location/util/SystemLocationPermissionsHelper.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.location.util;
+
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+
+import android.content.Context;
+import android.location.util.identity.CallerIdentity;
+import android.os.Binder;
+
+import com.android.server.FgThread;
+
+/**
+ * Provides accessors and listeners for location permissions, including appops.
+ */
+public class SystemLocationPermissionsHelper extends LocationPermissionsHelper {
+
+    private final Context mContext;
+
+    private boolean mInited;
+
+    public SystemLocationPermissionsHelper(Context context, AppOpsHelper appOps) {
+        super(appOps);
+        mContext = context;
+    }
+
+    /** Called when system is ready. */
+    public void onSystemReady() {
+        if (mInited) {
+            return;
+        }
+
+        mContext.getPackageManager().addOnPermissionsChangeListener(
+                uid -> {
+                    // invoked on ui thread, move to fg thread so ui thread isn't blocked
+                    FgThread.getHandler().post(() -> notifyLocationPermissionsChanged(uid));
+                });
+        mInited = true;
+    }
+
+    @Override
+    protected boolean hasPermission(String permission, CallerIdentity callerIdentity) {
+        long identity = Binder.clearCallingIdentity();
+        try {
+            return mContext.checkPermission(permission, callerIdentity.getPid(),
+                    callerIdentity.getUid()) == PERMISSION_GRANTED;
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/location/util/SystemLocationPowerSaveModeHelper.java b/services/core/java/com/android/server/location/util/SystemLocationPowerSaveModeHelper.java
new file mode 100644
index 0000000..c8d8202
--- /dev/null
+++ b/services/core/java/com/android/server/location/util/SystemLocationPowerSaveModeHelper.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.location.util;
+
+import android.content.Context;
+import android.os.PowerManager;
+import android.os.PowerManager.LocationPowerSaveMode;
+import android.os.PowerManagerInternal;
+import android.os.PowerSaveState;
+
+import com.android.internal.util.Preconditions;
+import com.android.server.FgThread;
+import com.android.server.LocalServices;
+
+import java.util.Objects;
+import java.util.function.Consumer;
+
+/**
+ * Provides accessors and listeners for location power save mode.
+ */
+public class SystemLocationPowerSaveModeHelper extends LocationPowerSaveModeHelper implements
+        Consumer<PowerSaveState> {
+
+    private final Context mContext;
+    private boolean mReady;
+
+    @LocationPowerSaveMode
+    private volatile int mLocationPowerSaveMode;
+
+    public SystemLocationPowerSaveModeHelper(Context context) {
+        mContext = context;
+    }
+
+    /** Called when system is ready. */
+    public void onSystemReady() {
+        if (mReady) {
+            return;
+        }
+
+        LocalServices.getService(PowerManagerInternal.class).registerLowPowerModeObserver(
+                PowerManager.ServiceType.LOCATION, this);
+        mLocationPowerSaveMode = Objects.requireNonNull(
+                mContext.getSystemService(PowerManager.class)).getLocationPowerSaveMode();
+
+        mReady = true;
+    }
+
+    @Override
+    public void accept(PowerSaveState powerSaveState) {
+        int locationPowerSaveMode;
+        if (!powerSaveState.batterySaverEnabled) {
+            locationPowerSaveMode = PowerManager.LOCATION_MODE_NO_CHANGE;
+        } else {
+            locationPowerSaveMode = powerSaveState.locationMode;
+        }
+
+        if (locationPowerSaveMode == mLocationPowerSaveMode) {
+            return;
+        }
+
+        mLocationPowerSaveMode = locationPowerSaveMode;
+
+        // invoked on ui thread, move to fg thread so we don't block the ui thread
+        FgThread.getHandler().post(() -> notifyLocationPowerSaveModeChanged(locationPowerSaveMode));
+    }
+
+    @LocationPowerSaveMode
+    @Override
+    public int getLocationPowerSaveMode() {
+        Preconditions.checkState(mReady);
+        return mLocationPowerSaveMode;
+    }
+}
diff --git a/services/core/java/com/android/server/location/util/SystemScreenInteractiveHelper.java b/services/core/java/com/android/server/location/util/SystemScreenInteractiveHelper.java
new file mode 100644
index 0000000..70cedac
--- /dev/null
+++ b/services/core/java/com/android/server/location/util/SystemScreenInteractiveHelper.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.location.util;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.UserHandle;
+
+import com.android.internal.util.Preconditions;
+import com.android.server.FgThread;
+
+/**
+ * Provides accessors and listeners for screen interactive state (screen on/off).
+ */
+public class SystemScreenInteractiveHelper extends ScreenInteractiveHelper {
+
+    private final Context mContext;
+
+    private boolean mReady;
+
+    private volatile boolean mIsInteractive;
+
+    public SystemScreenInteractiveHelper(Context context) {
+        mContext = context;
+    }
+
+    /** Called when system is ready. */
+    public void onSystemReady() {
+        if (mReady) {
+            return;
+        }
+
+        IntentFilter screenIntentFilter = new IntentFilter();
+        screenIntentFilter.addAction(Intent.ACTION_SCREEN_OFF);
+        screenIntentFilter.addAction(Intent.ACTION_SCREEN_ON);
+        mContext.registerReceiverAsUser(new BroadcastReceiver() {
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                boolean interactive;
+                if (Intent.ACTION_SCREEN_ON.equals(intent.getAction())) {
+                    interactive = true;
+                } else if (Intent.ACTION_SCREEN_OFF.equals(intent.getAction())) {
+                    interactive = false;
+                } else {
+                    return;
+                }
+
+                onScreenInteractiveChanged(interactive);
+            }
+        }, UserHandle.ALL, screenIntentFilter, null, FgThread.getHandler());
+
+        mReady = true;
+    }
+
+    private void onScreenInteractiveChanged(boolean interactive) {
+        if (interactive == mIsInteractive) {
+            return;
+        }
+
+        mIsInteractive = interactive;
+        notifyScreenInteractiveChanged(interactive);
+    }
+
+    @Override
+    public boolean isInteractive() {
+        Preconditions.checkState(mReady);
+        return mIsInteractive;
+    }
+}
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index 0eba69e..9f6c18d 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -26,8 +26,8 @@
 import static com.android.server.media.MediaKeyDispatcher.isTripleTapOverridden;
 
 import android.app.ActivityManager;
-import android.app.INotificationManager;
 import android.app.KeyguardManager;
+import android.app.NotificationManager;
 import android.app.PendingIntent;
 import android.content.ActivityNotFoundException;
 import android.content.ComponentName;
@@ -68,7 +68,6 @@
 import android.os.RemoteCallbackList;
 import android.os.RemoteException;
 import android.os.ResultReceiver;
-import android.os.ServiceManager;
 import android.os.ShellCallback;
 import android.os.UserHandle;
 import android.os.UserManager;
@@ -118,7 +117,7 @@
     private final SessionManagerImpl mSessionManagerImpl;
     private final MessageHandler mHandler = new MessageHandler();
     private final PowerManager.WakeLock mMediaEventWakeLock;
-    private final INotificationManager mNotificationManager;
+    private final NotificationManager mNotificationManager;
     private final Object mLock = new Object();
     private final HandlerThread mRecordThread = new HandlerThread("SessionRecordThread");
     // Keeps the full user id for each user.
@@ -158,10 +157,9 @@
         super(context);
         mContext = context;
         mSessionManagerImpl = new SessionManagerImpl();
-        PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
+        PowerManager pm = mContext.getSystemService(PowerManager.class);
         mMediaEventWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "handleMediaEvent");
-        mNotificationManager = INotificationManager.Stub.asInterface(
-                ServiceManager.getService(Context.NOTIFICATION_SERVICE));
+        mNotificationManager = mContext.getSystemService(NotificationManager.class);
     }
 
     @Override
@@ -507,11 +505,12 @@
     private void enforceMediaPermissions(ComponentName compName, int pid, int uid,
             int resolvedUserId) {
         if (hasStatusBarServicePermission(pid, uid)) return;
+        // TODO: Refactor to use hasMediaControlPermission and hasEnabledNotificationListener
         if (mContext
                 .checkPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL, pid, uid)
                 != PackageManager.PERMISSION_GRANTED
                 && !isEnabledNotificationListener(compName,
-                UserHandle.getUserHandleForUid(uid).getIdentifier(), resolvedUserId)) {
+                UserHandle.getUserHandleForUid(uid), resolvedUserId)) {
             throw new SecurityException("Missing permission to control media.");
         }
     }
@@ -547,13 +546,13 @@
      * they're running as.
      *
      * @param compName The component that is enabled.
-     * @param userId The user id of the caller.
+     * @param userHandle The user handle of the caller.
      * @param forUserId The user id they're making the request on behalf of.
      * @return True if the component is enabled, false otherwise
      */
-    private boolean isEnabledNotificationListener(ComponentName compName, int userId,
+    private boolean isEnabledNotificationListener(ComponentName compName, UserHandle userHandle,
             int forUserId) {
-        if (userId != forUserId) {
+        if (userHandle.getIdentifier() != forUserId) {
             // You may not access another user's content as an enabled listener.
             return false;
         }
@@ -561,12 +560,8 @@
             Log.d(TAG, "Checking if enabled notification listener " + compName);
         }
         if (compName != null) {
-            try {
-                return mNotificationManager.isNotificationListenerAccessGrantedForUser(
-                        compName, userId);
-            } catch (RemoteException e) {
-                Log.w(TAG, "Dead NotificationManager in isEnabledNotificationListener", e);
-            }
+            return mNotificationManager.hasEnabledNotificationListener(compName.getPackageName(),
+                    userHandle);
         }
         return false;
     }
@@ -1922,8 +1917,8 @@
          * @param controllerUid uid of the controller app
          */
         @Override
-        public boolean isTrusted(String controllerPackageName, int controllerPid, int controllerUid)
-                throws RemoteException {
+        public boolean isTrusted(String controllerPackageName, int controllerPid,
+                int controllerUid) {
             final int uid = Binder.getCallingUid();
             final int userId = UserHandle.getUserHandleForUid(uid).getIdentifier();
             final long token = Binder.clearCallingIdentity();
@@ -1937,7 +1932,7 @@
                 // Context#getPackageName() for getting package name that matches with the PID/UID,
                 // but it doesn't tell which package has created the MediaController, so useless.
                 return hasMediaControlPermission(controllerPid, controllerUid)
-                        || hasEnabledNotificationListener(userId, controllerPackageName);
+                        || hasEnabledNotificationListener(userId, controllerPackageName, uid);
             } finally {
                 Binder.restoreCallingIdentity(token);
             }
@@ -2001,28 +1996,20 @@
             return resolvedUserId;
         }
 
-        private boolean hasEnabledNotificationListener(int resolvedUserId, String packageName)
-                throws RemoteException {
+        private boolean hasEnabledNotificationListener(int resolvedUserId, String packageName,
+                int uid) {
+            // TODO: revisit this checking code
             // You may not access another user's content as an enabled listener.
             final int userId = UserHandle.getUserHandleForUid(resolvedUserId).getIdentifier();
             if (resolvedUserId != userId) {
                 return false;
             }
-
-            // TODO(jaewan): (Post-P) Propose NotificationManager#hasEnabledNotificationListener(
-            //               String pkgName) to notification team for optimization
-            final List<ComponentName> enabledNotificationListeners =
-                    mNotificationManager.getEnabledNotificationListeners(userId);
-            if (enabledNotificationListeners != null) {
-                for (int i = 0; i < enabledNotificationListeners.size(); i++) {
-                    if (TextUtils.equals(packageName,
-                            enabledNotificationListeners.get(i).getPackageName())) {
-                        return true;
-                    }
-                }
+            if (mNotificationManager.hasEnabledNotificationListener(packageName,
+                    UserHandle.getUserHandleForUid(uid))) {
+                return true;
             }
             if (DEBUG) {
-                Log.d(TAG, packageName + " (uid=" + resolvedUserId + ") doesn't have an enabled "
+                Log.d(TAG, packageName + " (uid=" + uid + ") doesn't have an enabled "
                         + "notification listener");
             }
             return false;
diff --git a/services/core/java/com/android/server/media/OWNERS b/services/core/java/com/android/server/media/OWNERS
index b460cb5..2e2d812 100644
--- a/services/core/java/com/android/server/media/OWNERS
+++ b/services/core/java/com/android/server/media/OWNERS
@@ -2,6 +2,7 @@
 hdmoon@google.com
 insun@google.com
 jaewan@google.com
+jinpark@google.com
 klhyun@google.com
 lajos@google.com
 sungsoo@google.com
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index deea589..cf48e02 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -516,7 +516,7 @@
 
     private static final int MY_UID = Process.myUid();
     private static final int MY_PID = Process.myPid();
-    private static final IBinder WHITELIST_TOKEN = new Binder();
+    private static final IBinder ALLOWLIST_TOKEN = new Binder();
     protected RankingHandler mRankingHandler;
     private long mLastOverRateLogTime;
     private float mMaxPackageEnqueueRate = DEFAULT_MAX_NOTIFICATION_ENQUEUE_RATE;
@@ -1723,7 +1723,7 @@
         super(context);
         mNotificationRecordLogger = notificationRecordLogger;
         mNotificationInstanceIdSequence = notificationInstanceIdSequence;
-        Notification.processWhitelistToken = WHITELIST_TOKEN;
+        Notification.processAllowlistToken = ALLOWLIST_TOKEN;
     }
 
     // TODO - replace these methods with new fields in the VisibleForTesting constructor
@@ -4754,6 +4754,12 @@
         }
 
         @Override
+        public boolean hasEnabledNotificationListener(String packageName, int userId) {
+            checkCallerIsSystem();
+            return mListeners.isPackageAllowed(packageName, userId);
+        }
+
+        @Override
         public boolean isNotificationListenerAccessGranted(ComponentName listener) {
             Objects.requireNonNull(listener);
             checkCallerIsSystemOrSameApp(listener.getPackageName());
@@ -5777,21 +5783,21 @@
             mShortcutHelper.cacheShortcut(info, user);
         }
 
-        // Whitelist pending intents.
+        // temporarily allow apps to perform extra work when their pending intents are launched
         if (notification.allPendingIntents != null) {
             final int intentCount = notification.allPendingIntents.size();
             if (intentCount > 0) {
                 final ActivityManagerInternal am = LocalServices
                         .getService(ActivityManagerInternal.class);
                 final long duration = LocalServices.getService(
-                        DeviceIdleInternal.class).getNotificationWhitelistDuration();
+                        DeviceIdleInternal.class).getNotificationAllowlistDuration();
                 for (int i = 0; i < intentCount; i++) {
                     PendingIntent pendingIntent = notification.allPendingIntents.valueAt(i);
                     if (pendingIntent != null) {
                         am.setPendingIntentWhitelistDuration(pendingIntent.getTarget(),
-                                WHITELIST_TOKEN, duration);
+                                ALLOWLIST_TOKEN, duration);
                         am.setPendingIntentAllowBgActivityStarts(pendingIntent.getTarget(),
-                                WHITELIST_TOKEN, (FLAG_ACTIVITY_SENDER | FLAG_BROADCAST_SENDER
+                                ALLOWLIST_TOKEN, (FLAG_ACTIVITY_SENDER | FLAG_BROADCAST_SENDER
                                         | FLAG_SERVICE_SENDER));
                     }
                 }
@@ -7644,7 +7650,7 @@
                     // make sure deleteIntent cannot be used to start activities from background
                     LocalServices.getService(ActivityManagerInternal.class)
                             .clearPendingIntentAllowBgActivityStarts(deleteIntent.getTarget(),
-                            WHITELIST_TOKEN);
+                                    ALLOWLIST_TOKEN);
                     deleteIntent.send();
                 } catch (PendingIntent.CanceledException ex) {
                     // do nothing - there's no relevant way to recover, and
diff --git a/services/core/java/com/android/server/notification/RankingReconsideration.java b/services/core/java/com/android/server/notification/RankingReconsideration.java
index 057f0f1..9b046b1 100644
--- a/services/core/java/com/android/server/notification/RankingReconsideration.java
+++ b/services/core/java/com/android/server/notification/RankingReconsideration.java
@@ -90,7 +90,7 @@
 
     /**
      * Apply any computed changes to the notification record.  This method will be
-     * called on the main service thread, synchronized on he mNotificationList.
+     * called on the main service thread, synchronized on the mNotificationList.
      * @param record The locked record to be updated.
      */
     public abstract void applyChangesLocked(NotificationRecord record);
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 00cb22e..391a08d 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -459,12 +459,8 @@
                     final int returnCode = args.argi1;
                     args.recycle();
 
-                    final boolean showNotification;
-                    synchronized (mLock) {
-                        showNotification = isInstallerDeviceOwnerOrAffiliatedProfileOwnerLocked();
-                    }
                     sendOnPackageInstalled(mContext, statusReceiver, sessionId,
-                            showNotification, userId,
+                            isInstallerDeviceOwnerOrAffiliatedProfileOwner(), userId,
                             packageName, returnCode, message, extras);
 
                     break;
@@ -494,8 +490,11 @@
     /**
      * @return {@code true} iff the installing is app an device owner or affiliated profile owner.
      */
-    @GuardedBy("mLock")
-    private boolean isInstallerDeviceOwnerOrAffiliatedProfileOwnerLocked() {
+    private boolean isInstallerDeviceOwnerOrAffiliatedProfileOwner() {
+        assertNotLocked("isInstallerDeviceOwnerOrAffiliatedProfileOwner");
+        // It is safe to access mInstallerUid and mInstallSource without lock
+        // because they are immutable after sealing.
+        assertSealed("isInstallerDeviceOwnerOrAffiliatedProfileOwner");
         if (userId != UserHandle.getUserId(mInstallerUid)) {
             return false;
         }
@@ -513,12 +512,17 @@
      *
      * @return {@code true} iff we need to ask to confirm the permissions?
      */
-    @GuardedBy("mLock")
-    private boolean needToAskForPermissionsLocked() {
-        if (mPermissionsManuallyAccepted) {
-            return false;
+    private boolean needToAskForPermissions() {
+        final String packageName;
+        synchronized (mLock) {
+            if (mPermissionsManuallyAccepted) {
+                return false;
+            }
+            packageName = mPackageName;
         }
 
+        // It is safe to access mInstallerUid and mInstallSource without lock
+        // because they are immutable after sealing.
         final boolean isInstallPermissionGranted =
                 (mPm.checkUidPermission(android.Manifest.permission.INSTALL_PACKAGES,
                         mInstallerUid) == PackageManager.PERMISSION_GRANTED);
@@ -528,7 +532,7 @@
         final boolean isUpdatePermissionGranted =
                 (mPm.checkUidPermission(android.Manifest.permission.INSTALL_PACKAGE_UPDATES,
                         mInstallerUid) == PackageManager.PERMISSION_GRANTED);
-        final int targetPackageUid = mPm.getPackageUid(mPackageName, 0, userId);
+        final int targetPackageUid = mPm.getPackageUid(packageName, 0, userId);
         final boolean isPermissionGranted = isInstallPermissionGranted
                 || (isUpdatePermissionGranted && targetPackageUid != -1)
                 || (isSelfUpdatePermissionGranted && targetPackageUid == mInstallerUid);
@@ -540,7 +544,7 @@
         // Device owners and affiliated profile owners  are allowed to silently install packages, so
         // the permission check is waived if the installer is the device owner.
         return forcePermissionPrompt || !(isPermissionGranted || isInstallerRoot
-                || isInstallerSystem || isInstallerDeviceOwnerOrAffiliatedProfileOwnerLocked());
+                || isInstallerSystem || isInstallerDeviceOwnerOrAffiliatedProfileOwner());
     }
 
     public PackageInstallerSession(PackageInstallerService.InternalCallback callback,
@@ -740,6 +744,18 @@
         }
     }
 
+    private void assertNotLocked(String cookie) {
+        if (Thread.holdsLock(mLock)) {
+            throw new IllegalStateException(cookie + " is holding mLock");
+        }
+    }
+
+    private void assertSealed(String cookie) {
+        if (!isSealed()) {
+            throw new IllegalStateException(cookie + " before sealing");
+        }
+    }
+
     @GuardedBy("mLock")
     private void assertPreparedAndNotSealedLocked(String cookie) {
         assertPreparedAndNotCommittedOrDestroyedLocked(cookie);
@@ -1693,11 +1709,7 @@
     }
 
     private void handleInstall() {
-        final boolean needsLogging;
-        synchronized (mLock) {
-            needsLogging = isInstallerDeviceOwnerOrAffiliatedProfileOwnerLocked();
-        }
-        if (needsLogging) {
+        if (isInstallerDeviceOwnerOrAffiliatedProfileOwner()) {
             DevicePolicyEventLogger
                     .createEvent(DevicePolicyEnums.INSTALL_PACKAGE)
                     .setAdmin(mInstallSource.installerPackageName)
@@ -1724,9 +1736,7 @@
         List<PackageInstallerSession> childSessions = getChildSessionsNotLocked();
 
         try {
-            synchronized (mLock) {
-                installNonStagedLocked(childSessions);
-            }
+            installNonStaged(childSessions);
         } catch (PackageManagerException e) {
             final String completeMsg = ExceptionUtils.getCompleteMessage(e);
             Slog.e(TAG, "Commit of session " + sessionId + " failed: " + completeMsg);
@@ -1735,11 +1745,10 @@
         }
     }
 
-    @GuardedBy("mLock")
-    private void installNonStagedLocked(List<PackageInstallerSession> childSessions)
+    private void installNonStaged(List<PackageInstallerSession> childSessions)
             throws PackageManagerException {
         final PackageManagerService.ActiveInstallSession installingSession =
-                makeSessionActiveLocked();
+                makeSessionActive();
         if (installingSession == null) {
             return;
         }
@@ -1752,7 +1761,7 @@
                 final PackageInstallerSession session = childSessions.get(i);
                 try {
                     final PackageManagerService.ActiveInstallSession installingChildSession =
-                            session.makeSessionActiveLocked();
+                            session.makeSessionActive();
                     if (installingChildSession != null) {
                         installingChildSessions.add(installingChildSession);
                     }
@@ -1762,8 +1771,12 @@
                 }
             }
             if (!success) {
-                sendOnPackageInstalled(mContext, mRemoteStatusReceiver, sessionId,
-                        isInstallerDeviceOwnerOrAffiliatedProfileOwnerLocked(), userId, null,
+                final IntentSender statusReceiver;
+                synchronized (mLock) {
+                    statusReceiver = mRemoteStatusReceiver;
+                }
+                sendOnPackageInstalled(mContext, statusReceiver, sessionId,
+                        isInstallerDeviceOwnerOrAffiliatedProfileOwner(), userId, null,
                         failure.error, failure.getLocalizedMessage(), null);
                 return;
             }
@@ -1778,41 +1791,58 @@
      * {@link PackageManagerService.ActiveInstallSession} representing this new staged state or null
      * in case permissions need to be requested before install can proceed.
      */
+    private PackageManagerService.ActiveInstallSession makeSessionActive()
+            throws PackageManagerException {
+        assertNotLocked("makeSessionActive");
+
+        synchronized (mLock) {
+            if (mRelinquished) {
+                throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
+                        "Session relinquished");
+            }
+            if (mDestroyed) {
+                throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
+                        "Session destroyed");
+            }
+            if (!mSealed) {
+                throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
+                        "Session not sealed");
+            }
+        }
+
+        if (!params.isMultiPackage && needToAskForPermissions()) {
+            // User needs to confirm installation;
+            // give installer an intent they can use to involve
+            // user.
+            final Intent intent = new Intent(PackageInstaller.ACTION_CONFIRM_INSTALL);
+            intent.setPackage(mPm.getPackageInstallerPackageName());
+            intent.putExtra(PackageInstaller.EXTRA_SESSION_ID, sessionId);
+
+            final IntentSender statusReceiver;
+            synchronized (mLock) {
+                statusReceiver = mRemoteStatusReceiver;
+            }
+            sendOnUserActionRequired(mContext, statusReceiver, sessionId, intent);
+
+            // Commit was keeping session marked as active until now; release
+            // that extra refcount so session appears idle.
+            closeInternal(false);
+            return null;
+        }
+
+        synchronized (mLock) {
+            return makeSessionActiveLocked();
+        }
+    }
+
     @GuardedBy("mLock")
     private PackageManagerService.ActiveInstallSession makeSessionActiveLocked()
             throws PackageManagerException {
-        if (mRelinquished) {
-            throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
-                    "Session relinquished");
-        }
-        if (mDestroyed) {
-            throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, "Session destroyed");
-        }
-        if (!mSealed) {
-            throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, "Session not sealed");
-        }
-
         if (!params.isMultiPackage) {
             Objects.requireNonNull(mPackageName);
             Objects.requireNonNull(mSigningDetails);
             Objects.requireNonNull(mResolvedBaseFile);
 
-            if (needToAskForPermissionsLocked()) {
-                // User needs to confirm installation;
-                // give installer an intent they can use to involve
-                // user.
-                final Intent intent = new Intent(PackageInstaller.ACTION_CONFIRM_INSTALL);
-                intent.setPackage(mPm.getPackageInstallerPackageName());
-                intent.putExtra(PackageInstaller.EXTRA_SESSION_ID, sessionId);
-
-                sendOnUserActionRequired(mContext, mRemoteStatusReceiver, sessionId, intent);
-
-                // Commit was keeping session marked as active until now; release
-                // that extra refcount so session appears idle.
-                closeInternal(false);
-                return null;
-            }
-
             // Inherit any packages and native libraries from existing install that
             // haven't been overridden.
             if (params.mode == SessionParams.MODE_INHERIT_EXISTING) {
@@ -2438,7 +2468,7 @@
      * Determine if creating hard links between source and destination is
      * possible. That is, do they all live on the same underlying device.
      */
-    private boolean isLinkPossible(List<File> fromFiles, File toDir) {
+    private static boolean isLinkPossible(List<File> fromFiles, File toDir) {
         try {
             final StructStat toStat = Os.stat(toDir.getAbsolutePath());
             for (File fromFile : fromFiles) {
@@ -2915,7 +2945,12 @@
                         }
                         case IDataLoaderStatusListener.DATA_LOADER_UNAVAILABLE: {
                             // Don't fail or commit the session. Allow caller to commit again.
-                            sendPendingStreaming("DataLoader unavailable");
+                            final IntentSender statusReceiver;
+                            synchronized (mLock) {
+                                statusReceiver = mRemoteStatusReceiver;
+                            }
+                            sendPendingStreaming(mContext, statusReceiver, sessionId,
+                                    "DataLoader unavailable");
                             break;
                         }
                         case IDataLoaderStatusListener.DATA_LOADER_UNRECOVERABLE:
@@ -2929,7 +2964,11 @@
                 } catch (RemoteException e) {
                     // In case of streaming failure we don't want to fail or commit the session.
                     // Just return from this method and allow caller to commit again.
-                    sendPendingStreaming(e.getMessage());
+                    final IntentSender statusReceiver;
+                    synchronized (mLock) {
+                        statusReceiver = mRemoteStatusReceiver;
+                    }
+                    sendPendingStreaming(mContext, statusReceiver, sessionId, e.getMessage());
                 }
             }
         };
@@ -3004,16 +3043,17 @@
                 detailMessage).sendToTarget();
     }
 
+    @GuardedBy("mLock")
+    private int[] getChildSessionIdsLocked() {
+        final int[] childSessionIds = mChildSessionIds.copyKeys();
+        return childSessionIds != null ? childSessionIds : EMPTY_CHILD_SESSION_ARRAY;
+    }
+
     @Override
     public int[] getChildSessionIds() {
-        final int[] childSessionIds;
         synchronized (mLock) {
-            childSessionIds = mChildSessionIds.copyKeys();
+            return getChildSessionIdsLocked();
         }
-        if (childSessionIds != null) {
-            return childSessionIds;
-        }
-        return EMPTY_CHILD_SESSION_ARRAY;
     }
 
     private boolean canBeAddedAsChild(int parentCandidate) {
@@ -3323,6 +3363,9 @@
         pw.decreaseIndent();
     }
 
+    /**
+     * This method doesn't change internal states and is safe to call outside the lock.
+     */
     private static void sendOnUserActionRequired(Context context, IntentSender target,
             int sessionId, Intent intent) {
         final Intent fillIn = new Intent();
@@ -3335,6 +3378,9 @@
         }
     }
 
+    /**
+     * This method doesn't change internal states and is safe to call outside the lock.
+     */
     private static void sendOnPackageInstalled(Context context, IntentSender target, int sessionId,
             boolean showNotification, int userId, String basePackageName, int returnCode,
             String msg, Bundle extras) {
@@ -3375,13 +3421,12 @@
         }
     }
 
-    private void sendPendingStreaming(@Nullable String cause) {
-        final IntentSender statusReceiver;
-        synchronized (mLock) {
-            statusReceiver = mRemoteStatusReceiver;
-        }
-
-        if (statusReceiver == null) {
+    /**
+     * This method doesn't change internal states and is safe to call outside the lock.
+     */
+    private static void sendPendingStreaming(Context context, IntentSender target, int sessionId,
+            @Nullable String cause) {
+        if (target == null) {
             Slog.e(TAG, "Missing receiver for pending streaming status.");
             return;
         }
@@ -3396,7 +3441,7 @@
             intent.putExtra(PackageInstaller.EXTRA_STATUS_MESSAGE, "Staging Image Not Ready");
         }
         try {
-            statusReceiver.sendIntent(mContext, 0, intent, null, null);
+            target.sendIntent(context, 0, intent, null, null);
         } catch (IntentSender.SendIntentException ignored) {
         }
     }
@@ -3470,10 +3515,10 @@
             if (stageCid != null) {
                 writeStringAttribute(out, ATTR_SESSION_STAGE_CID, stageCid);
             }
-            writeBooleanAttribute(out, ATTR_PREPARED, isPrepared());
-            writeBooleanAttribute(out, ATTR_COMMITTED, isCommitted());
-            writeBooleanAttribute(out, ATTR_DESTROYED, isDestroyed());
-            writeBooleanAttribute(out, ATTR_SEALED, isSealed());
+            writeBooleanAttribute(out, ATTR_PREPARED, mPrepared);
+            writeBooleanAttribute(out, ATTR_COMMITTED, mCommitted);
+            writeBooleanAttribute(out, ATTR_DESTROYED, mDestroyed);
+            writeBooleanAttribute(out, ATTR_SEALED, mSealed);
 
             writeBooleanAttribute(out, ATTR_MULTI_PACKAGE, params.isMultiPackage);
             writeBooleanAttribute(out, ATTR_STAGED_SESSION, params.isStaged);
@@ -3535,7 +3580,7 @@
 
                 params.appIconLastModified = appIconFile.lastModified();
             }
-            final int[] childSessionIds = getChildSessionIds();
+            final int[] childSessionIds = getChildSessionIdsLocked();
             for (int childSessionId : childSessionIds) {
                 out.startTag(null, TAG_CHILD_SESSION);
                 writeIntAttribute(out, ATTR_SESSION_ID, childSessionId);
@@ -3543,7 +3588,7 @@
             }
 
             final InstallationFile[] files = getInstallationFilesLocked();
-            for (InstallationFile file : getInstallationFilesLocked()) {
+            for (InstallationFile file : files) {
                 out.startTag(null, TAG_SESSION_FILE);
                 writeIntAttribute(out, ATTR_LOCATION, file.getLocation());
                 writeStringAttribute(out, ATTR_NAME, file.getName());
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index bd12fd5..2a59893a 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -651,6 +651,23 @@
     private static final long THROW_EXCEPTION_ON_REQUIRE_INSTALL_PACKAGES_TO_ADD_INSTALLER_PACKAGE =
             150857253;
 
+    /**
+     * Apps targeting Android S and above need to declare dependencies to the public native
+     * shared libraries that are defined by the device maker using {@code uses-native-library} tag
+     * in its {@code AndroidManifest.xml}.
+     *
+     * If any of the dependencies cannot be satisfied, i.e. one of the dependency doesn't exist,
+     * the package manager rejects to install the app. The dependency can be specified as optional
+     * using {@code android:required} attribute in the tag, in which case failing to satisfy the
+     * dependency doesn't stop the installation.
+     * <p>Once installed, an app is provided with only the native shared libraries that are
+     * specified in the app manifest. {@code dlopen}ing a native shared library that doesn't appear
+     * in the app manifest will fail even if it actually exists on the device.
+     */
+    @ChangeId
+    @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.R)
+    private static final long ENFORCE_NATIVE_SHARED_LIBRARY_DEPENDENCIES = 142191088;
+
     public static final String PLATFORM_PACKAGE_NAME = "android";
 
     private static final String PACKAGE_MIME_TYPE = "application/vnd.android.package-archive";
@@ -2952,9 +2969,8 @@
                     = systemConfig.getSharedLibraries();
             final int builtInLibCount = libConfig.size();
             for (int i = 0; i < builtInLibCount; i++) {
-                String name = libConfig.keyAt(i);
                 SystemConfig.SharedLibraryEntry entry = libConfig.valueAt(i);
-                addBuiltInSharedLibraryLocked(entry.filename, name);
+                addBuiltInSharedLibraryLocked(libConfig.valueAt(i));
             }
 
             // Now that we have added all the libraries, iterate again to add dependency
@@ -6429,9 +6445,14 @@
                     true /*allowDynamicSplits*/);
             Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
 
+            final boolean queryMayBeFiltered =
+                    UserHandle.getAppId(filterCallingUid) >= Process.FIRST_APPLICATION_UID
+                            && !resolveForStart;
+
             final ResolveInfo bestChoice =
                     chooseBestActivity(
-                            intent, resolvedType, flags, privateResolveFlags, query, userId);
+                            intent, resolvedType, flags, privateResolveFlags, query, userId,
+                            queryMayBeFiltered);
             final boolean nonBrowserOnly =
                     (privateResolveFlags & PackageManagerInternal.RESOLVE_NON_BROWSER_ONLY) != 0;
             if (nonBrowserOnly && bestChoice != null && bestChoice.handleAllWebDataURI) {
@@ -6595,7 +6616,8 @@
     }
 
     private ResolveInfo chooseBestActivity(Intent intent, String resolvedType,
-            int flags, int privateResolveFlags, List<ResolveInfo> query, int userId) {
+            int flags, int privateResolveFlags, List<ResolveInfo> query, int userId,
+            boolean queryMayBeFiltered) {
         if (query != null) {
             final int N = query.size();
             if (N == 1) {
@@ -6620,7 +6642,7 @@
                 // If we have saved a preference for a preferred activity for
                 // this Intent, use that.
                 ResolveInfo ri = findPreferredActivityNotLocked(intent, resolvedType,
-                        flags, query, r0.priority, true, false, debug, userId);
+                        flags, query, r0.priority, true, false, debug, userId, queryMayBeFiltered);
                 if (ri != null) {
                     return ri;
                 }
@@ -6802,11 +6824,19 @@
                 && intent.hasCategory(CATEGORY_DEFAULT);
     }
 
+    ResolveInfo findPreferredActivityNotLocked(Intent intent, String resolvedType, int flags,
+            List<ResolveInfo> query, int priority, boolean always,
+            boolean removeMatches, boolean debug, int userId) {
+        return findPreferredActivityNotLocked(
+                intent, resolvedType, flags, query, priority, always, removeMatches, debug, userId,
+                UserHandle.getAppId(Binder.getCallingUid()) >= Process.FIRST_APPLICATION_UID);
+    }
+
     // TODO: handle preferred activities missing while user has amnesia
     /** <b>must not hold {@link #mLock}</b> */
     ResolveInfo findPreferredActivityNotLocked(Intent intent, String resolvedType, int flags,
             List<ResolveInfo> query, int priority, boolean always,
-            boolean removeMatches, boolean debug, int userId) {
+            boolean removeMatches, boolean debug, int userId, boolean queryMayBeFiltered) {
         if (Thread.holdsLock(mLock)) {
             Slog.wtf(TAG, "Calling thread " + Thread.currentThread().getName()
                     + " is holding mLock", new Throwable());
@@ -6900,10 +6930,12 @@
                         }
                         final boolean excludeSetupWizardHomeActivity = isHomeIntent(intent)
                                 && !isDeviceProvisioned;
+                        final boolean allowSetMutation = !excludeSetupWizardHomeActivity
+                                && !queryMayBeFiltered;
                         if (ai == null) {
                             // Do not remove launcher's preferred activity during SetupWizard
                             // due to it may not install yet
-                            if (excludeSetupWizardHomeActivity) {
+                            if (!allowSetMutation) {
                                 continue;
                             }
 
@@ -6928,7 +6960,7 @@
                                 continue;
                             }
 
-                            if (removeMatches) {
+                            if (removeMatches && allowSetMutation) {
                                 pir.removeFilter(pa);
                                 changed = true;
                                 if (DEBUG_PREFERRED) {
@@ -6945,7 +6977,7 @@
 
                             if (always && !pa.mPref.sameSet(query, excludeSetupWizardHomeActivity)) {
                                 if (pa.mPref.isSuperset(query, excludeSetupWizardHomeActivity)) {
-                                    if (!excludeSetupWizardHomeActivity) {
+                                    if (allowSetMutation) {
                                         // some components of the set are no longer present in
                                         // the query, but the preferred activity can still be reused
                                         if (DEBUG_PREFERRED) {
@@ -6966,24 +6998,28 @@
                                         changed = true;
                                     } else {
                                         if (DEBUG_PREFERRED) {
-                                            Slog.i(TAG, "Do not remove preferred activity for launcher"
-                                                    + " during SetupWizard");
+                                            Slog.i(TAG, "Do not remove preferred activity");
                                         }
                                     }
                                 } else {
-                                    Slog.i(TAG,
-                                            "Result set changed, dropping preferred activity for "
-                                                    + intent + " type " + resolvedType);
-                                    if (DEBUG_PREFERRED) {
-                                        Slog.v(TAG, "Removing preferred activity since set changed "
-                                                + pa.mPref.mComponent);
+                                    if (allowSetMutation) {
+                                        Slog.i(TAG,
+                                                "Result set changed, dropping preferred activity "
+                                                        + "for " + intent + " type "
+                                                        + resolvedType);
+                                        if (DEBUG_PREFERRED) {
+                                            Slog.v(TAG,
+                                                    "Removing preferred activity since set changed "
+                                                            + pa.mPref.mComponent);
+                                        }
+                                        pir.removeFilter(pa);
+                                        // Re-add the filter as a "last chosen" entry (!always)
+                                        PreferredActivity lastChosen = new PreferredActivity(
+                                                pa, pa.mPref.mMatch, null, pa.mPref.mComponent,
+                                                false);
+                                        pir.addFilter(lastChosen);
+                                        changed = true;
                                     }
-                                    pir.removeFilter(pa);
-                                    // Re-add the filter as a "last chosen" entry (!always)
-                                    PreferredActivity lastChosen = new PreferredActivity(
-                                            pa, pa.mPref.mMatch, null, pa.mPref.mComponent, false);
-                                    pir.addFilter(lastChosen);
-                                    changed = true;
                                     return null;
                                 }
                             }
@@ -10486,6 +10522,19 @@
                     null, null, pkg.getPackageName(), false, pkg.getTargetSdkVersion(),
                     usesLibraryInfos, availablePackages, existingLibraries, newLibraries);
         }
+        // TODO(b/160928779) gate this behavior using ENFORCE_NATIVE_SHARED_LIBRARY_DEPENDENCIES
+        if (pkg.getTargetSdkVersion() > 30) {
+            if (!pkg.getUsesNativeLibraries().isEmpty() && pkg.getTargetSdkVersion() > 30) {
+                usesLibraryInfos = collectSharedLibraryInfos(pkg.getUsesNativeLibraries(), null,
+                        null, pkg.getPackageName(), true, pkg.getTargetSdkVersion(),
+                        usesLibraryInfos, availablePackages, existingLibraries, newLibraries);
+            }
+            if (!pkg.getUsesOptionalNativeLibraries().isEmpty()) {
+                usesLibraryInfos = collectSharedLibraryInfos(pkg.getUsesOptionalNativeLibraries(),
+                        null, null, pkg.getPackageName(), false, pkg.getTargetSdkVersion(),
+                        usesLibraryInfos, availablePackages, existingLibraries, newLibraries);
+            }
+        }
         return usesLibraryInfos;
     }
 
@@ -12177,15 +12226,16 @@
     }
 
     @GuardedBy("mLock")
-    private boolean addBuiltInSharedLibraryLocked(String path, String name) {
-        if (nonStaticSharedLibExistsLocked(name)) {
+    private boolean addBuiltInSharedLibraryLocked(SystemConfig.SharedLibraryEntry entry) {
+        if (nonStaticSharedLibExistsLocked(entry.name)) {
             return false;
         }
 
-        SharedLibraryInfo libraryInfo = new SharedLibraryInfo(path, null, null, name,
-                (long) SharedLibraryInfo.VERSION_UNDEFINED, SharedLibraryInfo.TYPE_BUILTIN,
-                new VersionedPackage(PLATFORM_PACKAGE_NAME, (long) 0),
-                null, null);
+        SharedLibraryInfo libraryInfo = new SharedLibraryInfo(entry.filename, null, null,
+                entry.name, (long) SharedLibraryInfo.VERSION_UNDEFINED,
+                SharedLibraryInfo.TYPE_BUILTIN,
+                new VersionedPackage(PLATFORM_PACKAGE_NAME, (long)0), null, null,
+                entry.isNative);
 
         commitSharedLibraryInfoLocked(libraryInfo);
         return true;
@@ -21900,7 +21950,11 @@
                             pw.print(" -> ");
                         }
                         if (libraryInfo.getPath() != null) {
-                            pw.print(" (jar) ");
+                            if (libraryInfo.isNative()) {
+                                pw.print(" (so) ");
+                            } else {
+                                pw.print(" (jar) ");
+                            }
                             pw.print(libraryInfo.getPath());
                         } else {
                             pw.print(" (apk) ");
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 13b927e..7106499 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -4784,6 +4784,23 @@
                 }
             }
 
+            List<String> usesNativeLibraries = pkg.getUsesNativeLibraries();
+            if (usesNativeLibraries.size() > 0) {
+                pw.print(prefix); pw.println("  usesNativeLibraries:");
+                for (int i=0; i< usesNativeLibraries.size(); i++) {
+                    pw.print(prefix); pw.print("    "); pw.println(usesNativeLibraries.get(i));
+                }
+            }
+
+            List<String> usesOptionalNativeLibraries = pkg.getUsesOptionalNativeLibraries();
+            if (usesOptionalNativeLibraries.size() > 0) {
+                pw.print(prefix); pw.println("  usesOptionalNativeLibraries:");
+                for (int i=0; i< usesOptionalNativeLibraries.size(); i++) {
+                    pw.print(prefix); pw.print("    ");
+                    pw.println(usesOptionalNativeLibraries.get(i));
+                }
+            }
+
             List<String> usesLibraryFiles = ps.getPkgState().getUsesLibraryFiles();
             if (usesLibraryFiles.size() > 0) {
                 pw.print(prefix); pw.println("  usesLibraryFiles:");
diff --git a/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackage.java b/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackage.java
index c9e0bb4..39784cf 100644
--- a/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackage.java
+++ b/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackage.java
@@ -252,6 +252,19 @@
     @NonNull
     List<String> getUsesOptionalLibraries();
 
+    /** @see R.styleabele#AndroidManifestUsesNativeLibrary */
+    @NonNull
+    List<String> getUsesNativeLibraries();
+
+    /**
+     * Like {@link #getUsesNativeLibraries()}, but marked optional by setting
+     * {@link R.styleable#AndroidManifestUsesNativeLibrary_required} to false . Application is
+     * expected to handle absence manually.
+     * @see R.styleable#AndroidManifestUsesNativeLibrary
+     */
+    @NonNull
+    List<String> getUsesOptionalNativeLibraries();
+
     /**
      * TODO(b/135203078): Move static library stuff to an inner data class
      * @see R.styleable#AndroidManifestUsesStaticLibrary
diff --git a/services/core/java/com/android/server/storage/StorageUserConnection.java b/services/core/java/com/android/server/storage/StorageUserConnection.java
index ed57067..361a506 100644
--- a/services/core/java/com/android/server/storage/StorageUserConnection.java
+++ b/services/core/java/com/android/server/storage/StorageUserConnection.java
@@ -34,6 +34,7 @@
 import android.os.ParcelableException;
 import android.os.RemoteCallback;
 import android.os.UserHandle;
+import android.os.UserManagerInternal;
 import android.os.storage.StorageManagerInternal;
 import android.os.storage.StorageVolume;
 import android.service.storage.ExternalStorageService;
@@ -62,19 +63,25 @@
 public final class StorageUserConnection {
     private static final String TAG = "StorageUserConnection";
 
-    public static final int REMOTE_TIMEOUT_SECONDS = 20;
+    private static final int DEFAULT_REMOTE_TIMEOUT_SECONDS = 20;
+    // TODO(b/161702661): Workaround for demo user to have shorter timeout.
+    // This allows the DevicePolicyManagerService#enableSystemApp call to succeed without ANR.
+    private static final int DEMO_USER_REMOTE_TIMEOUT_SECONDS = 5;
 
     private final Object mLock = new Object();
     private final Context mContext;
     private final int mUserId;
     private final StorageSessionController mSessionController;
     private final ActiveConnection mActiveConnection = new ActiveConnection();
+    private final boolean mIsDemoUser;
     @GuardedBy("mLock") private final Map<String, Session> mSessions = new HashMap<>();
 
     public StorageUserConnection(Context context, int userId, StorageSessionController controller) {
         mContext = Objects.requireNonNull(context);
         mUserId = Preconditions.checkArgumentNonnegative(userId);
         mSessionController = controller;
+        mIsDemoUser = LocalServices.getService(UserManagerInternal.class)
+                .getUserInfo(userId).isDemo();
     }
 
     /**
@@ -200,7 +207,8 @@
 
     private void waitForLatch(CountDownLatch latch, String reason) throws TimeoutException {
         try {
-            if (!latch.await(REMOTE_TIMEOUT_SECONDS, TimeUnit.SECONDS)) {
+            if (!latch.await(mIsDemoUser ? DEMO_USER_REMOTE_TIMEOUT_SECONDS
+                            : DEFAULT_REMOTE_TIMEOUT_SECONDS, TimeUnit.SECONDS)) {
                 // TODO(b/140025078): Call ActivityManager ANR API?
                 Slog.wtf(TAG, "Failed to bind to the ExternalStorageService for user " + mUserId);
                 throw new TimeoutException("Latch wait for " + reason + " elapsed");
diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java
index 2394baf..fd3c1f9 100644
--- a/services/core/java/com/android/server/trust/TrustManagerService.java
+++ b/services/core/java/com/android/server/trust/TrustManagerService.java
@@ -1469,7 +1469,7 @@
             if (userId > 0) {
                 return userId;
             } else {
-                Slog.wtf(TAG, "EXTRA_USER_HANDLE missing or invalid, value=" + userId);
+                Log.w(TAG, "EXTRA_USER_HANDLE missing or invalid, value=" + userId);
                 return -100;
             }
         }
diff --git a/services/core/java/com/android/server/uri/UriGrantsManagerService.java b/services/core/java/com/android/server/uri/UriGrantsManagerService.java
index f5eed30..f5e1602 100644
--- a/services/core/java/com/android/server/uri/UriGrantsManagerService.java
+++ b/services/core/java/com/android/server/uri/UriGrantsManagerService.java
@@ -51,6 +51,7 @@
 import android.app.GrantedUriPermission;
 import android.app.IUriGrantsManager;
 import android.content.ClipData;
+import android.content.ComponentName;
 import android.content.ContentProvider;
 import android.content.ContentResolver;
 import android.content.Context;
@@ -698,6 +699,11 @@
                                 final UriPermission perm = findOrCreateUriPermissionLocked(
                                         sourcePkg, targetPkg, targetUid, grantUri);
                                 perm.initPersistedModes(modeFlags, createdTime);
+                                mPmInternal.grantImplicitAccess(
+                                        targetUserId, null,
+                                        UserHandle.getAppId(targetUid),
+                                        pi.applicationInfo.uid,
+                                        false /* direct */);
                             }
                         } else {
                             Slog.w(TAG, "Persisted grant for " + uri + " had source " + sourcePkg
@@ -1171,6 +1177,9 @@
             // grant, we can skip generating any bookkeeping; when any advanced
             // features have been requested, we proceed below to make sure the
             // provider supports granting permissions
+            mPmInternal.grantImplicitAccess(
+                    UserHandle.getUserId(targetUid), null,
+                    UserHandle.getAppId(targetUid), pi.applicationInfo.uid, false);
             return -1;
         }
 
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 55962fc..90f87b1 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -1160,9 +1160,7 @@
             }
         };
 
-        private Runnable mTryToRebindRunnable = () -> {
-            tryToRebind();
-        };
+        private Runnable mTryToRebindRunnable = this::tryToRebind;
 
         WallpaperConnection(WallpaperInfo info, WallpaperData wallpaper, int clientUid) {
             mInfo = info;
@@ -1295,14 +1293,14 @@
                     // a short time in the future, specifically to allow any pending package
                     // update message on this same looper thread to be processed.
                     if (!mWallpaper.wallpaperUpdating) {
-                        mContext.getMainThreadHandler().postDelayed(() -> processDisconnect(this),
+                        mContext.getMainThreadHandler().postDelayed(mDisconnectRunnable,
                                 1000);
                     }
                 }
             }
         }
 
-        public void scheduleTimeoutLocked() {
+        private void scheduleTimeoutLocked() {
             // If we didn't reset it right away, do so after we couldn't connect to
             // it for an extended amount of time to avoid having a black wallpaper.
             final Handler fgHandler = FgThread.getHandler();
@@ -1342,11 +1340,11 @@
             }
         }
 
-        private void processDisconnect(final ServiceConnection connection) {
+        private Runnable mDisconnectRunnable = () -> {
             synchronized (mLock) {
                 // The wallpaper disappeared.  If this isn't a system-default one, track
                 // crashes and fall back to default if it continues to misbehave.
-                if (connection == mWallpaper.connection) {
+                if (this == mWallpaper.connection) {
                     final ComponentName wpService = mWallpaper.wallpaperComponent;
                     if (!mWallpaper.wallpaperUpdating
                             && mWallpaper.userId == mCurrentUserId
@@ -1374,7 +1372,7 @@
                     }
                 }
             }
-        }
+        };
 
         /**
          * Called by a live wallpaper if its colors have changed.
@@ -2786,6 +2784,13 @@
                     WallpaperConnection.DisplayConnector::disconnectLocked);
             wallpaper.connection.mService = null;
             wallpaper.connection.mDisplayConnector.clear();
+
+            FgThread.getHandler().removeCallbacks(wallpaper.connection.mResetRunnable);
+            mContext.getMainThreadHandler().removeCallbacks(
+                    wallpaper.connection.mDisconnectRunnable);
+            mContext.getMainThreadHandler().removeCallbacks(
+                    wallpaper.connection.mTryToRebindRunnable);
+
             wallpaper.connection = null;
             if (wallpaper == mLastWallpaper) mLastWallpaper = null;
         }
diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
index 0cd7ffc..04b1edc 100644
--- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
@@ -1446,7 +1446,9 @@
         mService.deferWindowLayout();
         try {
             stack.setWindowingMode(WINDOWING_MODE_UNDEFINED);
-            stack.setBounds(null);
+            if (stack.getWindowingMode() != WINDOWING_MODE_FREEFORM) {
+                stack.setBounds(null);
+            }
             toDisplay.getDefaultTaskDisplayArea().positionTaskBehindHome(stack);
 
             // Follow on the workaround: activities are kept force hidden till the new windowing
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index d766928..c4af3e2 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -145,7 +145,6 @@
 import android.app.IApplicationThread;
 import android.app.IAssistDataReceiver;
 import android.app.INotificationManager;
-import android.app.IRequestFinishCallback;
 import android.app.ITaskStackListener;
 import android.app.Notification;
 import android.app.NotificationManager;
@@ -2472,7 +2471,7 @@
     }
 
     @Override
-    public void onBackPressedOnTaskRoot(IBinder token, IRequestFinishCallback callback) {
+    public void onBackPressedOnTaskRoot(IBinder token) {
         synchronized (mGlobalLock) {
             ActivityRecord r = ActivityRecord.isInStackLocked(token);
             if (r == null) {
@@ -2486,18 +2485,13 @@
                 // callback
             } else if (stack != null && (stack.isSingleTaskInstance())) {
                 // Single-task stacks are used for activities which are presented in floating
-                // windows above full screen activities. Instead of directly finishing the
-                // task, a task change listener is used to notify SystemUI so the action can be
-                // handled specially.
+                // windows above full screen activities. A task change listener is used to notify
+                // SystemUI so the back action can be handled specially.
                 final Task task = r.getTask();
                 mTaskChangeNotificationController
                         .notifyBackPressedOnTaskRoot(task.getTaskInfo());
             } else {
-                try {
-                    callback.requestFinish();
-                } catch (RemoteException e) {
-                    Slog.e(TAG, "Failed to invoke request finish callback", e);
-                }
+                moveActivityTaskToBack(token, false /* nonRoot */);
             }
         }
     }
@@ -2843,7 +2837,7 @@
         }
 
         if (toTop) {
-            taskDisplayArea.positionStackAt(POSITION_TOP, primarySplitTask,
+            taskDisplayArea.positionChildAt(POSITION_TOP, primarySplitTask,
                     false /* includingParents */);
         }
         WindowContainerTransaction wct = new WindowContainerTransaction();
diff --git a/services/core/java/com/android/server/wm/ConfigurationContainer.java b/services/core/java/com/android/server/wm/ConfigurationContainer.java
index 98f57c5..0d8e882 100644
--- a/services/core/java/com/android/server/wm/ConfigurationContainer.java
+++ b/services/core/java/com/android/server/wm/ConfigurationContainer.java
@@ -368,7 +368,7 @@
     /** Sets the always on top flag for this configuration container.
      *  When you call this function, make sure that the following functions are called as well to
      *  keep proper z-order.
-     *  - {@Link DisplayContent#positionStackAt(POSITION_TOP, TaskStack)};
+     *  - {@link TaskDisplayArea#positionChildAt(int POSITION_TOP, Task, boolean)};
      * */
     public void setAlwaysOnTop(boolean alwaysOnTop) {
         mRequestsTmpConfig.setTo(getRequestedOverrideConfiguration());
diff --git a/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java b/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java
index c7cba77..e2c0749 100644
--- a/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java
+++ b/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java
@@ -16,8 +16,6 @@
 
 package com.android.server.wm;
 
-import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
-
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_VISIBILITY;
 import static com.android.server.wm.Task.TAG_VISIBILITY;
 
@@ -174,12 +172,7 @@
         }
 
         final int windowingMode = mContiner.getWindowingMode();
-        if (windowingMode == WINDOWING_MODE_FREEFORM) {
-            // The visibility of tasks and the activities they contain in freeform stack are
-            // determined individually unlike other stacks where the visibility or fullscreen
-            // status of an activity in a previous task affects other.
-            mBehindFullscreenActivity = !mContainerShouldBeVisible;
-        } else if (!mBehindFullscreenActivity && mContiner.isActivityTypeHome()
+        if (!mBehindFullscreenActivity && mContiner.isActivityTypeHome()
                 && r.isRootOfTask()) {
             if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Home task: at " + mContiner
                     + " stackShouldBeVisible=" + mContainerShouldBeVisible
diff --git a/services/core/java/com/android/server/wm/InputConsumerImpl.java b/services/core/java/com/android/server/wm/InputConsumerImpl.java
index 3b39b6b..852b3672 100644
--- a/services/core/java/com/android/server/wm/InputConsumerImpl.java
+++ b/services/core/java/com/android/server/wm/InputConsumerImpl.java
@@ -88,6 +88,7 @@
         mWindowHandle.ownerUid = Process.myUid();
         mWindowHandle.inputFeatures = 0;
         mWindowHandle.scaleFactor = 1.0f;
+        mWindowHandle.trustedOverlay = true;
 
         mInputSurface = mService.makeSurfaceBuilder(mService.mRoot.getDisplayContent(displayId).getSession())
                 .setContainerLayer()
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index 0216db4..20f1b9f 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -27,7 +27,18 @@
 import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
 import static android.view.WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_DISABLE_WALLPAPER_TOUCH_EVENTS;
+import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY;
+import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY;
+import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
+import static android.view.WindowManager.LayoutParams.TYPE_INPUT_CONSUMER;
+import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
+import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG;
+import static android.view.WindowManager.LayoutParams.TYPE_MAGNIFICATION_OVERLAY;
+import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
+import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL;
+import static android.view.WindowManager.LayoutParams.TYPE_NOTIFICATION_SHADE;
 import static android.view.WindowManager.LayoutParams.TYPE_SECURE_SYSTEM_OVERLAY;
+import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
 import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
 
 import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_FOCUS_LIGHT;
@@ -67,9 +78,6 @@
     private boolean mUpdateInputWindowsPending;
     private boolean mUpdateInputWindowsImmediately;
 
-    // Currently focused input window handle.
-    private InputWindowHandle mFocusedInputWindowHandle;
-
     private boolean mDisableWallpaperTouchEvents;
     private final Rect mTmpRect = new Rect();
     private final UpdateInputForAllWindowsConsumer mUpdateInputForAllWindowsConsumer;
@@ -309,10 +317,6 @@
             Slog.d(TAG_WM, "addInputWindowHandle: "
                     + child + ", " + inputWindowHandle);
         }
-
-        if (hasFocus) {
-            mFocusedInputWindowHandle = inputWindowHandle;
-        }
     }
 
     void setUpdateInputWindowsNeededLw() {
@@ -578,6 +582,7 @@
         inputWindowHandle.portalToDisplayId = INVALID_DISPLAY;
         inputWindowHandle.touchableRegion.setEmpty();
         inputWindowHandle.setTouchableRegionCrop(null);
+        inputWindowHandle.trustedOverlay = isTrustedOverlay(type);
     }
 
     /**
@@ -592,4 +597,17 @@
         populateOverlayInputInfo(inputWindowHandle, name, TYPE_SECURE_SYSTEM_OVERLAY, true);
         t.setInputWindowInfo(sc, inputWindowHandle);
     }
+
+    static boolean isTrustedOverlay(int type) {
+        return type == TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY
+                || type == TYPE_INPUT_METHOD || type == TYPE_INPUT_METHOD_DIALOG
+                || type == TYPE_MAGNIFICATION_OVERLAY || type == TYPE_STATUS_BAR
+                || type == TYPE_NOTIFICATION_SHADE
+                || type == TYPE_NAVIGATION_BAR
+                || type == TYPE_NAVIGATION_BAR_PANEL
+                || type == TYPE_SECURE_SYSTEM_OVERLAY
+                || type == TYPE_DOCK_DIVIDER
+                || type == TYPE_ACCESSIBILITY_OVERLAY
+                || type == TYPE_INPUT_CONSUMER;
+    }
 }
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index e6e92ea..821bd70 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -2459,7 +2459,7 @@
             // Since always on top is only on when the stack is freeform or pinned, the state
             // can be toggled when the windowing mode changes. We must make sure the stack is
             // placed properly when always on top state changes.
-            taskDisplayArea.positionStackAtTop(this, false /* includingParents */);
+            taskDisplayArea.positionChildAt(POSITION_TOP, this, false /* includingParents */);
         }
     }
 
@@ -5340,7 +5340,8 @@
         }
 
         if (isRootTask()) {
-            taskDisplayArea.positionStackAtTop(this, false /* includingParents */, reason);
+            taskDisplayArea.positionChildAt(POSITION_TOP, this, false /* includingParents */,
+                    reason);
         }
         if (task == null) {
             task = this;
@@ -5368,7 +5369,8 @@
             if (parentTask != null) {
                 parentTask.moveToBack(reason, this);
             } else {
-                displayArea.positionStackAtBottom(this, reason);
+                displayArea.positionChildAt(POSITION_BOTTOM, this, false /*includingParents*/,
+                        reason);
             }
             if (task != null && task != this) {
                 positionChildAtBottom(task);
@@ -7448,7 +7450,7 @@
         // always on top windows. Since the position the stack should be inserted into is calculated
         // properly in {@link DisplayContent#getTopInsertPosition()} in both cases, we can just
         // request that the stack is put at top here.
-        taskDisplayArea.positionStackAtTop(this, false /* includingParents */);
+        taskDisplayArea.positionChildAt(POSITION_TOP, this, false /* includingParents */);
     }
 
     /** NOTE: Should only be called from {@link Task#reparent}. */
@@ -7493,7 +7495,7 @@
             final Task task = getBottomMostTask();
             setWindowingMode(WINDOWING_MODE_UNDEFINED);
 
-            getDisplayArea().positionStackAtTop(this, false /* includingParents */);
+            getDisplayArea().positionChildAt(POSITION_TOP, this, false /* includingParents */);
 
             mStackSupervisor.scheduleUpdatePictureInPictureModeIfNeeded(task, this);
             MetricsLoggerWrapper.logPictureInPictureFullScreen(mAtmService.mContext,
diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java
index 4473bd6..246753a 100644
--- a/services/core/java/com/android/server/wm/TaskDisplayArea.java
+++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java
@@ -303,15 +303,18 @@
     }
 
     @Override
-    void addChild(Task stack, int position) {
-        if (DEBUG_STACK) Slog.d(TAG_WM, "Set stack=" + stack + " on taskDisplayArea=" + this);
-        addStackReferenceIfNeeded(stack);
-        position = findPositionForStack(position, stack, true /* adding */);
+    void addChild(Task task, int position) {
+        if (DEBUG_STACK) Slog.d(TAG_WM, "Set task=" + task + " on taskDisplayArea=" + this);
+        if (mDisplayContent.mSingleTaskInstance && getStackCount() == 1) {
+            throw new IllegalStateException("addChild: Can only have one task on display=" + this);
+        }
 
-        super.addChild(stack, position);
+        addStackReferenceIfNeeded(task);
+        position = findPositionForStack(position, task, true /* adding */);
+
+        super.addChild(task, position);
         mAtmService.updateSleepIfNeededLocked();
-
-        positionStackAt(stack, position);
+        onStackOrderChanged(task);
     }
 
     @Override
@@ -328,19 +331,42 @@
         return true;
     }
 
+    void positionChildAt(int position, Task child, boolean includingParents,
+            String updateLastFocusedTaskReason) {
+        final Task prevFocusedTask = updateLastFocusedTaskReason != null ? getFocusedStack() : null;
+
+        positionChildAt(position, child, includingParents);
+
+        if (updateLastFocusedTaskReason == null) {
+            return;
+        }
+
+        final Task currentFocusedStack = getFocusedStack();
+        if (currentFocusedStack == prevFocusedTask) {
+            return;
+        }
+
+        mLastFocusedStack = prevFocusedTask;
+        EventLogTags.writeWmFocusedStack(mRootWindowContainer.mCurrentUser,
+                mDisplayContent.mDisplayId,
+                currentFocusedStack == null ? -1 : currentFocusedStack.getRootTaskId(),
+                mLastFocusedStack == null ? -1 : mLastFocusedStack.getRootTaskId(),
+                updateLastFocusedTaskReason);
+    }
+
     @Override
     void positionChildAt(int position, Task child, boolean includingParents) {
         final boolean moveToTop = position >= getChildCount() - 1;
         final boolean moveToBottom = position <= 0;
 
+        final int oldPosition = mChildren.indexOf(child);
         if (child.getWindowConfiguration().isAlwaysOnTop() && !moveToTop) {
             // This stack is always-on-top, override the default behavior.
             Slog.w(TAG_WM, "Ignoring move of always-on-top stack=" + this + " to bottom");
 
             // Moving to its current position, as we must call super but we don't want to
             // perform any meaningful action.
-            final int currentPosition = mChildren.indexOf(child);
-            super.positionChildAt(currentPosition, child, false /* includingParents */);
+            super.positionChildAt(oldPosition, child, false /* includingParents */);
             return;
         }
         // We don't allow untrusted display to top when task stack moves to top,
@@ -358,7 +384,7 @@
 
         child.updateTaskMovement(moveToTop);
 
-        mDisplayContent.setLayoutNeeded();
+        mDisplayContent.layoutAndAssignWindowLayersIfNeeded();
 
         // The insert position may be adjusted to non-top when there is always-on-top stack. Since
         // the original position is preferred to be top, the stack should have higher priority when
@@ -373,6 +399,10 @@
 
         // Update the top resumed activity because the preferred top focusable task may be changed.
         mAtmService.mStackSupervisor.updateTopResumedActivityIfNeeded();
+
+        if (mChildren.indexOf(child) != oldPosition) {
+            onStackOrderChanged(child);
+        }
     }
 
     @Override
@@ -800,66 +830,6 @@
         }
     }
 
-    void positionStackAt(int position, Task child, boolean includingParents) {
-        positionChildAt(position, child, includingParents);
-        mDisplayContent.layoutAndAssignWindowLayersIfNeeded();
-    }
-
-    void positionStackAtTop(Task stack, boolean includingParents) {
-        positionStackAtTop(stack, includingParents, null /* updateLastFocusedStackReason */);
-    }
-
-    void positionStackAtTop(Task stack, boolean includingParents,
-            String updateLastFocusedStackReason) {
-        positionStackAt(stack, getStackCount(), includingParents,
-                updateLastFocusedStackReason);
-    }
-
-    void positionStackAtBottom(Task stack) {
-        positionStackAtBottom(stack, null /* updateLastFocusedStackReason */);
-    }
-
-    void positionStackAtBottom(Task stack, String updateLastFocusedStackReason) {
-        positionStackAt(stack, 0, false /* includingParents */,
-                updateLastFocusedStackReason);
-    }
-
-    void positionStackAt(Task stack, int position) {
-        positionStackAt(stack, position, false /* includingParents */,
-                null /* updateLastFocusedStackReason */);
-    }
-
-    void positionStackAt(Task stack, int position, boolean includingParents,
-            String updateLastFocusedStackReason) {
-        // TODO: Keep in sync with WindowContainer.positionChildAt(), once we change that to adjust
-        //       the position internally, also update the logic here
-        final Task prevFocusedStack = updateLastFocusedStackReason != null
-                ? getFocusedStack() : null;
-        final boolean wasContained = mChildren.contains(stack);
-        if (mDisplayContent.mSingleTaskInstance && getStackCount() == 1 && !wasContained) {
-            throw new IllegalStateException(
-                    "positionStackAt: Can only have one task on display=" + this);
-        }
-
-        // Since positionChildAt() is called during the creation process of pinned stacks,
-        // ActivityStack#getStack() can be null.
-        positionStackAt(position, stack, includingParents);
-
-        if (updateLastFocusedStackReason != null) {
-            final Task currentFocusedStack = getFocusedStack();
-            if (currentFocusedStack != prevFocusedStack) {
-                mLastFocusedStack = prevFocusedStack;
-                EventLogTags.writeWmFocusedStack(mRootWindowContainer.mCurrentUser,
-                        mDisplayContent.mDisplayId,
-                        currentFocusedStack == null ? -1 : currentFocusedStack.getRootTaskId(),
-                        mLastFocusedStack == null ? -1 : mLastFocusedStack.getRootTaskId(),
-                        updateLastFocusedStackReason);
-            }
-        }
-
-        onStackOrderChanged(stack);
-    }
-
     /**
      * Moves/reparents `task` to the back of whatever container the home stack is in. This is for
      * when we just want to move a task to "the back" vs. a specific place. The primary use-case
@@ -872,7 +842,7 @@
         if (homeParentTask == null) {
             // reparent throws if parent didn't change...
             if (task.getParent() == this) {
-                positionStackAtBottom(task);
+                positionChildAt(POSITION_BOTTOM, task, false /*includingParents*/);
             } else {
                 task.reparent(this, false /* onTop */);
             }
@@ -1091,7 +1061,7 @@
         if (launchRootTask != null) {
             launchRootTask.addChild(stack, onTop ? POSITION_TOP : POSITION_BOTTOM);
             if (onTop) {
-                positionStackAtTop(launchRootTask, false /* includingParents */);
+                positionChildAt(POSITION_TOP, launchRootTask, false /* includingParents */);
             }
         } else {
             addChild(stack, onTop ? POSITION_TOP : POSITION_BOTTOM);
@@ -1666,15 +1636,11 @@
             return;
         }
 
-        final boolean isRootTask = stack.isRootTask();
-        if (isRootTask) {
-            // Move the stack to the bottom to not affect the following visibility checks
-            positionStackAtBottom(stack);
-        } else {
-            stack.getParent().positionChildAt(POSITION_BOTTOM, stack, false /* includingParents */);
-        }
+        // Move the stack to the bottom to not affect the following visibility checks
+        stack.getParent().positionChildAt(POSITION_BOTTOM, stack, false /* includingParents */);
 
         // Find the next position where the stack should be placed
+        final boolean isRootTask = stack.isRootTask();
         final int numStacks = isRootTask ? getStackCount() : stack.getParent().getChildCount();
         for (int stackNdx = 0; stackNdx < numStacks; stackNdx++) {
             final Task s = isRootTask ? getStackAt(stackNdx)
@@ -1688,11 +1654,7 @@
             if (s.shouldBeVisible(null) && isValidWindowingMode) {
                 // Move the provided stack to behind this stack
                 final int position = Math.max(0, stackNdx - 1);
-                if (isRootTask) {
-                    positionStackAt(stack, position);
-                } else {
-                    stack.getParent().positionChildAt(position, stack, false /*includingParents */);
-                }
+                stack.getParent().positionChildAt(position, stack, false /*includingParents */);
                 break;
             }
         }
@@ -1722,11 +1684,7 @@
         final int insertIndex = stackIndex <= behindStackIndex
                 ? behindStackIndex - 1 : behindStackIndex;
         final int position = Math.max(0, insertIndex);
-        if (stack.isRootTask()) {
-            positionStackAt(stack, position);
-        } else {
-            parent.positionChildAt(position, stack, false /* includingParents */);
-        }
+        parent.positionChildAt(position, stack, false /* includingParents */);
     }
 
     boolean hasPinnedTask() {
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index 4c5ac93..548345e 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -345,29 +345,17 @@
                     final Task rootTask = (Task) (
                             (newParent != null && !(newParent instanceof TaskDisplayArea))
                                     ? newParent : task.getRootTask());
-                    if (hop.getToTop()) {
-                        as.getDisplayArea().positionStackAtTop(rootTask,
-                                false /* includingParents */);
-                    } else {
-                        as.getDisplayArea().positionStackAtBottom(rootTask);
-                    }
+                    as.getDisplayArea().positionChildAt(
+                            hop.getToTop() ? POSITION_TOP : POSITION_BOTTOM, rootTask,
+                            false /* includingParents */);
                 }
             } else {
                 throw new RuntimeException("Reparenting leaf Tasks is not supported now. " + task);
             }
         } else {
-            // Ugh, of course ActivityStack has its own special reorder logic...
-            if (task.isRootTask()) {
-                if (hop.getToTop()) {
-                    as.getDisplayArea().positionStackAtTop(as, false /* includingParents */);
-                } else {
-                    as.getDisplayArea().positionStackAtBottom(as);
-                }
-            } else {
-                task.getParent().positionChildAt(
-                        hop.getToTop() ? POSITION_TOP : POSITION_BOTTOM,
-                        task, false /* includingParents */);
-            }
+            task.getParent().positionChildAt(
+                    hop.getToTop() ? POSITION_TOP : POSITION_BOTTOM,
+                    task, false /* includingParents */);
         }
         return TRANSACT_EFFECTS_LIFECYCLE;
     }
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 9ab1572..f3c5092 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -63,7 +63,6 @@
 import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
 import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
 import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY;
-import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVERLAY;
@@ -88,7 +87,6 @@
 import static android.view.WindowManager.LayoutParams.TYPE_PRIVATE_PRESENTATION;
 import static android.view.WindowManager.LayoutParams.TYPE_SCREENSHOT;
 import static android.view.WindowManager.LayoutParams.TYPE_SEARCH_BAR;
-import static android.view.WindowManager.LayoutParams.TYPE_SECURE_SYSTEM_OVERLAY;
 import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
 import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_ADDITIONAL;
 import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL;
@@ -954,17 +952,7 @@
         mInputWindowHandle.trustedOverlay =
                 (mAttrs.privateFlags & PRIVATE_FLAG_TRUSTED_OVERLAY) != 0
                 && mOwnerCanAddInternalSystemWindow;
-        mInputWindowHandle.trustedOverlay |=
-                mAttrs.type == TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY
-                || mAttrs.type == TYPE_INPUT_METHOD || mAttrs.type == TYPE_INPUT_METHOD_DIALOG
-                || mAttrs.type == TYPE_MAGNIFICATION_OVERLAY || mAttrs.type == TYPE_STATUS_BAR
-                || mAttrs.type == TYPE_NOTIFICATION_SHADE
-                || mAttrs.type == TYPE_NAVIGATION_BAR
-                || mAttrs.type == TYPE_NAVIGATION_BAR_PANEL
-                || mAttrs.type == TYPE_SECURE_SYSTEM_OVERLAY
-                || mAttrs.type == TYPE_DOCK_DIVIDER
-                || mAttrs.type == TYPE_ACCESSIBILITY_OVERLAY
-                || mAttrs.type == TYPE_INPUT_CONSUMER;
+        mInputWindowHandle.trustedOverlay |= InputMonitor.isTrustedOverlay(mAttrs.type);
 
         // Make sure we initial all fields before adding to parentWindow, to prevent exception
         // during onDisplayChanged.
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index c6b93d6..7ec819f 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -3570,6 +3570,17 @@
         return new JournaledFile(new File(base), new File(base + ".tmp"));
     }
 
+    /**
+     * Persist modified values to disk by calling {@link #saveSettingsLocked} for each
+     * affected user ID.
+     */
+    @GuardedBy("getLockObject()")
+    private void saveSettingsForUsersLocked(Set<Integer> affectedUserIds) {
+        for (int userId : affectedUserIds) {
+            saveSettingsLocked(userId);
+        }
+    }
+
     private void saveSettingsLocked(int userHandle) {
         DevicePolicyData policy = getUserData(userHandle);
         JournaledFile journal = makeJournaledFile(userHandle);
@@ -4785,13 +4796,15 @@
 
     /**
      * Updates a flag that tells us whether the user's password currently satisfies the
-     * requirements set by all of the user's active admins. The flag is updated both in memory
-     * and persisted to disk by calling {@link #saveSettingsLocked}, for the value of the flag
-     * be the correct one upon boot.
-     * This should be called whenever the password or the admin policies have changed.
+     * requirements set by all of the user's active admins.
+     * This should be called whenever the password or the admin policies have changed. The caller
+     * is responsible for calling {@link #saveSettingsLocked} to persist the change.
+     *
+     * @return the set of user IDs that have been affected
      */
     @GuardedBy("getLockObject()")
-    private void updatePasswordValidityCheckpointLocked(int userHandle, boolean parent) {
+    private Set<Integer> updatePasswordValidityCheckpointLocked(int userHandle, boolean parent) {
+        final ArraySet<Integer> affectedUserIds = new ArraySet<>();
         final int credentialOwner = getCredentialOwner(userHandle, parent);
         DevicePolicyData policy = getUserData(credentialOwner);
         PasswordMetrics metrics = mLockSettingsInternal.getUserPasswordMetrics(credentialOwner);
@@ -4801,9 +4814,10 @@
                     metrics, userHandle, parent);
             if (newCheckpoint != policy.mPasswordValidAtLastCheckpoint) {
                 policy.mPasswordValidAtLastCheckpoint = newCheckpoint;
-                saveSettingsLocked(credentialOwner);
+                affectedUserIds.add(credentialOwner);
             }
         }
+        return affectedUserIds;
     }
 
     /**
@@ -6175,7 +6189,8 @@
         }
     }
 
-    private void removeCaApprovalsIfNeeded(int userId) {
+    private Set<Integer> removeCaApprovalsIfNeeded(int userId) {
+        final ArraySet<Integer> affectedUserIds = new ArraySet<>();
         for (UserInfo userInfo : mUserManager.getProfiles(userId)) {
             boolean isSecure = mLockPatternUtils.isSecure(userInfo.id);
             if (userInfo.isManagedProfile()){
@@ -6184,11 +6199,12 @@
             if (!isSecure) {
                 synchronized (getLockObject()) {
                     getUserData(userInfo.id).mAcceptedCaCertificates.clear();
-                    saveSettingsLocked(userInfo.id);
+                    affectedUserIds.add(userInfo.id);
                 }
                 mCertificateMonitor.onCertificateApprovalsChanged(userId);
             }
         }
+        return affectedUserIds;
     }
 
     @Override
@@ -7458,42 +7474,45 @@
         }
 
         DevicePolicyData policy = getUserData(userId);
+        final ArraySet<Integer> affectedUserIds = new ArraySet<>();
 
         synchronized (getLockObject()) {
             policy.mFailedPasswordAttempts = 0;
-            updatePasswordValidityCheckpointLocked(userId, /* parent */ false);
-            saveSettingsLocked(userId);
-            updatePasswordExpirationsLocked(userId);
+            affectedUserIds.add(userId);
+            affectedUserIds.addAll(updatePasswordValidityCheckpointLocked(
+                    userId, /* parent */ false));
+            affectedUserIds.addAll(updatePasswordExpirationsLocked(userId));
             setExpirationAlarmCheckLocked(mContext, userId, /* parent */ false);
 
             // Send a broadcast to each profile using this password as its primary unlock.
             sendAdminCommandForLockscreenPoliciesLocked(
                     DeviceAdminReceiver.ACTION_PASSWORD_CHANGED,
                     DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD, userId);
+
+            affectedUserIds.addAll(removeCaApprovalsIfNeeded(userId));
+            saveSettingsForUsersLocked(affectedUserIds);
         }
-        removeCaApprovalsIfNeeded(userId);
     }
 
     /**
      * Called any time the device password is updated. Resets all password expiration clocks.
+     *
+     * @return the set of user IDs that have been affected
      */
-    private void updatePasswordExpirationsLocked(int userHandle) {
-        ArraySet<Integer> affectedUserIds = new ArraySet<Integer>();
+    private Set<Integer> updatePasswordExpirationsLocked(int userHandle) {
+        final ArraySet<Integer> affectedUserIds = new ArraySet<>();
         List<ActiveAdmin> admins = getActiveAdminsForLockscreenPoliciesLocked(
                 userHandle, /* parent */ false);
-        final int N = admins.size();
-        for (int i = 0; i < N; i++) {
+        for (int i = 0; i < admins.size(); i++) {
             ActiveAdmin admin = admins.get(i);
             if (admin.info.usesPolicy(DeviceAdminInfo.USES_POLICY_EXPIRE_PASSWORD)) {
                 affectedUserIds.add(admin.getUserHandle().getIdentifier());
                 long timeout = admin.passwordExpirationTimeout;
-                long expiration = timeout > 0L ? (timeout + System.currentTimeMillis()) : 0L;
-                admin.passwordExpirationDate = expiration;
+                admin.passwordExpirationDate =
+                        timeout > 0L ? (timeout + System.currentTimeMillis()) : 0L;
             }
         }
-        for (int affectedUserId : affectedUserIds) {
-            saveSettingsLocked(affectedUserId);
-        }
+        return affectedUserIds;
     }
 
     @Override
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/util/FakeAppOpsHelper.java b/services/tests/mockingservicestests/src/com/android/server/location/util/FakeAppOpsHelper.java
index da794da..e947e89 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/util/FakeAppOpsHelper.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/util/FakeAppOpsHelper.java
@@ -57,7 +57,7 @@
     }
 
     @Override
-    protected boolean startOpNoThrow(int appOp, CallerIdentity callerIdentity) {
+    public boolean startOpNoThrow(int appOp, CallerIdentity callerIdentity) {
         AppOp myAppOp = getOp(callerIdentity.getPackageName(), appOp);
         if (!myAppOp.mAllowed) {
             return false;
@@ -68,20 +68,20 @@
     }
 
     @Override
-    protected void finishOp(int appOp, CallerIdentity callerIdentity) {
+    public void finishOp(int appOp, CallerIdentity callerIdentity) {
         AppOp myAppOp = getOp(callerIdentity.getPackageName(), appOp);
         Preconditions.checkState(myAppOp.mStarted);
         myAppOp.mStarted = false;
     }
 
     @Override
-    protected boolean checkOpNoThrow(int appOp, CallerIdentity callerIdentity) {
+    public boolean checkOpNoThrow(int appOp, CallerIdentity callerIdentity) {
         AppOp myAppOp = getOp(callerIdentity.getPackageName(), appOp);
         return myAppOp.mAllowed;
     }
 
     @Override
-    protected boolean noteOp(int appOp, CallerIdentity callerIdentity) {
+    public boolean noteOp(int appOp, CallerIdentity callerIdentity) {
         if (!noteOpNoThrow(appOp, callerIdentity)) {
             throw new SecurityException(
                     "noteOp not allowed for op " + appOp + " and caller " + callerIdentity);
@@ -91,7 +91,7 @@
     }
 
     @Override
-    protected boolean noteOpNoThrow(int appOp, CallerIdentity callerIdentity) {
+    public boolean noteOpNoThrow(int appOp, CallerIdentity callerIdentity) {
         AppOp myAppOp = getOp(callerIdentity.getPackageName(), appOp);
         if (!myAppOp.mAllowed) {
             return false;
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/util/FakeLocationPermissionsHelper.java b/services/tests/mockingservicestests/src/com/android/server/location/util/FakeLocationPermissionsHelper.java
new file mode 100644
index 0000000..e7d7e31
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/location/util/FakeLocationPermissionsHelper.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.location.util;
+
+import android.location.util.identity.CallerIdentity;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Version of LocationPermissionsHelper for testing. All permissions are granted unless notified
+ * otherwise.
+ */
+public class FakeLocationPermissionsHelper extends LocationPermissionsHelper {
+
+    private final HashMap<String, Set<String>> mRevokedPermissions;
+
+    public FakeLocationPermissionsHelper(AppOpsHelper appOps) {
+        super(appOps);
+        mRevokedPermissions = new HashMap<>();
+    }
+
+    public void grantPermission(String packageName, String permission) {
+        getRevokedPermissionsList(packageName).remove(permission);
+        notifyLocationPermissionsChanged(packageName);
+    }
+
+    public void revokePermission(String packageName, String permission) {
+        getRevokedPermissionsList(packageName).add(permission);
+        notifyLocationPermissionsChanged(packageName);
+    }
+
+    @Override
+    protected boolean hasPermission(String permission, CallerIdentity identity) {
+        return !getRevokedPermissionsList(identity.getPackageName()).contains(permission);
+    }
+
+    private Set<String> getRevokedPermissionsList(String packageName) {
+        return mRevokedPermissions.computeIfAbsent(packageName, p -> new HashSet<>());
+    }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/util/FakeLocationPowerSaveModeHelper.java b/services/tests/mockingservicestests/src/com/android/server/location/util/FakeLocationPowerSaveModeHelper.java
new file mode 100644
index 0000000..3ead5d4
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/location/util/FakeLocationPowerSaveModeHelper.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.location.util;
+
+import android.os.IPowerManager;
+import android.os.PowerManager.LocationPowerSaveMode;
+
+/**
+ * Version of LocationPowerSaveModeHelper for testing. Power save mode is initialized as "no
+ * change".
+ */
+public class FakeLocationPowerSaveModeHelper extends LocationPowerSaveModeHelper {
+
+    @LocationPowerSaveMode
+    private int mLocationPowerSaveMode;
+
+    public FakeLocationPowerSaveModeHelper() {
+        mLocationPowerSaveMode = IPowerManager.LOCATION_MODE_NO_CHANGE;
+    }
+
+    public void setLocationPowerSaveMode(int locationPowerSaveMode) {
+        if (locationPowerSaveMode == mLocationPowerSaveMode) {
+            return;
+        }
+
+        mLocationPowerSaveMode = locationPowerSaveMode;
+        notifyLocationPowerSaveModeChanged(locationPowerSaveMode);
+    }
+
+    @LocationPowerSaveMode
+    @Override
+    public int getLocationPowerSaveMode() {
+        return mLocationPowerSaveMode;
+    }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/util/FakeScreenInteractiveHelper.java b/services/tests/mockingservicestests/src/com/android/server/location/util/FakeScreenInteractiveHelper.java
new file mode 100644
index 0000000..df697fa
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/location/util/FakeScreenInteractiveHelper.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.location.util;
+
+/**
+ * Version of ScreenInteractiveHelper for testing. Screen is initialized as interactive (on).
+ */
+public class FakeScreenInteractiveHelper extends ScreenInteractiveHelper {
+
+    private boolean mIsInteractive;
+
+    public FakeScreenInteractiveHelper() {
+        mIsInteractive = true;
+    }
+
+    public void setScreenInteractive(boolean interactive) {
+        if (interactive == mIsInteractive) {
+            return;
+        }
+
+        mIsInteractive = interactive;
+        notifyScreenInteractiveChanged(interactive);
+    }
+
+    public boolean isInteractive() {
+        return mIsInteractive;
+    }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/util/FakeSettingsHelper.java b/services/tests/mockingservicestests/src/com/android/server/location/util/FakeSettingsHelper.java
index 726b1b8..1d0523f 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/util/FakeSettingsHelper.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/util/FakeSettingsHelper.java
@@ -90,7 +90,7 @@
 
     @Override
     public boolean isLocationEnabled(int userId) {
-        return mLocationEnabledSetting.getValue(Boolean.class);
+        return mLocationEnabledSetting.getValue(userId, Boolean.class);
     }
 
     @Override
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/util/FakeUserInfoHelper.java b/services/tests/mockingservicestests/src/com/android/server/location/util/FakeUserInfoHelper.java
index 336e28c..f5978da 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/util/FakeUserInfoHelper.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/util/FakeUserInfoHelper.java
@@ -16,7 +16,6 @@
 
 package com.android.server.location.util;
 
-import android.os.Process;
 import android.util.IndentingPrintWriter;
 import android.util.IntArray;
 import android.util.SparseArray;
@@ -27,19 +26,21 @@
 import java.io.FileDescriptor;
 
 /**
- * Version of UserInfoHelper for testing. The user this code is running under is set as the current
- * user by default, with no profiles.
+ * Version of UserInfoHelper for testing. By default there is one user that starts in a running
+ * state with a userId of 0;
  */
 public class FakeUserInfoHelper extends UserInfoHelper {
 
+    public static final int DEFAULT_USERID = 0;
+
     private final IntArray mRunningUserIds;
     private final SparseArray<IntArray> mProfiles;
 
     private int mCurrentUserId;
 
     public FakeUserInfoHelper() {
-        mCurrentUserId = Process.myUserHandle().getIdentifier();
-        mRunningUserIds = IntArray.wrap(new int[]{mCurrentUserId});
+        mCurrentUserId = DEFAULT_USERID;
+        mRunningUserIds = IntArray.wrap(new int[]{DEFAULT_USERID});
         mProfiles = new SparseArray<>();
     }
 
@@ -67,6 +68,10 @@
         dispatchOnUserStopped(userId);
     }
 
+    public void setCurrentUserId(int parentUser) {
+        setCurrentUserIds(parentUser, new int[]{parentUser});
+    }
+
     public void setCurrentUserIds(int parentUser, int[] currentProfileUserIds) {
         Preconditions.checkArgument(ArrayUtils.contains(currentProfileUserIds, parentUser));
         int oldUserId = mCurrentUserId;
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/util/LocationAttributionHelperTest.java b/services/tests/mockingservicestests/src/com/android/server/location/util/LocationAttributionHelperTest.java
index e6f6252..4165b6e 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/util/LocationAttributionHelperTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/util/LocationAttributionHelperTest.java
@@ -16,7 +16,11 @@
 
 package com.android.server.location.util;
 
+import static android.app.AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION;
+import static android.app.AppOpsManager.OP_MONITOR_LOCATION;
+
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -46,9 +50,7 @@
     public void setUp() {
         initMocks(this);
 
-        when(mAppOpsHelper.startLocationMonitoring(any(CallerIdentity.class))).thenReturn(true);
-        when(mAppOpsHelper.startHighPowerLocationMonitoring(any(CallerIdentity.class))).thenReturn(
-                true);
+        when(mAppOpsHelper.startOpNoThrow(anyInt(), any(CallerIdentity.class))).thenReturn(true);
 
         mHelper = new LocationAttributionHelper(mAppOpsHelper);
     }
@@ -63,30 +65,30 @@
         Object key4 = new Object();
 
         mHelper.reportLocationStart(caller1, "gps", key1);
-        verify(mAppOpsHelper).startLocationMonitoring(caller1);
-        verify(mAppOpsHelper, never()).stopLocationMonitoring(caller1);
+        verify(mAppOpsHelper).startOpNoThrow(OP_MONITOR_LOCATION, caller1);
+        verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_LOCATION, caller1);
 
         mHelper.reportLocationStart(caller1, "gps", key2);
-        verify(mAppOpsHelper).startLocationMonitoring(caller1);
-        verify(mAppOpsHelper, never()).stopLocationMonitoring(caller1);
+        verify(mAppOpsHelper).startOpNoThrow(OP_MONITOR_LOCATION, caller1);
+        verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_LOCATION, caller1);
 
         mHelper.reportLocationStart(caller2, "gps", key3);
-        verify(mAppOpsHelper).startLocationMonitoring(caller2);
-        verify(mAppOpsHelper, never()).stopLocationMonitoring(caller2);
+        verify(mAppOpsHelper).startOpNoThrow(OP_MONITOR_LOCATION, caller2);
+        verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_LOCATION, caller2);
 
         mHelper.reportLocationStart(caller2, "gps", key4);
-        verify(mAppOpsHelper).startLocationMonitoring(caller2);
-        verify(mAppOpsHelper, never()).stopLocationMonitoring(caller2);
+        verify(mAppOpsHelper).startOpNoThrow(OP_MONITOR_LOCATION, caller2);
+        verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_LOCATION, caller2);
 
         mHelper.reportLocationStop(caller1, "gps", key2);
-        verify(mAppOpsHelper, never()).stopLocationMonitoring(caller1);
+        verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_LOCATION, caller1);
         mHelper.reportLocationStop(caller1, "gps", key1);
-        verify(mAppOpsHelper).stopLocationMonitoring(caller1);
+        verify(mAppOpsHelper).finishOp(OP_MONITOR_LOCATION, caller1);
 
         mHelper.reportLocationStop(caller2, "gps", key3);
-        verify(mAppOpsHelper, never()).stopLocationMonitoring(caller2);
+        verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_LOCATION, caller2);
         mHelper.reportLocationStop(caller2, "gps", key4);
-        verify(mAppOpsHelper).stopLocationMonitoring(caller2);
+        verify(mAppOpsHelper).finishOp(OP_MONITOR_LOCATION, caller2);
     }
 
     @Test
@@ -99,29 +101,29 @@
         Object key4 = new Object();
 
         mHelper.reportHighPowerLocationStart(caller1, "gps", key1);
-        verify(mAppOpsHelper).startHighPowerLocationMonitoring(caller1);
-        verify(mAppOpsHelper, never()).stopHighPowerLocationMonitoring(caller1);
+        verify(mAppOpsHelper).startOpNoThrow(OP_MONITOR_HIGH_POWER_LOCATION, caller1);
+        verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_HIGH_POWER_LOCATION, caller1);
 
         mHelper.reportHighPowerLocationStart(caller1, "gps", key2);
-        verify(mAppOpsHelper).startHighPowerLocationMonitoring(caller1);
-        verify(mAppOpsHelper, never()).stopHighPowerLocationMonitoring(caller1);
+        verify(mAppOpsHelper).startOpNoThrow(OP_MONITOR_HIGH_POWER_LOCATION, caller1);
+        verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_HIGH_POWER_LOCATION, caller1);
 
         mHelper.reportHighPowerLocationStart(caller2, "gps", key3);
-        verify(mAppOpsHelper).startHighPowerLocationMonitoring(caller2);
-        verify(mAppOpsHelper, never()).stopHighPowerLocationMonitoring(caller2);
+        verify(mAppOpsHelper).startOpNoThrow(OP_MONITOR_HIGH_POWER_LOCATION, caller2);
+        verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_HIGH_POWER_LOCATION, caller2);
 
         mHelper.reportHighPowerLocationStart(caller2, "gps", key4);
-        verify(mAppOpsHelper).startHighPowerLocationMonitoring(caller2);
-        verify(mAppOpsHelper, never()).stopHighPowerLocationMonitoring(caller2);
+        verify(mAppOpsHelper).startOpNoThrow(OP_MONITOR_HIGH_POWER_LOCATION, caller2);
+        verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_HIGH_POWER_LOCATION, caller2);
 
         mHelper.reportHighPowerLocationStop(caller1, "gps", key2);
-        verify(mAppOpsHelper, never()).stopHighPowerLocationMonitoring(caller1);
+        verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_HIGH_POWER_LOCATION, caller1);
         mHelper.reportHighPowerLocationStop(caller1, "gps", key1);
-        verify(mAppOpsHelper).stopHighPowerLocationMonitoring(caller1);
+        verify(mAppOpsHelper).finishOp(OP_MONITOR_HIGH_POWER_LOCATION, caller1);
 
         mHelper.reportHighPowerLocationStop(caller2, "gps", key3);
-        verify(mAppOpsHelper, never()).stopHighPowerLocationMonitoring(caller2);
+        verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_HIGH_POWER_LOCATION, caller2);
         mHelper.reportHighPowerLocationStop(caller2, "gps", key4);
-        verify(mAppOpsHelper).stopHighPowerLocationMonitoring(caller2);
+        verify(mAppOpsHelper).finishOp(OP_MONITOR_HIGH_POWER_LOCATION, caller2);
     }
 }
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/util/SystemAppOpsHelperTest.java b/services/tests/mockingservicestests/src/com/android/server/location/util/SystemAppOpsHelperTest.java
index f40d316..093aa2e 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/util/SystemAppOpsHelperTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/util/SystemAppOpsHelperTest.java
@@ -20,12 +20,8 @@
 import static android.app.AppOpsManager.OP_COARSE_LOCATION;
 import static android.app.AppOpsManager.OP_FINE_LOCATION;
 import static android.app.AppOpsManager.OP_MOCK_LOCATION;
-import static android.app.AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION;
 import static android.app.AppOpsManager.OP_MONITOR_LOCATION;
 
-import static com.android.server.location.LocationPermissions.PERMISSION_COARSE;
-import static com.android.server.location.LocationPermissions.PERMISSION_FINE;
-
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.ArgumentMatchers.any;
@@ -34,10 +30,12 @@
 import static org.mockito.ArgumentMatchers.nullable;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.doThrow;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.timeout;
 import static org.mockito.Mockito.verify;
 import static org.mockito.MockitoAnnotations.initMocks;
+import static org.testng.Assert.assertThrows;
 
 import android.app.AppOpsManager;
 import android.content.Context;
@@ -105,41 +103,41 @@
     }
 
     @Test
-    public void testCheckLocationAccess() {
+    public void testCheckOp() {
         CallerIdentity identity = CallerIdentity.forTest(1000, 1000, "mypackage", "myfeature");
 
         doReturn(MODE_ALLOWED).when(
                 mAppOps).checkOpNoThrow(eq(OP_FINE_LOCATION), eq(1000), eq("mypackage"));
-        assertThat(mHelper.checkLocationAccess(identity, PERMISSION_FINE)).isTrue();
+        assertThat(mHelper.checkOpNoThrow(OP_FINE_LOCATION, identity)).isTrue();
 
         doReturn(MODE_IGNORED).when(
                 mAppOps).checkOpNoThrow(eq(OP_FINE_LOCATION), eq(1000), eq("mypackage"));
-        assertThat(mHelper.checkLocationAccess(identity, PERMISSION_FINE)).isFalse();
+        assertThat(mHelper.checkOpNoThrow(OP_FINE_LOCATION, identity)).isFalse();
 
         identity = CallerIdentity.forTest(1000, 1000, "mypackage", "myfeature");
 
         doReturn(MODE_ALLOWED).when(
                 mAppOps).checkOpNoThrow(eq(OP_COARSE_LOCATION), eq(1000), eq("mypackage"));
-        assertThat(mHelper.checkLocationAccess(identity, PERMISSION_COARSE)).isTrue();
+        assertThat(mHelper.checkOpNoThrow(OP_COARSE_LOCATION, identity)).isTrue();
 
         doReturn(MODE_IGNORED).when(
                 mAppOps).checkOpNoThrow(eq(OP_COARSE_LOCATION), eq(1000), eq("mypackage"));
-        assertThat(mHelper.checkLocationAccess(identity, PERMISSION_COARSE)).isFalse();
+        assertThat(mHelper.checkOpNoThrow(OP_COARSE_LOCATION, identity)).isFalse();
     }
 
     @Test
-    public void testNoteLocationAccess() {
+    public void testNoteOpNoThrow() {
         CallerIdentity identity = CallerIdentity.forTest(1000, 1000, "mypackage", "myfeature");
 
         doReturn(MODE_ALLOWED).when(
                 mAppOps).noteOpNoThrow(eq(OP_FINE_LOCATION), eq(1000), eq("mypackage"),
                         eq("myfeature"), nullable(String.class));
-        assertThat(mHelper.noteLocationAccess(identity, PERMISSION_FINE)).isTrue();
+        assertThat(mHelper.noteOpNoThrow(OP_FINE_LOCATION, identity)).isTrue();
 
         doReturn(MODE_IGNORED).when(
                 mAppOps).noteOpNoThrow(eq(OP_FINE_LOCATION), eq(1000), eq("mypackage"),
                         eq("myfeature"), nullable(String.class));
-        assertThat(mHelper.noteLocationAccess(identity, PERMISSION_FINE)).isFalse();
+        assertThat(mHelper.noteOpNoThrow(OP_FINE_LOCATION, identity)).isFalse();
 
 
         identity = CallerIdentity.forTest(1000, 1000, "mypackage", "myfeature");
@@ -147,74 +145,55 @@
         doReturn(MODE_ALLOWED).when(
                 mAppOps).noteOpNoThrow(eq(OP_COARSE_LOCATION), eq(1000), eq("mypackage"),
                         eq("myfeature"), nullable(String.class));
-        assertThat(mHelper.noteLocationAccess(identity, PERMISSION_COARSE)).isTrue();
+        assertThat(mHelper.noteOpNoThrow(OP_COARSE_LOCATION, identity)).isTrue();
 
         doReturn(MODE_IGNORED).when(
                 mAppOps).noteOpNoThrow(eq(OP_COARSE_LOCATION), eq(1000), eq("mypackage"),
                         eq("myfeature"), nullable(String.class));
-        assertThat(mHelper.noteLocationAccess(identity, PERMISSION_COARSE)).isFalse();
+        assertThat(mHelper.noteOpNoThrow(OP_COARSE_LOCATION, identity)).isFalse();
     }
 
     @Test
-    public void testStartLocationMonitoring() {
+    public void testStartOp() {
         CallerIdentity identity = CallerIdentity.forTest(1000, 1000, "mypackage", "myfeature");
 
         doReturn(MODE_ALLOWED).when(
                 mAppOps).startOpNoThrow(eq(OP_MONITOR_LOCATION), eq(1000), eq("mypackage"),
                         eq(false), eq("myfeature"), nullable(String.class));
-        assertThat(mHelper.startLocationMonitoring(identity)).isTrue();
+        assertThat(mHelper.startOpNoThrow(OP_MONITOR_LOCATION, identity)).isTrue();
 
         doReturn(MODE_IGNORED).when(
                 mAppOps).startOpNoThrow(eq(OP_MONITOR_LOCATION), eq(1000), eq("mypackage"),
                         eq(false), eq("myfeature"), nullable(String.class));
-        assertThat(mHelper.startLocationMonitoring(identity)).isFalse();
+        assertThat(mHelper.startOpNoThrow(OP_MONITOR_LOCATION, identity)).isFalse();
     }
 
     @Test
-    public void testStartHighPowerLocationMonitoring() {
+    public void testFinishOp() {
         CallerIdentity identity = CallerIdentity.forTest(1000, 1000, "mypackage", "myfeature");
 
-        doReturn(MODE_ALLOWED).when(
-                mAppOps).startOpNoThrow(eq(OP_MONITOR_HIGH_POWER_LOCATION), eq(1000),
-                        eq("mypackage"),
-                        eq(false), eq("myfeature"), nullable(String.class));
-        assertThat(mHelper.startHighPowerLocationMonitoring(identity)).isTrue();
-
-        doReturn(MODE_IGNORED).when(
-                mAppOps).startOpNoThrow(eq(OP_MONITOR_HIGH_POWER_LOCATION), eq(1000),
-                        eq("mypackage"),
-                        eq(false), eq("myfeature"), nullable(String.class));
-        assertThat(mHelper.startHighPowerLocationMonitoring(identity)).isFalse();
-    }
-
-    @Test
-    public void testStopLocationMonitoring() {
-        CallerIdentity identity = CallerIdentity.forTest(1000, 1000, "mypackage", "myfeature");
-
-        mHelper.stopLocationMonitoring(identity);
+        mHelper.finishOp(OP_MONITOR_LOCATION, identity);
         verify(mAppOps).finishOp(OP_MONITOR_LOCATION, 1000, "mypackage", "myfeature");
     }
 
     @Test
-    public void testStopHighPowerLocationMonitoring() {
-        CallerIdentity identity = CallerIdentity.forTest(1000, 1000, "mypackage", "myfeature");
-
-        mHelper.stopHighPowerLocationMonitoring(identity);
-        verify(mAppOps).finishOp(OP_MONITOR_HIGH_POWER_LOCATION, 1000, "mypackage", "myfeature");
-    }
-
-    @Test
-    public void testNoteMockLocationAccess() {
+    public void testNoteOp() {
         CallerIdentity identity = CallerIdentity.forTest(1000, 1000, "mypackage", "myfeature");
 
         doReturn(MODE_ALLOWED).when(
                 mAppOps).noteOp(eq(OP_MOCK_LOCATION), eq(1000), eq("mypackage"), eq("myfeature"),
                         nullable(String.class));
-        assertThat(mHelper.noteMockLocationAccess(identity)).isTrue();
+        assertThat(mHelper.noteOp(OP_MOCK_LOCATION, identity)).isTrue();
 
         doReturn(MODE_IGNORED).when(
                 mAppOps).noteOp(eq(OP_MOCK_LOCATION), eq(1000), eq("mypackage"), eq("myfeature"),
                         nullable(String.class));
-        assertThat(mHelper.noteMockLocationAccess(identity)).isFalse();
+        assertThat(mHelper.noteOp(OP_MOCK_LOCATION, identity)).isFalse();
+
+
+        doThrow(new SecurityException()).when(
+                mAppOps).noteOp(eq(OP_MOCK_LOCATION), eq(1000), eq("mypackage"), eq("myfeature"),
+                nullable(String.class));
+        assertThrows(SecurityException.class, () -> mHelper.noteOp(OP_MOCK_LOCATION, identity));
     }
 }
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/util/SystemLocationPowerSaveModeHelperTest.java b/services/tests/mockingservicestests/src/com/android/server/location/util/SystemLocationPowerSaveModeHelperTest.java
new file mode 100644
index 0000000..2acb70c
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/location/util/SystemLocationPowerSaveModeHelperTest.java
@@ -0,0 +1,170 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.location.util;
+
+import static android.os.PowerManager.LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF;
+import static android.os.PowerManager.LOCATION_MODE_FOREGROUND_ONLY;
+import static android.os.PowerManager.LOCATION_MODE_GPS_DISABLED_WHEN_SCREEN_OFF;
+import static android.os.PowerManager.LOCATION_MODE_NO_CHANGE;
+import static android.os.PowerManager.LOCATION_MODE_THROTTLE_REQUESTS_WHEN_SCREEN_OFF;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.after;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.timeout;
+import static org.mockito.Mockito.verify;
+import static org.mockito.MockitoAnnotations.initMocks;
+
+import android.content.Context;
+import android.os.PowerManager;
+import android.os.PowerManagerInternal;
+import android.os.PowerSaveState;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.LocalServices;
+import com.android.server.location.util.LocationPowerSaveModeHelper.LocationPowerSaveModeChangedListener;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Consumer;
+
+@Presubmit
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class SystemLocationPowerSaveModeHelperTest {
+
+    private static final long TIMEOUT_MS = 5000;
+    private static final long FAILURE_TIMEOUT_MS = 200;
+
+    @Mock
+    private PowerManagerInternal mPowerManagerInternal;
+
+    private List<Consumer<PowerSaveState>> mListeners = new ArrayList<>();
+
+    private SystemLocationPowerSaveModeHelper mHelper;
+
+    @Before
+    public void setUp() {
+        initMocks(this);
+
+        LocalServices.addService(PowerManagerInternal.class, mPowerManagerInternal);
+
+        doAnswer(invocation -> mListeners.add(invocation.getArgument(1))).when(
+                mPowerManagerInternal).registerLowPowerModeObserver(anyInt(), any(Consumer.class));
+
+        PowerManager powerManager = mock(PowerManager.class);
+        doReturn(LOCATION_MODE_NO_CHANGE).when(powerManager).getLocationPowerSaveMode();
+        Context context = mock(Context.class);
+        doReturn(powerManager).when(context).getSystemService(PowerManager.class);
+
+        mHelper = new SystemLocationPowerSaveModeHelper(context);
+        mHelper.onSystemReady();
+    }
+
+    @After
+    public void tearDown() {
+        LocalServices.removeServiceForTest(PowerManagerInternal.class);
+    }
+
+    private void sendPowerSaveState(PowerSaveState powerSaveState) {
+        for (Consumer<PowerSaveState> listener : mListeners) {
+            listener.accept(powerSaveState);
+        }
+    }
+
+    @Test
+    public void testListener() {
+        LocationPowerSaveModeChangedListener listener = mock(
+                LocationPowerSaveModeChangedListener.class);
+        mHelper.addListener(listener);
+
+        sendPowerSaveState(new PowerSaveState.Builder().setLocationMode(
+                LOCATION_MODE_NO_CHANGE).setBatterySaverEnabled(false).build());
+        verify(listener, after(FAILURE_TIMEOUT_MS).never()).onLocationPowerSaveModeChanged(
+                anyInt());
+        assertThat(mHelper.getLocationPowerSaveMode()).isEqualTo(LOCATION_MODE_NO_CHANGE);
+        sendPowerSaveState(new PowerSaveState.Builder().setLocationMode(
+                LOCATION_MODE_GPS_DISABLED_WHEN_SCREEN_OFF).setBatterySaverEnabled(false).build());
+        verify(listener, after(FAILURE_TIMEOUT_MS).never()).onLocationPowerSaveModeChanged(
+                anyInt());
+        assertThat(mHelper.getLocationPowerSaveMode()).isEqualTo(LOCATION_MODE_NO_CHANGE);
+        sendPowerSaveState(new PowerSaveState.Builder().setLocationMode(
+                LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF).setBatterySaverEnabled(false).build());
+        verify(listener, after(FAILURE_TIMEOUT_MS).never()).onLocationPowerSaveModeChanged(
+                anyInt());
+        assertThat(mHelper.getLocationPowerSaveMode()).isEqualTo(LOCATION_MODE_NO_CHANGE);
+        sendPowerSaveState(new PowerSaveState.Builder().setLocationMode(
+                LOCATION_MODE_FOREGROUND_ONLY).setBatterySaverEnabled(false).build());
+        verify(listener, after(FAILURE_TIMEOUT_MS).never()).onLocationPowerSaveModeChanged(
+                anyInt());
+        assertThat(mHelper.getLocationPowerSaveMode()).isEqualTo(LOCATION_MODE_NO_CHANGE);
+        sendPowerSaveState(new PowerSaveState.Builder().setLocationMode(
+                LOCATION_MODE_THROTTLE_REQUESTS_WHEN_SCREEN_OFF).setBatterySaverEnabled(
+                false).build());
+        verify(listener, after(FAILURE_TIMEOUT_MS).never()).onLocationPowerSaveModeChanged(
+                anyInt());
+        assertThat(mHelper.getLocationPowerSaveMode()).isEqualTo(LOCATION_MODE_NO_CHANGE);
+
+        sendPowerSaveState(new PowerSaveState.Builder().setLocationMode(
+                LOCATION_MODE_NO_CHANGE).setBatterySaverEnabled(true).build());
+        verify(listener, after(FAILURE_TIMEOUT_MS).never()).onLocationPowerSaveModeChanged(
+                anyInt());
+        assertThat(mHelper.getLocationPowerSaveMode()).isEqualTo(LOCATION_MODE_NO_CHANGE);
+        sendPowerSaveState(new PowerSaveState.Builder().setLocationMode(
+                LOCATION_MODE_GPS_DISABLED_WHEN_SCREEN_OFF).setBatterySaverEnabled(true).build());
+        verify(listener, timeout(TIMEOUT_MS)).onLocationPowerSaveModeChanged(
+                LOCATION_MODE_GPS_DISABLED_WHEN_SCREEN_OFF);
+        assertThat(mHelper.getLocationPowerSaveMode()).isEqualTo(
+                LOCATION_MODE_GPS_DISABLED_WHEN_SCREEN_OFF);
+        sendPowerSaveState(new PowerSaveState.Builder().setLocationMode(
+                LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF).setBatterySaverEnabled(true).build());
+        verify(listener, timeout(TIMEOUT_MS)).onLocationPowerSaveModeChanged(
+                LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF);
+        assertThat(mHelper.getLocationPowerSaveMode()).isEqualTo(
+                LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF);
+        sendPowerSaveState(new PowerSaveState.Builder().setLocationMode(
+                LOCATION_MODE_FOREGROUND_ONLY).setBatterySaverEnabled(true).build());
+        verify(listener, timeout(TIMEOUT_MS)).onLocationPowerSaveModeChanged(
+                LOCATION_MODE_FOREGROUND_ONLY);
+        assertThat(mHelper.getLocationPowerSaveMode()).isEqualTo(LOCATION_MODE_FOREGROUND_ONLY);
+        sendPowerSaveState(new PowerSaveState.Builder().setLocationMode(
+                LOCATION_MODE_THROTTLE_REQUESTS_WHEN_SCREEN_OFF).setBatterySaverEnabled(
+                true).build());
+        verify(listener, timeout(TIMEOUT_MS)).onLocationPowerSaveModeChanged(
+                LOCATION_MODE_THROTTLE_REQUESTS_WHEN_SCREEN_OFF);
+        assertThat(mHelper.getLocationPowerSaveMode()).isEqualTo(
+                LOCATION_MODE_THROTTLE_REQUESTS_WHEN_SCREEN_OFF);
+        sendPowerSaveState(new PowerSaveState.Builder().setLocationMode(
+                LOCATION_MODE_NO_CHANGE).setBatterySaverEnabled(true).build());
+        verify(listener, timeout(TIMEOUT_MS)).onLocationPowerSaveModeChanged(
+                LOCATION_MODE_NO_CHANGE);
+        assertThat(mHelper.getLocationPowerSaveMode()).isEqualTo(LOCATION_MODE_NO_CHANGE);
+    }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/util/TestInjector.java b/services/tests/mockingservicestests/src/com/android/server/location/util/TestInjector.java
index c22dc10..1867be0 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/util/TestInjector.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/util/TestInjector.java
@@ -16,22 +16,32 @@
 
 package com.android.server.location.util;
 
+import com.android.server.location.LocationRequestStatistics;
+
 public class TestInjector implements Injector {
 
     private final FakeUserInfoHelper mUserInfoHelper;
     private final FakeAppOpsHelper mAppOpsHelper;
+    private final FakeLocationPermissionsHelper mLocationPermissionsHelper;
     private final FakeSettingsHelper mSettingsHelper;
     private final FakeAppForegroundHelper mAppForegroundHelper;
-    private final LocationUsageLogger mLocationUsageLogger;
+    private final FakeLocationPowerSaveModeHelper mLocationPowerSaveModeHelper;
+    private final FakeScreenInteractiveHelper mScreenInteractiveHelper;
     private final LocationAttributionHelper mLocationAttributionHelper;
+    private final LocationUsageLogger mLocationUsageLogger;
+    private final LocationRequestStatistics mLocationRequestStatistics;
 
     public TestInjector() {
         mUserInfoHelper = new FakeUserInfoHelper();
         mAppOpsHelper = new FakeAppOpsHelper();
+        mLocationPermissionsHelper = new FakeLocationPermissionsHelper(mAppOpsHelper);
         mSettingsHelper = new FakeSettingsHelper();
         mAppForegroundHelper = new FakeAppForegroundHelper();
-        mLocationUsageLogger = new LocationUsageLogger();
+        mLocationPowerSaveModeHelper = new FakeLocationPowerSaveModeHelper();
+        mScreenInteractiveHelper = new FakeScreenInteractiveHelper();
         mLocationAttributionHelper = new LocationAttributionHelper(mAppOpsHelper);
+        mLocationUsageLogger = new LocationUsageLogger();
+        mLocationRequestStatistics = new LocationRequestStatistics();
     }
 
     @Override
@@ -45,6 +55,11 @@
     }
 
     @Override
+    public FakeLocationPermissionsHelper getLocationPermissionsHelper() {
+        return mLocationPermissionsHelper;
+    }
+
+    @Override
     public FakeSettingsHelper getSettingsHelper() {
         return mSettingsHelper;
     }
@@ -55,12 +70,27 @@
     }
 
     @Override
-    public LocationUsageLogger getLocationUsageLogger() {
-        return mLocationUsageLogger;
+    public FakeLocationPowerSaveModeHelper getLocationPowerSaveModeHelper() {
+        return mLocationPowerSaveModeHelper;
+    }
+
+    @Override
+    public FakeScreenInteractiveHelper getScreenInteractiveHelper() {
+        return mScreenInteractiveHelper;
     }
 
     @Override
     public LocationAttributionHelper getLocationAttributionHelper() {
         return mLocationAttributionHelper;
     }
+
+    @Override
+    public LocationUsageLogger getLocationUsageLogger() {
+        return mLocationUsageLogger;
+    }
+
+    @Override
+    public LocationRequestStatistics getLocationRequestStatistics() {
+        return mLocationRequestStatistics;
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/GestureLauncherServiceTest.java b/services/tests/servicestests/src/com/android/server/GestureLauncherServiceTest.java
index 4fbc587..6f37ff5 100644
--- a/services/tests/servicestests/src/com/android/server/GestureLauncherServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/GestureLauncherServiceTest.java
@@ -153,6 +153,20 @@
     }
 
     @Test
+    public void testIsPanicButtonGestureEnabled_settingDisabled() {
+        withPanicGestureEnabledSettingValue(false);
+        assertFalse(mGestureLauncherService.isPanicButtonGestureEnabled(
+                mContext, FAKE_USER_ID));
+    }
+
+    @Test
+    public void testIsPanicButtonGestureEnabled_settingEnabled() {
+        withPanicGestureEnabledSettingValue(true);
+        assertTrue(mGestureLauncherService.isPanicButtonGestureEnabled(
+                mContext, FAKE_USER_ID));
+    }
+
+    @Test
     public void testHandleCameraLaunchGesture_userSetupComplete() {
         withUserSetupCompleteValue(true);
 
@@ -882,6 +896,14 @@
                 UserHandle.USER_CURRENT);
     }
 
+    private void withPanicGestureEnabledSettingValue(boolean enable) {
+        Settings.Secure.putIntForUser(
+                mContentResolver,
+                Settings.Secure.PANIC_GESTURE_ENABLED,
+                enable ? 1 : 0,
+                UserHandle.USER_CURRENT);
+    }
+
     private void withUserSetupCompleteValue(boolean userSetupComplete) {
         int userSetupCompleteValue = userSetupComplete ? 1 : 0;
         Settings.Secure.putIntForUser(
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 daaabf8..9a465a91 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -48,8 +48,6 @@
 import static org.mockito.Matchers.anyString;
 import static org.mockito.Matchers.eq;
 import static org.mockito.Matchers.isNull;
-import static org.mockito.Mockito.atLeast;
-import static org.mockito.Mockito.atMost;
 import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.doReturn;
@@ -4931,20 +4929,7 @@
                 .thenReturn(passwordMetrics);
         dpm.reportPasswordChanged(userHandle);
 
-        // Drain ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED broadcasts as part of
-        // reportPasswordChanged()
-        // This broadcast should be sent 2-4 times:
-        // * Twice from calls to DevicePolicyManagerService.updatePasswordExpirationsLocked,
-        //   once for each affected user, in DevicePolicyManagerService.reportPasswordChanged.
-        // * Optionally, at most twice from calls to DevicePolicyManagerService.saveSettingsLocked
-        //   in DevicePolicyManagerService.reportPasswordChanged, once with the userId
-        //   the password change is relevant to and another with the credential owner of said
-        //   userId, if the password checkpoint value changes.
-        verify(mContext.spiedContext, atMost(4)).sendBroadcastAsUser(
-                MockUtils.checkIntentAction(
-                        DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED),
-                MockUtils.checkUserHandle(userHandle));
-        verify(mContext.spiedContext, atLeast(2)).sendBroadcastAsUser(
+        verify(mContext.spiedContext, times(1)).sendBroadcastAsUser(
                 MockUtils.checkIntentAction(
                         DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED),
                 MockUtils.checkUserHandle(userHandle));
diff --git a/services/tests/servicestests/src/com/android/server/uri/UriGrantsManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/uri/UriGrantsManagerServiceTest.java
index 62b6a65..614949c 100644
--- a/services/tests/servicestests/src/com/android/server/uri/UriGrantsManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/uri/UriGrantsManagerServiceTest.java
@@ -43,11 +43,19 @@
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.isNull;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
 
 import android.content.ClipData;
 import android.content.Intent;
 import android.content.pm.ProviderInfo;
 import android.net.Uri;
+import android.os.UserHandle;
 import android.util.ArraySet;
 
 import androidx.test.InstrumentationRegistry;
@@ -62,6 +70,12 @@
     private UriGrantsMockContext mContext;
     private UriGrantsManagerInternal mService;
 
+    // we expect the following only during grant if a grant is expected
+    private void verifyNoVisibilityGrant() {
+        verify(mContext.mPmInternal, never())
+                .grantImplicitAccess(anyInt(), any(), anyInt(), anyInt(), anyBoolean());
+    }
+
     @Before
     public void setUp() throws Exception {
         mContext = new UriGrantsMockContext(InstrumentationRegistry.getContext());
@@ -83,6 +97,7 @@
         assertEquals(UID_PRIMARY_SOCIAL, needed.targetUid);
         assertEquals(FLAG_READ, needed.flags);
         assertEquals(asSet(expectedGrant), needed.uris);
+        verifyNoVisibilityGrant();
     }
 
     /**
@@ -100,6 +115,7 @@
         assertEquals(UID_SECONDARY_SOCIAL, needed.targetUid);
         assertEquals(FLAG_READ, needed.flags);
         assertEquals(asSet(expectedGrant), needed.uris);
+        verifyNoVisibilityGrant();
     }
 
     /**
@@ -111,6 +127,8 @@
         final NeededUriGrants needed = mService.checkGrantUriPermissionFromIntent(
                 intent, UID_PRIMARY_PUBLIC, PKG_SOCIAL, USER_PRIMARY);
         assertNull(needed);
+        verify(mContext.mPmInternal).grantImplicitAccess(eq(USER_PRIMARY), isNull(), eq(
+                UserHandle.getAppId(UID_PRIMARY_SOCIAL)), eq(UID_PRIMARY_PUBLIC), eq(false));
     }
 
     /**
@@ -128,6 +146,7 @@
         assertEquals(UID_SECONDARY_SOCIAL, needed.targetUid);
         assertEquals(FLAG_READ, needed.flags);
         assertEquals(asSet(expectedGrant), needed.uris);
+        verifyNoVisibilityGrant();
     }
 
     /**
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 16aa87b..10976882 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -385,7 +385,7 @@
         MockitoAnnotations.initMocks(this);
 
         DeviceIdleInternal deviceIdleInternal = mock(DeviceIdleInternal.class);
-        when(deviceIdleInternal.getNotificationWhitelistDuration()).thenReturn(3000L);
+        when(deviceIdleInternal.getNotificationAllowlistDuration()).thenReturn(3000L);
         ActivityManagerInternal activityManagerInternal = mock(ActivityManagerInternal.class);
 
         LocalServices.removeServiceForTest(UriGrantsManagerInternal.class);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationShellCmdTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationShellCmdTest.java
index 0006193..fcff228 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationShellCmdTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationShellCmdTest.java
@@ -215,20 +215,14 @@
             "Charlotte"
     };
     static final String[] MESSAGES = {
-            "Shall I compare thee to a summer's day?",
-            "Thou art more lovely and more temperate:",
-            "Rough winds do shake the darling buds of May,",
-            "And summer's lease hath all too short a date;",
-            "Sometime too hot the eye of heaven shines,",
-            "And often is his gold complexion dimm'd;",
-            "And every fair from fair sometime declines,",
-            "By chance or nature's changing course untrimm'd;",
-            "But thy eternal summer shall not fade,",
-            "Nor lose possession of that fair thou ow'st;",
-            "Nor shall death brag thou wander'st in his shade,",
-            "When in eternal lines to time thou grow'st:",
-            "   So long as men can breathe or eyes can see,",
-            "   So long lives this, and this gives life to thee.",
+            "Who has seen the wind?",
+            "Neither I nor you.",
+            "But when the leaves hang trembling,",
+            "The wind is passing through.",
+            "Who has seen the wind?",
+            "Neither you nor I.",
+            "But when the trees bow down their heads,",
+            "The wind is passing by."
     };
 
     @Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java
index c60abe8..f69d7c3 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java
@@ -31,6 +31,7 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
 import static com.android.server.wm.ActivityStackSupervisor.ON_TOP;
+import static com.android.server.wm.WindowContainer.POSITION_TOP;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -113,7 +114,7 @@
     @Test
     public void testStackShouldNotBeFocusedAfterMovingToBackOrRemoving() {
         // Create a display which only contains 2 stacks.
-        final DisplayContent display = addNewDisplayContentAt(DisplayContent.POSITION_TOP);
+        final DisplayContent display = addNewDisplayContentAt(POSITION_TOP);
         final Task stack1 = createFullscreenStackWithSimpleActivityAt(display);
         final Task stack2 = createFullscreenStackWithSimpleActivityAt(display);
 
@@ -228,7 +229,8 @@
         final ActivityRecord activity = new ActivityBuilder(mService).setCreateTask(true)
                 .setStack(alwaysOnTopStack).build();
         alwaysOnTopStack.setAlwaysOnTop(true);
-        taskDisplayArea.positionStackAtTop(alwaysOnTopStack, false /* includingParents */);
+        taskDisplayArea.positionChildAt(POSITION_TOP, alwaysOnTopStack,
+                false /* includingParents */);
         assertTrue(alwaysOnTopStack.isAlwaysOnTop());
         // Ensure always on top state is synced to the children of the stack.
         assertTrue(alwaysOnTopStack.getTopNonFinishingActivity().isAlwaysOnTop());
@@ -242,7 +244,8 @@
         final Task anotherAlwaysOnTopStack = taskDisplayArea.createStack(
                 WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD, true /* onTop */);
         anotherAlwaysOnTopStack.setAlwaysOnTop(true);
-        taskDisplayArea.positionStackAtTop(anotherAlwaysOnTopStack, false /* includingParents */);
+        taskDisplayArea.positionChildAt(POSITION_TOP, anotherAlwaysOnTopStack,
+                false /* includingParents */);
         assertTrue(anotherAlwaysOnTopStack.isAlwaysOnTop());
         int topPosition = taskDisplayArea.getStackCount() - 1;
         // Ensure the new alwaysOnTop stack is put below the pinned stack, but on top of the
@@ -258,7 +261,8 @@
         assertEquals(nonAlwaysOnTopStack, taskDisplayArea.getStackAt(topPosition - 3));
 
         anotherAlwaysOnTopStack.setAlwaysOnTop(false);
-        taskDisplayArea.positionStackAtTop(anotherAlwaysOnTopStack, false /* includingParents */);
+        taskDisplayArea.positionChildAt(POSITION_TOP, anotherAlwaysOnTopStack,
+                false /* includingParents */);
         assertFalse(anotherAlwaysOnTopStack.isAlwaysOnTop());
         // Ensure, when always on top is turned off for a stack, the stack is put just below all
         // other always on top stacks.
@@ -325,7 +329,7 @@
 
         // Reordering stacks while removing stacks.
         doAnswer(invocation -> {
-            taskDisplayArea.positionStackAtTop(stack3, false);
+            taskDisplayArea.positionChildAt(POSITION_TOP, stack3, false /*includingParents*/);
             return true;
         }).when(mSupervisor).removeTask(eq(task4), anyBoolean(), anyBoolean(), any());
 
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 5153af2..775df74 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
@@ -48,6 +48,7 @@
 import static com.android.server.wm.Task.STACK_VISIBILITY_VISIBLE;
 import static com.android.server.wm.Task.STACK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT;
 import static com.android.server.wm.TaskDisplayArea.getStackAbove;
+import static com.android.server.wm.WindowContainer.POSITION_BOTTOM;
 import static com.android.server.wm.WindowContainer.POSITION_TOP;
 
 import static com.google.common.truth.Truth.assertThat;
@@ -210,7 +211,7 @@
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME);
         final Task secondaryTask = mDefaultTaskDisplayArea.createStack(
                 WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, ACTIVITY_TYPE_STANDARD, true /* onTop */);
-        mDefaultTaskDisplayArea.positionStackAtTop(organizer.mPrimary,
+        mDefaultTaskDisplayArea.positionChildAt(POSITION_TOP, organizer.mPrimary,
                 false /* includingParents */);
 
         // Move primary to back.
@@ -988,18 +989,14 @@
     @SuppressWarnings("TypeParameterUnusedInFormals")
     private Task createStackForShouldBeVisibleTest(
             TaskDisplayArea taskDisplayArea, int windowingMode, int activityType, boolean onTop) {
-        final Task stack;
+        final Task task;
         if (activityType == ACTIVITY_TYPE_HOME) {
             // Home stack and activity are created in ActivityTestsBase#setupActivityManagerService
-            stack = mDefaultTaskDisplayArea.getStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME);
-            if (onTop) {
-                mDefaultTaskDisplayArea.positionStackAtTop(stack,
-                        false /* includingParents */);
-            } else {
-                mDefaultTaskDisplayArea.positionStackAtBottom(stack);
-            }
+            task = mDefaultTaskDisplayArea.getStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME);
+            mDefaultTaskDisplayArea.positionChildAt(onTop ? POSITION_TOP : POSITION_BOTTOM, task,
+                    false /* includingParents */);
         } else {
-            stack = new StackBuilder(mRootWindowContainer)
+            task = new StackBuilder(mRootWindowContainer)
                     .setTaskDisplayArea(taskDisplayArea)
                     .setWindowingMode(windowingMode)
                     .setActivityType(activityType)
@@ -1007,7 +1004,7 @@
                     .setCreateActivity(true)
                     .build();
         }
-        return stack;
+        return task;
     }
 
     @Test
@@ -1256,7 +1253,8 @@
                     mDefaultTaskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
                     true /* onTop */);
             mDefaultTaskDisplayArea.registerStackOrderChangedListener(listener);
-            mDefaultTaskDisplayArea.positionStackAtBottom(fullscreenStack1);
+            mDefaultTaskDisplayArea.positionChildAt(POSITION_BOTTOM, fullscreenStack1,
+                    false /*includingParents*/);
         } finally {
             mDefaultTaskDisplayArea.unregisterStackOrderChangedListener(listener);
         }
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
index e3cfe11..e5d1e46 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
@@ -33,6 +33,7 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
 import static com.android.server.wm.RecentsAnimationController.REORDER_KEEP_IN_PLACE;
 import static com.android.server.wm.Task.ActivityState.PAUSED;
+import static com.android.server.wm.WindowContainer.POSITION_TOP;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -118,7 +119,8 @@
         TaskDisplayArea defaultTaskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea();
         final Task homeStack =
                 defaultTaskDisplayArea.getStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME);
-        defaultTaskDisplayArea.positionStackAtTop(homeStack, false /* includingParents */);
+        defaultTaskDisplayArea.positionChildAt(POSITION_TOP, homeStack,
+                false /* includingParents */);
         ActivityRecord topRunningHomeActivity = homeStack.topRunningActivity();
         if (topRunningHomeActivity == null) {
             topRunningHomeActivity = new ActivityBuilder(mService)
diff --git a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
index 3a5d333..74be2c9 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
@@ -40,6 +40,7 @@
 import static com.android.server.wm.ActivityStackSupervisor.ON_TOP;
 import static com.android.server.wm.RootWindowContainer.MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE;
 import static com.android.server.wm.Task.ActivityState.STOPPED;
+import static com.android.server.wm.WindowContainer.POSITION_BOTTOM;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -459,7 +460,7 @@
                 ACTIVITY_TYPE_STANDARD, false /* onTop */));
         final Task task = new TaskBuilder(mSupervisor).setStack(targetStack).build();
         final ActivityRecord activity = new ActivityBuilder(mService).setTask(task).build();
-        taskDisplayArea.positionStackAtBottom(targetStack);
+        taskDisplayArea.positionChildAt(POSITION_BOTTOM, targetStack, false /*includingParents*/);
 
         // Assume the stack is not at the topmost position (e.g. behind always-on-top stacks) but it
         // is the current top focused stack.
@@ -560,7 +561,7 @@
         final Task task = new TaskBuilder(mSupervisor).setStack(targetStack).build();
         final ActivityRecord activity = new ActivityBuilder(mService).setTask(task).build();
         activity.setState(ActivityState.RESUMED, "test");
-        taskDisplayArea.positionStackAtBottom(targetStack);
+        taskDisplayArea.positionChildAt(POSITION_BOTTOM, targetStack, false /*includingParents*/);
 
         // Assume the stack is at the topmost position
         assertFalse(targetStack.isTopStackInDisplayArea());
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
index bd52e9a..800a5d4 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
@@ -53,7 +53,6 @@
 import android.app.ActivityManager;
 import android.app.ActivityManager.RunningTaskInfo;
 import android.app.ActivityManager.StackInfo;
-import android.app.IRequestFinishCallback;
 import android.app.PictureInPictureParams;
 import android.content.pm.ActivityInfo;
 import android.content.res.Configuration;
@@ -976,8 +975,7 @@
         assertTrue(stack.isOrganized());
 
         // Verify a back pressed does not call the organizer
-        mWm.mAtmService.onBackPressedOnTaskRoot(activity.token,
-                new IRequestFinishCallback.Default());
+        mWm.mAtmService.onBackPressedOnTaskRoot(activity.token);
         verify(organizer, never()).onBackPressedOnTaskRoot(any());
 
         // Enable intercepting back
@@ -985,8 +983,7 @@
                 true);
 
         // Verify now that the back press does call the organizer
-        mWm.mAtmService.onBackPressedOnTaskRoot(activity.token,
-                new IRequestFinishCallback.Default());
+        mWm.mAtmService.onBackPressedOnTaskRoot(activity.token);
         verify(organizer, times(1)).onBackPressedOnTaskRoot(any());
     }
 
diff --git a/telephony/common/android/telephony/LocationAccessPolicy.java b/telephony/common/android/telephony/LocationAccessPolicy.java
index 7d3fde6..48794cb 100644
--- a/telephony/common/android/telephony/LocationAccessPolicy.java
+++ b/telephony/common/android/telephony/LocationAccessPolicy.java
@@ -306,11 +306,12 @@
     /** Check if location permissions have been granted */
     public static LocationPermissionResult checkLocationPermission(
             Context context, LocationPermissionQuery query) {
-        // Always allow the phone process and system server to access location. This avoid
-        // breaking legacy code that rely on public-facing APIs to access cell location, and
-        // it doesn't create an info leak risk because the cell location is stored in the phone
+        // Always allow the phone process, system server, and network stack to access location.
+        // This avoid breaking legacy code that rely on public-facing APIs to access cell location,
+        // and it doesn't create an info leak risk because the cell location is stored in the phone
         // process anyway, and the system server already has location access.
         if (query.callingUid == Process.PHONE_UID || query.callingUid == Process.SYSTEM_UID
+                || query.callingUid == Process.NETWORK_STACK_UID
                 || query.callingUid == Process.ROOT_UID) {
             return LocationPermissionResult.ALLOWED;
         }
diff --git a/telephony/java/android/telephony/PreciseDataConnectionState.java b/telephony/java/android/telephony/PreciseDataConnectionState.java
index 8660e38..3dbbf41 100644
--- a/telephony/java/android/telephony/PreciseDataConnectionState.java
+++ b/telephony/java/android/telephony/PreciseDataConnectionState.java
@@ -53,14 +53,13 @@
  *
  */
 public final class PreciseDataConnectionState implements Parcelable {
-
-    private @DataState int mState = TelephonyManager.DATA_UNKNOWN;
-    private @NetworkType int mNetworkType = TelephonyManager.NETWORK_TYPE_UNKNOWN;
-    private @DataFailureCause int mFailCause = DataFailCause.NONE;
-    private @ApnType int mApnTypes = ApnSetting.TYPE_NONE;
-    private String mApn = "";
-    private LinkProperties mLinkProperties = null;
-    private ApnSetting mApnSetting = null;
+    private final @DataState int mState;
+    private final @NetworkType int mNetworkType;
+    private final @DataFailureCause int mFailCause;
+    private final @ApnType int mApnTypes;
+    private final String mApn;
+    private final LinkProperties mLinkProperties;
+    private final ApnSetting mApnSetting;
 
     /**
      * Constructor
@@ -84,20 +83,21 @@
     /**
      * Constructor of PreciseDataConnectionState
      *
-     * @param state the state of the data connection
-     * @param networkType the access network that is/would carry this data connection
-     * @param apnTypes the APN types that this data connection carries
-     * @param apn the APN of this data connection
-     * @param linkProperties if the data connection is connected, the properties of the connection
-     * @param failCause in case a procedure related to this data connection fails, a non-zero error
+     * @param state The state of the data connection
+     * @param networkType The access network that is/would carry this data connection
+     * @param apnTypes The APN types that this data connection carries
+     * @param apn The APN of this data connection
+     * @param linkProperties If the data connection is connected, the properties of the connection
+     * @param failCause In case a procedure related to this data connection fails, a non-zero error
      *        code indicating the cause of the failure.
-     * @param apnSetting if there is a valid APN for this Data Connection, then the APN Settings;
+     * @param apnSetting If there is a valid APN for this Data Connection, then the APN Settings;
      *        if there is no valid APN setting for the specific type, then this will be null
      * @hide
      */
-    public PreciseDataConnectionState(@DataState int state,
+    private PreciseDataConnectionState(@DataState int state,
                                       @NetworkType int networkType,
-                                      @ApnType int apnTypes, @NonNull String apn,
+                                      @ApnType int apnTypes,
+                                      @NonNull String apn,
                                       @Nullable LinkProperties linkProperties,
                                       @DataFailureCause int failCause,
                                       @Nullable ApnSetting apnSetting) {
@@ -111,14 +111,6 @@
     }
 
     /**
-     * Empty Constructor
-     *
-     * @hide
-     */
-    public PreciseDataConnectionState() {
-    }
-
-    /**
      * Construct a PreciseDataConnectionState object from the given parcel.
      *
      * @hide
@@ -167,9 +159,9 @@
     }
 
     /**
-     * Returns the network type associated with this data connection.
+     * Get the network type associated with this data connection.
      *
-     * Return the current/latest (radio) bearer technology that carries this data connection.
+     * @return The current/latest (radio) bearer technology that carries this data connection.
      * For a variety of reasons, the network type can change during the life of the data
      * connection, and this information is not reliable unless the physical link is currently
      * active; (there is currently no mechanism to know whether the physical link is active at
@@ -274,24 +266,23 @@
 
     @Override
     public int hashCode() {
-        return Objects.hash(mState, mNetworkType, mApnTypes, mApn, mLinkProperties,
-                mFailCause, mApnSetting);
+        return Objects.hash(mState, mNetworkType, mFailCause, mApnTypes, mApn, mLinkProperties,
+                mApnSetting);
     }
 
+
     @Override
-    public boolean equals(@Nullable Object obj) {
-
-        if (!(obj instanceof PreciseDataConnectionState)) {
-            return false;
-        }
-
-        PreciseDataConnectionState other = (PreciseDataConnectionState) obj;
-        return Objects.equals(mApn, other.mApn) && mApnTypes == other.mApnTypes
-                && mFailCause == other.mFailCause
-                && Objects.equals(mLinkProperties, other.mLinkProperties)
-                && mNetworkType == other.mNetworkType
-                && mState == other.mState
-                && Objects.equals(mApnSetting, other.mApnSetting);
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        PreciseDataConnectionState that = (PreciseDataConnectionState) o;
+        return mState == that.mState
+                && mNetworkType == that.mNetworkType
+                && mFailCause == that.mFailCause
+                && mApnTypes == that.mApnTypes
+                && Objects.equals(mApn, that.mApn)
+                && Objects.equals(mLinkProperties, that.mLinkProperties)
+                && Objects.equals(mApnSetting, that.mApnSetting);
     }
 
     @NonNull
@@ -309,4 +300,123 @@
 
         return sb.toString();
     }
+
+    /**
+     * {@link PreciseDataConnectionState} builder
+     *
+     * @hide
+     */
+    public static final class Builder {
+        /** The state of the data connection */
+        private @DataState int mState = TelephonyManager.DATA_UNKNOWN;
+
+        /** The network type associated with this data connection */
+        private @NetworkType int mNetworkType = TelephonyManager.NETWORK_TYPE_UNKNOWN;
+
+        /** The APN types that this data connection carries */
+        private @ApnType int mApnTypes = ApnSetting.TYPE_NONE;
+
+        /** The APN of this data connection */
+        private @NonNull String mApn = "";
+
+        /** If the data connection is connected, the properties of the connection */
+        private @Nullable LinkProperties mLinkProperties = null;
+
+        /**
+         * In case a procedure related to this data connection fails, a non-zero error code
+         * indicating the cause of the failure.
+         */
+        private @DataFailureCause int mFailCause = DataFailCause.NONE;
+
+        /** The APN Setting for this data connection */
+        private @Nullable ApnSetting mApnSetting = null;
+
+        /**
+         * Set the state of the data connection.
+         *
+         * @param state The state of the data connection
+         * @return The builder
+         */
+        public Builder setState(@DataState int state) {
+            mState = state;
+            return this;
+        }
+
+        /**
+         * Set the network type associated with this data connection.
+         *
+         * @param networkType The network type
+         * @return The builder
+         */
+        public Builder setNetworkType(@NetworkType int networkType) {
+            mNetworkType = networkType;
+            return this;
+        }
+
+        /**
+         * Set the APN types that this data connection carries
+         *
+         * @param apnTypes The APN types
+         * @return The builder
+         */
+        public Builder setApnTypes(@ApnType int apnTypes) {
+            mApnTypes = apnTypes;
+            return this;
+        }
+
+        /**
+         * Set the APN of this data connection
+         *
+         * @param apn The APN of this data connection
+         * @return The builder
+         */
+        public Builder setApn(@NonNull String apn) {
+            mApn = apn;
+            return this;
+        }
+
+        /**
+         * Set the link properties of the connection.
+         *
+         * @param linkProperties Link properties
+         * @return The builder
+         */
+        public Builder setLinkProperties(@NonNull LinkProperties linkProperties) {
+            mLinkProperties = linkProperties;
+            return this;
+        }
+
+        /**
+         * Set the fail cause of the data connection.
+         *
+         * @param failCause In case a procedure related to this data connection fails, a non-zero
+         * error code indicating the cause of the failure.
+         * @return The builder
+         */
+        public Builder setFailCause(@DataFailureCause int failCause) {
+            mFailCause = failCause;
+            return this;
+        }
+
+        /**
+         * Set the APN Setting for this data connection.
+         *
+         * @param apnSetting APN setting
+         * @return This builder
+         */
+        public Builder setApnSetting(@NonNull ApnSetting apnSetting) {
+            mApnSetting = apnSetting;
+            return this;
+        }
+
+        /**
+         * Build the {@link PreciseDataConnectionState} instance.
+         *
+         * @return The {@link PreciseDataConnectionState} instance
+         */
+        public PreciseDataConnectionState build() {
+            return new PreciseDataConnectionState(mState, mNetworkType, mApnTypes, mApn,
+                    mLinkProperties, mFailCause, mApnSetting);
+        }
+    }
 }
diff --git a/tests/Internal/src/android/app/WallpaperColorsTest.java b/tests/Internal/src/android/app/WallpaperColorsTest.java
index e9bac71..45d3dade 100644
--- a/tests/Internal/src/android/app/WallpaperColorsTest.java
+++ b/tests/Internal/src/android/app/WallpaperColorsTest.java
@@ -47,7 +47,7 @@
     }
 
     /**
-     * Sanity check to guarantee that white supports dark text and black doesn't
+     * Check that white supports dark text and black doesn't
      */
     @Test
     public void colorHintsTest() {
diff --git a/tools/aapt2/cmd/Compile.cpp b/tools/aapt2/cmd/Compile.cpp
index 3268653..ff54fcc 100644
--- a/tools/aapt2/cmd/Compile.cpp
+++ b/tools/aapt2/cmd/Compile.cpp
@@ -75,8 +75,10 @@
 };
 
 // Resource file paths are expected to look like: [--/res/]type[-config]/name
-static Maybe<ResourcePathData> ExtractResourcePathData(const std::string& path, const char dir_sep,
-                                                       std::string* out_error) {
+static Maybe<ResourcePathData> ExtractResourcePathData(const std::string& path,
+                                                       const char dir_sep,
+                                                       std::string* out_error,
+                                                       const CompileOptions& options) {
   std::vector<std::string> parts = util::Split(path, dir_sep);
   if (parts.size() < 2) {
     if (out_error) *out_error = "bad resource path";
@@ -121,7 +123,11 @@
     }
   }
 
-  return ResourcePathData{Source(path),          dir_str.to_string(),    name.to_string(),
+  const Source res_path = options.source_path
+      ? StringPiece(options.source_path.value())
+      : StringPiece(path);
+
+  return ResourcePathData{res_path, dir_str.to_string(), name.to_string(),
                           extension.to_string(), config_str.to_string(), config};
 }
 
@@ -667,7 +673,8 @@
     // Extract resource type information from the full path
     std::string err_str;
     ResourcePathData path_data;
-    if (auto maybe_path_data = ExtractResourcePathData(path, inputs->GetDirSeparator(), &err_str)) {
+    if (auto maybe_path_data = ExtractResourcePathData(
+        path, inputs->GetDirSeparator(), &err_str, options)) {
       path_data = maybe_path_data.value();
     } else {
       context->GetDiagnostics()->Error(DiagMessage(file->GetSource()) << err_str);
@@ -747,6 +754,11 @@
     context.GetDiagnostics()->Error(DiagMessage()
                                       << "only one of --dir and --zip can be specified");
     return 1;
+  } else if ((options_.res_dir || options_.res_zip) &&
+              options_.source_path && args.size() > 1) {
+      context.GetDiagnostics()->Error(DiagMessage(kPath)
+      << "Cannot use an overriding source path with multiple files.");
+      return 1;
   } else if (options_.res_dir) {
     if (!args.empty()) {
       context.GetDiagnostics()->Error(DiagMessage() << "files given but --dir specified");
diff --git a/tools/aapt2/cmd/Compile.h b/tools/aapt2/cmd/Compile.h
index 1752a1a..1bc1f66 100644
--- a/tools/aapt2/cmd/Compile.h
+++ b/tools/aapt2/cmd/Compile.h
@@ -28,6 +28,7 @@
 
 struct CompileOptions {
   std::string output_path;
+  Maybe<std::string> source_path;
   Maybe<std::string> res_dir;
   Maybe<std::string> res_zip;
   Maybe<std::string> generate_text_symbols_path;
@@ -69,6 +70,9 @@
     AddOptionalSwitch("-v", "Enables verbose logging", &options_.verbose);
     AddOptionalFlag("--trace-folder", "Generate systrace json trace fragment to specified folder.",
                     &trace_folder_);
+    AddOptionalFlag("--source-path",
+                      "Sets the compiled resource file source file path to the given string.",
+                      &options_.source_path);
   }
 
   int Action(const std::vector<std::string>& args) override;
diff --git a/tools/aapt2/cmd/Compile_test.cpp b/tools/aapt2/cmd/Compile_test.cpp
index fb786a3..0aab94d3 100644
--- a/tools/aapt2/cmd/Compile_test.cpp
+++ b/tools/aapt2/cmd/Compile_test.cpp
@@ -24,6 +24,7 @@
 #include "io/ZipArchive.h"
 #include "java/AnnotationProcessor.h"
 #include "test/Test.h"
+#include "format/proto/ProtoDeserialize.h"
 
 namespace aapt {
 
@@ -253,4 +254,90 @@
   AssertTranslations(this, "donottranslate_foo", expected_not_translatable);
 }
 
+TEST_F(CompilerTest, RelativePathTest) {
+  StdErrDiagnostics diag;
+  const std::string res_path = BuildPath(
+      {android::base::Dirname(android::base::GetExecutablePath()),
+       "integration-tests", "CompileTest", "res"});
+
+  const std::string path_values_colors = GetTestPath("values/colors.xml");
+  WriteFile(path_values_colors, "<resources>"
+                   "<color name=\"color_one\">#008577</color>"
+                   "</resources>");
+
+  const std::string path_layout_layout_one = GetTestPath("layout/layout_one.xml");
+  WriteFile(path_layout_layout_one, "<LinearLayout "
+                   "xmlns:android=\"http://schemas.android.com/apk/res/android\">"
+                   "<TextBox android:id=\"@+id/text_one\" android:background=\"@color/color_one\"/>"
+                   "</LinearLayout>");
+
+  const std::string compiled_files_dir  = BuildPath(
+      {android::base::Dirname(android::base::GetExecutablePath()),
+       "integration-tests", "CompileTest", "compiled"});
+  CHECK(file::mkdirs(compiled_files_dir.data()));
+
+  const std::string path_values_colors_out =
+      BuildPath({compiled_files_dir,"values_colors.arsc.flat"});
+  const std::string path_layout_layout_one_out =
+      BuildPath({compiled_files_dir, "layout_layout_one.flat"});
+  ::android::base::utf8::unlink(path_values_colors_out.c_str());
+  ::android::base::utf8::unlink(path_layout_layout_one_out.c_str());
+  const std::string apk_path = BuildPath(
+      {android::base::Dirname(android::base::GetExecutablePath()),
+       "integration-tests", "CompileTest", "out.apk"});
+
+  const std::string source_set_res = BuildPath({"main", "res"});
+  const std::string relative_path_values_colors =
+      BuildPath({source_set_res, "values", "colors.xml"});
+  const std::string relative_path_layout_layout_one =
+      BuildPath({source_set_res, "layout", "layout_one.xml"});
+
+  CompileCommand(&diag).Execute({
+    path_values_colors,
+    "-o",
+    compiled_files_dir,
+    "--source-path",
+    relative_path_values_colors},
+        &std::cerr);
+
+  CompileCommand(&diag).Execute({
+    path_layout_layout_one,
+    "-o",
+    compiled_files_dir,
+    "--source-path",
+    relative_path_layout_layout_one},
+        &std::cerr);
+
+  std::ifstream ifs_values(path_values_colors_out);
+  std::string content_values((std::istreambuf_iterator<char>(ifs_values)),
+                             (std::istreambuf_iterator<char>()));
+  ASSERT_NE(content_values.find(relative_path_values_colors), -1);
+  ASSERT_EQ(content_values.find(path_values_colors), -1);
+
+  Link({"-o", apk_path, "--manifest", GetDefaultManifest(), "--proto-format"},
+      compiled_files_dir,  &diag);
+
+  std::unique_ptr<LoadedApk> apk = LoadedApk::LoadApkFromPath(apk_path, &diag);
+  ResourceTable* resource_table = apk.get()->GetResourceTable();
+  const std::vector<std::unique_ptr<StringPool::Entry>>& pool_strings =
+      resource_table->string_pool.strings();
+
+  ASSERT_EQ(pool_strings.size(), 2);
+  ASSERT_EQ(pool_strings[0]->value, "res/layout/layout_one.xml");
+  ASSERT_EQ(pool_strings[1]->value, "res/layout-v1/layout_one.xml");
+
+  // Check resources.pb contains relative sources.
+  io::IFile* proto_file =
+      apk.get()->GetFileCollection()->FindFile("resources.pb");
+  std::unique_ptr<io::InputStream> proto_stream = proto_file->OpenInputStream();
+  io::ProtoInputStreamReader proto_reader(proto_stream.get());
+  pb::ResourceTable pb_table;
+  proto_reader.ReadMessage(&pb_table);
+
+  const std::string pool_strings_proto = pb_table.source_pool().data();
+
+  ASSERT_NE(pool_strings_proto.find(relative_path_values_colors), -1);
+  ASSERT_NE(pool_strings_proto.find(relative_path_layout_layout_one), -1);
+}
+
 }  // namespace aapt
diff --git a/tools/aapt2/dump/DumpManifest.cpp b/tools/aapt2/dump/DumpManifest.cpp
index 4a6bfd0..53d9ffe 100644
--- a/tools/aapt2/dump/DumpManifest.cpp
+++ b/tools/aapt2/dump/DumpManifest.cpp
@@ -1405,6 +1405,29 @@
   }
 };
 
+/** Represents <uses-native-library> elements. **/
+class UsesNativeLibrary : public ManifestExtractor::Element {
+ public:
+  UsesNativeLibrary() = default;
+  std::string name;
+  int required;
+
+  void Extract(xml::Element* element) override {
+    auto parent_stack = extractor()->parent_stack();
+    if (parent_stack.size() > 0 && ElementCast<Application>(parent_stack[0])) {
+      name = GetAttributeStringDefault(FindAttribute(element, NAME_ATTR), "");
+      required = GetAttributeIntegerDefault(FindAttribute(element, REQUIRED_ATTR), 1);
+    }
+  }
+
+  void Print(text::Printer* printer) override {
+    if (!name.empty()) {
+      printer->Print(StringPrintf("uses-native-library%s:'%s'\n",
+                                 (required == 0) ? "-not-required" : "", name.data()));
+    }
+  }
+};
+
 /**
  * Represents <meta-data> elements. These tags are only printed when a flag is passed in to
  * explicitly enable meta data printing.
@@ -2245,6 +2268,7 @@
     {"uses-static-library", std::is_base_of<UsesStaticLibrary, T>::value},
     {"additional-certificate", std::is_base_of<AdditionalCertificate, T>::value},
     {"uses-sdk", std::is_base_of<UsesSdkBadging, T>::value},
+    {"uses-native-library", std::is_base_of<UsesNativeLibrary, T>::value},
   };
 
   auto check = kTagCheck.find(element->tag());
@@ -2295,6 +2319,7 @@
     {"uses-package", &CreateType<UsesPackage>},
     {"additional-certificate", &CreateType<AdditionalCertificate>},
     {"uses-sdk", &CreateType<UsesSdkBadging>},
+    {"uses-native-library", &CreateType<UsesNativeLibrary>},
   };
 
   // Attempt to map the xml tag to a element inflater
diff --git a/tools/aapt2/link/ManifestFixer.cpp b/tools/aapt2/link/ManifestFixer.cpp
index 3d69093..49f8e1b 100644
--- a/tools/aapt2/link/ManifestFixer.cpp
+++ b/tools/aapt2/link/ManifestFixer.cpp
@@ -422,6 +422,7 @@
   application_action.Action(OptionalNameIsJavaClassName);
 
   application_action["uses-library"].Action(RequiredNameIsNotEmpty);
+  application_action["uses-native-library"].Action(RequiredNameIsNotEmpty);
   application_action["library"].Action(RequiredNameIsNotEmpty);
   application_action["profileable"];