Merge "Implement a PowerCalculator to attribute system service power to apps"
diff --git a/ApiDocs.bp b/ApiDocs.bp
index 029699e..ca921ff 100644
--- a/ApiDocs.bp
+++ b/ApiDocs.bp
@@ -150,12 +150,10 @@
         ":current-support-api",
         ":current-androidx-api",
     ],
-    create_stubs: false,
 }
 
 doc_defaults {
     name: "framework-dokka-docs-default",
-    create_stubs: false,
 }
 
 droiddoc {
diff --git a/apct-tests/perftests/autofill/Android.bp b/apct-tests/perftests/autofill/Android.bp
index 9ac8c87..4f6c21a 100644
--- a/apct-tests/perftests/autofill/Android.bp
+++ b/apct-tests/perftests/autofill/Android.bp
@@ -20,8 +20,9 @@
         "androidx.test.rules",
         "androidx.annotation_annotation",
         "apct-perftests-utils",
-        "collector-device-lib-platform",
+        "collector-device-lib",
     ],
     platform_apis: true,
     test_suites: ["device-tests"],
+    data: [":perfetto_artifacts"],
 }
diff --git a/apct-tests/perftests/autofill/AndroidManifest.xml b/apct-tests/perftests/autofill/AndroidManifest.xml
index 51f6a76..57595a2 100644
--- a/apct-tests/perftests/autofill/AndroidManifest.xml
+++ b/apct-tests/perftests/autofill/AndroidManifest.xml
@@ -16,11 +16,6 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
     package="com.android.perftests.autofill">
 
-    <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.perftests.utils.PerfTestActivity"
diff --git a/apct-tests/perftests/autofill/AndroidTest.xml b/apct-tests/perftests/autofill/AndroidTest.xml
index 29f9f94..eee7bdc 100644
--- a/apct-tests/perftests/autofill/AndroidTest.xml
+++ b/apct-tests/perftests/autofill/AndroidTest.xml
@@ -21,8 +21,40 @@
         <option name="test-file-name" value="AutofillPerfTests.apk" />
     </target_preparer>
 
+    <!-- Needed for pushing the trace config file -->
+    <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/>
+    <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+        <option name="push-file" key="trace_config_detailed.textproto" value="/data/misc/perfetto-traces/trace_config.textproto" />
+    </target_preparer>
+
+    <!-- Needed for pulling the collected trace config on to the host -->
+    <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
+        <option name="pull-pattern-keys" value="perfetto_file_path" />
+    </metrics_collector>
+
+    <!-- Needed for storing the perfetto trace files in the sdcard/test_results-->
+    <option name="isolated-storage" value="false" />
+
     <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
         <option name="package" value="com.android.perftests.autofill" />
         <option name="hidden-api-checks" value="false"/>
+
+        <!-- Listener related args for collecting the traces and waiting for the device to stabilize. -->
+        <option name="device-listeners" value="android.device.collectors.ProcLoadListener,android.device.collectors.PerfettoListener" />
+        <!-- Guarantee that user defined RunListeners will be running before any of the default listeners defined in this runner. -->
+        <option name="instrumentation-arg" key="newRunListenerMode" value="true" />
+
+        <!-- ProcLoadListener related arguments -->
+        <!-- Wait for device last minute threshold to reach 3 with 2 minute timeout before starting the test run -->
+        <option name="instrumentation-arg" key="procload-collector:per_run" value="true" />
+        <option name="instrumentation-arg" key="proc-loadavg-threshold" value="3" />
+        <option name="instrumentation-arg" key="proc-loadavg-timeout" value="120000" />
+        <option name="instrumentation-arg" key="proc-loadavg-interval" value="10000" />
+
+        <!-- PerfettoListener related arguments -->
+        <option name="instrumentation-arg" key="perfetto_config_text_proto" value="true" />
+        <option name="instrumentation-arg" key="perfetto_config_file" value="trace_config.textproto" />
+
+        <option name="instrumentation-arg" key="newRunListenerMode" value="true" />
     </test>
 </configuration>
diff --git a/api/current.txt b/api/current.txt
index 09de005..b70103b 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -11900,6 +11900,7 @@
     field public static final int INVALID_ID = -1; // 0xffffffff
     field public static final int STAGED_SESSION_ACTIVATION_FAILED = 2; // 0x2
     field public static final int STAGED_SESSION_NO_ERROR = 0; // 0x0
+    field public static final int STAGED_SESSION_OTHER_ERROR = 4; // 0x4
     field public static final int STAGED_SESSION_UNKNOWN = 3; // 0x3
     field public static final int STAGED_SESSION_VERIFICATION_FAILED = 1; // 0x1
   }
diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp
index 36ff20f..9c79612 100644
--- a/cmds/bootanimation/BootAnimation.cpp
+++ b/cmds/bootanimation/BootAnimation.cpp
@@ -417,7 +417,7 @@
     // this guest property specifies multi-display IDs to show the boot animation
     // multiple ids can be set with comma (,) as separator, for example:
     // setprop persist.boot.animation.displays 19260422155234049,19261083906282754
-    Vector<uint64_t> physicalDisplayIds;
+    Vector<PhysicalDisplayId> physicalDisplayIds;
     char displayValue[PROPERTY_VALUE_MAX] = "";
     property_get(DISPLAYS_PROP_NAME, displayValue, "");
     bool isValid = displayValue[0] != '\0';
@@ -435,7 +435,7 @@
     }
     if (isValid) {
         std::istringstream stream(displayValue);
-        for (PhysicalDisplayId id; stream >> id; ) {
+        for (PhysicalDisplayId id; stream >> id.value; ) {
             physicalDisplayIds.add(id);
             if (stream.peek() == ',')
                 stream.ignore();
diff --git a/cmds/screencap/screencap.cpp b/cmds/screencap/screencap.cpp
index a46a54c..dec4a56 100644
--- a/cmds/screencap/screencap.cpp
+++ b/cmds/screencap/screencap.cpp
@@ -51,12 +51,11 @@
             "usage: %s [-hp] [-d display-id] [FILENAME]\n"
             "   -h: this message\n"
             "   -p: save the file as a png.\n"
-            "   -d: specify the physical display ID to capture (default: %"
-                    ANDROID_PHYSICAL_DISPLAY_ID_FORMAT ")\n"
+            "   -d: specify the physical display ID to capture (default: %s)\n"
             "       see \"dumpsys SurfaceFlinger --display-id\" for valid display IDs.\n"
             "If FILENAME ends with .png it will be saved as a png.\n"
             "If FILENAME is not given, the results will be printed to stdout.\n",
-            pname, displayId);
+            pname, to_string(displayId).c_str());
 }
 
 static int32_t flinger2bitmapFormat(PixelFormat f)
@@ -137,7 +136,7 @@
                 png = true;
                 break;
             case 'd':
-                displayId = atoll(optarg);
+                displayId = PhysicalDisplayId(atoll(optarg));
                 break;
             case '?':
             case 'h':
@@ -183,7 +182,7 @@
     ProcessState::self()->startThreadPool();
 
     ScreenCaptureResults captureResults;
-    status_t result = ScreenshotClient::captureDisplay(*displayId, captureResults);
+    status_t result = ScreenshotClient::captureDisplay(displayId->value, captureResults);
     if (result != NO_ERROR) {
         close(fd);
         return 1;
diff --git a/cmds/uiautomator/library/Android.bp b/cmds/uiautomator/library/Android.bp
index 95b179a..14b74da 100644
--- a/cmds/uiautomator/library/Android.bp
+++ b/cmds/uiautomator/library/Android.bp
@@ -54,7 +54,6 @@
     ],
     installable: false,
     custom_template: "droiddoc-templates-sdk",
-    create_stubs: false,
 }
 
 java_library_static {
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 2be9282..522b8cc 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -2880,7 +2880,7 @@
      * Return the number of actions that will be displayed in the picture-in-picture UI when the
      * user interacts with the activity currently in picture-in-picture mode. This number may change
      * if the global configuration changes (ie. if the device is plugged into an external display),
-     * but will always be larger than three.
+     * but will always be at least three.
      */
     public int getMaxNumPictureInPictureActions() {
         try {
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index 9cf0d2a..1f8cf8a 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -341,7 +341,8 @@
     /** @see com.android.server.am.ActivityManagerService#monitor */
     public abstract void monitor();
 
-    /** Input dispatch timeout to a window, start the ANR process. */
+    /** Input dispatch timeout to a window, start the ANR process. Return the timeout extension,
+     * in milliseconds, or 0 to abort dispatch. */
     public abstract long inputDispatchingTimedOut(int pid, boolean aboveSystem, String reason);
     public abstract boolean inputDispatchingTimedOut(Object proc, String activityShortComponentName,
             ApplicationInfo aInfo, String parentShortComponentName, Object parentProc,
diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java
index bd02210..31c77ee 100644
--- a/core/java/android/content/pm/ActivityInfo.java
+++ b/core/java/android/content/pm/ActivityInfo.java
@@ -963,7 +963,7 @@
     /** @hide */
     public static final int LOCK_TASK_LAUNCH_MODE_ALWAYS = 2;
     /** @hide */
-    public static final int LOCK_TASK_LAUNCH_MODE_IF_WHITELISTED = 3;
+    public static final int LOCK_TASK_LAUNCH_MODE_IF_ALLOWLISTED = 3;
 
     /** @hide */
     public static final String lockTaskLaunchModeToString(int lockTaskLaunchMode) {
@@ -974,8 +974,8 @@
                 return "LOCK_TASK_LAUNCH_MODE_NEVER";
             case LOCK_TASK_LAUNCH_MODE_ALWAYS:
                 return "LOCK_TASK_LAUNCH_MODE_ALWAYS";
-            case LOCK_TASK_LAUNCH_MODE_IF_WHITELISTED:
-                return "LOCK_TASK_LAUNCH_MODE_IF_WHITELISTED";
+            case LOCK_TASK_LAUNCH_MODE_IF_ALLOWLISTED:
+                return "LOCK_TASK_LAUNCH_MODE_IF_ALLOWLISTED";
             default:
                 return "unknown=" + lockTaskLaunchMode;
         }
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index df9db27..bed7b26 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -2075,7 +2075,8 @@
                 STAGED_SESSION_NO_ERROR,
                 STAGED_SESSION_VERIFICATION_FAILED,
                 STAGED_SESSION_ACTIVATION_FAILED,
-                STAGED_SESSION_UNKNOWN})
+                STAGED_SESSION_UNKNOWN,
+                STAGED_SESSION_OTHER_ERROR})
         @Retention(RetentionPolicy.SOURCE)
         public @interface StagedSessionErrorCode{}
         /**
@@ -2101,6 +2102,12 @@
          */
         public static final int STAGED_SESSION_UNKNOWN = 3;
 
+        /**
+         * Constant indicating that a known error occurred while processing this staged session, but
+         * the error could not be matched to other categories.
+         */
+        public static final int STAGED_SESSION_OTHER_ERROR = 4;
+
         /** {@hide} */
         @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
         public int sessionId;
diff --git a/core/java/android/view/InputApplicationHandle.java b/core/java/android/view/InputApplicationHandle.java
index 3d05e2a..108345e 100644
--- a/core/java/android/view/InputApplicationHandle.java
+++ b/core/java/android/view/InputApplicationHandle.java
@@ -34,7 +34,7 @@
     public String name;
 
     // Dispatching timeout.
-    public long dispatchingTimeoutNanos;
+    public long dispatchingTimeoutMillis;
 
     public final IBinder token;
 
@@ -46,7 +46,7 @@
 
     public InputApplicationHandle(InputApplicationHandle handle) {
         this.token = handle.token;
-        this.dispatchingTimeoutNanos = handle.dispatchingTimeoutNanos;
+        this.dispatchingTimeoutMillis = handle.dispatchingTimeoutMillis;
         this.name = handle.name;
     }
 
diff --git a/core/java/android/view/InputWindowHandle.java b/core/java/android/view/InputWindowHandle.java
index a7e0305..e341845 100644
--- a/core/java/android/view/InputWindowHandle.java
+++ b/core/java/android/view/InputWindowHandle.java
@@ -50,7 +50,7 @@
     public int layoutParamsType;
 
     // Dispatching timeout.
-    public long dispatchingTimeoutNanos;
+    public long dispatchingTimeoutMillis;
 
     // Window frame.
     public int frameLeft;
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 0818abe..8917821 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -23358,7 +23358,7 @@
      *            displaying, else return the result of calling through to the
      *            super class.
      *
-     * @return boolean If true than the Drawable is being displayed in the
+     * @return boolean If true then the Drawable is being displayed in the
      *         view; else false and it is not allowed to animate.
      *
      * @see #unscheduleDrawable(android.graphics.drawable.Drawable)
diff --git a/core/java/android/widget/inline/InlineContentView.java b/core/java/android/widget/inline/InlineContentView.java
index 699a7735..9712311 100644
--- a/core/java/android/widget/inline/InlineContentView.java
+++ b/core/java/android/widget/inline/InlineContentView.java
@@ -59,7 +59,7 @@
  */
 public class InlineContentView extends ViewGroup {
 
-    private static final String TAG = "InlineContentView_test2";
+    private static final String TAG = "InlineContentView";
 
     private static final boolean DEBUG = false;
 
diff --git a/core/java/com/android/internal/os/logging/MetricsLoggerWrapper.java b/core/java/com/android/internal/os/logging/MetricsLoggerWrapper.java
index ba60fa5..b42ea7d 100644
--- a/core/java/com/android/internal/os/logging/MetricsLoggerWrapper.java
+++ b/core/java/com/android/internal/os/logging/MetricsLoggerWrapper.java
@@ -16,14 +16,8 @@
 
 package com.android.internal.os.logging;
 
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.util.Pair;
 import android.view.WindowManager.LayoutParams;
 
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.internal.util.FrameworkStatsLog;
 
 /**
@@ -32,81 +26,6 @@
  */
 public class MetricsLoggerWrapper {
 
-    private static final int METRIC_VALUE_DISMISSED_BY_TAP = 0;
-    private static final int METRIC_VALUE_DISMISSED_BY_DRAG = 1;
-
-    public static void logPictureInPictureDismissByTap(Context context,
-            Pair<ComponentName, Integer> topActivityInfo) {
-        MetricsLogger.action(context, MetricsEvent.ACTION_PICTURE_IN_PICTURE_DISMISSED,
-                METRIC_VALUE_DISMISSED_BY_TAP);
-        FrameworkStatsLog.write(FrameworkStatsLog.PICTURE_IN_PICTURE_STATE_CHANGED,
-                getUid(context, topActivityInfo.first, topActivityInfo.second),
-                topActivityInfo.first.flattenToString(),
-                FrameworkStatsLog.PICTURE_IN_PICTURE_STATE_CHANGED__STATE__DISMISSED);
-    }
-
-    public static void logPictureInPictureDismissByDrag(Context context,
-            Pair<ComponentName, Integer> topActivityInfo) {
-        MetricsLogger.action(context,
-                MetricsEvent.ACTION_PICTURE_IN_PICTURE_DISMISSED,
-                METRIC_VALUE_DISMISSED_BY_DRAG);
-        FrameworkStatsLog.write(FrameworkStatsLog.PICTURE_IN_PICTURE_STATE_CHANGED,
-                getUid(context, topActivityInfo.first, topActivityInfo.second),
-                topActivityInfo.first.flattenToString(),
-                FrameworkStatsLog.PICTURE_IN_PICTURE_STATE_CHANGED__STATE__DISMISSED);
-    }
-
-    public static void logPictureInPictureMinimize(Context context, boolean isMinimized,
-            Pair<ComponentName, Integer> topActivityInfo) {
-        MetricsLogger.action(context, MetricsEvent.ACTION_PICTURE_IN_PICTURE_MINIMIZED,
-                isMinimized);
-        FrameworkStatsLog.write(FrameworkStatsLog.PICTURE_IN_PICTURE_STATE_CHANGED,
-                getUid(context, topActivityInfo.first, topActivityInfo.second),
-                topActivityInfo.first.flattenToString(),
-                FrameworkStatsLog.PICTURE_IN_PICTURE_STATE_CHANGED__STATE__MINIMIZED);
-    }
-
-    /**
-     * Get uid from component name and user Id
-     * @return uid. -1 if not found.
-     */
-    private static int getUid(Context context, ComponentName componentName, int userId) {
-        int uid = -1;
-        if (componentName == null) {
-            return uid;
-        }
-        try {
-            uid = context.getPackageManager().getApplicationInfoAsUser(
-                    componentName.getPackageName(), 0, userId).uid;
-        } catch (NameNotFoundException e) {
-        }
-        return uid;
-    }
-
-    public static void logPictureInPictureMenuVisible(Context context, boolean menuStateFull) {
-        MetricsLogger.visibility(context, MetricsEvent.ACTION_PICTURE_IN_PICTURE_MENU,
-                menuStateFull);
-    }
-
-    public static void logPictureInPictureEnter(Context context,
-            int uid, String shortComponentName, boolean supportsEnterPipOnTaskSwitch) {
-        MetricsLogger.action(context, MetricsEvent.ACTION_PICTURE_IN_PICTURE_ENTERED,
-                supportsEnterPipOnTaskSwitch);
-        FrameworkStatsLog.write(FrameworkStatsLog.PICTURE_IN_PICTURE_STATE_CHANGED, uid,
-                shortComponentName,
-                FrameworkStatsLog.PICTURE_IN_PICTURE_STATE_CHANGED__STATE__ENTERED);
-    }
-
-    public static void logPictureInPictureFullScreen(Context context, int uid,
-            String shortComponentName) {
-        MetricsLogger.action(context,
-                MetricsEvent.ACTION_PICTURE_IN_PICTURE_EXPANDED_TO_FULLSCREEN);
-        FrameworkStatsLog.write(FrameworkStatsLog.PICTURE_IN_PICTURE_STATE_CHANGED,
-                uid,
-                shortComponentName,
-                FrameworkStatsLog.PICTURE_IN_PICTURE_STATE_CHANGED__STATE__EXPANDED_TO_FULL_SCREEN);
-    }
-
     public static void logAppOverlayEnter(int uid, String packageName, boolean changed, int type, boolean usingAlertWindow) {
         if (changed) {
             if (type != LayoutParams.TYPE_APPLICATION_OVERLAY) {
diff --git a/core/jni/android_hardware_input_InputApplicationHandle.cpp b/core/jni/android_hardware_input_InputApplicationHandle.cpp
index ff73c74..7756a62 100644
--- a/core/jni/android_hardware_input_InputApplicationHandle.cpp
+++ b/core/jni/android_hardware_input_InputApplicationHandle.cpp
@@ -30,7 +30,7 @@
 static struct {
     jfieldID ptr;
     jfieldID name;
-    jfieldID dispatchingTimeoutNanos;
+    jfieldID dispatchingTimeoutMillis;
     jfieldID token;
 } gInputApplicationHandleClassInfo;
 
@@ -61,8 +61,8 @@
 
     mInfo.name = getStringField(env, obj, gInputApplicationHandleClassInfo.name, "<null>");
 
-    mInfo.dispatchingTimeoutNanos =
-            env->GetLongField(obj, gInputApplicationHandleClassInfo.dispatchingTimeoutNanos);
+    mInfo.dispatchingTimeoutMillis =
+            env->GetLongField(obj, gInputApplicationHandleClassInfo.dispatchingTimeoutMillis);
 
     jobject tokenObj = env->GetObjectField(obj,
             gInputApplicationHandleClassInfo.token);
@@ -144,9 +144,8 @@
     GET_FIELD_ID(gInputApplicationHandleClassInfo.name, clazz,
             "name", "Ljava/lang/String;");
 
-    GET_FIELD_ID(gInputApplicationHandleClassInfo.dispatchingTimeoutNanos,
-            clazz,
-            "dispatchingTimeoutNanos", "J");
+    GET_FIELD_ID(gInputApplicationHandleClassInfo.dispatchingTimeoutMillis, clazz,
+                 "dispatchingTimeoutMillis", "J");
 
     GET_FIELD_ID(gInputApplicationHandleClassInfo.token, clazz,
             "token", "Landroid/os/IBinder;");
diff --git a/core/jni/android_hardware_input_InputWindowHandle.cpp b/core/jni/android_hardware_input_InputWindowHandle.cpp
index 796c5c4..ecdba3f 100644
--- a/core/jni/android_hardware_input_InputWindowHandle.cpp
+++ b/core/jni/android_hardware_input_InputWindowHandle.cpp
@@ -47,7 +47,7 @@
     jfieldID name;
     jfieldID layoutParamsFlags;
     jfieldID layoutParamsType;
-    jfieldID dispatchingTimeoutNanos;
+    jfieldID dispatchingTimeoutMillis;
     jfieldID frameLeft;
     jfieldID frameTop;
     jfieldID frameRight;
@@ -118,8 +118,8 @@
             env->GetIntField(obj, gInputWindowHandleClassInfo.layoutParamsFlags));
     mInfo.type = static_cast<InputWindowInfo::Type>(
             env->GetIntField(obj, gInputWindowHandleClassInfo.layoutParamsType));
-    mInfo.dispatchingTimeout = decltype(mInfo.dispatchingTimeout)(
-            env->GetLongField(obj, gInputWindowHandleClassInfo.dispatchingTimeoutNanos));
+    mInfo.dispatchingTimeout = std::chrono::milliseconds(
+            env->GetLongField(obj, gInputWindowHandleClassInfo.dispatchingTimeoutMillis));
     mInfo.frameLeft = env->GetIntField(obj,
             gInputWindowHandleClassInfo.frameLeft);
     mInfo.frameTop = env->GetIntField(obj,
@@ -293,8 +293,8 @@
     GET_FIELD_ID(gInputWindowHandleClassInfo.layoutParamsType, clazz,
             "layoutParamsType", "I");
 
-    GET_FIELD_ID(gInputWindowHandleClassInfo.dispatchingTimeoutNanos, clazz,
-            "dispatchingTimeoutNanos", "J");
+    GET_FIELD_ID(gInputWindowHandleClassInfo.dispatchingTimeoutMillis, clazz,
+                 "dispatchingTimeoutMillis", "J");
 
     GET_FIELD_ID(gInputWindowHandleClassInfo.frameLeft, clazz,
             "frameLeft", "I");
diff --git a/core/jni/android_view_DisplayEventReceiver.cpp b/core/jni/android_view_DisplayEventReceiver.cpp
index 7daefd3..e715be2 100644
--- a/core/jni/android_view_DisplayEventReceiver.cpp
+++ b/core/jni/android_view_DisplayEventReceiver.cpp
@@ -95,8 +95,8 @@
     ScopedLocalRef<jobject> receiverObj(env, jniGetReferent(env, mReceiverWeakGlobal));
     if (receiverObj.get()) {
         ALOGV("receiver %p ~ Invoking vsync handler.", this);
-        env->CallVoidMethod(receiverObj.get(),
-                gDisplayEventReceiverClassInfo.dispatchVsync, timestamp, displayId, count);
+        env->CallVoidMethod(receiverObj.get(), gDisplayEventReceiverClassInfo.dispatchVsync,
+                            timestamp, displayId.value, count);
         ALOGV("receiver %p ~ Returned from vsync handler.", this);
     }
 
@@ -110,8 +110,8 @@
     ScopedLocalRef<jobject> receiverObj(env, jniGetReferent(env, mReceiverWeakGlobal));
     if (receiverObj.get()) {
         ALOGV("receiver %p ~ Invoking hotplug handler.", this);
-        env->CallVoidMethod(receiverObj.get(),
-                gDisplayEventReceiverClassInfo.dispatchHotplug, timestamp, displayId, connected);
+        env->CallVoidMethod(receiverObj.get(), gDisplayEventReceiverClassInfo.dispatchHotplug,
+                            timestamp, displayId.value, connected);
         ALOGV("receiver %p ~ Returned from hotplug handler.", this);
     }
 
@@ -126,9 +126,8 @@
                                       jniGetReferent(env, mReceiverWeakGlobal));
   if (receiverObj.get()) {
     ALOGV("receiver %p ~ Invoking config changed handler.", this);
-    env->CallVoidMethod(receiverObj.get(),
-                        gDisplayEventReceiverClassInfo.dispatchConfigChanged,
-                        timestamp, displayId, configId);
+    env->CallVoidMethod(receiverObj.get(), gDisplayEventReceiverClassInfo.dispatchConfigChanged,
+                        timestamp, displayId.value, configId);
     ALOGV("receiver %p ~ Returned from config changed handler.", this);
   }
 
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index 814a07e..d6a773f 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -703,7 +703,7 @@
 
     jlong* values = env->GetLongArrayElements(array, 0);
     for (size_t i = 0; i < displayIds.size(); ++i) {
-        values[i] = static_cast<jlong>(displayIds[i]);
+        values[i] = static_cast<jlong>(displayIds[i].value);
     }
 
     env->ReleaseLongArrayElements(array, values, 0);
@@ -711,7 +711,8 @@
 }
 
 static jobject nativeGetPhysicalDisplayToken(JNIEnv* env, jclass clazz, jlong physicalDisplayId) {
-    sp<IBinder> token = SurfaceComposerClient::getPhysicalDisplayToken(physicalDisplayId);
+    sp<IBinder> token =
+            SurfaceComposerClient::getPhysicalDisplayToken(PhysicalDisplayId(physicalDisplayId));
     return javaObjectForIBinder(env, token);
 }
 
diff --git a/libs/hwui/jni/Shader.cpp b/libs/hwui/jni/Shader.cpp
index 0f68376..e76aace 100644
--- a/libs/hwui/jni/Shader.cpp
+++ b/libs/hwui/jni/Shader.cpp
@@ -228,9 +228,12 @@
 
 static jlong RuntimeShader_createShaderFactory(JNIEnv* env, jobject, jstring sksl) {
     ScopedUtfChars strSksl(env, sksl);
-    sk_sp<SkRuntimeEffect> effect = std::get<0>(SkRuntimeEffect::Make(SkString(strSksl.c_str())));
-    ThrowIAE_IfNull(env, effect);
-
+    auto result = SkRuntimeEffect::Make(SkString(strSksl.c_str()));
+    sk_sp<SkRuntimeEffect> effect = std::get<0>(result);
+    if (!effect) {
+        const auto& err = std::get<1>(result);
+        doThrowIAE(env, err.c_str());
+    }
     return reinterpret_cast<jlong>(effect.release());
 }
 
diff --git a/location/java/android/location/LocationManagerInternal.java b/location/java/android/location/LocationManagerInternal.java
index 542737b4..ef68814 100644
--- a/location/java/android/location/LocationManagerInternal.java
+++ b/location/java/android/location/LocationManagerInternal.java
@@ -21,6 +21,8 @@
 import android.annotation.Nullable;
 import android.location.util.identity.CallerIdentity;
 
+import java.util.List;
+
 /**
  * Location manager local system service interface.
  *
@@ -29,6 +31,16 @@
 public abstract class LocationManagerInternal {
 
     /**
+     * Listener for changes in provider enabled state.
+     */
+    public interface ProviderEnabledListener {
+        /**
+         * Called when the provider enabled state changes for a particular user.
+         */
+        void onProviderEnabledChanged(String provider, int userId, boolean enabled);
+    }
+
+    /**
      * Returns true if the given provider is enabled for the given user.
      *
      * @param provider A location provider as listed by {@link LocationManager#getAllProviders()}
@@ -38,6 +50,24 @@
     public abstract boolean isProviderEnabledForUser(@NonNull String provider, int userId);
 
     /**
+     * Adds a provider enabled listener. The given provider must exist.
+     *
+     * @param provider The provider to listen for changes
+     * @param listener The listener
+     */
+    public abstract void addProviderEnabledListener(String provider,
+            ProviderEnabledListener listener);
+
+    /**
+     * Removes a provider enabled listener. The given provider must exist.
+     *
+     * @param provider The provider to listen for changes
+     * @param listener The listener
+     */
+    public abstract void removeProviderEnabledListener(String provider,
+            ProviderEnabledListener listener);
+
+    /**
      * Returns true if the given identity is a location provider.
      *
      * @param provider The provider to check, or null to check every provider
@@ -52,4 +82,10 @@
      */
     // TODO: there is no reason for this to exist as part of any API. move all the logic into gnss
     public abstract void sendNiResponse(int notifId, int userResponse);
+
+    /**
+     * Should only be used by GNSS code.
+     */
+    // TODO: there is no reason for this to exist as part of any API. create a real batching API
+    public abstract void reportGnssBatchLocations(List<Location> locations);
 }
diff --git a/location/java/android/location/LocationRequest.java b/location/java/android/location/LocationRequest.java
index bb36c2a..280bd05 100644
--- a/location/java/android/location/LocationRequest.java
+++ b/location/java/android/location/LocationRequest.java
@@ -32,6 +32,8 @@
 
 import com.android.internal.util.Preconditions;
 
+import java.util.Objects;
+
 
 /**
  * A data object that contains quality of service parameters for requests
@@ -150,8 +152,6 @@
 
     @UnsupportedAppUsage
     private String mProvider;
-    // if true, client requests coarse location, if false, client requests fine location
-    private boolean mCoarseLocation;
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private int mQuality;
     @UnsupportedAppUsage
@@ -257,7 +257,6 @@
     public LocationRequest() {
         this(
                 /* provider= */ LocationManager.FUSED_PROVIDER,
-                /* coarseLocation= */ false,
                 /* quality= */ POWER_LOW,
                 /* interval= */ DEFAULT_INTERVAL_MS,
                 /* fastestInterval= */ (long) (DEFAULT_INTERVAL_MS / FASTEST_INTERVAL_FACTOR),
@@ -276,7 +275,6 @@
     public LocationRequest(LocationRequest src) {
         this(
                 src.mProvider,
-                src.mCoarseLocation,
                 src.mQuality,
                 src.mInterval,
                 src.mFastestInterval,
@@ -293,7 +291,6 @@
 
     private LocationRequest(
             @NonNull String provider,
-            boolean coarseLocation,
             int quality,
             long intervalMs,
             long fastestIntervalMs,
@@ -310,7 +307,6 @@
         checkQuality(quality);
 
         mProvider = provider;
-        mCoarseLocation = coarseLocation;
         mQuality = quality;
         mInterval = intervalMs;
         mFastestInterval = fastestIntervalMs;
@@ -327,20 +323,6 @@
     }
 
     /**
-     * @hide
-     */
-    public boolean isCoarse() {
-        return mCoarseLocation;
-    }
-
-    /**
-     * @hide
-     */
-    public void setCoarse(boolean coarse) {
-        mCoarseLocation = coarse;
-    }
-
-    /**
      * Set the quality of the request.
      *
      * <p>Use with a accuracy constant such as {@link #ACCURACY_FINE}, or a power
@@ -720,7 +702,6 @@
                 public LocationRequest createFromParcel(Parcel in) {
                     return new LocationRequest(
                             /* provider= */ in.readString(),
-                            /* coarseLocation= */ in.readBoolean(),
                             /* quality= */ in.readInt(),
                             /* interval= */ in.readLong(),
                             /* fastestInterval= */ in.readLong(),
@@ -749,7 +730,6 @@
     @Override
     public void writeToParcel(Parcel parcel, int flags) {
         parcel.writeString(mProvider);
-        parcel.writeBoolean(mCoarseLocation);
         parcel.writeInt(mQuality);
         parcel.writeLong(mInterval);
         parcel.writeLong(mFastestInterval);
@@ -784,6 +764,36 @@
         }
     }
 
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+
+        LocationRequest that = (LocationRequest) o;
+        return mQuality == that.mQuality
+                && mInterval == that.mInterval
+                && mFastestInterval == that.mFastestInterval
+                && mExplicitFastestInterval == that.mExplicitFastestInterval
+                && mExpireAt == that.mExpireAt
+                && mExpireIn == that.mExpireIn
+                && mNumUpdates == that.mNumUpdates
+                && Float.compare(that.mSmallestDisplacement, mSmallestDisplacement) == 0
+                && mHideFromAppOps == that.mHideFromAppOps
+                && mLocationSettingsIgnored == that.mLocationSettingsIgnored
+                && mLowPowerMode == that.mLowPowerMode
+                && mProvider.equals(that.mProvider)
+                && Objects.equals(mWorkSource, that.mWorkSource);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mProvider, mInterval, mWorkSource);
+    }
+
     @NonNull
     @Override
     public String toString() {
@@ -794,7 +804,7 @@
         if (mQuality != POWER_NONE) {
             s.append(" interval=");
             TimeUtils.formatDuration(mInterval, s);
-            if (mExplicitFastestInterval) {
+            if (mExplicitFastestInterval && mFastestInterval != mInterval) {
                 s.append(" fastestInterval=");
                 TimeUtils.formatDuration(mFastestInterval, s);
             }
diff --git a/non-updatable-api/current.txt b/non-updatable-api/current.txt
index 54618a5..70ef69d 100644
--- a/non-updatable-api/current.txt
+++ b/non-updatable-api/current.txt
@@ -11900,6 +11900,7 @@
     field public static final int INVALID_ID = -1; // 0xffffffff
     field public static final int STAGED_SESSION_ACTIVATION_FAILED = 2; // 0x2
     field public static final int STAGED_SESSION_NO_ERROR = 0; // 0x0
+    field public static final int STAGED_SESSION_OTHER_ERROR = 4; // 0x4
     field public static final int STAGED_SESSION_UNKNOWN = 3; // 0x3
     field public static final int STAGED_SESSION_VERIFICATION_FAILED = 1; // 0x1
   }
diff --git a/packages/CarSystemUI/src/com/android/systemui/wm/BarControlPolicy.java b/packages/CarSystemUI/src/com/android/systemui/wm/BarControlPolicy.java
index 5f9665f..0452b83 100644
--- a/packages/CarSystemUI/src/com/android/systemui/wm/BarControlPolicy.java
+++ b/packages/CarSystemUI/src/com/android/systemui/wm/BarControlPolicy.java
@@ -172,32 +172,32 @@
     private static class Filter {
         private static final String ALL = "*";
 
-        private final ArraySet<String> mWhitelist;
-        private final ArraySet<String> mBlacklist;
+        private final ArraySet<String> mToInclude;
+        private final ArraySet<String> mToExclude;
 
-        private Filter(ArraySet<String> whitelist, ArraySet<String> blacklist) {
-            mWhitelist = whitelist;
-            mBlacklist = blacklist;
+        private Filter(ArraySet<String> toInclude, ArraySet<String> toExclude) {
+            mToInclude = toInclude;
+            mToExclude = toExclude;
         }
 
         boolean matches(String packageName) {
             if (packageName == null) return false;
-            if (onBlacklist(packageName)) return false;
-            return onWhitelist(packageName);
+            if (toExclude(packageName)) return false;
+            return toInclude(packageName);
         }
 
-        private boolean onBlacklist(String packageName) {
-            return mBlacklist.contains(packageName) || mBlacklist.contains(ALL);
+        private boolean toExclude(String packageName) {
+            return mToExclude.contains(packageName) || mToExclude.contains(ALL);
         }
 
-        private boolean onWhitelist(String packageName) {
-            return mWhitelist.contains(ALL) || mWhitelist.contains(packageName);
+        private boolean toInclude(String packageName) {
+            return mToInclude.contains(ALL) || mToInclude.contains(packageName);
         }
 
         void dump(PrintWriter pw) {
             pw.print("Filter[");
-            dump("whitelist", mWhitelist, pw); pw.print(',');
-            dump("blacklist", mBlacklist, pw); pw.print(']');
+            dump("toInclude", mToInclude, pw); pw.print(',');
+            dump("toExclude", mToExclude, pw); pw.print(']');
         }
 
         private void dump(String name, ArraySet<String> set, PrintWriter pw) {
@@ -221,18 +221,18 @@
         // e.g. "com.package1", or "com.android.systemui, com.android.keyguard" or "*"
         static Filter parse(String value) {
             if (value == null) return null;
-            ArraySet<String> whitelist = new ArraySet<String>();
-            ArraySet<String> blacklist = new ArraySet<String>();
+            ArraySet<String> toInclude = new ArraySet<String>();
+            ArraySet<String> toExclude = new ArraySet<String>();
             for (String token : value.split(",")) {
                 token = token.trim();
                 if (token.startsWith("-") && token.length() > 1) {
                     token = token.substring(1);
-                    blacklist.add(token);
+                    toExclude.add(token);
                 } else {
-                    whitelist.add(token);
+                    toInclude.add(token);
                 }
             }
-            return new Filter(whitelist, blacklist);
+            return new Filter(toInclude, toExclude);
         }
     }
 
diff --git a/packages/CarSystemUI/tests/src/com/android/AAAPlusPlusVerifySysuiRequiredTestPropertiesTest.java b/packages/CarSystemUI/tests/src/com/android/AAAPlusPlusVerifySysuiRequiredTestPropertiesTest.java
index d769cac..86b86d4 100644
--- a/packages/CarSystemUI/tests/src/com/android/AAAPlusPlusVerifySysuiRequiredTestPropertiesTest.java
+++ b/packages/CarSystemUI/tests/src/com/android/AAAPlusPlusVerifySysuiRequiredTestPropertiesTest.java
@@ -63,7 +63,7 @@
 
     private static final String TAG = "AAA++VerifyTest";
 
-    private static final Class[] BASE_CLS_WHITELIST = {
+    private static final Class[] BASE_CLS_TO_INCLUDE = {
             SysuiTestCase.class,
             SysuiBaseFragmentTest.class,
     };
@@ -85,7 +85,7 @@
             if (!isTestClass(cls)) continue;
 
             boolean hasParent = false;
-            for (Class<?> parent : BASE_CLS_WHITELIST) {
+            for (Class<?> parent : BASE_CLS_TO_INCLUDE) {
                 if (parent.isAssignableFrom(cls)) {
                     hasParent = true;
                     break;
@@ -129,7 +129,7 @@
     }
 
     private String getClsStr() {
-        return TextUtils.join(",", Arrays.asList(BASE_CLS_WHITELIST)
+        return TextUtils.join(",", Arrays.asList(BASE_CLS_TO_INCLUDE)
                 .stream().map(cls -> cls.getSimpleName()).toArray());
     }
 
diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/car/voicerecognition/ConnectedDeviceVoiceRecognitionNotifierTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/voicerecognition/ConnectedDeviceVoiceRecognitionNotifierTest.java
index 31f1170..f77294e 100644
--- a/packages/CarSystemUI/tests/src/com/android/systemui/car/voicerecognition/ConnectedDeviceVoiceRecognitionNotifierTest.java
+++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/voicerecognition/ConnectedDeviceVoiceRecognitionNotifierTest.java
@@ -19,6 +19,8 @@
 import static com.android.systemui.car.voicerecognition.ConnectedDeviceVoiceRecognitionNotifier.INVALID_VALUE;
 import static com.android.systemui.car.voicerecognition.ConnectedDeviceVoiceRecognitionNotifier.VOICE_RECOGNITION_STARTED;
 
+import static com.google.common.truth.Truth.assertThat;
+
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.spy;
@@ -40,11 +42,13 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
 
 @CarSystemUiTest
 @RunWith(AndroidTestingRunner.class)
 @TestableLooper.RunWithLooper
 @SmallTest
+// TODO(b/162866441): Refactor to use the Executor pattern instead.
 public class ConnectedDeviceVoiceRecognitionNotifierTest extends SysuiTestCase {
 
     private static final String BLUETOOTH_PERM = android.Manifest.permission.BLUETOOTH;
@@ -52,13 +56,15 @@
 
     private ConnectedDeviceVoiceRecognitionNotifier mVoiceRecognitionNotifier;
     private TestableLooper mTestableLooper;
+    private Handler mHandler;
     private Handler mTestHandler;
     private BluetoothDevice mBluetoothDevice;
 
     @Before
     public void setUp() throws Exception {
         mTestableLooper = TestableLooper.get(this);
-        mTestHandler = spy(new Handler(mTestableLooper.getLooper()));
+        mHandler = new Handler(mTestableLooper.getLooper());
+        mTestHandler = spy(mHandler);
         mBluetoothDevice = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(
                 BLUETOOTH_REMOTE_ADDRESS);
         mVoiceRecognitionNotifier = new ConnectedDeviceVoiceRecognitionNotifier(
@@ -74,8 +80,14 @@
 
         mContext.sendBroadcast(intent, BLUETOOTH_PERM);
         mTestableLooper.processAllMessages();
+        waitForIdleSync();
 
-        verify(mTestHandler).post(any());
+        mHandler.post(() -> {
+            ArgumentCaptor<Runnable> argumentCaptor = ArgumentCaptor.forClass(Runnable.class);
+            verify(mTestHandler).post(argumentCaptor.capture());
+            assertThat(argumentCaptor.getValue()).isNotNull();
+            assertThat(argumentCaptor.getValue()).isNotEqualTo(this);
+        });
     }
 
     @Test
@@ -86,8 +98,11 @@
 
         mContext.sendBroadcast(intent, BLUETOOTH_PERM);
         mTestableLooper.processAllMessages();
+        waitForIdleSync();
 
-        verify(mTestHandler, never()).post(any());
+        mHandler.post(() -> {
+            verify(mTestHandler, never()).post(any());
+        });
     }
 
     @Test
@@ -97,8 +112,11 @@
 
         mContext.sendBroadcast(intent, BLUETOOTH_PERM);
         mTestableLooper.processAllMessages();
+        waitForIdleSync();
 
-        verify(mTestHandler, never()).post(any());
+        mHandler.post(() -> {
+            verify(mTestHandler, never()).post(any());
+        });
     }
 
     @Test
@@ -108,7 +126,10 @@
 
         mContext.sendBroadcast(intent, BLUETOOTH_PERM);
         mTestableLooper.processAllMessages();
+        waitForIdleSync();
 
-        verify(mTestHandler, never()).post(any());
+        mHandler.post(() -> {
+            verify(mTestHandler, never()).post(any());
+        });
     }
 }
diff --git a/packages/SettingsLib/res/values-af/strings.xml b/packages/SettingsLib/res/values-af/strings.xml
index 704d264..6751fa4 100644
--- a/packages/SettingsLib/res/values-af/strings.xml
+++ b/packages/SettingsLib/res/values-af/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Wys Bluetooth-toestelle sonder name"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Deaktiveer absolute volume"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Aktiveer Gabeldorsche"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"Verbeterde konnektiwiteit"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bluetooth AVRCP-weergawe"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Kies Bluetooth AVRCP-weergawe"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetooth MAP-weergawe"</string>
diff --git a/packages/SettingsLib/res/values-am/strings.xml b/packages/SettingsLib/res/values-am/strings.xml
index 585924d..470e780 100644
--- a/packages/SettingsLib/res/values-am/strings.xml
+++ b/packages/SettingsLib/res/values-am/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"የብሉቱዝ መሣሪያዎችን ያለ ስሞች አሳይ"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"ፍጹማዊ ድምፅን አሰናክል"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gabeldorscheን አንቃ"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"የተሻሻለ ተገናኝነት"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"የብሉቱዝ AVRCP ስሪት"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"የብሉቱዝ AVRCP ስሪት ይምረጡ"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"የብሉቱዝ MAP ስሪት"</string>
diff --git a/packages/SettingsLib/res/values-ar/strings.xml b/packages/SettingsLib/res/values-ar/strings.xml
index 39777cd..9acfa0d 100644
--- a/packages/SettingsLib/res/values-ar/strings.xml
+++ b/packages/SettingsLib/res/values-ar/strings.xml
@@ -152,7 +152,7 @@
     <string name="user_guest" msgid="6939192779649870792">"ضيف"</string>
     <string name="unknown" msgid="3544487229740637809">"غير معروف"</string>
     <string name="running_process_item_user_label" msgid="3988506293099805796">"المستخدم: <xliff:g id="USER_NAME">%1$s</xliff:g>"</string>
-    <string name="launch_defaults_some" msgid="3631650616557252926">"تم تعيين بعض الإعدادات التلقائية"</string>
+    <string name="launch_defaults_some" msgid="3631650616557252926">"تم ضبط بعض الإعدادات التلقائية"</string>
     <string name="launch_defaults_none" msgid="8049374306261262709">"لم يتم تعيين إعدادات تلقائية"</string>
     <string name="tts_settings" msgid="8130616705989351312">"إعدادات تحويل النص إلى كلام"</string>
     <string name="tts_settings_title" msgid="7602210956640483039">"تحويل النص إلى كلام"</string>
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"عرض أجهزة البلوتوث بدون أسماء"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"إيقاف مستوى الصوت المطلق"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"‏تفعيل Gabeldorsche"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"إمكانية اتصال محسّن"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"‏إصدار Bluetooth AVRCP"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"‏اختيار إصدار Bluetooth AVRCP"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"‏إصدار Bluetooth MAP"</string>
diff --git a/packages/SettingsLib/res/values-as/strings.xml b/packages/SettingsLib/res/values-as/strings.xml
index e0455cb..f993dba 100644
--- a/packages/SettingsLib/res/values-as/strings.xml
+++ b/packages/SettingsLib/res/values-as/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"নামবিহীন ব্লুটুথ ডিভাইচসমূহ দেখুৱাওক"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"পূৰ্ণ মাত্ৰাৰ ভলিউম অক্ষম কৰক"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gabeldorsche সক্ষম কৰক"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"উন্নত সংযোগ"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"ব্লুটুথ AVRCP সংস্কৰণ"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"ব্লুটুথ AVRCP সংস্কৰণ বাছনি কৰক"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"ব্লুটুথ MAP সংস্কৰণ"</string>
diff --git a/packages/SettingsLib/res/values-az/strings.xml b/packages/SettingsLib/res/values-az/strings.xml
index 6565d53..6a50661 100644
--- a/packages/SettingsLib/res/values-az/strings.xml
+++ b/packages/SettingsLib/res/values-az/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Bluetooth cihazlarını adsız göstərin"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Mütləq səs həcmi deaktiv edin"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gabeldorsche\'ni aktiv edin"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"Təkmilləşdirilmiş Bağlantı"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bluetooth AVRCP Versiya"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Bluetooth AVRCP Versiyasını seçin"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetooth MAP Versiyası"</string>
diff --git a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
index 3ced29b..cf988ab 100644
--- a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
+++ b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Prikaži Bluetooth uređaje bez naziva"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Onemogući glavno podešavanje jačine zvuka"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Omogući Gabeldorsche"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"Poboljšano povezivanje"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Verzija Bluetooth AVRCP-a"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Izaberite verziju Bluetooth AVRCP-a"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Verzija Bluetooth MAP-a"</string>
diff --git a/packages/SettingsLib/res/values-be/strings.xml b/packages/SettingsLib/res/values-be/strings.xml
index 01d7682..8f71509 100644
--- a/packages/SettingsLib/res/values-be/strings.xml
+++ b/packages/SettingsLib/res/values-be/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Паказваць прылады Bluetooth без назваў"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Адключыць абсалютны гук"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Уключыць Gabeldorsche"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"Палепшанае падключэнне"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Версія Bluetooth AVRCP"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Выбраць версію Bluetooth AVRCP"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Версія Bluetooth MAP"</string>
diff --git a/packages/SettingsLib/res/values-bg/strings.xml b/packages/SettingsLib/res/values-bg/strings.xml
index d042c0f..747cb26 100644
--- a/packages/SettingsLib/res/values-bg/strings.xml
+++ b/packages/SettingsLib/res/values-bg/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Показване на устройствата с Bluetooth без имена"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Деактивиране на пълната сила на звука"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Активиране на Gabeldorsche"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"Подобрена свързаност"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Версия на AVRCP за Bluetooth"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Избиране на версия на AVRCP за Bluetooth"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"MAP версия за Bluetooth"</string>
diff --git a/packages/SettingsLib/res/values-bn/strings.xml b/packages/SettingsLib/res/values-bn/strings.xml
index 2db23f7..87f3b7a 100644
--- a/packages/SettingsLib/res/values-bn/strings.xml
+++ b/packages/SettingsLib/res/values-bn/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"নামহীন ব্লুটুথ ডিভাইসগুলি দেখুন"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"চূড়ান্ত ভলিউম অক্ষম করুন"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gabeldorsche ফিচার চালু করুন"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"কানেক্টিভিটি উন্নত করা হয়েছে"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"ব্লুটুথ AVRCP ভার্সন"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"ব্লুটুথ AVRCP ভার্সন বেছে নিন"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"ব্লুটুথ MAP ভার্সন"</string>
diff --git a/packages/SettingsLib/res/values-bs/strings.xml b/packages/SettingsLib/res/values-bs/strings.xml
index f26fe9d..e329c99 100644
--- a/packages/SettingsLib/res/values-bs/strings.xml
+++ b/packages/SettingsLib/res/values-bs/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Prikaži Bluetooth uređaje bez naziva"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Onemogući apsolutnu jačinu zvuka"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Omogući Gabeldorsche"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"Poboljšana povezivost"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bluetooth AVRCP verzija"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Odaberite Bluetooth AVRCP verziju"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetooth MAP verzija"</string>
diff --git a/packages/SettingsLib/res/values-ca/strings.xml b/packages/SettingsLib/res/values-ca/strings.xml
index 3ca9d52..5ffdacd 100644
--- a/packages/SettingsLib/res/values-ca/strings.xml
+++ b/packages/SettingsLib/res/values-ca/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Mostra els dispositius Bluetooth sense el nom"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Desactiva el volum absolut"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Activa Gabeldorsche"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"Connectivitat millorada"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Versió AVRCP de Bluetooth"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Selecciona la versió AVRCP de Bluetooth"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Versió MAP de Bluetooth"</string>
diff --git a/packages/SettingsLib/res/values-cs/strings.xml b/packages/SettingsLib/res/values-cs/strings.xml
index a5532e0..0aef99f 100644
--- a/packages/SettingsLib/res/values-cs/strings.xml
+++ b/packages/SettingsLib/res/values-cs/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Zobrazovat zařízení Bluetooth bez názvů"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Zakázat absolutní hlasitost"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Zapnout funkci Gabeldorsche"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"Lepší připojování"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Verze profilu Bluetooth AVRCP"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Vyberte verzi profilu Bluetooth AVRCP"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Verze MAP pro Bluetooth"</string>
diff --git a/packages/SettingsLib/res/values-da/strings.xml b/packages/SettingsLib/res/values-da/strings.xml
index 8ca22d7..98068cb 100644
--- a/packages/SettingsLib/res/values-da/strings.xml
+++ b/packages/SettingsLib/res/values-da/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Vis Bluetooth-enheder uden navne"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Deaktiver absolut lydstyrke"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Aktivér Gabeldorsche"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"Enhanced Connectivity"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"AVRCP-version for Bluetooth"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Vælg AVRCP-version for Bluetooth"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"MAP-version for Bluetooth"</string>
diff --git a/packages/SettingsLib/res/values-de/strings.xml b/packages/SettingsLib/res/values-de/strings.xml
index 6b0ae2e2..0837ad3 100644
--- a/packages/SettingsLib/res/values-de/strings.xml
+++ b/packages/SettingsLib/res/values-de/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Bluetooth-Geräte ohne Namen anzeigen"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Absolute Lautstärkeregelung deaktivieren"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Bluetooth-Gabeldorsche aktivieren"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"Verbesserte Konnektivität"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bluetooth AVRCP-Version"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Bluetooth AVRCP-Version auswählen"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetooth MAP-Version"</string>
diff --git a/packages/SettingsLib/res/values-el/strings.xml b/packages/SettingsLib/res/values-el/strings.xml
index 4d7c882..1f9d977 100644
--- a/packages/SettingsLib/res/values-el/strings.xml
+++ b/packages/SettingsLib/res/values-el/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Εμφάνιση συσκευών Bluetooth χωρίς ονόματα"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Απενεργοποίηση απόλυτης έντασης"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Ενεργοποίηση Gabeldorsche"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"Βελτιωμένη συνδεσιμότητα"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Έκδοση AVRCP Bluetooth"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Επιλογή έκδοσης AVRCP Bluetooth"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Έκδοση MAP Bluetooth"</string>
diff --git a/packages/SettingsLib/res/values-en-rAU/strings.xml b/packages/SettingsLib/res/values-en-rAU/strings.xml
index cc3b2aa..abe61a9 100644
--- a/packages/SettingsLib/res/values-en-rAU/strings.xml
+++ b/packages/SettingsLib/res/values-en-rAU/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Show Bluetooth devices without names"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Disable absolute volume"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Enable Gabeldorsche"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"Enhanced connectivity"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bluetooth AVRCP version"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Select Bluetooth AVRCP Version"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetooth MAP version"</string>
diff --git a/packages/SettingsLib/res/values-en-rCA/strings.xml b/packages/SettingsLib/res/values-en-rCA/strings.xml
index a9f039a..959ad46 100644
--- a/packages/SettingsLib/res/values-en-rCA/strings.xml
+++ b/packages/SettingsLib/res/values-en-rCA/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Show Bluetooth devices without names"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Disable absolute volume"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Enable Gabeldorsche"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"Enhanced connectivity"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bluetooth AVRCP version"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Select Bluetooth AVRCP Version"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetooth MAP version"</string>
diff --git a/packages/SettingsLib/res/values-en-rGB/strings.xml b/packages/SettingsLib/res/values-en-rGB/strings.xml
index cc3b2aa..abe61a9 100644
--- a/packages/SettingsLib/res/values-en-rGB/strings.xml
+++ b/packages/SettingsLib/res/values-en-rGB/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Show Bluetooth devices without names"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Disable absolute volume"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Enable Gabeldorsche"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"Enhanced connectivity"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bluetooth AVRCP version"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Select Bluetooth AVRCP Version"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetooth MAP version"</string>
diff --git a/packages/SettingsLib/res/values-en-rIN/strings.xml b/packages/SettingsLib/res/values-en-rIN/strings.xml
index cc3b2aa..abe61a9 100644
--- a/packages/SettingsLib/res/values-en-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-en-rIN/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Show Bluetooth devices without names"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Disable absolute volume"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Enable Gabeldorsche"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"Enhanced connectivity"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bluetooth AVRCP version"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Select Bluetooth AVRCP Version"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetooth MAP version"</string>
diff --git a/packages/SettingsLib/res/values-en-rXC/strings.xml b/packages/SettingsLib/res/values-en-rXC/strings.xml
index 41c20e0..738dd2a 100644
--- a/packages/SettingsLib/res/values-en-rXC/strings.xml
+++ b/packages/SettingsLib/res/values-en-rXC/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‎‎‏‏‎‎‏‏‎‏‎‎‎‏‎‎‏‏‏‎‏‏‎‎‏‏‏‎‏‏‏‏‎‎‎‏‎‎‎‏‏‎‎‏‏‏‎‏‏‏‎‎‏‏‏‏‏‏‏‎‏‏‎Show Bluetooth devices without names‎‏‎‎‏‎"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‎‏‏‎‏‎‎‎‎‏‎‎‏‏‏‏‏‎‎‎‎‎‏‏‎‏‎‎‏‏‎‎‎‏‎‏‏‏‏‎‎‎‏‏‎‎‏‎‏‏‏‎‎‏‏‏‏‏‎‏‎‏‎‎Disable absolute volume‎‏‎‎‏‎"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‏‏‏‏‎‏‎‏‏‏‎‏‎‎‏‏‎‏‏‎‎‎‏‏‎‏‎‎‏‏‏‎‏‎‏‎‎‏‎‏‏‎‎‎‏‏‎‏‏‎‎‏‏‎‏‎‏‏‎‎‎‎Enable Gabeldorsche‎‏‎‎‏‎"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‎‎‎‏‏‏‏‏‎‏‏‏‏‏‎‎‎‏‎‏‏‎‎‏‏‎‎‏‎‎‏‏‎‎‏‏‏‎‎‎‎‎‏‎‎‎‎‏‏‎‎‏‏‏‏‏‏‎‏‎‎‎Enhanced Connectivity‎‏‎‎‏‎"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‎‏‏‎‏‏‏‏‎‏‏‏‏‎‏‎‎‏‎‏‎‏‏‏‏‎‏‎‎‎‏‎‎‎‎‎‎‏‏‏‎‎‎‎‎‎‏‏‏‏‎‎‎‎‎‏‏‏‏‎‏‏‏‎Bluetooth AVRCP Version‎‏‎‎‏‎"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‎‏‏‎‎‏‏‏‎‎‏‎‏‏‏‎‏‏‏‏‎‎‎‏‏‏‏‏‏‏‎‎‎‎‎‎‎‏‎‎‎‏‎‎‎‏‏‎‎‎‎‏‎‏‏‎‎‎‎‎‏‎Select Bluetooth AVRCP Version‎‏‎‎‏‎"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‎‏‏‏‏‏‎‏‎‎‏‏‎‏‏‏‎‏‎‎‏‎‎‏‏‏‏‎‎‎‎‎‏‏‎‎‏‏‏‎‎‎‎‎‎‎‎‏‎‏‏‏‏‎‎‏‎‏‏‏‏‏‎Bluetooth MAP Version‎‏‎‎‏‎"</string>
diff --git a/packages/SettingsLib/res/values-es-rUS/strings.xml b/packages/SettingsLib/res/values-es-rUS/strings.xml
index 287a1ac..d1e4fb5 100644
--- a/packages/SettingsLib/res/values-es-rUS/strings.xml
+++ b/packages/SettingsLib/res/values-es-rUS/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Mostrar dispositivos Bluetooth sin nombre"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Inhabilitar volumen absoluto"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Habilitar Gabeldorsche"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"Conectividad mejorada"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Versión de AVRCP del Bluetooth"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Selecciona la versión de AVRCP del Bluetooth"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Versión de MAP de Bluetooth"</string>
diff --git a/packages/SettingsLib/res/values-es/strings.xml b/packages/SettingsLib/res/values-es/strings.xml
index 9d34557..9ad71e2 100644
--- a/packages/SettingsLib/res/values-es/strings.xml
+++ b/packages/SettingsLib/res/values-es/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Mostrar dispositivos Bluetooth sin nombre"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Inhabilitar volumen absoluto"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Habilitar Gabeldorsche"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"Conectividad mejorada"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Versión AVRCP de Bluetooth"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Selecciona la versión AVRCP de Bluetooth"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Versión de MAP de Bluetooth"</string>
diff --git a/packages/SettingsLib/res/values-et/strings.xml b/packages/SettingsLib/res/values-et/strings.xml
index d003ef0..14d3b57 100644
--- a/packages/SettingsLib/res/values-et/strings.xml
+++ b/packages/SettingsLib/res/values-et/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Kuva ilma nimedeta Bluetoothi seadmed"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Keela absoluutne helitugevus"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Luba Gabeldorsche"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"Täiustatud ühenduvus"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bluetoothi AVRCP versioon"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Valige Bluetoothi AVRCP versioon"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetoothi MAP-i versioon"</string>
diff --git a/packages/SettingsLib/res/values-eu/strings.xml b/packages/SettingsLib/res/values-eu/strings.xml
index 0042321..dcdadbd 100644
--- a/packages/SettingsLib/res/values-eu/strings.xml
+++ b/packages/SettingsLib/res/values-eu/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Erakutsi Bluetooth bidezko gailuak izenik gabe"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Desgaitu bolumen absolutua"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gaitu Gabeldorsche"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"Konexio hobeak"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bluetooth AVRCP bertsioa"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Hautatu Bluetooth AVRCP bertsioa"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetooth MAParen bertsioa"</string>
diff --git a/packages/SettingsLib/res/values-fa/strings.xml b/packages/SettingsLib/res/values-fa/strings.xml
index 1c08815..f3b22d3 100644
--- a/packages/SettingsLib/res/values-fa/strings.xml
+++ b/packages/SettingsLib/res/values-fa/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"نمایش دستگاه‌های بلوتوث بدون نام"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"غیرفعال کردن میزان صدای مطلق"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"‏فعال کردن Gabeldorsche"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"اتصال بهبودیافته"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"‏نسخه AVRCP بلوتوث"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"‏انتخاب نسخه AVRCP بلوتوث"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"‏نسخه MAP بلوتوث"</string>
diff --git a/packages/SettingsLib/res/values-fi/strings.xml b/packages/SettingsLib/res/values-fi/strings.xml
index 3945e55..3d28f1d 100644
--- a/packages/SettingsLib/res/values-fi/strings.xml
+++ b/packages/SettingsLib/res/values-fi/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Näytä nimettömät Bluetooth-laitteet"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Poista yleinen äänenvoimakkuuden säätö käytöstä"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Ota Gabeldorsche käyttöön"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"Parannetut yhteydet"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bluetoothin AVRCP-versio"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Valitse Bluetoothin AVRCP-versio"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetoothin MAP-versio"</string>
diff --git a/packages/SettingsLib/res/values-fr-rCA/strings.xml b/packages/SettingsLib/res/values-fr-rCA/strings.xml
index 140d4ce..87d3de1 100644
--- a/packages/SettingsLib/res/values-fr-rCA/strings.xml
+++ b/packages/SettingsLib/res/values-fr-rCA/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Afficher les appareils Bluetooth sans nom"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Désactiver le volume absolu"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Activer le Gabeldorsche"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"Connectivité améliorée"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Version du profil Bluetooth AVRCP"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Sélectionner la version du profil Bluetooth AVRCP"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Version du profil Bluetooth MAP"</string>
diff --git a/packages/SettingsLib/res/values-fr/strings.xml b/packages/SettingsLib/res/values-fr/strings.xml
index 1b1ae8e..ddf2bc0 100644
--- a/packages/SettingsLib/res/values-fr/strings.xml
+++ b/packages/SettingsLib/res/values-fr/strings.xml
@@ -251,13 +251,12 @@
     <string name="wifi_display_certification" msgid="1805579519992520381">"Certification affichage sans fil"</string>
     <string name="wifi_verbose_logging" msgid="1785910450009679371">"Autoriser l\'enregistrement d\'infos Wi-Fi détaillées"</string>
     <string name="wifi_scan_throttling" msgid="2985624788509913617">"Limiter la recherche Wi‑Fi"</string>
-    <string name="wifi_enhanced_mac_randomization" msgid="5437378364995776979">"Chgt aléatoire d\'adresse MAC sur Wi-Fi"</string>
+    <string name="wifi_enhanced_mac_randomization" msgid="5437378364995776979">"Chgt aléatoire d\'adresse MAC en Wi-Fi"</string>
     <string name="mobile_data_always_on" msgid="8275958101875563572">"Données mobiles toujours actives"</string>
     <string name="tethering_hardware_offload" msgid="4116053719006939161">"Accélération matérielle pour le partage de connexion"</string>
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Afficher les appareils Bluetooth sans nom"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Désactiver le volume absolu"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Activer Gabeldorsche"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"Connectivité améliorée"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Version Bluetooth AVRCP"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Sélectionner la version Bluetooth AVRCP"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Version Bluetooth MAP"</string>
@@ -284,7 +283,7 @@
     <string name="wifi_display_certification_summary" msgid="8111151348106907513">"Afficher les options pour la certification de l\'affichage sans fil"</string>
     <string name="wifi_verbose_logging_summary" msgid="4993823188807767892">"Détailler les infos Wi-Fi, afficher par RSSI de SSID dans l\'outil de sélection Wi-Fi"</string>
     <string name="wifi_scan_throttling_summary" msgid="2577105472017362814">"Réduit la décharge de la batterie et améliore les performances du réseau"</string>
-    <string name="wifi_enhanced_mac_randomization_summary" msgid="1210663439867489931">"Lorsque ce mode est activé, l\'adresse e-mail MAC de cet appareil peut changer lors de chaque connexion à un réseau pour lequel le changement aléatoire d\'adresse MAC est activé."</string>
+    <string name="wifi_enhanced_mac_randomization_summary" msgid="1210663439867489931">"Lorsque ce mode est activé, l\'adresse MAC de cet appareil peut changer lors de chaque connexion à un réseau Wi-Fi pour lequel le changement aléatoire d\'adresse MAC est activé"</string>
     <string name="wifi_metered_label" msgid="8737187690304098638">"Facturé à l\'usage"</string>
     <string name="wifi_unmetered_label" msgid="6174142840934095093">"Non facturé à l\'usage"</string>
     <string name="select_logd_size_title" msgid="1604578195914595173">"Tailles des tampons de l\'enregistreur"</string>
diff --git a/packages/SettingsLib/res/values-gl/strings.xml b/packages/SettingsLib/res/values-gl/strings.xml
index f9d57c4..af43099 100644
--- a/packages/SettingsLib/res/values-gl/strings.xml
+++ b/packages/SettingsLib/res/values-gl/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Mostrar dispositivos Bluetooth sen nomes"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Desactivar volume absoluto"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Activar Gabeldorsche"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"Conectividade mellorada"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Versión de Bluetooth AVRCP"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Selecciona a versión de Bluetooth AVRCP"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Versión de MAP de Bluetooth"</string>
diff --git a/packages/SettingsLib/res/values-gu/strings.xml b/packages/SettingsLib/res/values-gu/strings.xml
index aa1f960..3261f69 100644
--- a/packages/SettingsLib/res/values-gu/strings.xml
+++ b/packages/SettingsLib/res/values-gu/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"નામ વિનાના બ્લૂટૂથ ઉપકરણો બતાવો"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"ચોક્કસ વૉલ્યૂમને અક્ષમ કરો"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gabeldorsche ચાલુ કરો"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"વિસ્તૃત કનેક્ટિવિટી"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"બ્લૂટૂથ AVRCP સંસ્કરણ"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"બ્લૂટૂથ AVRCP સંસ્કરણ પસંદ કરો"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"બ્લૂટૂથ MAP વર્ઝન"</string>
diff --git a/packages/SettingsLib/res/values-hi/strings.xml b/packages/SettingsLib/res/values-hi/strings.xml
index 9b6a27a..904a70e 100644
--- a/packages/SettingsLib/res/values-hi/strings.xml
+++ b/packages/SettingsLib/res/values-hi/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"बिना नाम वाले ब्लूटूथ डिवाइस दिखाएं"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"ब्लूटूथ से आवाज़ के नियंत्रण की सुविधा रोकें"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gabeldorsche चालू करें"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"कनेक्टिविटी बेहतर बनाएं"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"ब्लूटूथ एवीआरसीपी वर्शन"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"ब्लूटूथ AVRCP वर्शन चुनें"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"ब्लूटूथ का MAP वर्शन"</string>
diff --git a/packages/SettingsLib/res/values-hr/strings.xml b/packages/SettingsLib/res/values-hr/strings.xml
index 14e3330..3edc452 100644
--- a/packages/SettingsLib/res/values-hr/strings.xml
+++ b/packages/SettingsLib/res/values-hr/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Prikaži Bluetooth uređaje bez naziva"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Onemogući apsolutnu glasnoću"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Omogući Gabeldorsche"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"Poboljšana povezivost"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Verzija AVRCP-a za Bluetooth"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Odaberite verziju AVRCP-a za Bluetooth"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Verzija MAP-a za Bluetooth"</string>
diff --git a/packages/SettingsLib/res/values-hu/strings.xml b/packages/SettingsLib/res/values-hu/strings.xml
index d16ff03..fec2dd6 100644
--- a/packages/SettingsLib/res/values-hu/strings.xml
+++ b/packages/SettingsLib/res/values-hu/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Név nélküli Bluetooth-eszközök megjelenítése"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Abszolút hangerő funkció letiltása"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"A Gabeldorsche engedélyezése"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"Enhanced Connectivity"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"A Bluetooth AVRCP-verziója"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"A Bluetooth AVRCP-verziójának kiválasztása"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"A Bluetooth MAP-verziója"</string>
diff --git a/packages/SettingsLib/res/values-hy/strings.xml b/packages/SettingsLib/res/values-hy/strings.xml
index b010b50..f219d24 100644
--- a/packages/SettingsLib/res/values-hy/strings.xml
+++ b/packages/SettingsLib/res/values-hy/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Ցուցադրել Bluetooth սարքերն առանց անունների"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Անջատել ձայնի բացարձակ ուժգնությունը"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Միացնել Gabeldorsche-ը"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"Տվյալների լավացված փոխանակում"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bluetooth AVRCP տարբերակը"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Ընտրել Bluetooth AVRCP տարբերակը"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetooth MAP-ի տարբերակ"</string>
diff --git a/packages/SettingsLib/res/values-in/arrays.xml b/packages/SettingsLib/res/values-in/arrays.xml
index 37cf189f..3ab50cc 100644
--- a/packages/SettingsLib/res/values-in/arrays.xml
+++ b/packages/SettingsLib/res/values-in/arrays.xml
@@ -31,7 +31,7 @@
     <item msgid="7852381437933824454">"Memutus sambungan..."</item>
     <item msgid="5046795712175415059">"Sambungan terputus"</item>
     <item msgid="2473654476624070462">"Gagal"</item>
-    <item msgid="9146847076036105115">"Dicekal"</item>
+    <item msgid="9146847076036105115">"Diblokir"</item>
     <item msgid="4543924085816294893">"Menghindari sambungan buruk untuk sementara"</item>
   </string-array>
   <string-array name="wifi_status_with_ssid">
diff --git a/packages/SettingsLib/res/values-in/strings.xml b/packages/SettingsLib/res/values-in/strings.xml
index 42ccd53..a6f8846 100644
--- a/packages/SettingsLib/res/values-in/strings.xml
+++ b/packages/SettingsLib/res/values-in/strings.xml
@@ -180,8 +180,8 @@
     <string name="tts_engine_settings_button" msgid="477155276199968948">"Luncurkan setelan mesin"</string>
     <string name="tts_engine_preference_section_title" msgid="3861562305498624904">"Mesin yang dipilih"</string>
     <string name="tts_general_section_title" msgid="8919671529502364567">"Umum"</string>
-    <string name="tts_reset_speech_pitch_title" msgid="7149398585468413246">"Setel ulang tinggi nada ucapan"</string>
-    <string name="tts_reset_speech_pitch_summary" msgid="6822904157021406449">"Setel ulang tinggi nada diucapkannya teks menjadi default."</string>
+    <string name="tts_reset_speech_pitch_title" msgid="7149398585468413246">"Reset tinggi nada ucapan"</string>
+    <string name="tts_reset_speech_pitch_summary" msgid="6822904157021406449">"Reset tinggi nada diucapkannya teks menjadi default."</string>
   <string-array name="tts_rate_entries">
     <item msgid="9004239613505400644">"Sangat lambat"</item>
     <item msgid="1815382991399815061">"Lambat"</item>
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Tampilkan perangkat Bluetooth tanpa nama"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Nonaktifkan volume absolut"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Aktifkan Gabeldorsche"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"Konektivitas Yang Disempurnakan"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Versi AVRCP Bluetooth"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Pilih Versi AVRCP Bluetooth"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Versi MAP Bluetooth"</string>
diff --git a/packages/SettingsLib/res/values-is/strings.xml b/packages/SettingsLib/res/values-is/strings.xml
index 0ebc341..caf2323 100644
--- a/packages/SettingsLib/res/values-is/strings.xml
+++ b/packages/SettingsLib/res/values-is/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Sýna Bluetooth-tæki án heita"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Slökkva á samstillingu hljóðstyrks"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Virkja Gabeldorsche"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"Aukin tengigeta"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bluetooth AVRCP-útgáfa"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Velja Bluetooth AVRCP-útgáfu"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetooth MAP-útgáfa"</string>
diff --git a/packages/SettingsLib/res/values-it/strings.xml b/packages/SettingsLib/res/values-it/strings.xml
index 50fdfc9..8d18727 100644
--- a/packages/SettingsLib/res/values-it/strings.xml
+++ b/packages/SettingsLib/res/values-it/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Mostra dispositivi Bluetooth senza nome"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Disattiva volume assoluto"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Attiva Gabeldorsche"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"Connettività migliorata"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Versione Bluetooth AVRCP"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Seleziona versione Bluetooth AVRCP"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Versione Bluetooth MAP"</string>
diff --git a/packages/SettingsLib/res/values-iw/strings.xml b/packages/SettingsLib/res/values-iw/strings.xml
index fb7d00f..fff881c 100644
--- a/packages/SettingsLib/res/values-iw/strings.xml
+++ b/packages/SettingsLib/res/values-iw/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"‏הצגת מכשירי Bluetooth ללא שמות"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"השבת עוצמת קול מוחלטת"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"‏הפעלת Gabeldorsche"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"קישוריות משופרת"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"‏Bluetooth גרסה AVRCP"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"‏בחר Bluetooth גרסה AVRCP"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"‏גרסת Bluetooth MAP"</string>
diff --git a/packages/SettingsLib/res/values-ja/strings.xml b/packages/SettingsLib/res/values-ja/strings.xml
index 3537cea..5e579b7 100644
--- a/packages/SettingsLib/res/values-ja/strings.xml
+++ b/packages/SettingsLib/res/values-ja/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Bluetooth デバイスを名前なしで表示"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"絶対音量を無効にする"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gabeldorsche を有効にする"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"接続強化"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bluetooth AVRCP バージョン"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Bluetooth AVRCP バージョンを選択する"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetooth MAP バージョン"</string>
diff --git a/packages/SettingsLib/res/values-ka/strings.xml b/packages/SettingsLib/res/values-ka/strings.xml
index 9b671a8..1b5fae9 100644
--- a/packages/SettingsLib/res/values-ka/strings.xml
+++ b/packages/SettingsLib/res/values-ka/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Bluetooth-მოწყობილობების ჩვენება სახელების გარეშე"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"ხმის აბსოლუტური სიძლიერის გათიშვა"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gabeldorsche-ის ჩართვა"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"კავშირის გაძლიერებული შესაძლებლობა"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bluetooth-ის AVRCP-ის ვერსია"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"აირჩიეთ Bluetooth-ის AVRCP-ის ვერსია"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetooth MAP-ის ვერსია"</string>
diff --git a/packages/SettingsLib/res/values-kk/strings.xml b/packages/SettingsLib/res/values-kk/strings.xml
index 279aca0..9c290e9 100644
--- a/packages/SettingsLib/res/values-kk/strings.xml
+++ b/packages/SettingsLib/res/values-kk/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Bluetooth құрылғыларын атаусыз көрсету"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Абсолютті дыбыс деңгейін өшіру"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gabeldorsche функциясын іске қосу"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"Жетілдірілген байланыс"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bluetooth AVRCP нұсқасы"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Bluetooth AVRCP нұсқасын таңдау"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetooth MAP нұсқасы"</string>
diff --git a/packages/SettingsLib/res/values-km/strings.xml b/packages/SettingsLib/res/values-km/strings.xml
index 03065e8..2878db1 100644
--- a/packages/SettingsLib/res/values-km/strings.xml
+++ b/packages/SettingsLib/res/values-km/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"បង្ហាញ​ឧបករណ៍​ប្ល៊ូធូស​គ្មានឈ្មោះ"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"បិទកម្រិតសំឡេងលឺខ្លាំង"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"បើក Gabeldorsche"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"ការតភ្ជាប់​ដែលបានធ្វើឱ្យប្រសើរឡើង"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"កំណែប្ល៊ូធូស AVRCP"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"ជ្រើសរើសកំណែប្ល៊ូធូស AVRCP"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"កំណែ​ប៊្លូធូស MAP"</string>
diff --git a/packages/SettingsLib/res/values-kn/strings.xml b/packages/SettingsLib/res/values-kn/strings.xml
index 71c5e49..7b01056 100644
--- a/packages/SettingsLib/res/values-kn/strings.xml
+++ b/packages/SettingsLib/res/values-kn/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"ಹೆಸರುಗಳಿಲ್ಲದ ಬ್ಲೂಟೂತ್ ಸಾಧನಗಳನ್ನು ತೋರಿಸಿ"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"ಸಂಪೂರ್ಣ ವಾಲ್ಯೂಮ್‌ ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಿ"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gabeldorsche ಅನ್ನು ಸಕ್ರಿಯಗೊಳಿಸಿ"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"ವರ್ಧಿತ ಸಂಪರ್ಕ"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"ಬ್ಲೂಟೂತ್ AVRCP ಆವೃತ್ತಿ"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"ಬ್ಲೂಟೂತ್ AVRCP ಆವೃತ್ತಿಯನ್ನು ಆಯ್ಕೆ ಮಾಡಿ"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"ಬ್ಲೂಟೂತ್ MAP ಆವೃತ್ತಿ"</string>
diff --git a/packages/SettingsLib/res/values-ko/strings.xml b/packages/SettingsLib/res/values-ko/strings.xml
index 5d82eae..696ed29 100644
--- a/packages/SettingsLib/res/values-ko/strings.xml
+++ b/packages/SettingsLib/res/values-ko/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"이름이 없는 블루투스 기기 표시"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"절대 볼륨 사용 안함"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gabeldorsche 사용 설정"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"향상된 연결"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"블루투스 AVRCP 버전"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"블루투스 AVRCP 버전 선택"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"블루투스 MAP 버전"</string>
diff --git a/packages/SettingsLib/res/values-ky/strings.xml b/packages/SettingsLib/res/values-ky/strings.xml
index 2702392..c4b5f7e 100644
--- a/packages/SettingsLib/res/values-ky/strings.xml
+++ b/packages/SettingsLib/res/values-ky/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Аталышсыз Bluetooth түзмөктөрү көрсөтүлсүн"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Үндүн абсолюттук деңгээли өчүрүлсүн"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gabeldorsche функциясын иштетүү"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"Жакшыртылган туташуу"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bluetooth AVRCP версиясы"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Bluetooth AVRCP версиясын тандоо"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetooth MAP версиясы"</string>
diff --git a/packages/SettingsLib/res/values-lo/strings.xml b/packages/SettingsLib/res/values-lo/strings.xml
index 7a2c338..a72861e 100644
--- a/packages/SettingsLib/res/values-lo/strings.xml
+++ b/packages/SettingsLib/res/values-lo/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"ສະແດງອຸປະກອນ Bluetooth ທີ່ບໍ່ມີຊື່"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"ປິດໃຊ້ລະດັບສຽງສົມບູນ"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"ເປີດໃຊ້ Gabeldorsche"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"ການເຊື່ອມຕໍ່ທີ່ເສີມແຕ່ງແລ້ວ"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"ເວີຊັນ Bluetooth AVRCP"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"ເລືອກເວີຊັນ Bluetooth AVRCP"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"ເວີຊັນ Bluetooth MAP"</string>
diff --git a/packages/SettingsLib/res/values-lt/strings.xml b/packages/SettingsLib/res/values-lt/strings.xml
index b73aa66..c72bf21 100644
--- a/packages/SettingsLib/res/values-lt/strings.xml
+++ b/packages/SettingsLib/res/values-lt/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Rodyti „Bluetooth“ įrenginius be pavadinimų"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Išjungti didžiausią garsą"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Įgalinti „Gabeldorsche“"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"Patobulintas ryšys"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"„Bluetooth“ AVRCP versija"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Pasirinkite „Bluetooth“ AVRCP versiją"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"„Bluetooth“ MRK versija"</string>
diff --git a/packages/SettingsLib/res/values-lv/strings.xml b/packages/SettingsLib/res/values-lv/strings.xml
index 8e5d24c..d95e57d 100644
--- a/packages/SettingsLib/res/values-lv/strings.xml
+++ b/packages/SettingsLib/res/values-lv/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Rādīt Bluetooth ierīces bez nosaukumiem"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Atspējot absolūto skaļumu"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Iespējot Gabeldorsche"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"Uzlabota savienojamība"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bluetooth AVRCP versija"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Atlasiet Bluetooth AVRCP versiju"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetooth MAP versija"</string>
diff --git a/packages/SettingsLib/res/values-mk/strings.xml b/packages/SettingsLib/res/values-mk/strings.xml
index 34299d8..fb7b634 100644
--- a/packages/SettingsLib/res/values-mk/strings.xml
+++ b/packages/SettingsLib/res/values-mk/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Прикажувај уреди со Bluetooth без имиња"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Оневозможете апсолутна јачина на звук"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Овозможи Gabeldorsche"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"Подобрена поврзливост"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Верзија Bluetooth AVRCP"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Изберете верзија Bluetooth AVRCP"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Верзија на Bluetooth MAP"</string>
diff --git a/packages/SettingsLib/res/values-ml/strings.xml b/packages/SettingsLib/res/values-ml/strings.xml
index c95f8bf..3c281c8 100644
--- a/packages/SettingsLib/res/values-ml/strings.xml
+++ b/packages/SettingsLib/res/values-ml/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"പേരില്ലാത്ത Bluetooth ഉപകരണങ്ങൾ കാണിക്കുക"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"അബ്‌സൊല്യൂട്ട് വോളിയം പ്രവർത്തനരഹിതമാക്കുക"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gabeldorsche പ്രവർത്തനക്ഷമമാക്കുക"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"മെച്ചപ്പെടുത്തിയ കണക്റ്റിവിറ്റി"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bluetooth AVRCP പതിപ്പ്"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Bluetooth AVRCP പതിപ്പ് തിരഞ്ഞെടുക്കുക"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetooth MAP പതിപ്പ്"</string>
diff --git a/packages/SettingsLib/res/values-mn/strings.xml b/packages/SettingsLib/res/values-mn/strings.xml
index 8407db6..37fc5b4 100644
--- a/packages/SettingsLib/res/values-mn/strings.xml
+++ b/packages/SettingsLib/res/values-mn/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Нэргүй Bluetooth төхөөрөмжийг харуулах"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Үнэмлэхүй дууны түвшинг идэвхгүй болгох"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gabeldorsche-г идэвхжүүлэх"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"Сайжруулсан холболт"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bluetooth AVRCP хувилбар"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Bluetooth AVRCP хувилбарыг сонгох"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetooth MAP хувилбар"</string>
diff --git a/packages/SettingsLib/res/values-mr/strings.xml b/packages/SettingsLib/res/values-mr/strings.xml
index c50f365..360f158 100644
--- a/packages/SettingsLib/res/values-mr/strings.xml
+++ b/packages/SettingsLib/res/values-mr/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"नावांशिवाय ब्‍लूटूथ डिव्‍हाइस दाखवा"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"संपूर्ण आवाज बंद करा"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"गाबलडॉर्ष सुरू करा"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"वर्धित कनेक्टिव्हिटी"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"ब्लूटूथ AVRCP आवृत्ती"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"ब्लूटूथ AVRCP आवृत्ती निवडा"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"ब्लूटूथ MAP आवृत्ती"</string>
diff --git a/packages/SettingsLib/res/values-ms/strings.xml b/packages/SettingsLib/res/values-ms/strings.xml
index a0a434f..68356df2 100644
--- a/packages/SettingsLib/res/values-ms/strings.xml
+++ b/packages/SettingsLib/res/values-ms/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Tunjukkan peranti Bluetooth tanpa nama"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Lumpuhkan kelantangan mutlak"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Dayakan Gabeldorsche"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"Kesambungan Dipertingkat"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Versi AVRCP Bluetooth"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Pilih Versi AVRCP Bluetooth"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Versi MAP Bluetooth"</string>
diff --git a/packages/SettingsLib/res/values-my/strings.xml b/packages/SettingsLib/res/values-my/strings.xml
index fa49929..3729a83 100644
--- a/packages/SettingsLib/res/values-my/strings.xml
+++ b/packages/SettingsLib/res/values-my/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"အမည်မရှိသော ဘလူးတုသ်စက်ပစ္စည်းများကို ပြသရန်"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"ပကတိ အသံနှုန်း သတ်မှတ်ချက် ပိတ်ရန်"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gabeldorsche ကို ဖွင့်ရန်"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"အရည်အသွေးမြှင့်တင်ထားသော ချိတ်ဆက်နိုင်မှု"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"ဘလူးတုသ် AVRCP ဗားရှင်း"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"ဘလူးတုသ် AVRCP ဗားရှင်းကို ရွေးပါ"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"ဘလူးတုသ် MAP ဗားရှင်း"</string>
diff --git a/packages/SettingsLib/res/values-nb/strings.xml b/packages/SettingsLib/res/values-nb/strings.xml
index aeaba31..0e0e761 100644
--- a/packages/SettingsLib/res/values-nb/strings.xml
+++ b/packages/SettingsLib/res/values-nb/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Vis Bluetooth-enheter uten navn"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Slå av funksjonen for absolutt volum"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Aktiver Gabeldorsche"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"Forbedret tilkobling"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bluetooth AVRCP-versjon"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Velg Bluetooth AVRCP-versjon"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetooth MAP-versjon"</string>
diff --git a/packages/SettingsLib/res/values-ne/strings.xml b/packages/SettingsLib/res/values-ne/strings.xml
index 4a2c171..762d830 100644
--- a/packages/SettingsLib/res/values-ne/strings.xml
+++ b/packages/SettingsLib/res/values-ne/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"नामकरण नगरिएका ब्लुटुथ यन्त्रहरू देखाउनुहोस्"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"निरपेक्ष आवाज असक्षम गर्नुहोस्"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gabeldorsche सक्षम पार्नुहोस्"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"परिष्कृत जडान"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"ब्लुटुथको AVRCP संस्करण"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"ब्लुटुथको AVRCP संस्करण चयन गर्नुहोस्"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"ब्लुटुथको MAP संस्करण"</string>
diff --git a/packages/SettingsLib/res/values-nl/strings.xml b/packages/SettingsLib/res/values-nl/strings.xml
index 32cc39e..83b72e9 100644
--- a/packages/SettingsLib/res/values-nl/strings.xml
+++ b/packages/SettingsLib/res/values-nl/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Bluetooth-apparaten zonder namen weergeven"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Absoluut volume uitschakelen"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gabeldorsche inschakelen"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"Verbeterde connectiviteit"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bluetooth-AVRCP-versie"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Bluetooth-AVRCP-versie selecteren"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"MAP-versie voor bluetooth"</string>
diff --git a/packages/SettingsLib/res/values-or/strings.xml b/packages/SettingsLib/res/values-or/strings.xml
index 8e5bf25..d200f50 100644
--- a/packages/SettingsLib/res/values-or/strings.xml
+++ b/packages/SettingsLib/res/values-or/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"ବ୍ଲୁଟୂଥ୍‍‌ ଡିଭାଇସ୍‌ଗୁଡ଼ିକୁ ନାମ ବିନା ଦେଖନ୍ତୁ"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"ପୂର୍ଣ୍ଣ ଭଲ୍ୟୁମ୍‌ ଅକ୍ଷମ କରନ୍ତୁ"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"ଗାବେଲ୍‌ଡୋର୍ସ ସକ୍ରିୟ କରନ୍ତୁ"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"ଏନହାନ୍ସଡ୍ କନେକ୍ଟିଭିଟି"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"ବ୍ଲୁଟୂଥ୍‌ AVRCP ଭର୍ସନ୍"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"ବ୍ଲୁଟୂଥ୍‍‌ AVRCP ଭର୍ସନ୍‌"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"ବ୍ଲୁଟୁଥ୍ MAP ସଂସ୍କରଣ"</string>
diff --git a/packages/SettingsLib/res/values-pa/strings.xml b/packages/SettingsLib/res/values-pa/strings.xml
index 1570013..354ee12 100644
--- a/packages/SettingsLib/res/values-pa/strings.xml
+++ b/packages/SettingsLib/res/values-pa/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"ਅਨਾਮ ਬਲੂਟੁੱਥ ਡੀਵਾਈਸਾਂ ਦਿਖਾਓ"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"ਪੂਰਨ ਅਵਾਜ਼ ਨੂੰ ਬੰਦ ਕਰੋ"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gabeldorsche ਨੂੰ ਚਾਲੂ ਕਰੋ"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"ਵਿਸਤ੍ਰਿਤ ਕਨੈਕਟੀਵਿਟੀ"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"ਬਲੂਟੁੱਥ AVRCP ਵਰਜਨ"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"ਬਲੂਟੁੱਥ AVRCP ਵਰਜਨ ਚੁਣੋ"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetooth MAP ਵਰਜਨ"</string>
diff --git a/packages/SettingsLib/res/values-pl/strings.xml b/packages/SettingsLib/res/values-pl/strings.xml
index 1120c50..095412c 100644
--- a/packages/SettingsLib/res/values-pl/strings.xml
+++ b/packages/SettingsLib/res/values-pl/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Pokaż urządzenia Bluetooth bez nazw"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Wyłącz głośność bezwzględną"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Włącz Gabeldorsche"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"Lepsza obsługa połączeń"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Wersja AVRCP Bluetooth"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Wybierz wersję AVRCP Bluetooth"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Wersja MAP Bluetooth"</string>
diff --git a/packages/SettingsLib/res/values-pt-rBR/strings.xml b/packages/SettingsLib/res/values-pt-rBR/strings.xml
index 4214a27..895a987 100644
--- a/packages/SettingsLib/res/values-pt-rBR/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rBR/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Mostrar dispositivos Bluetooth sem nomes"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Desativar volume absoluto"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Ativar Gabeldorsche"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"Conectividade melhorada"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Versão do Bluetooth AVRCP"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Selecionar versão do Bluetooth AVRCP"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Versão MAP do Bluetooth"</string>
diff --git a/packages/SettingsLib/res/values-pt-rPT/strings.xml b/packages/SettingsLib/res/values-pt-rPT/strings.xml
index 508cbfc..2d9f037 100644
--- a/packages/SettingsLib/res/values-pt-rPT/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rPT/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Mostrar dispositivos Bluetooth sem nomes"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Desativar volume absoluto"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Ativar o Gabeldorsche"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"Conetividade melhorada"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Versão de Bluetooth AVRCP"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Selecionar versão de Bluetooth AVRCP"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Versão do MAP do Bluetooth"</string>
diff --git a/packages/SettingsLib/res/values-pt/strings.xml b/packages/SettingsLib/res/values-pt/strings.xml
index 4214a27..895a987 100644
--- a/packages/SettingsLib/res/values-pt/strings.xml
+++ b/packages/SettingsLib/res/values-pt/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Mostrar dispositivos Bluetooth sem nomes"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Desativar volume absoluto"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Ativar Gabeldorsche"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"Conectividade melhorada"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Versão do Bluetooth AVRCP"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Selecionar versão do Bluetooth AVRCP"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Versão MAP do Bluetooth"</string>
diff --git a/packages/SettingsLib/res/values-ro/strings.xml b/packages/SettingsLib/res/values-ro/strings.xml
index 663d3f7..728db17 100644
--- a/packages/SettingsLib/res/values-ro/strings.xml
+++ b/packages/SettingsLib/res/values-ro/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Afișați dispozitivele Bluetooth fără nume"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Dezactivați volumul absolut"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Activați Gabeldorsche"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"Conectivitate îmbunătățită"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Versiunea AVRCP pentru Bluetooth"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Selectați versiunea AVRCP pentru Bluetooth"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Versiunea MAP pentru Bluetooth"</string>
diff --git a/packages/SettingsLib/res/values-ru/strings.xml b/packages/SettingsLib/res/values-ru/strings.xml
index 9c0a2f5..ff2115e 100644
--- a/packages/SettingsLib/res/values-ru/strings.xml
+++ b/packages/SettingsLib/res/values-ru/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Показывать Bluetooth-устройства без названий"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Отключить абсолютный уровень громкости"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Включить Gabeldorsche"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"Улучшенный обмен данными"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Версия Bluetooth AVRCP"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Выберите версию Bluetooth AVRCP"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Версия Bluetooth MAP"</string>
diff --git a/packages/SettingsLib/res/values-si/strings.xml b/packages/SettingsLib/res/values-si/strings.xml
index 8d0e93e..a883cc6 100644
--- a/packages/SettingsLib/res/values-si/strings.xml
+++ b/packages/SettingsLib/res/values-si/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"නම් නොමැති බ්ලූටූත් උපාංග පෙන්වන්න"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"නිරපේක්ෂ හඩ පරිමාව අබල කරන්න"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gabeldorsche සබල කරන්න"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"වැඩිදියුණු කළ සබැඳුම් හැකියාව"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"බ්ලූටූත් AVRCP අනුවාදය"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"බ්ලූටූත් AVRCP අනුවාදය තෝරන්න"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetooth MAP අනුවාදය"</string>
diff --git a/packages/SettingsLib/res/values-sk/strings.xml b/packages/SettingsLib/res/values-sk/strings.xml
index f39a741..05c6379 100644
--- a/packages/SettingsLib/res/values-sk/strings.xml
+++ b/packages/SettingsLib/res/values-sk/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Zobrazovať zariadenia Bluetooth bez názvov"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Zakázať absolútnu hlasitosť"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Povoliť Gabeldorsche"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"Zlepšené možnosti pripojenia"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Verzia rozhrania Bluetooth AVRCP"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Zvoľte verziu rozhrania Bluetooth AVRCP"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Verzia profilu Bluetooth MAP"</string>
diff --git a/packages/SettingsLib/res/values-sl/strings.xml b/packages/SettingsLib/res/values-sl/strings.xml
index 233c8e4..fd216e8 100644
--- a/packages/SettingsLib/res/values-sl/strings.xml
+++ b/packages/SettingsLib/res/values-sl/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Prikaži naprave Bluetooth brez imen"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Onemogočanje absolutne glasnosti"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Omogoči Gabeldorsche"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"Izboljšana povezljivost"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Različica profila AVRCP za Bluetooth"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Izberite različico profila AVRCP za Bluetooth"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Različica profila MAP za Bluetooth"</string>
diff --git a/packages/SettingsLib/res/values-sq/strings.xml b/packages/SettingsLib/res/values-sq/strings.xml
index 6af1062..002c7fc 100644
--- a/packages/SettingsLib/res/values-sq/strings.xml
+++ b/packages/SettingsLib/res/values-sq/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Shfaq pajisjet me Bluetooth pa emra"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Çaktivizo volumin absolut"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Aktivizo Gabeldorsche"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"Lidhshmëria e përmirësuar"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Versioni AVRCP i Bluetooth-it"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Zgjidh versionin AVRCP të Bluetooth-it"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Versioni MAP i Bluetooth-it"</string>
diff --git a/packages/SettingsLib/res/values-sr/strings.xml b/packages/SettingsLib/res/values-sr/strings.xml
index 74c2aec..25a1beb7 100644
--- a/packages/SettingsLib/res/values-sr/strings.xml
+++ b/packages/SettingsLib/res/values-sr/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Прикажи Bluetooth уређаје без назива"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Онемогући главно подешавање јачине звука"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Омогући Gabeldorsche"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"Побољшано повезивање"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Верзија Bluetooth AVRCP-а"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Изаберите верзију Bluetooth AVRCP-а"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Верзија Bluetooth MAP-а"</string>
diff --git a/packages/SettingsLib/res/values-sv/strings.xml b/packages/SettingsLib/res/values-sv/strings.xml
index fe1b0a8..352cb0a 100644
--- a/packages/SettingsLib/res/values-sv/strings.xml
+++ b/packages/SettingsLib/res/values-sv/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Visa namnlösa Bluetooth-enheter"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Inaktivera Absolute volume"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Aktivera Gabeldorsche"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"Förbättrad anslutning"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"AVRCP-version för Bluetooth"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Välj AVRCP-version för Bluetooth"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"MAP-version för Bluetooth"</string>
diff --git a/packages/SettingsLib/res/values-sw/strings.xml b/packages/SettingsLib/res/values-sw/strings.xml
index 5c80627..d2891a0 100644
--- a/packages/SettingsLib/res/values-sw/strings.xml
+++ b/packages/SettingsLib/res/values-sw/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Onyesha vifaa vya Bluetooth visivyo na majina"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Zima sauti kamili"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Washa Gabeldorsche"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"Muunganisho Ulioboreshwa"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Toleo la Bluetooth AVRCP"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Chagua Toleo la Bluetooth AVRCP"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Toleo la Ramani ya Bluetooth"</string>
diff --git a/packages/SettingsLib/res/values-ta/strings.xml b/packages/SettingsLib/res/values-ta/strings.xml
index 241644f..7837dd8 100644
--- a/packages/SettingsLib/res/values-ta/strings.xml
+++ b/packages/SettingsLib/res/values-ta/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"பெயர்கள் இல்லாத புளூடூத் சாதனங்களைக் காட்டு"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"அப்சல்யூட் ஒலியளவு அம்சத்தை முடக்கு"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gabeldorscheவை இயக்கு"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"மேம்படுத்தப்பட்ட இணைப்புநிலை"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"புளூடூத் AVRCP பதிப்பு"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"புளூடூத் AVRCP பதிப்பைத் தேர்ந்தெடு"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"புளூடூத்தின் MAP பதிப்பு"</string>
diff --git a/packages/SettingsLib/res/values-te/strings.xml b/packages/SettingsLib/res/values-te/strings.xml
index a9ec2ea9..60001a0 100644
--- a/packages/SettingsLib/res/values-te/strings.xml
+++ b/packages/SettingsLib/res/values-te/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"పేర్లు లేని బ్లూటూత్ పరికరాలు  చూపించు"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"సంపూర్ణ వాల్యూమ్‌‍ను నిలిపివేయి"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gabeldorscheను ఎనేబుల్ చేయి"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"మెరుగైన కనెక్టివిటీ"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"బ్లూటూత్ AVRCP వెర్షన్"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"బ్లూటూత్ AVRCP సంస్కరణను ఎంచుకోండి"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"బ్లూటూత్ MAP వెర్షన్‌"</string>
diff --git a/packages/SettingsLib/res/values-th/strings.xml b/packages/SettingsLib/res/values-th/strings.xml
index b8343c6..defc33e 100644
--- a/packages/SettingsLib/res/values-th/strings.xml
+++ b/packages/SettingsLib/res/values-th/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"แสดงอุปกรณ์บลูทูธที่ไม่มีชื่อ"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"ปิดใช้การควบคุมระดับเสียงของอุปกรณ์อื่น"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"เปิดใช้ Gabeldorsche"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"การเชื่อมต่อที่ปรับปรุงแล้ว"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"เวอร์ชันของบลูทูธ AVRCP"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"เลือกเวอร์ชันของบลูทูธ AVRCP"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"เวอร์ชัน MAP ของบลูทูธ"</string>
diff --git a/packages/SettingsLib/res/values-tl/strings.xml b/packages/SettingsLib/res/values-tl/strings.xml
index 8aeb392..5d4e975 100644
--- a/packages/SettingsLib/res/values-tl/strings.xml
+++ b/packages/SettingsLib/res/values-tl/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Ipakita ang mga Bluetooth device na walang pangalan"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"I-disable ang absolute volume"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"I-enable ang Gabeldorsche"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"Pinagandang Pagkakonekta"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bersyon ng AVRCP ng Bluetooth"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Pumili ng Bersyon ng AVRCP ng Bluetooth"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bersyon ng MAP ng Bluetooth"</string>
diff --git a/packages/SettingsLib/res/values-tr/strings.xml b/packages/SettingsLib/res/values-tr/strings.xml
index e6d9380..f01f3fa 100644
--- a/packages/SettingsLib/res/values-tr/strings.xml
+++ b/packages/SettingsLib/res/values-tr/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Adsız Bluetooth cihazlarını göster"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Mutlak sesi iptal et"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gabeldorsche\'yi etkileştir"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"Gelişmiş Bağlantı"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bluetooth AVRCP Sürümü"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Bluetooth AVRCP Sürümünü seçin"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetooth MAP Sürümü"</string>
diff --git a/packages/SettingsLib/res/values-uk/strings.xml b/packages/SettingsLib/res/values-uk/strings.xml
index cf1fafd..9ca2f06 100644
--- a/packages/SettingsLib/res/values-uk/strings.xml
+++ b/packages/SettingsLib/res/values-uk/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Показувати пристрої Bluetooth без назв"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Вимкнути абсолютну гучність"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Увімкнути Gabeldorsche"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"Покращене з\'єднання"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Версія Bluetooth AVRCP"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Виберіть версію Bluetooth AVRCP"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Версія Bluetooth MAP"</string>
diff --git a/packages/SettingsLib/res/values-ur/strings.xml b/packages/SettingsLib/res/values-ur/strings.xml
index b7fbe6f..8953f50 100644
--- a/packages/SettingsLib/res/values-ur/strings.xml
+++ b/packages/SettingsLib/res/values-ur/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"بغیر نام والے بلوٹوتھ آلات دکھائیں"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"مطلق والیوم کو غیر فعال کریں"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"‏Gabeldorsche فعال کریں"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"بہتر کردہ کنیکٹوٹی"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"‏بلوٹوتھ AVRCP ورژن"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"‏بلوٹوتھ AVRCP ورژن منتخب کریں"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"‏بلوٹوتھ MAP ورژن"</string>
diff --git a/packages/SettingsLib/res/values-uz/strings.xml b/packages/SettingsLib/res/values-uz/strings.xml
index f81731a..f25b3ac 100644
--- a/packages/SettingsLib/res/values-uz/strings.xml
+++ b/packages/SettingsLib/res/values-uz/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Bluetooth qurilmalarini nomlarisiz ko‘rsatish"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Tovush balandligining mutlaq darajasini faolsizlantirish"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gabeldorsche funksiyasini yoqish"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"Kuchaytirilgan aloqa"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bluetooth AVRCP versiyasi"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Bluetooth AVRCP versiyasini tanlang"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetooth MAP versiyasi"</string>
diff --git a/packages/SettingsLib/res/values-vi/strings.xml b/packages/SettingsLib/res/values-vi/strings.xml
index b7ccf8d..b5798f3 100644
--- a/packages/SettingsLib/res/values-vi/strings.xml
+++ b/packages/SettingsLib/res/values-vi/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Hiển thị các thiết bị Bluetooth không có tên"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Vô hiệu hóa âm lượng tuyệt đối"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Bật tính năng Gabeldorsche"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"Kết nối nâng cao"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Phiên bản Bluetooth AVRCP"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Chọn phiên bản Bluetooth AVRCP"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Phiên bản Bluetooth MAP"</string>
diff --git a/packages/SettingsLib/res/values-zh-rCN/strings.xml b/packages/SettingsLib/res/values-zh-rCN/strings.xml
index 75c1333..c4dcfff 100644
--- a/packages/SettingsLib/res/values-zh-rCN/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rCN/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"显示没有名称的蓝牙设备"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"停用绝对音量功能"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"启用“Gabeldorsche”"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"增强连接性"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"蓝牙 AVRCP 版本"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"选择蓝牙 AVRCP 版本"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"蓝牙 MAP 版本"</string>
diff --git a/packages/SettingsLib/res/values-zh-rHK/strings.xml b/packages/SettingsLib/res/values-zh-rHK/strings.xml
index 26ddfb1..e04651c 100644
--- a/packages/SettingsLib/res/values-zh-rHK/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rHK/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"顯示沒有名稱的藍牙裝置"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"停用絕對音量功能"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"啟用 Gabeldorsche"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"強化連線功能"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"藍牙 AVRCP 版本"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"選擇藍牙 AVRCP 版本"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"藍牙 MAP 版本"</string>
diff --git a/packages/SettingsLib/res/values-zh-rTW/strings.xml b/packages/SettingsLib/res/values-zh-rTW/strings.xml
index 72ea043..a1ae6b6 100644
--- a/packages/SettingsLib/res/values-zh-rTW/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rTW/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"顯示沒有名稱的藍牙裝置"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"停用絕對音量功能"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"啟用 Gabeldorsche"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"加強型連線"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"藍牙 AVRCP 版本"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"選取藍牙 AVRCP 版本"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"藍牙 MAP 版本"</string>
diff --git a/packages/SettingsLib/res/values-zu/strings.xml b/packages/SettingsLib/res/values-zu/strings.xml
index 6b8739f..2dafad8 100644
--- a/packages/SettingsLib/res/values-zu/strings.xml
+++ b/packages/SettingsLib/res/values-zu/strings.xml
@@ -257,7 +257,6 @@
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Bonisa amadivayisi e-Bluetooth ngaphandle kwamagama"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Khubaza ivolumu ngokuphelele"</string>
     <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Nika amandla i-Gabeldorsche"</string>
-    <string name="enhanced_connectivity" msgid="7201127377781666804">"Ukuxhumeka Okuthuthukisiwe"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Inguqulo ye-Bluetooth ye-AVRCP"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Khetha inguqulo ye-Bluetooth AVRCP"</string>
     <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Inguqulo ye-Bluetooth MAP"</string>
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
index c1543fd..bfd5b1cc 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
@@ -81,6 +81,7 @@
         sBroadcastOnRestore.add(Settings.Secure.DARK_THEME_CUSTOM_START_TIME);
         sBroadcastOnRestore.add(Settings.Secure.DARK_THEME_CUSTOM_END_TIME);
         sBroadcastOnRestore.add(Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED);
+        sBroadcastOnRestore.add(Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS);
     }
 
     private interface SettingsLookup {
diff --git a/packages/SystemUI/res/layout/media_view.xml b/packages/SystemUI/res/layout/media_view.xml
index 3c641af..ed870f8 100644
--- a/packages/SystemUI/res/layout/media_view.xml
+++ b/packages/SystemUI/res/layout/media_view.xml
@@ -210,8 +210,98 @@
         android:layout_width="@dimen/qs_media_icon_size"
         android:layout_height="@dimen/qs_media_icon_size" />
 
-    <!-- Buttons to remove this view when no longer needed -->
-    <include
-        layout="@layout/qs_media_panel_options"
-        android:visibility="gone" />
+    <!-- Constraints are set here as they are the same regardless of host -->
+    <TextView
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="@dimen/qs_media_panel_outer_padding"
+        android:layout_marginStart="@dimen/qs_media_panel_outer_padding"
+        android:layout_marginEnd="@dimen/qs_media_panel_outer_padding"
+        android:id="@+id/media_text"
+        android:fontFamily="@*android:string/config_headlineFontFamilyMedium"
+        android:textColor="@color/media_primary_text"
+        android:text="@string/controls_media_title"
+        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintBottom_toTopOf="@id/remove_text"
+        app:layout_constraintVertical_chainStyle="spread_inside"/>
+
+    <TextView
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginStart="@dimen/qs_media_panel_outer_padding"
+        android:layout_marginEnd="@dimen/qs_media_panel_outer_padding"
+        android:id="@+id/remove_text"
+        android:fontFamily="@*android:string/config_headlineFontFamily"
+        android:singleLine="true"
+        android:textColor="@color/media_primary_text"
+        android:text="@string/controls_media_close_session"
+        app:layout_constraintTop_toBottomOf="@id/media_text"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintBottom_toTopOf="@id/settings"/>
+
+    <FrameLayout
+        android:id="@+id/settings"
+        android:background="@drawable/qs_media_light_source"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginStart="@dimen/qs_media_panel_outer_padding"
+        android:paddingBottom="@dimen/qs_media_panel_outer_padding"
+        android:minWidth="48dp"
+        android:minHeight="48dp"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toBottomOf="@id/remove_text">
+
+        <TextView
+            android:layout_gravity="bottom"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:fontFamily="@*android:string/config_headlineFontFamilyMedium"
+            android:textColor="@android:color/white"
+            android:text="@string/controls_media_settings_button" />
+    </FrameLayout>
+
+    <FrameLayout
+        android:id="@+id/cancel"
+        android:background="@drawable/qs_media_light_source"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginEnd="@dimen/qs_media_panel_outer_padding"
+        android:paddingBottom="@dimen/qs_media_panel_outer_padding"
+        android:minWidth="48dp"
+        android:minHeight="48dp"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintEnd_toStartOf="@id/dismiss" >
+
+        <TextView
+            android:layout_gravity="bottom"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:fontFamily="@*android:string/config_headlineFontFamilyMedium"
+            android:textColor="@android:color/white"
+            android:text="@string/cancel" />
+    </FrameLayout>
+
+    <FrameLayout
+        android:id="@+id/dismiss"
+        android:background="@drawable/qs_media_light_source"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginEnd="@dimen/qs_media_panel_outer_padding"
+        android:paddingBottom="@dimen/qs_media_panel_outer_padding"
+        android:minWidth="48dp"
+        android:minHeight="48dp"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintEnd_toEndOf="parent">
+
+        <TextView
+            android:layout_gravity="bottom"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:fontFamily="@*android:string/config_headlineFontFamilyMedium"
+            android:textColor="@android:color/white"
+            android:text="@string/controls_media_dismiss_button"
+        />
+    </FrameLayout>
 </com.android.systemui.util.animation.TransitionLayout>
diff --git a/packages/SystemUI/res/layout/qs_media_panel_options.xml b/packages/SystemUI/res/layout/qs_media_panel_options.xml
deleted file mode 100644
index e72c0e8..0000000
--- a/packages/SystemUI/res/layout/qs_media_panel_options.xml
+++ /dev/null
@@ -1,61 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2019 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License
-  -->
-<LinearLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/qs_media_controls_options"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    android:gravity="center"
-    android:padding="16dp"
-    android:orientation="vertical">
-    <LinearLayout
-        android:layout_width="wrap_content"
-        android:layout_height="48dp"
-        android:layout_weight="1"
-        android:minWidth="48dp"
-        android:layout_gravity="start|bottom"
-        android:gravity="bottom"
-        android:id="@+id/remove"
-        android:orientation="horizontal">
-        <ImageView
-            android:layout_width="18dp"
-            android:layout_height="18dp"
-            android:id="@+id/remove_icon"
-            android:layout_marginEnd="16dp"
-            android:tint="@color/media_primary_text"
-            android:src="@drawable/ic_clear"/>
-        <TextView
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:id="@+id/remove_text"
-            android:fontFamily="@*android:string/config_headlineFontFamilyMedium"
-            android:singleLine="true"
-            android:textColor="@color/media_primary_text"
-            android:text="@string/controls_media_close_session" />
-    </LinearLayout>
-    <TextView
-        android:id="@+id/cancel"
-        android:layout_width="wrap_content"
-        android:layout_height="48dp"
-        android:layout_weight="1"
-        android:minWidth="48dp"
-        android:layout_gravity="end|bottom"
-        android:gravity="bottom"
-        android:fontFamily="@*android:string/config_headlineFontFamilyMedium"
-        android:textColor="@android:color/white"
-        android:text="@string/cancel" />
-</LinearLayout>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 77d3f45..823c1ff 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -2799,7 +2799,7 @@
     <!-- Explanation for closing controls associated with a specific media session [CHAR_LIMIT=NONE] -->
     <string name="controls_media_close_session">Hide the current session.</string>
     <!-- Label for a button that will hide media controls [CHAR_LIMIT=30] -->
-    <string name="controls_media_dismiss_button">Hide</string>
+    <string name="controls_media_dismiss_button">Dismiss</string>
     <!-- Label for button to resume media playback [CHAR_LIMIT=NONE] -->
     <string name="controls_media_resume">Resume</string>
     <!-- Label for button to go to media control settings screen [CHAR_LIMIT=30] -->
diff --git a/packages/SystemUI/src/com/android/systemui/controls/CustomIconCache.kt b/packages/SystemUI/src/com/android/systemui/controls/CustomIconCache.kt
new file mode 100644
index 0000000..cca0f16
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/controls/CustomIconCache.kt
@@ -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.controls
+
+import android.content.ComponentName
+import android.graphics.drawable.Icon
+import androidx.annotation.GuardedBy
+import javax.inject.Inject
+import javax.inject.Singleton
+
+/**
+ * Icon cache for custom icons sent with controls.
+ *
+ * It assumes that only one component can be current at the time, to minimize the number of icons
+ * stored at a given time.
+ */
+@Singleton
+class CustomIconCache @Inject constructor() {
+
+    private var currentComponent: ComponentName? = null
+    @GuardedBy("cache")
+    private val cache: MutableMap<String, Icon> = LinkedHashMap()
+
+    /**
+     * Store an icon in the cache.
+     *
+     * If the icons currently stored do not correspond to the component to be stored, the cache is
+     * cleared first.
+     */
+    fun store(component: ComponentName, controlId: String, icon: Icon?) {
+        if (component != currentComponent) {
+            clear()
+            currentComponent = component
+        }
+        synchronized(cache) {
+            if (icon != null) {
+                cache.put(controlId, icon)
+            } else {
+                cache.remove(controlId)
+            }
+        }
+    }
+
+    /**
+     * Retrieves a custom icon stored in the cache.
+     *
+     * It will return null if the component requested is not the one whose icons are stored, or if
+     * there is no icon cached for that id.
+     */
+    fun retrieve(component: ComponentName, controlId: String): Icon? {
+        if (component != currentComponent) return null
+        return synchronized(cache) {
+            cache.get(controlId)
+        }
+    }
+
+    private fun clear() {
+        synchronized(cache) {
+            cache.clear()
+        }
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsEditingActivity.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsEditingActivity.kt
index ff40a8a..f68388d 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsEditingActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsEditingActivity.kt
@@ -29,6 +29,7 @@
 import androidx.recyclerview.widget.RecyclerView
 import com.android.systemui.R
 import com.android.systemui.broadcast.BroadcastDispatcher
+import com.android.systemui.controls.CustomIconCache
 import com.android.systemui.controls.controller.ControlsControllerImpl
 import com.android.systemui.controls.controller.StructureInfo
 import com.android.systemui.globalactions.GlobalActionsComponent
@@ -42,7 +43,8 @@
 class ControlsEditingActivity @Inject constructor(
     private val controller: ControlsControllerImpl,
     broadcastDispatcher: BroadcastDispatcher,
-    private val globalActionsComponent: GlobalActionsComponent
+    private val globalActionsComponent: GlobalActionsComponent,
+    private val customIconCache: CustomIconCache
 ) : LifecycleActivity() {
 
     companion object {
@@ -170,7 +172,7 @@
 
     private fun setUpList() {
         val controls = controller.getFavoritesForStructure(component, structure)
-        model = FavoritesModel(component, controls, favoritesModelCallback)
+        model = FavoritesModel(customIconCache, component, controls, favoritesModelCallback)
         val elevation = resources.getFloat(R.dimen.control_card_elevation)
         val recyclerView = requireViewById<RecyclerView>(R.id.list)
         recyclerView.alpha = 0.0f
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsModel.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsModel.kt
index 4ef64a5..ad0e7a5 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsModel.kt
@@ -114,11 +114,27 @@
     val controlStatus: ControlStatus
 ) : ElementWrapper(), ControlInterface by controlStatus
 
+private fun nullIconGetter(_a: ComponentName, _b: String): Icon? = null
+
 data class ControlInfoWrapper(
     override val component: ComponentName,
     val controlInfo: ControlInfo,
     override var favorite: Boolean
 ) : ElementWrapper(), ControlInterface {
+
+    var customIconGetter: (ComponentName, String) -> Icon? = ::nullIconGetter
+        private set
+
+    // Separate constructor so the getter is not used in auto-generated methods
+    constructor(
+        component: ComponentName,
+        controlInfo: ControlInfo,
+        favorite: Boolean,
+        customIconGetter: (ComponentName, String) -> Icon?
+    ): this(component, controlInfo, favorite) {
+        this.customIconGetter = customIconGetter
+    }
+
     override val controlId: String
         get() = controlInfo.controlId
     override val title: CharSequence
@@ -128,8 +144,7 @@
     override val deviceType: Int
         get() = controlInfo.deviceType
     override val customIcon: Icon?
-        // Will need to address to support for edit activity
-        get() = null
+        get() = customIconGetter(component, controlId)
 }
 
 data class DividerWrapper(
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/FavoritesModel.kt b/packages/SystemUI/src/com/android/systemui/controls/management/FavoritesModel.kt
index 5242501..f9ce636 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/FavoritesModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/FavoritesModel.kt
@@ -21,6 +21,7 @@
 import androidx.recyclerview.widget.ItemTouchHelper
 import androidx.recyclerview.widget.RecyclerView
 import com.android.systemui.controls.ControlInterface
+import com.android.systemui.controls.CustomIconCache
 import com.android.systemui.controls.controller.ControlInfo
 import java.util.Collections
 
@@ -35,6 +36,7 @@
  * @property favoritesModelCallback callback to notify on first change and empty favorites
  */
 class FavoritesModel(
+    private val customIconCache: CustomIconCache,
     private val componentName: ComponentName,
     favorites: List<ControlInfo>,
     private val favoritesModelCallback: FavoritesModelCallback
@@ -83,7 +85,7 @@
         }
 
     override val elements: List<ElementWrapper> = favorites.map {
-        ControlInfoWrapper(componentName, it, true)
+        ControlInfoWrapper(componentName, it, true, customIconCache::retrieve)
     } + DividerWrapper()
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt
index 22d6b6b..e15380b 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt
@@ -92,7 +92,7 @@
     override fun setValue(cvh: ControlViewHolder, templateId: String, newValue: Float) {
         bouncerOrRun(Action(cvh.cws.ci.controlId, {
             cvh.action(FloatAction(templateId, newValue))
-        }, true /* blockable */))
+        }, false /* blockable */))
     }
 
     override fun longPress(cvh: ControlViewHolder) {
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
index 1eb7e21..5f75c96 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
@@ -44,6 +44,7 @@
 import android.widget.TextView
 import com.android.systemui.R
 import com.android.systemui.controls.ControlsServiceInfo
+import com.android.systemui.controls.CustomIconCache
 import com.android.systemui.controls.controller.ControlInfo
 import com.android.systemui.controls.controller.ControlsController
 import com.android.systemui.controls.controller.StructureInfo
@@ -75,7 +76,8 @@
     @Main val sharedPreferences: SharedPreferences,
     val controlActionCoordinator: ControlActionCoordinator,
     private val activityStarter: ActivityStarter,
-    private val shadeController: ShadeController
+    private val shadeController: ShadeController,
+    private val iconCache: CustomIconCache
 ) : ControlsUiController {
 
     companion object {
@@ -502,6 +504,7 @@
         controls.forEach { c ->
             controlsById.get(ControlKey(componentName, c.getControlId()))?.let {
                 Log.d(ControlsUiController.TAG, "onRefreshState() for id: " + c.getControlId())
+                iconCache.store(componentName, c.controlId, c.customIcon)
                 val cws = ControlWithState(componentName, it.ci, c)
                 val key = ControlKey(componentName, c.getControlId())
                 controlsById.put(key, cws)
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
index de53168..7f610d1 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
@@ -3,7 +3,6 @@
 import android.content.Context
 import android.content.Intent
 import android.content.res.Configuration
-import android.graphics.Color
 import android.provider.Settings.ACTION_MEDIA_CONTROLS_SETTINGS
 import android.util.Log
 import android.util.MathUtils
@@ -151,7 +150,7 @@
         pageIndicator = mediaFrame.requireViewById(R.id.media_page_indicator)
         mediaCarouselScrollHandler = MediaCarouselScrollHandler(mediaCarousel, pageIndicator,
                 executor, mediaManager::onSwipeToDismiss, this::updatePageIndicatorLocation,
-                falsingManager)
+                this::closeGuts, falsingManager)
         isRtl = context.resources.configuration.layoutDirection == View.LAYOUT_DIRECTION_RTL
         inflateSettingsButton()
         mediaContent = mediaCarousel.requireViewById(R.id.media_carousel)
@@ -470,6 +469,12 @@
         }
     }
 
+    fun closeGuts() {
+        mediaPlayers.values.forEach {
+            it.closeGuts(true)
+        }
+    }
+
     /**
      * Update the size of the carousel, remeasuring it if necessary.
      */
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselScrollHandler.kt b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselScrollHandler.kt
index 3096908..77cac50 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselScrollHandler.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselScrollHandler.kt
@@ -56,6 +56,7 @@
     private val mainExecutor: DelayableExecutor,
     private val dismissCallback: () -> Unit,
     private var translationChangedListener: () -> Unit,
+    private val closeGuts: () -> Unit,
     private val falsingManager: FalsingManager
 ) {
     /**
@@ -452,6 +453,7 @@
         val nowScrolledIn = scrollIntoCurrentMedia != 0
         if (newIndex != activeMediaIndex || wasScrolledIn != nowScrolledIn) {
             activeMediaIndex = newIndex
+            closeGuts()
             updatePlayerVisibilities()
         }
         val relativeLocation = activeMediaIndex.toFloat() + if (playerWidthPlusPadding > 0)
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
index 3fc162e..e55678dc 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
@@ -16,6 +16,8 @@
 
 package com.android.systemui.media;
 
+import static android.provider.Settings.ACTION_MEDIA_CONTROLS_SETTINGS;
+
 import android.app.PendingIntent;
 import android.content.Context;
 import android.content.Intent;
@@ -45,6 +47,7 @@
 import com.android.systemui.R;
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.statusbar.phone.KeyguardDismissUtil;
 import com.android.systemui.util.animation.TransitionLayout;
 
 import java.util.List;
@@ -52,6 +55,8 @@
 
 import javax.inject.Inject;
 
+import dagger.Lazy;
+
 /**
  * A view controller used for Media Playback.
  */
@@ -59,6 +64,8 @@
     private static final String TAG = "MediaControlPanel";
     private static final float DISABLED_ALPHA = 0.38f;
 
+    private static final Intent SETTINGS_INTENT = new Intent(ACTION_MEDIA_CONTROLS_SETTINGS);
+
     // Button IDs for QS controls
     static final int[] ACTION_IDS = {
             R.id.action0,
@@ -78,6 +85,8 @@
     private MediaViewController mMediaViewController;
     private MediaSession.Token mToken;
     private MediaController mController;
+    private KeyguardDismissUtil mKeyguardDismissUtil;
+    private Lazy<MediaDataManager> mMediaDataManagerLazy;
     private int mBackgroundColor;
     private int mAlbumArtSize;
     private int mAlbumArtRadius;
@@ -93,12 +102,15 @@
     @Inject
     public MediaControlPanel(Context context, @Background Executor backgroundExecutor,
             ActivityStarter activityStarter, MediaViewController mediaViewController,
-            SeekBarViewModel seekBarViewModel) {
+            SeekBarViewModel seekBarViewModel, Lazy<MediaDataManager> lazyMediaDataManager,
+            KeyguardDismissUtil keyguardDismissUtil) {
         mContext = context;
         mBackgroundExecutor = backgroundExecutor;
         mActivityStarter = activityStarter;
         mSeekBarViewModel = seekBarViewModel;
         mMediaViewController = mediaViewController;
+        mMediaDataManagerLazy = lazyMediaDataManager;
+        mKeyguardDismissUtil = keyguardDismissUtil;
         loadDimens();
 
         mViewOutlineProvider = new ViewOutlineProvider() {
@@ -174,6 +186,21 @@
         mSeekBarViewModel.getProgress().observeForever(mSeekBarObserver);
         mSeekBarViewModel.attachTouchHandlers(vh.getSeekBar());
         mMediaViewController.attach(player);
+
+        mViewHolder.getPlayer().setOnLongClickListener(v -> {
+            if (!mMediaViewController.isGutsVisible()) {
+                mMediaViewController.openGuts();
+                return true;
+            } else {
+                return false;
+            }
+        });
+        mViewHolder.getCancel().setOnClickListener(v -> {
+            closeGuts();
+        });
+        mViewHolder.getSettings().setOnClickListener(v -> {
+            mActivityStarter.startActivity(SETTINGS_INTENT, true /* dismissShade */);
+        });
     }
 
     /**
@@ -205,6 +232,7 @@
         PendingIntent clickIntent = data.getClickIntent();
         if (clickIntent != null) {
             mViewHolder.getPlayer().setOnClickListener(v -> {
+                if (mMediaViewController.isGutsVisible()) return;
                 mActivityStarter.postStartActivityDismissingKeyguard(clickIntent);
             });
         }
@@ -329,14 +357,38 @@
         final MediaController controller = getController();
         mBackgroundExecutor.execute(() -> mSeekBarViewModel.updateController(controller));
 
-        // Set up long press menu
-        // TODO: b/156036025 bring back media guts
+        // Dismiss
+        mViewHolder.getDismiss().setOnClickListener(v -> {
+            if (data.getNotificationKey() != null) {
+                closeGuts();
+                mKeyguardDismissUtil.executeWhenUnlocked(() -> {
+                    mMediaDataManagerLazy.get().dismissMediaData(data.getNotificationKey(),
+                            MediaViewController.GUTS_ANIMATION_DURATION + 100);
+                    return true;
+                }, /* requiresShadeOpen */ true);
+            } else {
+                Log.w(TAG, "Dismiss media with null notification. Token uid="
+                        + data.getToken().getUid());
+            }
+        });
 
         // TODO: We don't need to refresh this state constantly, only if the state actually changed
         // to something which might impact the measurement
         mMediaViewController.refreshState();
     }
 
+    /**
+     * Close the guts for this player.
+     * @param immediate {@code true} if it should be closed without animation
+     */
+    public void closeGuts(boolean immediate) {
+        mMediaViewController.closeGuts(immediate);
+    }
+
+    private void closeGuts() {
+        closeGuts(false);
+    }
+
     @UiThread
     private Drawable scaleDrawable(Icon icon) {
         if (icon == null) {
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
index d82150f2..8a51c85 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
@@ -48,6 +48,7 @@
 import com.android.systemui.statusbar.notification.row.HybridGroupManager
 import com.android.systemui.util.Assert
 import com.android.systemui.util.Utils
+import com.android.systemui.util.concurrency.DelayableExecutor
 import java.io.FileDescriptor
 import java.io.IOException
 import java.io.PrintWriter
@@ -90,7 +91,7 @@
 class MediaDataManager(
     private val context: Context,
     @Background private val backgroundExecutor: Executor,
-    @Main private val foregroundExecutor: Executor,
+    @Main private val foregroundExecutor: DelayableExecutor,
     private val mediaControllerFactory: MediaControllerFactory,
     private val broadcastDispatcher: BroadcastDispatcher,
     dumpManager: DumpManager,
@@ -107,7 +108,7 @@
     constructor(
         context: Context,
         @Background backgroundExecutor: Executor,
-        @Main foregroundExecutor: Executor,
+        @Main foregroundExecutor: DelayableExecutor,
         mediaControllerFactory: MediaControllerFactory,
         dumpManager: DumpManager,
         broadcastDispatcher: BroadcastDispatcher,
@@ -183,10 +184,7 @@
         val listenersCopy = listeners.toSet()
         val toRemove = mediaEntries.filter { it.value.packageName == packageName }
         toRemove.forEach {
-            mediaEntries.remove(it.key)
-            listenersCopy.forEach { listener ->
-                listener.onMediaDataRemoved(it.key)
-            }
+            removeEntry(it.key, listenersCopy)
         }
     }
 
@@ -269,6 +267,18 @@
         }
     }
 
+    private fun removeEntry(key: String, listenersCopy: Set<Listener>) {
+        mediaEntries.remove(key)
+        listenersCopy.forEach {
+            it.onMediaDataRemoved(key)
+        }
+    }
+
+    fun dismissMediaData(key: String, delay: Long) {
+        val listenersCopy = listeners.toSet()
+        foregroundExecutor.executeDelayed({ removeEntry(key, listenersCopy) }, delay)
+    }
+
     private fun loadMediaDataInBgForResumption(
         userId: Int,
         desc: MediaDescription,
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt
index fc33391..70f01d5 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt
@@ -293,6 +293,13 @@
         return viewHost
     }
 
+    /**
+     * Close the guts in all players in [MediaCarouselController].
+     */
+    fun closeGuts() {
+        mediaCarouselController.closeGuts()
+    }
+
     private fun createUniqueObjectHost(): UniqueObjectHostView {
         val viewHost = UniqueObjectHostView(context)
         viewHost.addOnAttachStateChangeListener(object : View.OnAttachStateChangeListener {
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaViewController.kt b/packages/SystemUI/src/com/android/systemui/media/MediaViewController.kt
index 38817d7..92eeed4 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaViewController.kt
@@ -37,6 +37,11 @@
     private val mediaHostStatesManager: MediaHostStatesManager
 ) {
 
+    companion object {
+        @JvmField
+        val GUTS_ANIMATION_DURATION = 500L
+    }
+
     /**
      * A listener when the current dimensions of the player change
      */
@@ -169,6 +174,12 @@
      */
     val expandedLayout = ConstraintSet()
 
+    /**
+     * Whether the guts are visible for the associated player.
+     */
+    var isGutsVisible = false
+        private set
+
     init {
         collapsedLayout.load(context, R.xml.media_collapsed)
         expandedLayout.load(context, R.xml.media_expanded)
@@ -189,6 +200,37 @@
         configurationController.removeCallback(configurationListener)
     }
 
+    /**
+     * Show guts with an animated transition.
+     */
+    fun openGuts() {
+        if (isGutsVisible) return
+        isGutsVisible = true
+        animatePendingStateChange(GUTS_ANIMATION_DURATION, 0L)
+        setCurrentState(currentStartLocation,
+                currentEndLocation,
+                currentTransitionProgress,
+                applyImmediately = false)
+    }
+
+    /**
+     * Close the guts for the associated player.
+     *
+     * @param immediate if `false`, it will animate the transition.
+     */
+    @JvmOverloads
+    fun closeGuts(immediate: Boolean = false) {
+        if (!isGutsVisible) return
+        isGutsVisible = false
+        if (!immediate) {
+            animatePendingStateChange(GUTS_ANIMATION_DURATION, 0L)
+        }
+        setCurrentState(currentStartLocation,
+                currentEndLocation,
+                currentTransitionProgress,
+                applyImmediately = immediate)
+    }
+
     private fun ensureAllMeasurements() {
         val mediaStates = mediaHostStatesManager.mediaHostStates
         for (entry in mediaStates) {
@@ -203,6 +245,24 @@
             if (expansion > 0) expandedLayout else collapsedLayout
 
     /**
+     * Set the views to be showing/hidden based on the [isGutsVisible] for a given
+     * [TransitionViewState].
+     */
+    private fun setGutsViewState(viewState: TransitionViewState) {
+        PlayerViewHolder.controlsIds.forEach { id ->
+            viewState.widgetStates.get(id)?.let { state ->
+                // Make sure to use the unmodified state if guts are not visible
+                state.alpha = if (isGutsVisible) 0f else state.alpha
+                state.gone = if (isGutsVisible) true else state.gone
+            }
+        }
+        PlayerViewHolder.gutsIds.forEach { id ->
+            viewState.widgetStates.get(id)?.alpha = if (isGutsVisible) 1f else 0f
+            viewState.widgetStates.get(id)?.gone = !isGutsVisible
+        }
+    }
+
+    /**
      * Obtain a new viewState for a given media state. This usually returns a cached state, but if
      * it's not available, it will recreate one by measuring, which may be expensive.
      */
@@ -211,7 +271,7 @@
             return null
         }
         // Only a subset of the state is relevant to get a valid viewState. Let's get the cachekey
-        var cacheKey = getKey(state, tmpKey)
+        var cacheKey = getKey(state, isGutsVisible, tmpKey)
         val viewState = viewStates[cacheKey]
         if (viewState != null) {
             // we already have cached this measurement, let's continue
@@ -228,6 +288,7 @@
                         constraintSetForExpansion(state.expansion),
                         TransitionViewState())
 
+                setGutsViewState(result)
                 // We don't want to cache interpolated or null states as this could quickly fill up
                 // our cache. We only cache the start and the end states since the interpolation
                 // is cheap
@@ -252,11 +313,12 @@
         return result
     }
 
-    private fun getKey(state: MediaHostState, result: CacheKey): CacheKey {
+    private fun getKey(state: MediaHostState, guts: Boolean, result: CacheKey): CacheKey {
         result.apply {
             heightMeasureSpec = state.measurementInput?.heightMeasureSpec ?: 0
             widthMeasureSpec = state.measurementInput?.widthMeasureSpec ?: 0
             expansion = state.expansion
+            gutsVisible = guts
         }
         return result
     }
@@ -432,5 +494,6 @@
 private data class CacheKey(
     var widthMeasureSpec: Int = -1,
     var heightMeasureSpec: Int = -1,
-    var expansion: Float = 0.0f
+    var expansion: Float = 0.0f,
+    var gutsVisible: Boolean = false
 )
diff --git a/packages/SystemUI/src/com/android/systemui/media/PlayerViewHolder.kt b/packages/SystemUI/src/com/android/systemui/media/PlayerViewHolder.kt
index 600fdc2..11551ac 100644
--- a/packages/SystemUI/src/com/android/systemui/media/PlayerViewHolder.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/PlayerViewHolder.kt
@@ -23,6 +23,7 @@
 import android.widget.ImageView
 import android.widget.SeekBar
 import android.widget.TextView
+import androidx.constraintlayout.widget.ConstraintSet
 import com.android.systemui.R
 import com.android.systemui.util.animation.TransitionLayout
 
@@ -59,6 +60,11 @@
     val action3 = itemView.requireViewById<ImageButton>(R.id.action3)
     val action4 = itemView.requireViewById<ImageButton>(R.id.action4)
 
+    // Settings screen
+    val cancel = itemView.requireViewById<View>(R.id.cancel)
+    val dismiss = itemView.requireViewById<View>(R.id.dismiss)
+    val settings = itemView.requireViewById<View>(R.id.settings)
+
     init {
         (player.background as IlluminationDrawable).let {
             it.registerLightSource(seamless)
@@ -67,6 +73,9 @@
             it.registerLightSource(action2)
             it.registerLightSource(action3)
             it.registerLightSource(action4)
+            it.registerLightSource(cancel)
+            it.registerLightSource(dismiss)
+            it.registerLightSource(settings)
         }
     }
 
@@ -83,9 +92,6 @@
         }
     }
 
-    // Settings screen
-    val options = itemView.requireViewById<View>(R.id.qs_media_controls_options)
-
     companion object {
         /**
          * Creates a PlayerViewHolder.
@@ -105,5 +111,29 @@
                 progressTimes.layoutDirection = View.LAYOUT_DIRECTION_LTR
             }
         }
+
+        val controlsIds = setOf(
+                R.id.icon,
+                R.id.app_name,
+                R.id.album_art,
+                R.id.header_title,
+                R.id.header_artist,
+                R.id.media_seamless,
+                R.id.notification_media_progress_time,
+                R.id.media_progress_bar,
+                R.id.action0,
+                R.id.action1,
+                R.id.action2,
+                R.id.action3,
+                R.id.action4,
+                R.id.icon
+        )
+        val gutsIds = setOf(
+                R.id.media_text,
+                R.id.remove_text,
+                R.id.cancel,
+                R.id.dismiss,
+                R.id.settings
+        )
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java b/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java
index 7c3743b..025341c 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java
@@ -99,6 +99,7 @@
     private final Handler mUpdateHandler;
     private final PipBoundsHandler mPipBoundsHandler;
     private final PipAnimationController mPipAnimationController;
+    private final PipUiEventLogger mPipUiEventLoggerLogger;
     private final List<PipTransitionCallback> mPipTransitionCallbacks = new ArrayList<>();
     private final Rect mLastReportedBounds = new Rect();
     private final int mEnterExitAnimationDuration;
@@ -209,7 +210,8 @@
             @NonNull PipSurfaceTransactionHelper surfaceTransactionHelper,
             @Nullable Divider divider,
             @NonNull DisplayController displayController,
-            @NonNull PipAnimationController pipAnimationController) {
+            @NonNull PipAnimationController pipAnimationController,
+            @NonNull PipUiEventLogger pipUiEventLogger) {
         mMainHandler = new Handler(Looper.getMainLooper());
         mUpdateHandler = new Handler(PipUpdateThread.get().getLooper(), mUpdateCallbacks);
         mPipBoundsHandler = boundsHandler;
@@ -217,6 +219,7 @@
                 .getInteger(R.integer.config_pipResizeAnimationDuration);
         mSurfaceTransactionHelper = surfaceTransactionHelper;
         mPipAnimationController = pipAnimationController;
+        mPipUiEventLoggerLogger = pipUiEventLogger;
         mSurfaceControlTransactionFactory = SurfaceControl.Transaction::new;
         mSplitDivider = divider;
         displayController.addDisplayWindowListener(this);
@@ -279,6 +282,8 @@
             return;
         }
 
+        mPipUiEventLoggerLogger.log(
+                PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_EXPAND_TO_FULLSCREEN);
         final Configuration initialConfig = mInitialState.remove(mToken.asBinder());
         final boolean orientationDiffers = initialConfig.windowConfiguration.getRotation()
                 != mPipBoundsHandler.getDisplayRotation();
@@ -381,6 +386,9 @@
         mInitialState.put(mToken.asBinder(), new Configuration(mTaskInfo.configuration));
         mPictureInPictureParams = mTaskInfo.pictureInPictureParams;
 
+        mPipUiEventLoggerLogger.setTaskInfo(mTaskInfo);
+        mPipUiEventLoggerLogger.log(PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_ENTER);
+
         if (mShouldDeferEnteringPip) {
             if (DEBUG) Log.d(TAG, "Defer entering PiP animation, fixed rotation is ongoing");
             // if deferred, hide the surface till fixed rotation is completed
@@ -454,10 +462,11 @@
 
     private void sendOnPipTransitionStarted(
             @PipAnimationController.TransitionDirection int direction) {
+        final Rect pipBounds = new Rect(mLastReportedBounds);
         runOnMainHandler(() -> {
             for (int i = mPipTransitionCallbacks.size() - 1; i >= 0; i--) {
                 final PipTransitionCallback callback = mPipTransitionCallbacks.get(i);
-                callback.onPipTransitionStarted(mTaskInfo.baseActivity, direction);
+                callback.onPipTransitionStarted(mTaskInfo.baseActivity, direction, pipBounds);
             }
         });
     }
@@ -513,6 +522,7 @@
         mPictureInPictureParams = null;
         mInPip = false;
         mExitingPip = false;
+        mPipUiEventLoggerLogger.setTaskInfo(null);
     }
 
     @Override
@@ -628,7 +638,7 @@
      * {@link PictureInPictureParams} would affect the bounds.
      */
     private boolean applyPictureInPictureParams(@NonNull PictureInPictureParams params) {
-        final boolean changed = (mPictureInPictureParams == null) ? true : !Objects.equals(
+        final boolean changed = (mPictureInPictureParams == null) || !Objects.equals(
                 mPictureInPictureParams.getAspectRatioRational(), params.getAspectRatioRational());
         if (changed) {
             mPictureInPictureParams = params;
@@ -973,7 +983,7 @@
         /**
          * Callback when the pip transition is started.
          */
-        void onPipTransitionStarted(ComponentName activity, int direction);
+        void onPipTransitionStarted(ComponentName activity, int direction, Rect pipBounds);
 
         /**
          * Callback when the pip transition is finished.
diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipUiEventLogger.java b/packages/SystemUI/src/com/android/systemui/pip/PipUiEventLogger.java
new file mode 100644
index 0000000..5e2cd9c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/pip/PipUiEventLogger.java
@@ -0,0 +1,97 @@
+/*
+ * 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.pip;
+
+import android.app.TaskInfo;
+
+import com.android.internal.logging.UiEvent;
+import com.android.internal.logging.UiEventLogger;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+
+/**
+ * Helper class that ends PiP log to UiEvent, see also go/uievent
+ */
+@Singleton
+public class PipUiEventLogger {
+
+    private final UiEventLogger mUiEventLogger;
+
+    private TaskInfo mTaskInfo;
+
+    @Inject
+    public PipUiEventLogger(UiEventLogger uiEventLogger) {
+        mUiEventLogger = uiEventLogger;
+    }
+
+    public void setTaskInfo(TaskInfo taskInfo) {
+        mTaskInfo = taskInfo;
+    }
+
+    /**
+     * Sends log via UiEvent, reference go/uievent for how to debug locally
+     */
+    public void log(PipUiEventEnum event) {
+        if (mTaskInfo == null) {
+            return;
+        }
+        mUiEventLogger.log(event, mTaskInfo.userId, mTaskInfo.topActivity.getPackageName());
+    }
+
+    /**
+     * Enums for logging the PiP events to UiEvent
+     */
+    public enum PipUiEventEnum implements UiEventLogger.UiEventEnum {
+        @UiEvent(doc = "Activity enters picture-in-picture mode")
+        PICTURE_IN_PICTURE_ENTER(603),
+
+        @UiEvent(doc = "Expands from picture-in-picture to fullscreen")
+        PICTURE_IN_PICTURE_EXPAND_TO_FULLSCREEN(604),
+
+        @UiEvent(doc = "Removes picture-in-picture by tap close button")
+        PICTURE_IN_PICTURE_TAP_TO_REMOVE(605),
+
+        @UiEvent(doc = "Removes picture-in-picture by drag to dismiss area")
+        PICTURE_IN_PICTURE_DRAG_TO_REMOVE(606),
+
+        @UiEvent(doc = "Shows picture-in-picture menu")
+        PICTURE_IN_PICTURE_SHOW_MENU(607),
+
+        @UiEvent(doc = "Hides picture-in-picture menu")
+        PICTURE_IN_PICTURE_HIDE_MENU(608),
+
+        @UiEvent(doc = "Changes the aspect ratio of picture-in-picture window. This is inherited"
+                + " from previous Tron-based logging and currently not in use.")
+        PICTURE_IN_PICTURE_CHANGE_ASPECT_RATIO(609),
+
+        @UiEvent(doc = "User resize of the picture-in-picture window")
+        PICTURE_IN_PICTURE_RESIZE(610);
+
+        private final int mId;
+
+        PipUiEventEnum(int id) {
+            mId = id;
+        }
+
+        @Override
+        public int getId() {
+            return mId;
+        }
+    }
+}
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 6e75253..9dfa864 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
@@ -47,6 +47,7 @@
 import com.android.systemui.pip.PipBoundsHandler;
 import com.android.systemui.pip.PipSnapAlgorithm;
 import com.android.systemui.pip.PipTaskOrganizer;
+import com.android.systemui.pip.PipUiEventLogger;
 import com.android.systemui.pip.phone.dagger.PipMenuActivityClass;
 import com.android.systemui.shared.recents.IPinnedStackAnimationListener;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
@@ -259,7 +260,8 @@
             PipSnapAlgorithm pipSnapAlgorithm,
             PipTaskOrganizer pipTaskOrganizer,
             SysUiState sysUiState,
-            ConfigurationController configController) {
+            ConfigurationController configController,
+            PipUiEventLogger pipUiEventLogger) {
         mContext = context;
         mActivityManager = ActivityManager.getService();
 
@@ -280,7 +282,8 @@
                 mMediaController, mInputConsumerController);
         mTouchHandler = new PipTouchHandler(context, mActivityManager,
                 mMenuController, mInputConsumerController, mPipBoundsHandler, mPipTaskOrganizer,
-                floatingContentCoordinator, deviceConfig, pipSnapAlgorithm, sysUiState);
+                floatingContentCoordinator, deviceConfig, pipSnapAlgorithm, sysUiState,
+                pipUiEventLogger);
         mAppOpsListener = new PipAppOpsListener(context, mActivityManager,
                 mTouchHandler.getMotionHelper());
         displayController.addDisplayChangingController(mRotationController);
@@ -373,10 +376,10 @@
     }
 
     @Override
-    public void onPipTransitionStarted(ComponentName activity, int direction) {
+    public void onPipTransitionStarted(ComponentName activity, int direction, Rect pipBounds) {
         if (isOutPipDirection(direction)) {
             // Exiting PIP, save the reentry bounds to restore to when re-entering.
-            updateReentryBounds();
+            updateReentryBounds(pipBounds);
             mPipBoundsHandler.onSaveReentryBounds(activity, mReentryBounds);
         }
         // Disable touches while the animation is running
@@ -393,15 +396,8 @@
     /**
      * Update the bounds used to save the re-entry size and snap fraction when exiting PIP.
      */
-    public void updateReentryBounds() {
-        // On phones, the expansion animation that happens on pip tap before restoring
-        // to fullscreen makes it so that the last reported bounds are the expanded
-        // bounds. We want to restore to the unexpanded bounds when re-entering pip,
-        // so we use the bounds before expansion (normal) instead of the reported
-        // bounds.
-        Rect reentryBounds = mTouchHandler.getNormalBounds();
-        // Apply the snap fraction of the current bounds to the normal bounds.
-        final Rect bounds = mPipTaskOrganizer.getLastReportedBounds();
+    public void updateReentryBounds(Rect bounds) {
+        final Rect reentryBounds = mTouchHandler.getUserResizeBounds();
         float snapFraction = mPipBoundsHandler.getSnapFraction(bounds);
         mPipBoundsHandler.applySnapFraction(reentryBounds, snapFraction);
         mReentryBounds.set(reentryBounds);
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
index ee8f295..19138fdb 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
@@ -154,7 +154,7 @@
     private final PipTaskOrganizer.PipTransitionCallback mPipTransitionCallback =
             new PipTaskOrganizer.PipTransitionCallback() {
         @Override
-        public void onPipTransitionStarted(ComponentName activity, int direction) {}
+        public void onPipTransitionStarted(ComponentName activity, int direction, Rect pipBounds) {}
 
         @Override
         public void onPipTransitionFinished(ComponentName activity, int direction) {
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java
index d884fa9..9c42f8b 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java
@@ -52,6 +52,7 @@
 import com.android.systemui.model.SysUiState;
 import com.android.systemui.pip.PipBoundsHandler;
 import com.android.systemui.pip.PipTaskOrganizer;
+import com.android.systemui.pip.PipUiEventLogger;
 import com.android.systemui.util.DeviceConfigProxy;
 import com.android.wm.shell.R;
 
@@ -88,6 +89,7 @@
     private final Point mMaxSize = new Point();
     private final Point mMinSize = new Point();
     private final Rect mLastResizeBounds = new Rect();
+    private final Rect mUserResizeBounds = new Rect();
     private final Rect mLastDownBounds = new Rect();
     private final Rect mDragCornerSize = new Rect();
     private final Rect mTmpTopLeftCorner = new Rect();
@@ -109,13 +111,15 @@
     private InputMonitor mInputMonitor;
     private InputEventReceiver mInputEventReceiver;
     private PipTaskOrganizer mPipTaskOrganizer;
+    private PipUiEventLogger mPipUiEventLogger;
 
     private int mCtrlType;
 
     public PipResizeGestureHandler(Context context, PipBoundsHandler pipBoundsHandler,
             PipMotionHelper motionHelper, DeviceConfigProxy deviceConfig,
             PipTaskOrganizer pipTaskOrganizer, Function<Rect, Rect> movementBoundsSupplier,
-            Runnable updateMovementBoundsRunnable, SysUiState sysUiState) {
+            Runnable updateMovementBoundsRunnable, SysUiState sysUiState,
+            PipUiEventLogger pipUiEventLogger) {
         mContext = context;
         mDisplayId = context.getDisplayId();
         mMainExecutor = context.getMainExecutor();
@@ -125,6 +129,7 @@
         mMovementBoundsSupplier = movementBoundsSupplier;
         mUpdateMovementBoundsRunnable = updateMovementBoundsRunnable;
         mSysUiState = sysUiState;
+        mPipUiEventLogger = pipUiEventLogger;
 
         context.getDisplay().getRealSize(mMaxSize);
         reloadResources();
@@ -181,6 +186,7 @@
 
     void onActivityUnpinned() {
         mIsAttached = false;
+        mUserResizeBounds.setEmpty();
         updateIsEnabled();
     }
 
@@ -329,6 +335,7 @@
                 case MotionEvent.ACTION_UP:
                 case MotionEvent.ACTION_CANCEL:
                     if (!mLastResizeBounds.isEmpty()) {
+                        mUserResizeBounds.set(mLastResizeBounds);
                         mPipTaskOrganizer.scheduleFinishResizePip(mLastResizeBounds,
                                 (Rect bounds) -> {
                                     new Handler(Looper.getMainLooper()).post(() -> {
@@ -337,6 +344,8 @@
                                         resetState();
                                     });
                                 });
+                        mPipUiEventLogger.log(
+                                PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_RESIZE);
                     } else {
                         resetState();
                     }
@@ -351,6 +360,14 @@
         mThresholdCrossed = false;
     }
 
+    void setUserResizeBounds(Rect bounds) {
+        mUserResizeBounds.set(bounds);
+    }
+
+    Rect getUserResizeBounds() {
+        return mUserResizeBounds;
+    }
+
     void updateMaxSize(int maxX, int maxY) {
         mMaxSize.set(maxX, maxY);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
index 7fc2823..b20ea4e 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
@@ -34,7 +34,6 @@
 import android.os.Handler;
 import android.os.RemoteException;
 import android.util.Log;
-import android.util.Pair;
 import android.util.Size;
 import android.view.Gravity;
 import android.view.IPinnedStackController;
@@ -55,13 +54,13 @@
 import androidx.dynamicanimation.animation.SpringForce;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.os.logging.MetricsLoggerWrapper;
 import com.android.systemui.R;
 import com.android.systemui.model.SysUiState;
 import com.android.systemui.pip.PipAnimationController;
 import com.android.systemui.pip.PipBoundsHandler;
 import com.android.systemui.pip.PipSnapAlgorithm;
 import com.android.systemui.pip.PipTaskOrganizer;
+import com.android.systemui.pip.PipUiEventLogger;
 import com.android.systemui.shared.system.InputConsumerController;
 import com.android.systemui.util.DeviceConfigProxy;
 import com.android.systemui.util.DismissCircleView;
@@ -94,6 +93,8 @@
     private final WindowManager mWindowManager;
     private final IActivityManager mActivityManager;
     private final PipBoundsHandler mPipBoundsHandler;
+    private final PipUiEventLogger mPipUiEventLogger;
+
     private PipResizeGestureHandler mPipResizeGestureHandler;
     private IPinnedStackController mPinnedStackController;
 
@@ -132,9 +133,6 @@
 
     // The current movement bounds
     private Rect mMovementBounds = new Rect();
-    // The current resized bounds, changed by user resize.
-    // This is used during expand/un-expand to save/restore the user's resized size.
-    @VisibleForTesting Rect mResizedBounds = new Rect();
 
     // The reference inset bounds, used to determine the dismiss fraction
     private Rect mInsetBounds = new Rect();
@@ -198,11 +196,7 @@
 
         @Override
         public void onPipDismiss() {
-            Pair<ComponentName, Integer> topPipActivity = PipUtils.getTopPipActivity(mContext,
-                    mActivityManager);
-            if (topPipActivity.first != null) {
-                MetricsLoggerWrapper.logPictureInPictureDismissByTap(mContext, topPipActivity);
-            }
+            mPipUiEventLogger.log(PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_TAP_TO_REMOVE);
             mTouchState.removeDoubleTapTimeoutCallback();
             mMotionHelper.dismissPip();
         }
@@ -223,7 +217,8 @@
             FloatingContentCoordinator floatingContentCoordinator,
             DeviceConfigProxy deviceConfig,
             PipSnapAlgorithm pipSnapAlgorithm,
-            SysUiState sysUiState) {
+            SysUiState sysUiState,
+            PipUiEventLogger pipUiEventLogger) {
         // Initialize the Pip input consumer
         mContext = context;
         mActivityManager = activityManager;
@@ -238,7 +233,7 @@
         mPipResizeGestureHandler =
                 new PipResizeGestureHandler(context, pipBoundsHandler, mMotionHelper,
                         deviceConfig, pipTaskOrganizer, this::getMovementBounds,
-                        this::updateMovementBounds, sysUiState);
+                        this::updateMovementBounds, sysUiState, pipUiEventLogger);
         mTouchState = new PipTouchState(ViewConfiguration.get(context), mHandler,
                 () -> mMenuController.showMenuWithDelay(MENU_STATE_FULL, mMotionHelper.getBounds(),
                         true /* allowMenuTimeout */, willResizeMenu(), shouldShowResizeHandle()),
@@ -259,6 +254,8 @@
                 pipTaskOrganizer, pipSnapAlgorithm, this::onAccessibilityShowMenu,
                 this::updateMovementBounds, mHandler);
 
+        mPipUiEventLogger = pipUiEventLogger;
+
         mTargetView = new DismissCircleView(context);
         mTargetViewContainer = new FrameLayout(context);
         mTargetViewContainer.setBackgroundDrawable(
@@ -307,11 +304,8 @@
                     hideDismissTarget();
                 });
 
-                Pair<ComponentName, Integer> topPipActivity = PipUtils.getTopPipActivity(mContext,
-                        mActivityManager);
-                if (topPipActivity.first != null) {
-                    MetricsLoggerWrapper.logPictureInPictureDismissByDrag(mContext, topPipActivity);
-                }
+                mPipUiEventLogger.log(
+                        PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_DRAG_TO_REMOVE);
             }
         });
 
@@ -383,7 +377,6 @@
 
             mFloatingContentCoordinator.onContentRemoved(mMotionHelper);
         }
-        mResizedBounds.setEmpty();
         mPipResizeGestureHandler.onActivityUnpinned();
     }
 
@@ -393,9 +386,8 @@
         mMotionHelper.synchronizePinnedStackBounds();
         updateMovementBounds();
         if (direction == TRANSITION_DIRECTION_TO_PIP) {
-            // updates mResizedBounds only if it's an entering PiP animation
-            // mResized should be otherwise updated in setMenuState.
-            mResizedBounds.set(mMotionHelper.getBounds());
+            // Set the initial bounds as the user resize bounds.
+            mPipResizeGestureHandler.setUserResizeBounds(mMotionHelper.getBounds());
         }
 
         if (mShowPipMenuOnAnimationEnd) {
@@ -808,9 +800,7 @@
             // Save the current snap fraction and if we do not drag or move the PiP, then
             // we store back to this snap fraction.  Otherwise, we'll reset the snap
             // fraction and snap to the closest edge.
-            // Also save the current resized bounds so when the menu disappears, we can restore it.
             if (resize) {
-                mResizedBounds.set(mMotionHelper.getBounds());
                 Rect expandedBounds = new Rect(mExpandedBounds);
                 mSavedSnapFraction = mMotionHelper.animateToExpandedState(expandedBounds,
                         mMovementBounds, mExpandedMovementBounds, callback);
@@ -839,7 +829,7 @@
                 }
 
                 if (mDeferResizeToNormalBoundsUntilRotation == -1) {
-                    Rect restoreBounds = new Rect(mResizedBounds);
+                    Rect restoreBounds = new Rect(getUserResizeBounds());
                     Rect restoredMovementBounds = new Rect();
                     mSnapAlgorithm.getMovementBounds(restoreBounds, mInsetBounds,
                             restoredMovementBounds, mIsImeShowing ? mImeHeight : 0);
@@ -856,8 +846,10 @@
         // If pip menu has dismissed, we should register the A11y ActionReplacingConnection for pip
         // as well, or it can't handle a11y focus and pip menu can't perform any action.
         onRegistrationChanged(menuState == MENU_STATE_NONE);
-        if (menuState != MENU_STATE_CLOSE) {
-            MetricsLoggerWrapper.logPictureInPictureMenuVisible(mContext, menuState == MENU_STATE_FULL);
+        if (menuState == MENU_STATE_NONE) {
+            mPipUiEventLogger.log(PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_HIDE_MENU);
+        } else if (menuState == MENU_STATE_FULL) {
+            mPipUiEventLogger.log(PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_SHOW_MENU);
         }
     }
 
@@ -890,6 +882,10 @@
         return mNormalBounds;
     }
 
+    Rect getUserResizeBounds() {
+        return mPipResizeGestureHandler.getUserResizeBounds();
+    }
+
     /**
      * Gesture controlling normal movement of the PIP.
      */
diff --git a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
index 2138f09..0167028 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
@@ -663,7 +663,7 @@
     };
 
     @Override
-    public void onPipTransitionStarted(ComponentName activity, int direction) { }
+    public void onPipTransitionStarted(ComponentName activity, int direction, Rect pipBounds) { }
 
     @Override
     public void onPipTransitionFinished(ComponentName activity, int direction) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
index 560998b..22c735d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
@@ -185,7 +185,9 @@
             fakeDragBy(getScrollX() - mScroller.getCurrX());
         } else if (isFakeDragging()) {
             endFakeDrag();
-            mBounceAnimatorSet.start();
+            if (mBounceAnimatorSet != null) {
+                mBounceAnimatorSet.start();
+            }
             setOffscreenPageLimit(1);
         }
         super.computeScroll();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
index 8c485a6..d2aaaede 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
@@ -451,15 +451,17 @@
         if (listening) {
             if (mListeners.add(listener) && mListeners.size() == 1) {
                 if (DEBUG) Log.d(TAG, "handleSetListening true");
-                mLifecycle.setCurrentState(RESUMED);
                 handleSetListening(listening);
-                refreshState(); // Ensure we get at least one refresh after listening.
+                mUiHandler.post(() -> {
+                    mLifecycle.setCurrentState(RESUMED);
+                    refreshState(); // Ensure we get at least one refresh after listening.
+                });
             }
         } else {
             if (mListeners.remove(listener) && mListeners.size() == 0) {
                 if (DEBUG) Log.d(TAG, "handleSetListening false");
-                mLifecycle.setCurrentState(STARTED);
                 handleSetListening(listening);
+                mUiHandler.post(() -> mLifecycle.setCurrentState(STARTED));
             }
         }
         updateIsFullQs();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
index 99cb476..a87311a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -2620,6 +2620,7 @@
         super.onClosingFinished();
         resetHorizontalPanelPosition();
         setClosingWithAlphaFadeout(false);
+        mMediaHierarchyManager.closeGuts();
     }
 
     private void setClosingWithAlphaFadeout(boolean closing) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/CustomIconCacheTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/CustomIconCacheTest.kt
new file mode 100644
index 0000000..4d0f2ed
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/CustomIconCacheTest.kt
@@ -0,0 +1,101 @@
+/*
+ * 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.controls
+
+import android.content.ComponentName
+import android.graphics.drawable.Icon
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import org.junit.Assert.assertNull
+import org.junit.Assert.assertTrue
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class CustomIconCacheTest : SysuiTestCase() {
+
+    companion object {
+        private val TEST_COMPONENT1 = ComponentName.unflattenFromString("pkg/.cls1")!!
+        private val TEST_COMPONENT2 = ComponentName.unflattenFromString("pkg/.cls2")!!
+        private const val CONTROL_ID_1 = "TEST_CONTROL_1"
+        private const val CONTROL_ID_2 = "TEST_CONTROL_2"
+    }
+
+    @Mock(stubOnly = true)
+    private lateinit var icon1: Icon
+    @Mock(stubOnly = true)
+    private lateinit var icon2: Icon
+    private lateinit var customIconCache: CustomIconCache
+
+    @Before
+    fun setUp() {
+        MockitoAnnotations.initMocks(this)
+
+        customIconCache = CustomIconCache()
+    }
+
+    @Test
+    fun testIconStoredCorrectly() {
+        customIconCache.store(TEST_COMPONENT1, CONTROL_ID_1, icon1)
+
+        assertTrue(icon1 === customIconCache.retrieve(TEST_COMPONENT1, CONTROL_ID_1))
+    }
+
+    @Test
+    fun testIconNotStoredReturnsNull() {
+        customIconCache.store(TEST_COMPONENT1, CONTROL_ID_1, icon1)
+
+        assertNull(customIconCache.retrieve(TEST_COMPONENT1, CONTROL_ID_2))
+    }
+
+    @Test
+    fun testWrongComponentReturnsNull() {
+        customIconCache.store(TEST_COMPONENT1, CONTROL_ID_1, icon1)
+
+        assertNull(customIconCache.retrieve(TEST_COMPONENT2, CONTROL_ID_1))
+    }
+
+    @Test
+    fun testChangeComponentOldComponentIsRemoved() {
+        customIconCache.store(TEST_COMPONENT1, CONTROL_ID_1, icon1)
+        customIconCache.store(TEST_COMPONENT2, CONTROL_ID_2, icon2)
+
+        assertNull(customIconCache.retrieve(TEST_COMPONENT1, CONTROL_ID_1))
+        assertNull(customIconCache.retrieve(TEST_COMPONENT1, CONTROL_ID_2))
+    }
+
+    @Test
+    fun testChangeComponentCorrectIconRetrieved() {
+        customIconCache.store(TEST_COMPONENT1, CONTROL_ID_1, icon1)
+        customIconCache.store(TEST_COMPONENT2, CONTROL_ID_1, icon2)
+
+        assertTrue(icon2 === customIconCache.retrieve(TEST_COMPONENT2, CONTROL_ID_1))
+    }
+
+    @Test
+    fun testStoreNull() {
+        customIconCache.store(TEST_COMPONENT1, CONTROL_ID_1, icon1)
+        customIconCache.store(TEST_COMPONENT1, CONTROL_ID_1, null)
+
+        assertNull(customIconCache.retrieve(TEST_COMPONENT1, CONTROL_ID_1))
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/management/FavoritesModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/management/FavoritesModelTest.kt
index ce33a8d..f0003ed 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/management/FavoritesModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/management/FavoritesModelTest.kt
@@ -22,6 +22,7 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.controls.ControlInterface
+import com.android.systemui.controls.CustomIconCache
 import com.android.systemui.controls.controller.ControlInfo
 import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.eq
@@ -57,6 +58,8 @@
     private lateinit var callback: FavoritesModel.FavoritesModelCallback
     @Mock
     private lateinit var adapter: RecyclerView.Adapter<*>
+    @Mock
+    private lateinit var customIconCache: CustomIconCache
     private lateinit var model: FavoritesModel
     private lateinit var dividerWrapper: DividerWrapper
 
@@ -64,7 +67,7 @@
     fun setUp() {
         MockitoAnnotations.initMocks(this)
 
-        model = FavoritesModel(TEST_COMPONENT, INITIAL_FAVORITES, callback)
+        model = FavoritesModel(customIconCache, TEST_COMPONENT, INITIAL_FAVORITES, callback)
         model.attachAdapter(adapter)
         dividerWrapper = model.elements.first { it is DividerWrapper } as DividerWrapper
     }
@@ -89,7 +92,7 @@
     @Test
     fun testInitialElements() {
         val expected = INITIAL_FAVORITES.map {
-            ControlInfoWrapper(TEST_COMPONENT, it, true)
+            ControlInfoWrapper(TEST_COMPONENT, it, true, customIconCache::retrieve)
         } + DividerWrapper()
         assertEquals(expected, model.elements)
     }
@@ -287,5 +290,13 @@
         verify(callback).onFirstChange()
     }
 
+    @Test
+    fun testCacheCalledWhenGettingCustomIcon() {
+        val wrapper = model.elements[0] as ControlInfoWrapper
+        wrapper.customIcon
+
+        verify(customIconCache).retrieve(TEST_COMPONENT, wrapper.controlId)
+    }
+
     private fun getDividerPosition(): Int = model.elements.indexOf(dividerWrapper)
 }
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java
index ac8c671..4c54954 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java
@@ -88,7 +88,7 @@
 
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
-@TestableLooper.RunWithLooper()
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
 public class GlobalActionsDialogTest extends SysuiTestCase {
     private GlobalActionsDialog mGlobalActionsDialog;
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt
index c63781c..8a30b00 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.media
 
+import android.content.Intent
 import android.content.res.ColorStateList
 import android.graphics.Color
 import android.graphics.drawable.GradientDrawable
@@ -23,6 +24,7 @@
 import android.media.MediaMetadata
 import android.media.session.MediaSession
 import android.media.session.PlaybackState
+import android.provider.Settings.ACTION_MEDIA_CONTROLS_SETTINGS
 import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
 import android.view.View
@@ -35,24 +37,31 @@
 import androidx.constraintlayout.widget.ConstraintSet
 import androidx.lifecycle.LiveData
 import androidx.test.filters.SmallTest
-import com.android.systemui.R
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.statusbar.phone.KeyguardDismissUtil
 import com.android.systemui.util.animation.TransitionLayout
 import com.android.systemui.util.concurrency.FakeExecutor
+import com.android.systemui.util.mockito.eq
+import com.android.systemui.util.mockito.any
 import com.android.systemui.util.time.FakeSystemClock
 import com.google.common.truth.Truth.assertThat
+import dagger.Lazy
 import org.junit.After
 import org.junit.Before
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.ArgumentCaptor
+import org.mockito.ArgumentMatchers
+import org.mockito.ArgumentMatchers.anyLong
 import org.mockito.Mock
+import org.mockito.Mockito.anyBoolean
 import org.mockito.Mockito.mock
+import org.mockito.Mockito.never
 import org.mockito.Mockito.verify
-import org.mockito.Mockito.`when` as whenever
 import org.mockito.junit.MockitoJUnit
+import org.mockito.Mockito.`when` as whenever
 
 private const val KEY = "TEST_KEY"
 private const val APP = "APP"
@@ -81,6 +90,8 @@
     @Mock private lateinit var seekBarViewModel: SeekBarViewModel
     @Mock private lateinit var seekBarData: LiveData<SeekBarViewModel.Progress>
     @Mock private lateinit var mediaViewController: MediaViewController
+    @Mock private lateinit var keyguardDismissUtil: KeyguardDismissUtil
+    @Mock private lateinit var mediaDataManager: MediaDataManager
     @Mock private lateinit var expandedSet: ConstraintSet
     @Mock private lateinit var collapsedSet: ConstraintSet
     private lateinit var appIcon: ImageView
@@ -100,6 +111,9 @@
     private lateinit var action2: ImageButton
     private lateinit var action3: ImageButton
     private lateinit var action4: ImageButton
+    private lateinit var settings: View
+    private lateinit var cancel: View
+    private lateinit var dismiss: View
 
     private lateinit var session: MediaSession
     private val device = MediaDeviceData(true, null, DEVICE_NAME)
@@ -114,7 +128,7 @@
         whenever(mediaViewController.collapsedLayout).thenReturn(collapsedSet)
 
         player = MediaControlPanel(context, bgExecutor, activityStarter, mediaViewController,
-                seekBarViewModel)
+                seekBarViewModel, Lazy { mediaDataManager }, keyguardDismissUtil)
         whenever(seekBarViewModel.progress).thenReturn(seekBarData)
 
         // Mock out a view holder for the player to attach to.
@@ -156,6 +170,12 @@
         whenever(holder.action3).thenReturn(action3)
         action4 = ImageButton(context)
         whenever(holder.action4).thenReturn(action4)
+        settings = View(context)
+        whenever(holder.settings).thenReturn(settings)
+        cancel = View(context)
+        whenever(holder.cancel).thenReturn(cancel)
+        dismiss = View(context)
+        whenever(holder.dismiss).thenReturn(dismiss)
 
         // Create media session
         val metadataBuilder = MediaMetadata.Builder().apply {
@@ -254,4 +274,79 @@
         assertThat(seamlessText.getText()).isEqualTo(DEVICE_NAME)
         assertThat(seamless.isEnabled()).isFalse()
     }
+
+    @Test
+    fun longClick_gutsClosed() {
+        player.attach(holder)
+        whenever(mediaViewController.isGutsVisible).thenReturn(false)
+
+        val captor = ArgumentCaptor.forClass(View.OnLongClickListener::class.java)
+        verify(holder.player).setOnLongClickListener(captor.capture())
+
+        captor.value.onLongClick(holder.player)
+        verify(mediaViewController).openGuts()
+    }
+
+    @Test
+    fun longClick_gutsOpen() {
+        player.attach(holder)
+        whenever(mediaViewController.isGutsVisible).thenReturn(true)
+
+        val captor = ArgumentCaptor.forClass(View.OnLongClickListener::class.java)
+        verify(holder.player).setOnLongClickListener(captor.capture())
+
+        captor.value.onLongClick(holder.player)
+        verify(mediaViewController, never()).openGuts()
+    }
+
+    @Test
+    fun cancelButtonClick_animation() {
+        player.attach(holder)
+
+        cancel.callOnClick()
+
+        verify(mediaViewController).closeGuts(false)
+    }
+
+    @Test
+    fun settingsButtonClick() {
+        player.attach(holder)
+
+        settings.callOnClick()
+
+        val captor = ArgumentCaptor.forClass(Intent::class.java)
+        verify(activityStarter).startActivity(captor.capture(), eq(true))
+
+        assertThat(captor.value.action).isEqualTo(ACTION_MEDIA_CONTROLS_SETTINGS)
+    }
+
+    @Test
+    fun dismissButtonClick() {
+        player.attach(holder)
+        val state = MediaData(USER_ID, true, BG_COLOR, APP, null, ARTIST, TITLE, null, emptyList(),
+                emptyList(), PACKAGE, session.getSessionToken(), null, null, true, null,
+                notificationKey = KEY)
+        player.bind(state)
+
+        dismiss.callOnClick()
+        val captor = ArgumentCaptor.forClass(ActivityStarter.OnDismissAction::class.java)
+        verify(keyguardDismissUtil).executeWhenUnlocked(captor.capture(), anyBoolean())
+
+        captor.value.onDismiss()
+        verify(mediaDataManager).dismissMediaData(eq(KEY), anyLong())
+    }
+
+    @Test
+    fun dismissButtonClick_nullNotificationKey() {
+        player.attach(holder)
+        val state = MediaData(USER_ID, true, BG_COLOR, APP, null, ARTIST, TITLE, null, emptyList(),
+                emptyList(), PACKAGE, session.getSessionToken(), null, null, true, null)
+        player.bind(state)
+
+        verify(keyguardDismissUtil, never())
+                .executeWhenUnlocked(
+                        any(ActivityStarter.OnDismissAction::class.java),
+                        ArgumentMatchers.anyBoolean()
+                )
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt
index 59c2d0e..3789e6e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt
@@ -217,6 +217,20 @@
         assertThat(data.actions).hasSize(1)
     }
 
+    @Test
+    fun testDismissMedia_listenerCalled() {
+        val listener = mock(MediaDataManager.Listener::class.java)
+        mediaDataManager.addListener(listener)
+        mediaDataManager.onNotificationAdded(KEY, mediaNotification)
+        mediaDataManager.onMediaDataLoaded(KEY, oldKey = null, data = mock(MediaData::class.java))
+        mediaDataManager.dismissMediaData(KEY, 0L)
+
+        foregroundExecutor.advanceClockToLast()
+        foregroundExecutor.runAllReady()
+
+        verify(listener).onMediaDataRemoved(eq(KEY))
+    }
+
     /**
      * Simple implementation of [MediaDataManager.Listener] for the test.
      *
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaHierarchyManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaHierarchyManagerTest.kt
index 91c5ff8..d86dfa5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaHierarchyManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaHierarchyManagerTest.kt
@@ -142,4 +142,11 @@
         verify(mediaCarouselController).onDesiredLocationChanged(ArgumentMatchers.anyInt(),
                 any(MediaHostState::class.java), anyBoolean(), anyLong(), anyLong())
     }
+
+    @Test
+    fun testCloseGutsRelayToCarousel() {
+        mediaHiearchyManager.closeGuts()
+
+        verify(mediaCarouselController).closeGuts()
+    }
 }
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/pip/phone/PipTouchHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/pip/phone/PipTouchHandlerTest.java
index 96bb521..9f67722 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/pip/phone/PipTouchHandlerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/pip/phone/PipTouchHandlerTest.java
@@ -37,6 +37,7 @@
 import com.android.systemui.pip.PipBoundsHandler;
 import com.android.systemui.pip.PipSnapAlgorithm;
 import com.android.systemui.pip.PipTaskOrganizer;
+import com.android.systemui.pip.PipUiEventLogger;
 import com.android.systemui.shared.system.InputConsumerController;
 import com.android.systemui.util.DeviceConfigProxy;
 import com.android.systemui.util.FloatingContentCoordinator;
@@ -85,6 +86,9 @@
     @Mock
     private SysUiState mSysUiState;
 
+    @Mock
+    private PipUiEventLogger mPipUiEventLogger;
+
     private PipSnapAlgorithm mPipSnapAlgorithm;
     private PipMotionHelper mMotionHelper;
     private PipResizeGestureHandler mPipResizeGestureHandler;
@@ -104,7 +108,7 @@
         mPipTouchHandler = new PipTouchHandler(mContext, mActivityManager,
                 mPipMenuActivityController, mInputConsumerController, mPipBoundsHandler,
                 mPipTaskOrganizer, mFloatingContentCoordinator, mDeviceConfigProxy,
-                mPipSnapAlgorithm, mSysUiState);
+                mPipSnapAlgorithm, mSysUiState, mPipUiEventLogger);
         mMotionHelper = Mockito.spy(mPipTouchHandler.getMotionHelper());
         mPipResizeGestureHandler = Mockito.spy(mPipTouchHandler.getPipResizeGestureHandler());
         mPipTouchHandler.setPipMotionHelper(mMotionHelper);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java
index 103e558..cccb65d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java
@@ -74,7 +74,7 @@
 import org.mockito.MockitoAnnotations;
 
 @RunWith(AndroidTestingRunner.class)
-@RunWithLooper
+@RunWithLooper(setAsMainLooper = true)
 @SmallTest
 public class QSTileImplTest extends SysuiTestCase {
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java
index 2d276bb..6b54791 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java
@@ -47,7 +47,7 @@
 import org.mockito.MockitoAnnotations;
 
 @RunWith(AndroidTestingRunner.class)
-@TestableLooper.RunWithLooper()
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
 @SmallTest
 public class ScreenRecordTileTest extends SysuiTestCase {
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java
index ae87eef..781f875 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java
@@ -41,7 +41,7 @@
 import org.mockito.Mockito;
 
 @RunWith(AndroidTestingRunner.class)
-@RunWithLooper()
+@RunWithLooper(setAsMainLooper = true)
 @SmallTest
 public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CallbackControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CallbackControllerTest.java
index a16fb5e..86dacc1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CallbackControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CallbackControllerTest.java
@@ -37,7 +37,7 @@
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
 
-@RunWithLooper
+@RunWithLooper(setAsMainLooper = true)
 @RunWith(AndroidTestingRunner.class)
 @SmallTest
 public class CallbackControllerTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/LifecycleFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/LifecycleFragmentTest.java
index 0e1c560..b2f57d0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/LifecycleFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/LifecycleFragmentTest.java
@@ -39,7 +39,7 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-@RunWithLooper
+@RunWithLooper(setAsMainLooper = true)
 @RunWith(AndroidTestingRunner.class)
 @SmallTest
 public class LifecycleFragmentTest extends SysuiBaseFragmentTest {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/SysuiLifecycleTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/SysuiLifecycleTest.java
index 486939d..4f509ea 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/SysuiLifecycleTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/SysuiLifecycleTest.java
@@ -49,7 +49,7 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-@RunWithLooper
+@RunWithLooper(setAsMainLooper = true)
 @RunWith(AndroidTestingRunner.class)
 @SmallTest
 public class SysuiLifecycleTest extends SysuiTestCase {
diff --git a/packages/Tethering/res/values/config.xml b/packages/Tethering/res/values/config.xml
index 9b9dcde..5f8d299 100644
--- a/packages/Tethering/res/values/config.xml
+++ b/packages/Tethering/res/values/config.xml
@@ -73,6 +73,9 @@
     <!-- Use the old dnsmasq DHCP server for tethering instead of the framework implementation. -->
     <bool translatable="false" name="config_tether_enable_legacy_dhcp_server">false</bool>
 
+    <!-- Use legacy wifi p2p dedicated address instead of randomize address. -->
+    <bool translatable="false" name="config_tether_enable_legacy_wifi_p2p_dedicated_ip">false</bool>
+
     <!-- Dhcp range (min, max) to use for tethering purposes -->
     <string-array translatable="false" name="config_tether_dhcp_range">
     </string-array>
diff --git a/packages/Tethering/res/values/overlayable.xml b/packages/Tethering/res/values/overlayable.xml
index 6a33d55..0ee7a99 100644
--- a/packages/Tethering/res/values/overlayable.xml
+++ b/packages/Tethering/res/values/overlayable.xml
@@ -30,6 +30,7 @@
             -->
             <item type="bool" name="config_tether_enable_bpf_offload"/>
             <item type="bool" name="config_tether_enable_legacy_dhcp_server"/>
+            <item type="bool" name="config_tether_enable_legacy_wifi_p2p_dedicated_ip"/>
             <item type="integer" name="config_tether_offload_poll_interval"/>
             <item type="array" name="config_tether_upstream_types"/>
             <item type="bool" name="config_tether_upstream_automatic"/>
diff --git a/packages/Tethering/src/com/android/networkstack/tethering/PrivateAddressCoordinator.java b/packages/Tethering/src/com/android/networkstack/tethering/PrivateAddressCoordinator.java
index aa58a4b..fd9e360 100644
--- a/packages/Tethering/src/com/android/networkstack/tethering/PrivateAddressCoordinator.java
+++ b/packages/Tethering/src/com/android/networkstack/tethering/PrivateAddressCoordinator.java
@@ -15,6 +15,8 @@
  */
 package com.android.networkstack.tethering;
 
+import static android.net.TetheringManager.TETHERING_WIFI_P2P;
+
 import static java.util.Arrays.asList;
 
 import android.content.Context;
@@ -58,6 +60,7 @@
     private static final int BYTE_MASK = 0xff;
     // reserved for bluetooth tethering.
     private static final int BLUETOOTH_RESERVED = 44;
+    private static final int WIFI_P2P_RESERVED = 49;
     private static final byte DEFAULT_ID = (byte) 42;
 
     // Upstream monitor would be stopped when tethering is down. When tethering restart, downstream
@@ -71,15 +74,18 @@
     // 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16
     // Tethering use 192.168.0.0/16 that has 256 contiguous class C network numbers.
     private static final String DEFAULT_TETHERING_PREFIX = "192.168.0.0/16";
+    private static final String LEGACY_WIFI_P2P_IFACE_ADDRESS = "192.168.49.1/24";
     private final IpPrefix mTetheringPrefix;
     private final ConnectivityManager mConnectivityMgr;
+    private final TetheringConfiguration mConfig;
 
-    public PrivateAddressCoordinator(Context context) {
+    public PrivateAddressCoordinator(Context context, TetheringConfiguration config) {
         mDownstreams = new ArraySet<>();
         mUpstreamPrefixMap = new ArrayMap<>();
         mTetheringPrefix = new IpPrefix(DEFAULT_TETHERING_PREFIX);
         mConnectivityMgr = (ConnectivityManager) context.getSystemService(
                 Context.CONNECTIVITY_SERVICE);
+        mConfig = config;
     }
 
     /**
@@ -141,12 +147,21 @@
         mUpstreamPrefixMap.removeAll(toBeRemoved);
     }
 
+    private boolean isReservedSubnet(final int subnet) {
+        return subnet == BLUETOOTH_RESERVED || subnet == WIFI_P2P_RESERVED;
+    }
+
     /**
      * Pick a random available address and mark its prefix as in use for the provided IpServer,
      * returns null if there is no available address.
      */
     @Nullable
     public LinkAddress requestDownstreamAddress(final IpServer ipServer) {
+        if (mConfig.shouldEnableWifiP2pDedicatedIp()
+                && ipServer.interfaceType() == TETHERING_WIFI_P2P) {
+            return new LinkAddress(LEGACY_WIFI_P2P_IFACE_ADDRESS);
+        }
+
         // Address would be 192.168.[subAddress]/24.
         final byte[] bytes = mTetheringPrefix.getRawAddress();
         final int subAddress = getRandomSubAddr();
@@ -154,7 +169,7 @@
         bytes[3] = getSanitizedAddressSuffix(subAddress, (byte) 0, (byte) 1, (byte) 0xff);
         for (int i = 0; i < MAX_UBYTE; i++) {
             final int newSubNet = (subNet + i) & BYTE_MASK;
-            if (newSubNet == BLUETOOTH_RESERVED) continue;
+            if (isReservedSubnet(newSubNet)) continue;
 
             bytes[2] = (byte) newSubNet;
             final InetAddress addr;
diff --git a/packages/Tethering/src/com/android/networkstack/tethering/Tethering.java b/packages/Tethering/src/com/android/networkstack/tethering/Tethering.java
index cfc6575..7dd5290 100644
--- a/packages/Tethering/src/com/android/networkstack/tethering/Tethering.java
+++ b/packages/Tethering/src/com/android/networkstack/tethering/Tethering.java
@@ -320,10 +320,13 @@
         mExecutor = new TetheringThreadExecutor(mHandler);
         mActiveDataSubIdListener = new ActiveDataSubIdListener(mExecutor);
         mNetdCallback = new NetdCallback();
-        mPrivateAddressCoordinator = new PrivateAddressCoordinator(mContext);
 
         // Load tethering configuration.
         updateConfiguration();
+        // It is OK for the configuration to be passed to the PrivateAddressCoordinator at
+        // construction time because the only part of the configuration it uses is
+        // shouldEnableWifiP2pDedicatedIp(), and currently do not support changing that.
+        mPrivateAddressCoordinator = new PrivateAddressCoordinator(mContext, mConfig);
 
         // Must be initialized after tethering configuration is loaded because BpfCoordinator
         // constructor needs to use the configuration.
diff --git a/packages/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java b/packages/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java
index e1771a5..5783805 100644
--- a/packages/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java
+++ b/packages/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java
@@ -84,6 +84,9 @@
     public static final String TETHER_ENABLE_LEGACY_DHCP_SERVER =
             "tether_enable_legacy_dhcp_server";
 
+    public static final String USE_LEGACY_WIFI_P2P_DEDICATED_IP =
+            "use_legacy_wifi_p2p_dedicated_ip";
+
     /**
      * Default value that used to periodic polls tether offload stats from tethering offload HAL
      * to make the data warnings work.
@@ -113,6 +116,7 @@
     private final int mOffloadPollInterval;
     // TODO: Add to TetheringConfigurationParcel if required.
     private final boolean mEnableBpfOffload;
+    private final boolean mEnableWifiP2pDedicatedIp;
 
     public TetheringConfiguration(Context ctx, SharedLog log, int id) {
         final SharedLog configLog = log.forSubComponent("config");
@@ -156,6 +160,10 @@
                 R.integer.config_tether_offload_poll_interval,
                 DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS);
 
+        mEnableWifiP2pDedicatedIp = getResourceBoolean(res,
+                R.bool.config_tether_enable_legacy_wifi_p2p_dedicated_ip,
+                false /* defaultValue */);
+
         configLog.log(toString());
     }
 
@@ -199,6 +207,11 @@
         return !TextUtils.isEmpty(provisioningAppNoUi);
     }
 
+    /** Check whether dedicated wifi p2p address is enabled. */
+    public boolean shouldEnableWifiP2pDedicatedIp() {
+        return mEnableWifiP2pDedicatedIp;
+    }
+
     /** Does the dumping.*/
     public void dump(PrintWriter pw) {
         pw.print("activeDataSubId: ");
@@ -233,6 +246,9 @@
 
         pw.print("enableLegacyDhcpServer: ");
         pw.println(enableLegacyDhcpServer);
+
+        pw.print("enableWifiP2pDedicatedIp: ");
+        pw.println(mEnableWifiP2pDedicatedIp);
     }
 
     /** Returns the string representation of this object.*/
diff --git a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/PrivateAddressCoordinatorTest.java b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/PrivateAddressCoordinatorTest.java
index 2c0df6f..8e93c2e 100644
--- a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/PrivateAddressCoordinatorTest.java
+++ b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/PrivateAddressCoordinatorTest.java
@@ -15,6 +15,11 @@
  */
 package com.android.networkstack.tethering;
 
+import static android.net.TetheringManager.TETHERING_ETHERNET;
+import static android.net.TetheringManager.TETHERING_USB;
+import static android.net.TetheringManager.TETHERING_WIFI;
+import static android.net.TetheringManager.TETHERING_WIFI_P2P;
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotEquals;
 import static org.mockito.Mockito.never;
@@ -54,22 +59,34 @@
     @Mock private IpServer mHotspotIpServer;
     @Mock private IpServer mUsbIpServer;
     @Mock private IpServer mEthernetIpServer;
+    @Mock private IpServer mWifiP2pIpServer;
     @Mock private Context mContext;
     @Mock private ConnectivityManager mConnectivityMgr;
+    @Mock private TetheringConfiguration mConfig;
 
     private PrivateAddressCoordinator mPrivateAddressCoordinator;
     private final IpPrefix mBluetoothPrefix = new IpPrefix("192.168.44.0/24");
+    private final LinkAddress mLegacyWifiP2pAddress = new LinkAddress("192.168.49.1/24");
     private final Network mWifiNetwork = new Network(1);
     private final Network mMobileNetwork = new Network(2);
     private final Network[] mAllNetworks = {mMobileNetwork, mWifiNetwork};
 
+    private void setUpIpServers() throws Exception {
+        when(mUsbIpServer.interfaceType()).thenReturn(TETHERING_USB);
+        when(mEthernetIpServer.interfaceType()).thenReturn(TETHERING_ETHERNET);
+        when(mHotspotIpServer.interfaceType()).thenReturn(TETHERING_WIFI);
+        when(mWifiP2pIpServer.interfaceType()).thenReturn(TETHERING_WIFI_P2P);
+    }
+
     @Before
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
 
         when(mContext.getSystemService(Context.CONNECTIVITY_SERVICE)).thenReturn(mConnectivityMgr);
         when(mConnectivityMgr.getAllNetworks()).thenReturn(mAllNetworks);
-        mPrivateAddressCoordinator = spy(new PrivateAddressCoordinator(mContext));
+        when(mConfig.shouldEnableWifiP2pDedicatedIp()).thenReturn(false);
+        setUpIpServers();
+        mPrivateAddressCoordinator = spy(new PrivateAddressCoordinator(mContext, mConfig));
     }
 
     @Test
@@ -256,4 +273,38 @@
         final IpPrefix ethPrefix = PrefixUtils.asIpPrefix(ethAddr);
         assertEquals(predefinedPrefix, ethPrefix);
     }
+
+    private int getSubAddress(final byte... ipv4Address) {
+        assertEquals(4, ipv4Address.length);
+
+        int subnet = Byte.toUnsignedInt(ipv4Address[2]);
+        return (subnet << 8) + ipv4Address[3];
+    }
+
+    private void assertReseveredWifiP2pPrefix() throws Exception {
+        LinkAddress address = mPrivateAddressCoordinator.requestDownstreamAddress(
+                mHotspotIpServer);
+        final IpPrefix hotspotPrefix = PrefixUtils.asIpPrefix(address);
+        final IpPrefix legacyWifiP2pPrefix = PrefixUtils.asIpPrefix(mLegacyWifiP2pAddress);
+        assertNotEquals(legacyWifiP2pPrefix, hotspotPrefix);
+        mPrivateAddressCoordinator.releaseDownstream(mHotspotIpServer);
+    }
+
+    @Test
+    public void testEnableLegacyWifiP2PAddress() throws Exception {
+        when(mPrivateAddressCoordinator.getRandomSubAddr()).thenReturn(
+                getSubAddress(mLegacyWifiP2pAddress.getAddress().getAddress()));
+        // No matter #shouldEnableWifiP2pDedicatedIp() is enabled or not, legacy wifi p2p prefix
+        // is resevered.
+        assertReseveredWifiP2pPrefix();
+
+        when(mConfig.shouldEnableWifiP2pDedicatedIp()).thenReturn(true);
+        assertReseveredWifiP2pPrefix();
+
+        // If #shouldEnableWifiP2pDedicatedIp() is enabled, wifi P2P gets the configured address.
+        LinkAddress address = mPrivateAddressCoordinator.requestDownstreamAddress(
+                mWifiP2pIpServer);
+        assertEquals(mLegacyWifiP2pAddress, address);
+        mPrivateAddressCoordinator.releaseDownstream(mWifiP2pIpServer);
+    }
 }
diff --git a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java
index a9ac4e2..dc0940c 100644
--- a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java
+++ b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java
@@ -128,6 +128,8 @@
                 .thenReturn(new String[0]);
         when(mResources.getBoolean(R.bool.config_tether_enable_legacy_dhcp_server)).thenReturn(
                 false);
+        when(mResources.getBoolean(R.bool.config_tether_enable_legacy_wifi_p2p_dedicated_ip))
+                .thenReturn(false);
         initializeBpfOffloadConfiguration(true, null /* unset */);
 
         mHasTelephonyManager = true;
@@ -413,4 +415,17 @@
                 R.string.config_mobile_hotspot_provision_response)).thenReturn(
                 PROVISIONING_APP_RESPONSE);
     }
+
+    @Test
+    public void testEnableLegacyWifiP2PAddress() throws Exception {
+        final TetheringConfiguration defaultCfg = new TetheringConfiguration(
+                mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
+        assertFalse(defaultCfg.shouldEnableWifiP2pDedicatedIp());
+
+        when(mResources.getBoolean(R.bool.config_tether_enable_legacy_wifi_p2p_dedicated_ip))
+                .thenReturn(true);
+        final TetheringConfiguration testCfg = new TetheringConfiguration(
+                mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
+        assertTrue(testCfg.shouldEnableWifiP2pDedicatedIp());
+    }
 }
diff --git a/proto/src/metrics_constants/metrics_constants.proto b/proto/src/metrics_constants/metrics_constants.proto
index b0e401b..3f712dd 100644
--- a/proto/src/metrics_constants/metrics_constants.proto
+++ b/proto/src/metrics_constants/metrics_constants.proto
@@ -3706,33 +3706,40 @@
     // OS: O
     BACKUP_SETTINGS = 818;
 
+    // DEPRECATED: The metrics has been migrated to UiEvent per go/uievent.
     // ACTION: Picture-in-picture was explicitly entered for an activity
     // VALUE: true if it was entered while hiding as a result of moving to
     // another task, false otherwise
-    ACTION_PICTURE_IN_PICTURE_ENTERED = 819;
+    ACTION_PICTURE_IN_PICTURE_ENTERED = 819 [deprecated=true];
 
+    // DEPRECATED: The metrics has been migrated to UiEvent per go/uievent.
     // ACTION: The activity currently in picture-in-picture was expanded back to fullscreen
     // PACKAGE: The package name of the activity that was expanded back to fullscreen
-    ACTION_PICTURE_IN_PICTURE_EXPANDED_TO_FULLSCREEN = 820;
+    ACTION_PICTURE_IN_PICTURE_EXPANDED_TO_FULLSCREEN = 820 [deprecated=true];
 
+    // DEPRECATED: The metrics no longer used after migration to UiEvent per go/uievent.
     // ACTION: The activity currently in picture-in-picture was minimized
     // VALUE: True if the PiP was minimized, false otherwise
-    ACTION_PICTURE_IN_PICTURE_MINIMIZED = 821;
+    ACTION_PICTURE_IN_PICTURE_MINIMIZED = 821 [deprecated=true];
 
+    // DEPRECATED: The metrics has been migrated to UiEvent per go/uievent.
     // ACTION: Picture-in-picture was dismissed via the dismiss button
     // VALUE: 0 if dismissed by tap, 1 if dismissed by drag
-    ACTION_PICTURE_IN_PICTURE_DISMISSED = 822;
+    ACTION_PICTURE_IN_PICTURE_DISMISSED = 822 [deprecated=true];
 
-    // ACTION: The visibility of the picture-in-picture meny
+    // DEPRECATED: The metrics has been migrated to UiEvent per go/uievent.
+    // ACTION: The visibility of the picture-in-picture menu
     // VALUE: Whether or not the menu is visible
-    ACTION_PICTURE_IN_PICTURE_MENU = 823;
+    ACTION_PICTURE_IN_PICTURE_MENU = 823 [deprecated=true];
 
+    // DEPRECATED: The metrics has been migrated to UiEvent per go/uievent.
     // Enclosing category for group of PICTURE_IN_PICTURE_ASPECT_RATIO_FOO events,
     // logged when the aspect ratio changes
-    ACTION_PICTURE_IN_PICTURE_ASPECT_RATIO_CHANGED = 824;
+    ACTION_PICTURE_IN_PICTURE_ASPECT_RATIO_CHANGED = 824 [deprecated=true];
 
+    // DEPRECATED: The metrics no longer used after migration to UiEvent per go/uievent.
     // The current aspect ratio of the PiP, logged when it changes.
-    PICTURE_IN_PICTURE_ASPECT_RATIO = 825;
+    PICTURE_IN_PICTURE_ASPECT_RATIO = 825 [deprecated=true];
 
     // FIELD - length in dp of ACTION_LS_* gestures, or zero if not applicable
     // CATEGORY: GLOBAL_SYSTEM_UI
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 2cd4c69..b134022 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -443,7 +443,11 @@
                     if (reboundAService || configurationChanged) {
                         onUserStateChangedLocked(userState);
                     }
-                    migrateAccessibilityButtonSettingsIfNecessaryLocked(userState, packageName);
+                    // Passing 0 for restoreFromSdkInt to have this migration check execute each
+                    // time. It can make sure a11y button settings are correctly if there's an a11y
+                    // service updated and modifies the a11y button configuration.
+                    migrateAccessibilityButtonSettingsIfNecessaryLocked(userState, packageName,
+                            /* restoreFromSdkInt = */0);
                 }
             }
 
@@ -554,7 +558,9 @@
                         synchronized (mLock) {
                             restoreEnabledAccessibilityServicesLocked(
                                     intent.getStringExtra(Intent.EXTRA_SETTING_PREVIOUS_VALUE),
-                                    intent.getStringExtra(Intent.EXTRA_SETTING_NEW_VALUE));
+                                    intent.getStringExtra(Intent.EXTRA_SETTING_NEW_VALUE),
+                                    intent.getIntExtra(Intent.EXTRA_SETTING_RESTORED_FROM_SDK_INT,
+                                            0));
                         }
                     } else if (ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED.equals(which)) {
                         synchronized (mLock) {
@@ -563,6 +569,12 @@
                                     intent.getIntExtra(Intent.EXTRA_SETTING_RESTORED_FROM_SDK_INT,
                                             0));
                         }
+                    } else if (Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS.equals(which)) {
+                        synchronized (mLock) {
+                            restoreAccessibilityButtonTargetsLocked(
+                                    intent.getStringExtra(Intent.EXTRA_SETTING_PREVIOUS_VALUE),
+                                    intent.getStringExtra(Intent.EXTRA_SETTING_NEW_VALUE));
+                        }
                     }
                 }
             }
@@ -588,7 +600,7 @@
         final AccessibilityUserState userState = getUserStateLocked(UserHandle.USER_SYSTEM);
         final Set<String> targetsFromSetting = new ArraySet<>();
         readColonDelimitedSettingToSet(Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS,
-                userState.mUserId, targetsFromSetting, str -> str);
+                userState.mUserId, str -> str, targetsFromSetting);
         final boolean targetsContainMagnification = targetsFromSetting.contains(
                 MAGNIFICATION_CONTROLLER_NAME);
         if (targetsContainMagnification == displayMagnificationNavBarEnabled) {
@@ -1189,7 +1201,14 @@
             // the state since the context in which the current user
             // state was used has changed since it was inactive.
             onUserStateChangedLocked(userState);
-            migrateAccessibilityButtonSettingsIfNecessaryLocked(userState, null);
+            // It's better to have this migration in SettingsProvider. Unfortunately,
+            // SettingsProvider migrated database in a very early stage which A11yManagerService
+            // haven't finished or started the initialization. We cannot get enough information from
+            // A11yManagerService to execute these migrations in SettingsProvider. Passing 0 for
+            // restoreFromSdkInt to have this migration check execute every time, because we did not
+            // find out a way to detect the device finished the OTA and switch the user.
+            migrateAccessibilityButtonSettingsIfNecessaryLocked(userState, null,
+                    /* restoreFromSdkInt = */0);
 
             if (announceNewUser) {
                 // Schedule announcement of the current user if needed.
@@ -1234,7 +1253,8 @@
 
     // Called only during settings restore; currently supports only the owner user
     // TODO: http://b/22388012
-    void restoreEnabledAccessibilityServicesLocked(String oldSetting, String newSetting) {
+    void restoreEnabledAccessibilityServicesLocked(String oldSetting, String newSetting,
+            int restoreFromSdkInt) {
         readComponentNamesFromStringLocked(oldSetting, mTempComponentNameSet, false);
         readComponentNamesFromStringLocked(newSetting, mTempComponentNameSet, true);
 
@@ -1246,7 +1266,32 @@
                 userState.mEnabledServices,
                 UserHandle.USER_SYSTEM);
         onUserStateChangedLocked(userState);
-        migrateAccessibilityButtonSettingsIfNecessaryLocked(userState, null);
+        migrateAccessibilityButtonSettingsIfNecessaryLocked(userState, null, restoreFromSdkInt);
+    }
+
+    /**
+     * User could enable accessibility services and configure accessibility button during the SUW.
+     * Merges current value of accessibility button settings into the restored one to make sure
+     * user's preferences of accessibility button updated in SUW are not lost.
+     *
+     * Called only during settings restore; currently supports only the owner user
+     * TODO: http://b/22388012
+     */
+    void restoreAccessibilityButtonTargetsLocked(String oldSetting, String newSetting) {
+        final Set<String> targetsFromSetting = new ArraySet<>();
+        readColonDelimitedStringToSet(oldSetting, str -> str, targetsFromSetting,
+                /* doMerge = */false);
+        readColonDelimitedStringToSet(newSetting, str -> str, targetsFromSetting,
+                /* doMerge = */true);
+
+        final AccessibilityUserState userState = getUserStateLocked(UserHandle.USER_SYSTEM);
+        userState.mAccessibilityButtonTargets.clear();
+        userState.mAccessibilityButtonTargets.addAll(targetsFromSetting);
+        persistColonDelimitedSetToSettingLocked(Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS,
+                UserHandle.USER_SYSTEM, userState.mAccessibilityButtonTargets, str -> str);
+
+        scheduleNotifyClientsOfServicesStateChangeLocked(userState);
+        onUserStateChangedLocked(userState);
     }
 
     private int getClientStateLocked(AccessibilityUserState userState) {
@@ -1569,8 +1614,8 @@
      */
     private void readComponentNamesFromSettingLocked(String settingName, int userId,
             Set<ComponentName> outComponentNames) {
-        readColonDelimitedSettingToSet(settingName, userId, outComponentNames,
-                str -> ComponentName.unflattenFromString(str));
+        readColonDelimitedSettingToSet(settingName, userId,
+                str -> ComponentName.unflattenFromString(str), outComponentNames);
     }
 
     /**
@@ -1585,8 +1630,8 @@
     private void readComponentNamesFromStringLocked(String names,
             Set<ComponentName> outComponentNames,
             boolean doMerge) {
-        readColonDelimitedStringToSet(names, outComponentNames, doMerge,
-                str -> ComponentName.unflattenFromString(str));
+        readColonDelimitedStringToSet(names, str -> ComponentName.unflattenFromString(str),
+                outComponentNames, doMerge);
     }
 
     @Override
@@ -1596,15 +1641,15 @@
                 componentName -> componentName.flattenToShortString());
     }
 
-    private <T> void readColonDelimitedSettingToSet(String settingName, int userId, Set<T> outSet,
-            Function<String, T> toItem) {
+    private <T> void readColonDelimitedSettingToSet(String settingName, int userId,
+            Function<String, T> toItem, Set<T> outSet) {
         final String settingValue = Settings.Secure.getStringForUser(mContext.getContentResolver(),
                 settingName, userId);
-        readColonDelimitedStringToSet(settingValue, outSet, false, toItem);
+        readColonDelimitedStringToSet(settingValue, toItem, outSet, false);
     }
 
-    private <T> void readColonDelimitedStringToSet(String names, Set<T> outSet, boolean doMerge,
-            Function<String, T> toItem) {
+    private <T> void readColonDelimitedStringToSet(String names, Function<String, T> toItem,
+            Set<T> outSet, boolean doMerge) {
         if (!doMerge) {
             outSet.clear();
         }
@@ -2104,7 +2149,7 @@
         final String settingValue = Settings.Secure.getStringForUser(mContext.getContentResolver(),
                 Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE, userState.mUserId);
         final Set<String> targetsFromSetting = new ArraySet<>();
-        readColonDelimitedStringToSet(settingValue, targetsFromSetting, false, str -> str);
+        readColonDelimitedStringToSet(settingValue, str -> str, targetsFromSetting, false);
         // Fall back to device's default a11y service, only when setting is never updated.
         if (settingValue == null) {
             final String defaultService = mContext.getString(
@@ -2128,7 +2173,7 @@
     private boolean readAccessibilityButtonTargetsLocked(AccessibilityUserState userState) {
         final Set<String> targetsFromSetting = new ArraySet<>();
         readColonDelimitedSettingToSet(Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS,
-                userState.mUserId, targetsFromSetting, str -> str);
+                userState.mUserId, str -> str, targetsFromSetting);
 
         final Set<String> currentTargets =
                 userState.getShortcutTargetsLocked(ACCESSIBILITY_BUTTON);
@@ -2392,9 +2437,15 @@
      *    (It happens when an enabled accessibility service package is upgraded.)
      *
      * @param packageName The package name to check, or {@code null} to check all services.
+     * @param restoreFromSdkInt The target sdk version of the restored source device, or {@code 0}
+     *                          if the caller is not related to the restore.
      */
     private void migrateAccessibilityButtonSettingsIfNecessaryLocked(
-            AccessibilityUserState userState, @Nullable String packageName) {
+            AccessibilityUserState userState, @Nullable String packageName, int restoreFromSdkInt) {
+        // No need to migrate settings if they are restored from a version after Q.
+        if (restoreFromSdkInt > Build.VERSION_CODES.Q) {
+            return;
+        }
         final Set<String> buttonTargets =
                 userState.getShortcutTargetsLocked(ACCESSIBILITY_BUTTON);
         int lastSize = buttonTargets.size();
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 1093515..e76ec74 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -79,6 +79,7 @@
         ":framework_native_aidl",
         ":gsiservice_aidl",
         ":idmap2_aidl",
+        ":inputconstants_aidl",
         ":installd_aidl",
         ":storaged_aidl",
         ":vold_aidl",
diff --git a/services/core/java/com/android/server/VibratorService.java b/services/core/java/com/android/server/VibratorService.java
index 72f29b4..2534a53 100644
--- a/services/core/java/com/android/server/VibratorService.java
+++ b/services/core/java/com/android/server/VibratorService.java
@@ -76,6 +76,8 @@
 import com.android.internal.util.DumpUtils;
 import com.android.internal.util.FrameworkStatsLog;
 
+import libcore.util.NativeAllocationRegistry;
+
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.ArrayList;
@@ -115,7 +117,6 @@
     // If HAL supports callbacks set the timeout to ASYNC_TIMEOUT_MULTIPLIER * duration.
     private static final long ASYNC_TIMEOUT_MULTIPLIER = 2;
 
-
     // A mapping from the intensity adjustment to the scaling to apply, where the intensity
     // adjustment is defined as the delta between the default intensity level and the user selected
     // intensity level. It's important that we apply the scaling on the delta between the two so
@@ -128,8 +129,6 @@
     private final LinkedList<VibrationInfo> mPreviousVibrations;
     private final int mPreviousVibrationsLimit;
     private final boolean mAllowPriorityVibrationsInLowPowerMode;
-    private final boolean mSupportsAmplitudeControl;
-    private final boolean mSupportsExternalControl;
     private final List<Integer> mSupportedEffects;
     private final long mCapabilities;
     private final int mDefaultVibrationAmplitude;
@@ -174,22 +173,23 @@
     private int mRingIntensity;
     private SparseArray<Vibration> mAlwaysOnEffects = new SparseArray<>();
 
-    static native boolean vibratorExists();
-    static native void vibratorInit();
+    static native long vibratorInit();
+
+    static native long vibratorGetFinalizer();
+    static native boolean vibratorExists(long controllerPtr);
     static native void vibratorOn(long milliseconds);
-    static native void vibratorOff();
-    static native boolean vibratorSupportsAmplitudeControl();
-    static native void vibratorSetAmplitude(int amplitude);
-    static native int[] vibratorGetSupportedEffects();
+    static native void vibratorOff(long controllerPtr);
+    static native void vibratorSetAmplitude(long controllerPtr, int amplitude);
+    static native int[] vibratorGetSupportedEffects(long controllerPtr);
     static native long vibratorPerformEffect(long effect, long strength, Vibration vibration,
             boolean withCallback);
     static native void vibratorPerformComposedEffect(
             VibrationEffect.Composition.PrimitiveEffect[] effect, Vibration vibration);
-    static native boolean vibratorSupportsExternalControl();
-    static native void vibratorSetExternalControl(boolean enabled);
-    static native long vibratorGetCapabilities();
-    static native void vibratorAlwaysOnEnable(long id, long effect, long strength);
-    static native void vibratorAlwaysOnDisable(long id);
+    static native void vibratorSetExternalControl(long controllerPtr, boolean enabled);
+    static native long vibratorGetCapabilities(long controllerPtr);
+    static native void vibratorAlwaysOnEnable(long controllerPtr, long id, long effect,
+            long strength);
+    static native void vibratorAlwaysOnDisable(long controllerPtr, long id);
 
     private final IUidObserver mUidObserver = new IUidObserver.Stub() {
         @Override public void onUidStateChanged(int uid, int procState, long procStateSeq,
@@ -370,13 +370,20 @@
         mNativeWrapper = injector.getNativeWrapper();
         mH = injector.createHandler(Looper.myLooper());
 
-        mNativeWrapper.vibratorInit();
+        long controllerPtr = mNativeWrapper.vibratorInit();
+        long finalizerPtr = mNativeWrapper.vibratorGetFinalizer();
+
+        if (finalizerPtr != 0) {
+            NativeAllocationRegistry registry =
+                    NativeAllocationRegistry.createMalloced(
+                            VibratorService.class.getClassLoader(), finalizerPtr);
+            registry.registerNativeAllocation(this, controllerPtr);
+        }
+
         // Reset the hardware to a default state, in case this is a runtime
         // restart instead of a fresh boot.
         mNativeWrapper.vibratorOff();
 
-        mSupportsAmplitudeControl = mNativeWrapper.vibratorSupportsAmplitudeControl();
-        mSupportsExternalControl = mNativeWrapper.vibratorSupportsExternalControl();
         mSupportedEffects = asList(mNativeWrapper.vibratorGetSupportedEffects());
         mCapabilities = mNativeWrapper.vibratorGetCapabilities();
 
@@ -605,7 +612,8 @@
         synchronized (mInputDeviceVibrators) {
             // Input device vibrators don't support amplitude controls yet, but are still used over
             // the system vibrator when connected.
-            return mSupportsAmplitudeControl && mInputDeviceVibrators.isEmpty();
+            return hasCapability(IVibrator.CAP_AMPLITUDE_CONTROL)
+                    && mInputDeviceVibrators.isEmpty();
         }
     }
 
@@ -1288,7 +1296,7 @@
     }
 
     private void doVibratorSetAmplitude(int amplitude) {
-        if (mSupportsAmplitudeControl) {
+        if (hasCapability(IVibrator.CAP_AMPLITUDE_CONTROL)) {
             mNativeWrapper.vibratorSetAmplitude(amplitude);
         }
     }
@@ -1708,14 +1716,25 @@
     @VisibleForTesting
     public static class NativeWrapper {
 
+        private long mNativeControllerPtr = 0;
+
         /** Checks if vibrator exists on device. */
         public boolean vibratorExists() {
-            return VibratorService.vibratorExists();
+            return VibratorService.vibratorExists(mNativeControllerPtr);
         }
 
-        /** Initializes connection to vibrator HAL service. */
-        public void vibratorInit() {
-            VibratorService.vibratorInit();
+        /**
+         * Returns native pointer to newly created controller and initializes connection to vibrator
+         * HAL service.
+         */
+        public long vibratorInit() {
+            mNativeControllerPtr = VibratorService.vibratorInit();
+            return mNativeControllerPtr;
+        }
+
+        /** Returns pointer to native finalizer function to be called by GC. */
+        public long vibratorGetFinalizer() {
+            return VibratorService.vibratorGetFinalizer();
         }
 
         /** Turns vibrator on for given time. */
@@ -1725,22 +1744,17 @@
 
         /** Turns vibrator off. */
         public void vibratorOff() {
-            VibratorService.vibratorOff();
-        }
-
-        /** Returns true if vibrator supports {@link #vibratorSetAmplitude(int)}. */
-        public boolean vibratorSupportsAmplitudeControl() {
-            return VibratorService.vibratorSupportsAmplitudeControl();
+            VibratorService.vibratorOff(mNativeControllerPtr);
         }
 
         /** Sets the amplitude for the vibrator to run. */
         public void vibratorSetAmplitude(int amplitude) {
-            VibratorService.vibratorSetAmplitude(amplitude);
+            VibratorService.vibratorSetAmplitude(mNativeControllerPtr, amplitude);
         }
 
         /** Returns all predefined effects supported by the device vibrator. */
         public int[] vibratorGetSupportedEffects() {
-            return VibratorService.vibratorGetSupportedEffects();
+            return VibratorService.vibratorGetSupportedEffects(mNativeControllerPtr);
         }
 
         /** Turns vibrator on to perform one of the supported effects. */
@@ -1755,29 +1769,24 @@
             VibratorService.vibratorPerformComposedEffect(effect, vibration);
         }
 
-        /** Returns true if vibrator supports {@link #vibratorSetExternalControl(boolean)}. */
-        public boolean vibratorSupportsExternalControl() {
-            return VibratorService.vibratorSupportsExternalControl();
-        }
-
         /** Enabled the device vibrator to be controlled by another service. */
         public void vibratorSetExternalControl(boolean enabled) {
-            VibratorService.vibratorSetExternalControl(enabled);
+            VibratorService.vibratorSetExternalControl(mNativeControllerPtr, enabled);
         }
 
         /** Returns all capabilities of the device vibrator. */
         public long vibratorGetCapabilities() {
-            return VibratorService.vibratorGetCapabilities();
+            return VibratorService.vibratorGetCapabilities(mNativeControllerPtr);
         }
 
         /** Enable always-on vibration with given id and effect. */
         public void vibratorAlwaysOnEnable(long id, long effect, long strength) {
-            VibratorService.vibratorAlwaysOnEnable(id, effect, strength);
+            VibratorService.vibratorAlwaysOnEnable(mNativeControllerPtr, id, effect, strength);
         }
 
         /** Disable always-on vibration for given id. */
         public void vibratorAlwaysOnDisable(long id) {
-            VibratorService.vibratorAlwaysOnDisable(id);
+            VibratorService.vibratorAlwaysOnDisable(mNativeControllerPtr, id);
         }
     }
 
@@ -1852,7 +1861,7 @@
 
         @Override
         public int onExternalVibrationStart(ExternalVibration vib) {
-            if (!mSupportsExternalControl) {
+            if (!hasCapability(IVibrator.CAP_EXTERNAL_CONTROL)) {
                 return SCALE_MUTE;
             }
             if (ActivityManager.checkComponentPermission(android.Manifest.permission.VIBRATE,
@@ -2142,10 +2151,10 @@
                 if (hasCapability(IVibrator.CAP_COMPOSE_EFFECTS)) {
                     pw.println("  Compose effects");
                 }
-                if (mSupportsAmplitudeControl || hasCapability(IVibrator.CAP_AMPLITUDE_CONTROL)) {
+                if (hasCapability(IVibrator.CAP_AMPLITUDE_CONTROL)) {
                     pw.println("  Amplitude control");
                 }
-                if (mSupportsExternalControl || hasCapability(IVibrator.CAP_EXTERNAL_CONTROL)) {
+                if (hasCapability(IVibrator.CAP_EXTERNAL_CONTROL)) {
                     pw.println("  External control");
                 }
                 if (hasCapability(IVibrator.CAP_EXTERNAL_AMPLITUDE_CONTROL)) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index c187772..e546a28 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -41,6 +41,7 @@
 import static android.content.pm.PackageManager.MATCH_UNINSTALLED_PACKAGES;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 import static android.os.FactoryTest.FACTORY_TEST_OFF;
+import static android.os.IInputConstants.DEFAULT_DISPATCHING_TIMEOUT_MILLIS;
 import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_CRITICAL;
 import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_HIGH;
 import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_NORMAL;
@@ -130,10 +131,10 @@
 import static com.android.server.wm.ActivityTaskManagerService.DUMP_RECENTS_CMD;
 import static com.android.server.wm.ActivityTaskManagerService.DUMP_RECENTS_SHORT_CMD;
 import static com.android.server.wm.ActivityTaskManagerService.DUMP_STARTER_CMD;
-import static com.android.server.wm.ActivityTaskManagerService.KEY_DISPATCHING_TIMEOUT_MS;
 import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_NONE;
 import static com.android.server.wm.ActivityTaskManagerService.relaunchReasonToString;
 
+
 import android.Manifest;
 import android.Manifest.permission;
 import android.annotation.BroadcastBehavior;
@@ -18353,19 +18354,20 @@
             throw new SecurityException("Requires permission " + FILTER_EVENTS);
         }
         ProcessRecord proc;
-        long timeout;
+        long timeoutMillis;
         synchronized (this) {
             synchronized (mPidsSelfLocked) {
                 proc = mPidsSelfLocked.get(pid);
             }
-            timeout = proc != null ? proc.getInputDispatchingTimeout() : KEY_DISPATCHING_TIMEOUT_MS;
+            timeoutMillis = proc != null ? proc.getInputDispatchingTimeoutMillis() :
+                    DEFAULT_DISPATCHING_TIMEOUT_MILLIS;
         }
 
         if (inputDispatchingTimedOut(proc, null, null, null, null, aboveSystem, reason)) {
-            return -1;
+            return 0;
         }
 
-        return timeout;
+        return timeoutMillis;
     }
 
     /**
diff --git a/services/core/java/com/android/server/am/PendingTempWhitelists.java b/services/core/java/com/android/server/am/PendingTempWhitelists.java
index b36e3c7..50d58f0 100644
--- a/services/core/java/com/android/server/am/PendingTempWhitelists.java
+++ b/services/core/java/com/android/server/am/PendingTempWhitelists.java
@@ -32,13 +32,13 @@
 
     void put(int uid, ActivityManagerService.PendingTempWhitelist value) {
         mPendingTempWhitelist.put(uid, value);
-        mService.mAtmInternal.onUidAddedToPendingTempWhitelist(uid, value.tag);
+        mService.mAtmInternal.onUidAddedToPendingTempAllowlist(uid, value.tag);
     }
 
     void removeAt(int index) {
         final int uid = mPendingTempWhitelist.keyAt(index);
         mPendingTempWhitelist.removeAt(index);
-        mService.mAtmInternal.onUidRemovedFromPendingTempWhitelist(uid);
+        mService.mAtmInternal.onUidRemovedFromPendingTempAllowlist(uid);
     }
 
     ActivityManagerService.PendingTempWhitelist get(int uid) {
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index cd4302b..1647fda 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -1522,8 +1522,8 @@
         }
     }
 
-    public long getInputDispatchingTimeout() {
-        return mWindowProcessController.getInputDispatchingTimeout();
+    public long getInputDispatchingTimeoutMillis() {
+        return mWindowProcessController.getInputDispatchingTimeoutMillis();
     }
 
     public int getProcessClassEnum() {
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index e6480fc..ee441bf 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -1889,6 +1889,7 @@
         synchronized (this) {
             if (mWriteScheduled) {
                 mWriteScheduled = false;
+                mHandler.removeCallbacks(mWriteRunner);
                 doWrite = true;
             }
         }
diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
index 45f95fd..2bbbbf1 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
@@ -224,12 +224,35 @@
             if (!addSpeakerphoneClient(cb, pid, on)) {
                 return false;
             }
+            if (on) {
+                // Cancel BT SCO ON request by this same client: speakerphone and BT SCO routes
+                // are mutually exclusive.
+                // See symmetrical operation for startBluetoothScoForClient_Sync().
+                mBtHelper.stopBluetoothScoForPid(pid);
+            }
             final boolean wasOn = isSpeakerphoneOn();
             updateSpeakerphoneOn(eventSource);
             return (wasOn != isSpeakerphoneOn());
         }
     }
 
+    /**
+     * Turns speakerphone off for a given pid and update speakerphone state.
+     * @param pid
+     */
+    @GuardedBy("mDeviceStateLock")
+    private void setSpeakerphoneOffForPid(int pid) {
+        SpeakerphoneClient client = getSpeakerphoneClientForPid(pid);
+        if (client == null) {
+            return;
+        }
+        client.unregisterDeathRecipient();
+        mSpeakerphoneClients.remove(client);
+        final String eventSource = new StringBuilder("setSpeakerphoneOffForPid(")
+                .append(pid).append(")").toString();
+        updateSpeakerphoneOn(eventSource);
+    }
+
     @GuardedBy("mDeviceStateLock")
     private void updateSpeakerphoneOn(String eventSource) {
         if (isSpeakerphoneOnRequested()) {
@@ -488,6 +511,10 @@
     /*package*/ void startBluetoothScoForClient_Sync(IBinder cb, int scoAudioMode,
                 @NonNull String eventSource) {
         synchronized (mDeviceStateLock) {
+            // Cancel speakerphone ON request by this same client: speakerphone and BT SCO routes
+            // are mutually exclusive.
+            // See symmetrical operation for setSpeakerphoneOn(true).
+            setSpeakerphoneOffForPid(Binder.getCallingPid());
             mBtHelper.startBluetoothScoForClient(cb, scoAudioMode, eventSource);
         }
     }
@@ -1379,6 +1406,16 @@
         return false;
     }
 
+    @GuardedBy("mDeviceStateLock")
+    private SpeakerphoneClient getSpeakerphoneClientForPid(int pid) {
+        for (SpeakerphoneClient cl : mSpeakerphoneClients) {
+            if (cl.getPid() == pid) {
+                return cl;
+            }
+        }
+        return null;
+    }
+
     // List of clients requesting speakerPhone ON
     @GuardedBy("mDeviceStateLock")
     private final @NonNull ArrayList<SpeakerphoneClient> mSpeakerphoneClients =
diff --git a/services/core/java/com/android/server/audio/BtHelper.java b/services/core/java/com/android/server/audio/BtHelper.java
index 5e8f1ef..ded0f9a3 100644
--- a/services/core/java/com/android/server/audio/BtHelper.java
+++ b/services/core/java/com/android/server/audio/BtHelper.java
@@ -432,19 +432,35 @@
         // and this must be done on behalf of system server to make sure permissions are granted.
         final long ident = Binder.clearCallingIdentity();
         if (client != null) {
-            AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent(eventSource));
-            client.requestScoState(BluetoothHeadset.STATE_AUDIO_DISCONNECTED,
-                    SCO_MODE_VIRTUAL_CALL);
-            // If a disconnection is pending, the client will be removed whne clearAllScoClients()
-            // is called form receiveBtEvent()
-            if (mScoAudioState != SCO_STATE_DEACTIVATE_REQ
-                    && mScoAudioState != SCO_STATE_DEACTIVATING) {
-                client.remove(false /*stop */, true /*unregister*/);
-            }
+            stopAndRemoveClient(client, eventSource);
         }
         Binder.restoreCallingIdentity(ident);
     }
 
+    // @GuardedBy("AudioDeviceBroker.mSetModeLock")
+    @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
+    /*package*/ synchronized void stopBluetoothScoForPid(int pid) {
+        ScoClient client = getScoClientForPid(pid);
+        if (client == null) {
+            return;
+        }
+        final String eventSource = new StringBuilder("stopBluetoothScoForPid(")
+                .append(pid).append(")").toString();
+        stopAndRemoveClient(client, eventSource);
+    }
+
+    @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
+    private void stopAndRemoveClient(ScoClient client, @NonNull String eventSource) {
+        AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent(eventSource));
+        client.requestScoState(BluetoothHeadset.STATE_AUDIO_DISCONNECTED,
+                SCO_MODE_VIRTUAL_CALL);
+        // If a disconnection is pending, the client will be removed when clearAllScoClients()
+        // is called form receiveBtEvent()
+        if (mScoAudioState != SCO_STATE_DEACTIVATE_REQ
+                && mScoAudioState != SCO_STATE_DEACTIVATING) {
+            client.remove(false /*stop */, true /*unregister*/);
+        }
+    }
 
     /*package*/ synchronized void setHearingAidVolume(int index, int streamType) {
         if (mHearingAid == null) {
@@ -982,6 +998,16 @@
         return null;
     }
 
+    @GuardedBy("BtHelper.this")
+    private ScoClient getScoClientForPid(int pid) {
+        for (ScoClient cl : mScoClients) {
+            if (cl.getPid() == pid) {
+                return cl;
+            }
+        }
+        return null;
+    }
+
     // @GuardedBy("AudioDeviceBroker.mSetModeLock")
     //@GuardedBy("AudioDeviceBroker.mDeviceStateLock")
     @GuardedBy("BtHelper.this")
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index 74ed815..5a423793 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -17,6 +17,7 @@
 package com.android.server.input;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.app.Notification;
 import android.app.NotificationManager;
 import android.app.PendingIntent;
@@ -66,6 +67,7 @@
 import android.provider.Settings;
 import android.provider.Settings.SettingNotFoundException;
 import android.text.TextUtils;
+import android.util.ArrayMap;
 import android.util.Log;
 import android.util.Slog;
 import android.util.SparseArray;
@@ -181,8 +183,7 @@
 
     // State for vibrator tokens.
     private Object mVibratorLock = new Object();
-    private HashMap<IBinder, VibratorToken> mVibratorTokens =
-            new HashMap<IBinder, VibratorToken>();
+    private Map<IBinder, VibratorToken> mVibratorTokens = new ArrayMap<IBinder, VibratorToken>();
     private int mNextVibratorTokenValue;
 
     // State for the currently installed input filter.
@@ -190,12 +191,16 @@
     IInputFilter mInputFilter; // guarded by mInputFilterLock
     InputFilterHost mInputFilterHost; // guarded by mInputFilterLock
 
+    private final Object mGestureMonitorPidsLock = new Object();
+    @GuardedBy("mGestureMonitorPidsLock")
+    private final ArrayMap<IBinder, Integer> mGestureMonitorPidsByToken = new ArrayMap<>();
+
     // The associations of input devices to displays by port. Maps from input device port (String)
     // to display id (int). Currently only accessed by InputReader.
     private final Map<String, Integer> mStaticAssociations;
     private final Object mAssociationsLock = new Object();
     @GuardedBy("mAssociationLock")
-    private final Map<String, Integer> mRuntimeAssociations = new HashMap<String, Integer>();
+    private final Map<String, Integer> mRuntimeAssociations = new ArrayMap<String, Integer>();
 
     private static native long nativeInit(InputManagerService service,
             Context context, MessageQueue messageQueue);
@@ -540,13 +545,17 @@
         if (displayId < Display.DEFAULT_DISPLAY) {
             throw new IllegalArgumentException("displayId must >= 0.");
         }
+        final int pid = Binder.getCallingPid();
 
         final long ident = Binder.clearCallingIdentity();
         try {
             InputChannel[] inputChannels = InputChannel.openInputChannelPair(inputChannelName);
             InputMonitorHost host = new InputMonitorHost(inputChannels[0]);
-            nativeRegisterInputMonitor(mPtr, inputChannels[0], displayId,
-                    true /*isGestureMonitor*/);
+            nativeRegisterInputMonitor(
+                    mPtr, inputChannels[0], displayId, true /*isGestureMonitor*/);
+            synchronized (mGestureMonitorPidsLock) {
+                mGestureMonitorPidsByToken.put(inputChannels[1].getToken(), pid);
+            }
             return new InputMonitor(inputChannels[1], host);
         } finally {
             Binder.restoreCallingIdentity(ident);
@@ -575,6 +584,9 @@
         if (inputChannel == null) {
             throw new IllegalArgumentException("inputChannel must not be null.");
         }
+        synchronized (mGestureMonitorPidsLock) {
+            mGestureMonitorPidsByToken.remove(inputChannel.getToken());
+        }
 
         nativeUnregisterInputChannel(mPtr, inputChannel);
     }
@@ -1838,6 +1850,7 @@
         if (dumpStr != null) {
             pw.println(dumpStr);
             dumpAssociations(pw);
+            dumpGestureMonitorPidsByToken(pw);
         }
     }
 
@@ -1861,6 +1874,19 @@
         }
     }
 
+    private void dumpGestureMonitorPidsByToken(PrintWriter pw) {
+        synchronized (mGestureMonitorPidsLock) {
+            if (!mGestureMonitorPidsByToken.isEmpty()) {
+                pw.println("Gesture monitor pids by token:");
+                for (int i = 0; i < mGestureMonitorPidsByToken.size(); i++) {
+                    pw.print("  " + i + ": ");
+                    pw.print(" token: " + mGestureMonitorPidsByToken.keyAt(i));
+                    pw.println(" pid: " + mGestureMonitorPidsByToken.valueAt(i));
+                }
+            }
+        }
+    }
+
     private boolean checkCallingPermission(String permission, String func) {
         // Quick check: if the calling permission is me, it's all okay.
         if (Binder.getCallingPid() == Process.myPid()) {
@@ -1883,6 +1909,7 @@
     public void monitor() {
         synchronized (mInputFilterLock) { }
         synchronized (mAssociationsLock) { /* Test if blocked by associations lock. */}
+        synchronized (mGestureMonitorPidsLock) { /* Test if blocked by gesture monitor pids lock */}
         nativeMonitor(mPtr);
     }
 
@@ -1944,6 +1971,9 @@
 
     // Native callback.
     private void notifyInputChannelBroken(IBinder token) {
+        synchronized (mGestureMonitorPidsLock) {
+            mGestureMonitorPidsByToken.remove(token);
+        }
         mWindowManagerCallbacks.notifyInputChannelBroken(token);
     }
 
@@ -1959,8 +1989,12 @@
     // Native callback.
     private long notifyANR(InputApplicationHandle inputApplicationHandle, IBinder token,
             String reason) {
-        return mWindowManagerCallbacks.notifyANR(inputApplicationHandle,
-                token, reason);
+        Integer gestureMonitorPid;
+        synchronized (mGestureMonitorPidsLock) {
+            gestureMonitorPid = mGestureMonitorPidsByToken.get(token);
+        }
+        return mWindowManagerCallbacks.notifyANR(inputApplicationHandle, token, gestureMonitorPid,
+                reason);
     }
 
     // Native callback.
@@ -2206,22 +2240,48 @@
      * Callback interface implemented by the Window Manager.
      */
     public interface WindowManagerCallbacks {
+        /**
+         * This callback is invoked when the confuguration changes.
+         */
         public void notifyConfigurationChanged();
 
+        /**
+         * This callback is invoked when the lid switch changes state.
+         * @param whenNanos the time when the change occurred
+         * @param lidOpen true if the lid is open
+         */
         public void notifyLidSwitchChanged(long whenNanos, boolean lidOpen);
 
+        /**
+         * This callback is invoked when the camera lens cover switch changes state.
+         * @param whenNanos the time when the change occurred
+         * @param lensCovered true is the lens is covered
+         */
         public void notifyCameraLensCoverSwitchChanged(long whenNanos, boolean lensCovered);
 
+        /**
+         * This callback is invoked when an input channel is closed unexpectedly.
+         * @param token the connection token of the broken channel
+         */
         public void notifyInputChannelBroken(IBinder token);
 
         /**
-         * Notifies the window manager about an application that is not responding.
-         * Returns a new timeout to continue waiting in nanoseconds, or 0 to abort dispatch.
+         * Notify the window manager about an application that is not responding.
+         * Return a new timeout to continue waiting in nanoseconds, or 0 to abort dispatch.
          */
         long notifyANR(InputApplicationHandle inputApplicationHandle, IBinder token,
-                String reason);
+                @Nullable Integer pid, String reason);
 
-        public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags);
+        /**
+         * This callback is invoked when an event first arrives to InputDispatcher and before it is
+         * placed onto InputDispatcher's queue. If this event is intercepted, it will never be
+         * processed by InputDispacher.
+         * @param event The key event that's arriving to InputDispatcher
+         * @param policyFlags The policy flags
+         * @return the flags that tell InputDispatcher how to handle the event (for example, whether
+         * to pass it to the user)
+         */
+        int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags);
 
         /**
          * Provides an opportunity for the window manager policy to intercept early motion event
@@ -2231,11 +2291,23 @@
         int interceptMotionBeforeQueueingNonInteractive(int displayId, long whenNanos,
                 int policyFlags);
 
-        public long interceptKeyBeforeDispatching(IBinder token,
-                KeyEvent event, int policyFlags);
+        /**
+         * This callback is invoked just before the key is about to be sent to an application.
+         * This allows the policy to make some last minute decisions on whether to intercept this
+         * key.
+         * @param token the window token that's about to receive this event
+         * @param event the key event that's being dispatched
+         * @param policyFlags the policy flags
+         * @return negative value if the key should be skipped (not sent to the app). 0 if the key
+         * should proceed getting dispatched to the app. positive value to indicate the additional
+         * time delay, in nanoseconds, to wait before sending this key to the app.
+         */
+        long interceptKeyBeforeDispatching(IBinder token, KeyEvent event, int policyFlags);
 
-        public KeyEvent dispatchUnhandledKey(IBinder token,
-                KeyEvent event, int policyFlags);
+        /**
+         * Dispatch unhandled key
+         */
+        KeyEvent dispatchUnhandledKey(IBinder token, KeyEvent event, int policyFlags);
 
         public int getPointerLayer();
 
diff --git a/services/core/java/com/android/server/location/LocationFudger.java b/services/core/java/com/android/server/location/LocationFudger.java
index 1f458ed..6f35c8b 100644
--- a/services/core/java/com/android/server/location/LocationFudger.java
+++ b/services/core/java/com/android/server/location/LocationFudger.java
@@ -112,7 +112,7 @@
     public Location createCoarse(Location fine) {
         synchronized (this) {
             if (fine == mCachedFineLocation) {
-                return new Location(mCachedCoarseLocation);
+                return mCachedCoarseLocation;
             }
         }
 
@@ -154,7 +154,7 @@
             mCachedCoarseLocation = coarse;
         }
 
-        return new Location(mCachedCoarseLocation);
+        return mCachedCoarseLocation;
     }
 
     /**
diff --git a/services/core/java/com/android/server/location/LocationManagerService.java b/services/core/java/com/android/server/location/LocationManagerService.java
index d3558f1..71f1833 100644
--- a/services/core/java/com/android/server/location/LocationManagerService.java
+++ b/services/core/java/com/android/server/location/LocationManagerService.java
@@ -17,43 +17,27 @@
 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;
-import static android.location.LocationManager.EXTRA_LOCATION_ENABLED;
-import static android.location.LocationManager.EXTRA_PROVIDER_ENABLED;
-import static android.location.LocationManager.EXTRA_PROVIDER_NAME;
 import static android.location.LocationManager.FUSED_PROVIDER;
 import static android.location.LocationManager.GPS_PROVIDER;
-import static android.location.LocationManager.KEY_LOCATION_CHANGED;
-import static android.location.LocationManager.KEY_PROVIDER_ENABLED;
-import static android.location.LocationManager.MODE_CHANGED_ACTION;
 import static android.location.LocationManager.NETWORK_PROVIDER;
-import static android.location.LocationManager.PASSIVE_PROVIDER;
-import static android.location.LocationManager.PROVIDERS_CHANGED_ACTION;
-import static android.location.LocationManager.invalidateLocalLocationEnabledCaches;
-import static android.os.PowerManager.locationPowerSaveModeToString;
 
 import static com.android.server.location.LocationPermissions.PERMISSION_COARSE;
 import static com.android.server.location.LocationPermissions.PERMISSION_FINE;
-import static com.android.server.location.LocationPermissions.PERMISSION_NONE;
+import static com.android.server.location.LocationProviderManager.FASTEST_COARSE_INTERVAL_MS;
 
 import static java.util.concurrent.TimeUnit.NANOSECONDS;
 
 import android.Manifest.permission;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.UserIdInt;
 import android.app.ActivityManager;
 import android.app.AppOpsManager;
 import android.app.PendingIntent;
-import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
-import android.content.IntentFilter;
 import android.location.Criteria;
 import android.location.GeocoderParams;
 import android.location.Geofence;
@@ -78,17 +62,9 @@
 import android.location.LocationTime;
 import android.location.util.identity.CallerIdentity;
 import android.os.Binder;
-import android.os.Build;
 import android.os.Bundle;
-import android.os.CancellationSignal;
-import android.os.Handler;
-import android.os.IBinder;
 import android.os.ICancellationSignal;
-import android.os.IRemoteCallback;
 import android.os.ParcelFileDescriptor;
-import android.os.PowerManager;
-import android.os.PowerManager.ServiceType;
-import android.os.PowerManagerInternal;
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.SystemClock;
@@ -96,24 +72,16 @@
 import android.os.WorkSource;
 import android.os.WorkSource.WorkChain;
 import android.stats.location.LocationStatsEnums;
-import android.text.TextUtils;
-import android.util.EventLog;
 import android.util.IndentingPrintWriter;
 import android.util.Log;
-import android.util.SparseArray;
 import android.util.TimeUtils;
 
 import com.android.internal.annotations.GuardedBy;
-import com.android.internal.content.PackageMonitor;
 import com.android.internal.location.ProviderProperties;
-import com.android.internal.location.ProviderRequest;
 import com.android.internal.util.DumpUtils;
 import com.android.internal.util.Preconditions;
-import com.android.server.FgThread;
 import com.android.server.LocalServices;
-import com.android.server.PendingIntentUtils;
 import com.android.server.SystemService;
-import com.android.server.location.AbstractLocationProvider.State;
 import com.android.server.location.LocationPermissions.PermissionLevel;
 import com.android.server.location.LocationRequestStatistics.PackageProviderKey;
 import com.android.server.location.LocationRequestStatistics.PackageStatistics;
@@ -137,24 +105,17 @@
 import com.android.server.location.util.SystemSettingsHelper;
 import com.android.server.location.util.SystemUserInfoHelper;
 import com.android.server.location.util.UserInfoHelper;
-import com.android.server.location.util.UserInfoHelper.UserListener;
 import com.android.server.pm.permission.PermissionManagerServiceInternal;
 
-import java.io.ByteArrayOutputStream;
 import java.io.FileDescriptor;
-import java.io.PrintStream;
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
-import java.util.Map.Entry;
 import java.util.Objects;
 import java.util.TreeMap;
 import java.util.concurrent.CopyOnWriteArrayList;
-import java.util.concurrent.Executor;
 
 /**
  * The service class that manages LocationProviders and issues location
@@ -241,54 +202,21 @@
     public static final String TAG = "LocationManagerService";
     public static final boolean D = Log.isLoggable(TAG, Log.DEBUG);
 
-    private static final String WAKELOCK_KEY = "*location*";
-
     private static final String NETWORK_LOCATION_SERVICE_ACTION =
             "com.android.location.service.v3.NetworkLocationProvider";
     private static final String FUSED_LOCATION_SERVICE_ACTION =
             "com.android.location.service.FusedLocationProvider";
 
-    // The maximum interval a location request can have and still be considered "high power".
-    private static final long HIGH_POWER_INTERVAL_MS = 5 * 60 * 1000;
-
-    // The fastest interval that applications can receive coarse locations
-    private static final long FASTEST_COARSE_INTERVAL_MS = 10 * 60 * 1000;
-
-    // maximum age of a location before it is no longer considered "current"
-    private static final long MAX_CURRENT_LOCATION_AGE_MS = 10 * 1000;
-
-    // Location Providers may sometimes deliver location updates
-    // slightly faster that requested - provide grace period so
-    // we don't unnecessarily filter events that are otherwise on
-    // time
-    private static final int MAX_PROVIDER_SCHEDULING_JITTER_MS = 100;
-
-    private static final long GET_CURRENT_LOCATION_MAX_TIMEOUT_MS = 30000;
-
     private static final String ATTRIBUTION_TAG = "LocationService";
 
     private final Object mLock = new Object();
 
-    private final Handler mHandler;
+    private final Context mContext;
+    private final Injector mInjector;
     private final LocalService mLocalService;
 
-    private final Injector mInjector;
-
-    private final Context mContext;
-    private final AppOpsHelper mAppOpsHelper;
-    private final UserInfoHelper mUserInfoHelper;
-    private final SettingsHelper mSettingsHelper;
-    private final AppForegroundHelper mAppForegroundHelper;
-    private final LocationUsageLogger mLocationUsageLogger;
-
     private final GeofenceManager mGeofenceManager;
-
     @Nullable private volatile GnssManagerService mGnssManagerService = null;
-
-    private final PassiveLocationProviderManager mPassiveManager;
-
-    private PowerManager mPowerManager;
-
     private GeocoderProxy mGeocodeProvider;
 
     @GuardedBy("mLock")
@@ -296,46 +224,30 @@
     @GuardedBy("mLock")
     private boolean mExtraLocationControllerPackageEnabled;
 
-    // @GuardedBy("mLock")
-    // hold lock for write or to prevent write, no lock for read
-    final CopyOnWriteArrayList<LocationProviderManager> mProviderManagers =
+    // location provider managers
+
+    private final PassiveLocationProviderManager mPassiveManager;
+
+    // @GuardedBy("mProviderManagers")
+    // hold lock for writes, no lock necessary for simple reads
+    private final CopyOnWriteArrayList<LocationProviderManager> mProviderManagers =
             new CopyOnWriteArrayList<>();
 
-    @GuardedBy("mLock")
-    private final HashMap<Object, Receiver> mReceivers = new HashMap<>();
-    private final HashMap<String, ArrayList<UpdateRecord>> mRecordsByProvider =
-            new HashMap<>();
-
-    private final LocationRequestStatistics mRequestStatistics = new LocationRequestStatistics();
-
-    @GuardedBy("mLock")
-    @PowerManager.LocationPowerSaveMode
-    private int mBatterySaverMode;
-
     LocationManagerService(Context context, Injector injector) {
-        mHandler = FgThread.getHandler();
-        mLocalService = new LocalService();
-
-        LocalServices.addService(LocationManagerInternal.class, mLocalService);
-
+        mContext = context.createAttributionContext(ATTRIBUTION_TAG);
         mInjector = injector;
 
-        mContext = context.createAttributionContext(ATTRIBUTION_TAG);
-        mUserInfoHelper = injector.getUserInfoHelper();
-        mAppOpsHelper = injector.getAppOpsHelper();
-        mSettingsHelper = injector.getSettingsHelper();
-        mAppForegroundHelper = injector.getAppForegroundHelper();
-        mLocationUsageLogger = injector.getLocationUsageLogger();
+        mLocalService = new LocalService();
+        LocalServices.addService(LocationManagerInternal.class, mLocalService);
 
         mGeofenceManager = new GeofenceManager(mContext, injector);
 
-        // set up passive provider - we do this early because it has no dependencies on system
-        // services or external code that isn't ready yet, and because this allows the variable to
-        // be final. other more complex providers are initialized later, when system services are
-        // ready
-        mPassiveManager = new PassiveLocationProviderManager();
-        mProviderManagers.add(mPassiveManager);
-        mPassiveManager.setRealProvider(new PassiveProvider(mContext));
+        // set up passive provider first since it will be required for all other location providers,
+        // which are loaded later once the system is ready.
+        mPassiveManager = new PassiveLocationProviderManager(mContext, injector);
+        addLocationProviderManager(mPassiveManager, new PassiveProvider(mContext));
+
+        // TODO: load the gps provider here as well, which will require refactoring
 
         // Let the package manager query which are the default location
         // providers as they get certain permissions granted by default.
@@ -347,253 +259,77 @@
         permissionManagerInternal.setLocationExtraPackagesProvider(
                 userId -> mContext.getResources().getStringArray(
                         com.android.internal.R.array.config_locationExtraPackageNames));
+    }
 
-        // most startup is deferred until systemReady()
+    @Nullable
+    private LocationProviderManager getLocationProviderManager(String providerName) {
+        if (providerName == null) {
+            return null;
+        }
+
+        for (LocationProviderManager manager : mProviderManagers) {
+            if (providerName.equals(manager.getName())) {
+                return manager;
+            }
+        }
+
+        return null;
+    }
+
+    private LocationProviderManager getOrAddLocationProviderManager(String providerName) {
+        synchronized (mProviderManagers) {
+            for (LocationProviderManager manager : mProviderManagers) {
+                if (providerName.equals(manager.getName())) {
+                    return manager;
+                }
+            }
+
+            LocationProviderManager manager = new LocationProviderManager(mContext, mInjector,
+                    providerName, mPassiveManager);
+            addLocationProviderManager(manager, null);
+            return manager;
+        }
+    }
+
+    private void addLocationProviderManager(LocationProviderManager manager,
+            @Nullable AbstractLocationProvider realProvider) {
+        synchronized (mProviderManagers) {
+            Preconditions.checkState(getLocationProviderManager(manager.getName()) == null);
+
+            manager.startManager();
+            if (realProvider != null) {
+                manager.setRealProvider(realProvider);
+            }
+            mProviderManagers.add(manager);
+        }
+    }
+
+    private void removeLocationProviderManager(LocationProviderManager manager) {
+        synchronized (mProviderManagers) {
+            Preconditions.checkState(getLocationProviderManager(manager.getName()) == manager);
+
+            mProviderManagers.remove(manager);
+            manager.setMockProvider(null);
+            manager.setRealProvider(null);
+            manager.stopManager();
+        }
     }
 
     void onSystemReady() {
-        synchronized (mLock) {
-            mPowerManager = mContext.getSystemService(PowerManager.class);
-
-            // add listeners
-            mContext.getPackageManager().addOnPermissionsChangeListener(
-                    uid -> {
-                        // listener invoked on ui thread, move to our thread to reduce risk of
-                        // blocking ui thread
-                        mHandler.post(() -> {
-                            synchronized (mLock) {
-                                onPermissionsChangedLocked();
-                            }
-                        });
-                    });
-
-            LocalServices.getService(PowerManagerInternal.class).registerLowPowerModeObserver(
-                    ServiceType.LOCATION,
-                    state -> {
-                        // listener invoked on ui thread, move to our thread to reduce risk of
-                        // blocking ui thread
-                        mHandler.post(() -> {
-                            synchronized (mLock) {
-                                onBatterySaverModeChangedLocked(state.locationMode);
-                            }
-                        });
-                    });
-            mBatterySaverMode = mPowerManager.getLocationPowerSaveMode();
-
-            mAppOpsHelper.addListener(this::onAppOpChanged);
-
-            mSettingsHelper.addOnLocationEnabledChangedListener(this::onLocationModeChanged);
-            mSettingsHelper.addOnBackgroundThrottleIntervalChangedListener(
-                    this::onBackgroundThrottleIntervalChanged);
-            mSettingsHelper.addOnBackgroundThrottlePackageWhitelistChangedListener(
-                    this::onBackgroundThrottleWhitelistChanged);
-            mSettingsHelper.addOnIgnoreSettingsPackageWhitelistChangedListener(
-                    this::onIgnoreSettingsWhitelistChanged);
-
-            PackageMonitor packageMonitor = new PackageMonitor() {
-                @Override
-                public void onPackageDisappeared(String packageName, int reason) {
-                    synchronized (mLock) {
-                        LocationManagerService.this.onPackageDisappeared(packageName);
-                    }
-                }
-            };
-            packageMonitor.register(mContext, null, true, mHandler);
-
-            mUserInfoHelper.addListener(this::onUserChanged);
-
-            mAppForegroundHelper.addListener(this::onAppForegroundChanged);
-
-            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) {
-                    if (Intent.ACTION_SCREEN_ON.equals(intent.getAction())
-                            || Intent.ACTION_SCREEN_OFF.equals(intent.getAction())) {
-                        onScreenStateChanged();
-                    }
-                }
-            }, UserHandle.ALL, screenIntentFilter, null, mHandler);
-
-            // initialize the current users. we would get the user started notifications for these
-            // users eventually anyways, but this takes care of it as early as possible.
-            onUserChanged(UserHandle.USER_ALL, UserListener.USER_STARTED);
-        }
+        mInjector.getSettingsHelper().addOnLocationEnabledChangedListener(
+                this::onLocationModeChanged);
     }
 
     void onSystemThirdPartyAppsCanStart() {
-        synchronized (mLock) {
-            // prepare providers
-            initializeProvidersLocked();
-        }
-
-        // initialize gnss last because it has no awareness of boot phases and blindly assumes that
-        // all other location providers are loaded at initialization
-        initializeGnss();
-    }
-
-    private void onAppOpChanged(String packageName) {
-        synchronized (mLock) {
-            for (Receiver receiver : mReceivers.values()) {
-                if (receiver.mCallerIdentity.getPackageName().equals(packageName)) {
-                    receiver.updateMonitoring(true);
-                }
-            }
-
-            HashSet<String> affectedProviders = new HashSet<>(mRecordsByProvider.size());
-            for (Entry<String, ArrayList<UpdateRecord>> entry : mRecordsByProvider.entrySet()) {
-                String provider = entry.getKey();
-                for (UpdateRecord record : entry.getValue()) {
-                    if (record.mReceiver.mCallerIdentity.getPackageName().equals(packageName)) {
-                        affectedProviders.add(provider);
-                    }
-                }
-            }
-            for (String provider : affectedProviders) {
-                applyRequirementsLocked(provider);
-            }
-        }
-    }
-
-    @GuardedBy("mLock")
-    private void onPermissionsChangedLocked() {
-        for (LocationProviderManager manager : mProviderManagers) {
-            applyRequirementsLocked(manager);
-        }
-    }
-
-    @GuardedBy("mLock")
-    private void onBatterySaverModeChangedLocked(int newLocationMode) {
-        if (mBatterySaverMode == newLocationMode) {
-            return;
-        }
-
-        if (D) {
-            Log.d(TAG,
-                    "Battery Saver location mode changed from "
-                            + locationPowerSaveModeToString(mBatterySaverMode) + " to "
-                            + locationPowerSaveModeToString(newLocationMode));
-        }
-
-        mBatterySaverMode = newLocationMode;
-
-        for (LocationProviderManager manager : mProviderManagers) {
-            applyRequirementsLocked(manager);
-        }
-    }
-
-    private void onScreenStateChanged() {
-        synchronized (mLock) {
-            if (mBatterySaverMode == PowerManager.LOCATION_MODE_THROTTLE_REQUESTS_WHEN_SCREEN_OFF) {
-                for (LocationProviderManager manager : mProviderManagers) {
-                    applyRequirementsLocked(manager);
-                }
-            }
-        }
-    }
-
-    private void onLocationModeChanged(int userId) {
-        boolean enabled = mSettingsHelper.isLocationEnabled(userId);
-        LocationManager.invalidateLocalLocationEnabledCaches();
-
-        if (D) {
-            Log.d(TAG, "[u" + userId + "] location enabled = " + enabled);
-        }
-
-        Intent intent = new Intent(MODE_CHANGED_ACTION)
-                .putExtra(EXTRA_LOCATION_ENABLED, enabled)
-                .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY)
-                .addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
-        mContext.sendBroadcastAsUser(intent, UserHandle.of(userId));
-
-        synchronized (mLock) {
-            for (LocationProviderManager manager : mProviderManagers) {
-                manager.onEnabledChangedLocked(userId);
-            }
-        }
-    }
-
-    private void onPackageDisappeared(String packageName) {
-        synchronized (mLock) {
-            ArrayList<Receiver> deadReceivers = null;
-
-            for (Receiver receiver : mReceivers.values()) {
-                if (receiver.mCallerIdentity.getPackageName().equals(packageName)) {
-                    if (deadReceivers == null) {
-                        deadReceivers = new ArrayList<>();
-                    }
-                    deadReceivers.add(receiver);
-                }
-            }
-
-            // perform removal outside of mReceivers loop
-            if (deadReceivers != null) {
-                for (Receiver receiver : deadReceivers) {
-                    removeUpdatesLocked(receiver);
-                }
-            }
-        }
-    }
-
-    private void onAppForegroundChanged(int uid, boolean foreground) {
-        synchronized (mLock) {
-            HashSet<String> affectedProviders = new HashSet<>(mRecordsByProvider.size());
-            for (Entry<String, ArrayList<UpdateRecord>> entry : mRecordsByProvider.entrySet()) {
-                String provider = entry.getKey();
-                for (UpdateRecord record : entry.getValue()) {
-                    if (record.mReceiver.mCallerIdentity.getUid() == uid
-                            && record.mIsForegroundUid != foreground) {
-                        record.updateForeground(foreground);
-
-                        if (!isThrottlingExempt(record.mReceiver.mCallerIdentity)) {
-                            affectedProviders.add(provider);
-                        }
-                    }
-                }
-            }
-            for (String provider : affectedProviders) {
-                applyRequirementsLocked(provider);
-            }
-        }
-    }
-
-    private void onBackgroundThrottleIntervalChanged() {
-        synchronized (mLock) {
-            for (LocationProviderManager manager : mProviderManagers) {
-                applyRequirementsLocked(manager);
-            }
-        }
-    }
-
-    private void onBackgroundThrottleWhitelistChanged() {
-        synchronized (mLock) {
-            for (LocationProviderManager manager : mProviderManagers) {
-                applyRequirementsLocked(manager);
-            }
-        }
-    }
-
-    private void onIgnoreSettingsWhitelistChanged() {
-        synchronized (mLock) {
-            for (LocationProviderManager manager : mProviderManagers) {
-                applyRequirementsLocked(manager);
-            }
-        }
-    }
-
-    @GuardedBy("mLock")
-    private void initializeProvidersLocked() {
         LocationProviderProxy networkProvider = LocationProviderProxy.createAndRegister(
                 mContext,
                 NETWORK_LOCATION_SERVICE_ACTION,
                 com.android.internal.R.bool.config_enableNetworkLocationOverlay,
                 com.android.internal.R.string.config_networkLocationProviderPackageName);
         if (networkProvider != null) {
-            LocationProviderManager networkManager = new LocationProviderManager(NETWORK_PROVIDER);
-            mProviderManagers.add(networkManager);
-            networkManager.setRealProvider(networkProvider);
+            LocationProviderManager networkManager = new LocationProviderManager(mContext,
+                    mInjector, NETWORK_PROVIDER, mPassiveManager);
+            addLocationProviderManager(networkManager, networkProvider);
         } else {
             Log.w(TAG, "no network location provider found");
         }
@@ -604,18 +340,28 @@
                 MATCH_DIRECT_BOOT_AWARE | MATCH_SYSTEM_ONLY, UserHandle.USER_SYSTEM).isEmpty(),
                 "Unable to find a direct boot aware fused location provider");
 
-        // bind to fused provider
         LocationProviderProxy fusedProvider = LocationProviderProxy.createAndRegister(
                 mContext,
                 FUSED_LOCATION_SERVICE_ACTION,
                 com.android.internal.R.bool.config_enableFusedLocationOverlay,
                 com.android.internal.R.string.config_fusedLocationProviderPackageName);
         if (fusedProvider != null) {
-            LocationProviderManager fusedManager = new LocationProviderManager(FUSED_PROVIDER);
-            mProviderManagers.add(fusedManager);
-            fusedManager.setRealProvider(fusedProvider);
+            LocationProviderManager fusedManager = new LocationProviderManager(mContext, mInjector,
+                    FUSED_PROVIDER, mPassiveManager);
+            addLocationProviderManager(fusedManager, fusedProvider);
         } else {
-            Log.e(TAG, "no fused location provider found");
+            Log.wtf(TAG, "no fused location provider found");
+        }
+
+        // initialize gnss last because it has no awareness of boot phases and blindly assumes that
+        // all other location providers are loaded at initialization
+        if (GnssManagerService.isGnssSupported()) {
+            mGnssManagerService = new GnssManagerService(mContext, mInjector);
+            mGnssManagerService.onSystemReady();
+
+            LocationProviderManager gnssManager = new LocationProviderManager(mContext, mInjector,
+                    GPS_PROVIDER, mPassiveManager);
+            addLocationProviderManager(gnssManager, mGnssManagerService.getGnssLocationProvider());
         }
 
         // bind to geocoder provider
@@ -631,6 +377,18 @@
             Log.e(TAG, "unable to bind ActivityRecognitionProxy");
         }
 
+        // bind to gnss geofence proxy
+        if (GnssManagerService.isGnssSupported()) {
+            IGpsGeofenceHardware gpsGeofenceHardware = mGnssManagerService.getGpsGeofenceProxy();
+            if (gpsGeofenceHardware != null) {
+                GeofenceProxy provider = GeofenceProxy.createAndBind(mContext, gpsGeofenceHardware);
+                if (provider == null) {
+                    Log.e(TAG, "unable to bind to GeofenceProxy");
+                }
+            }
+        }
+
+        // create any predefined test providers
         String[] testProviderStrings = mContext.getResources().getStringArray(
                 com.android.internal.R.array.config_testLocationProviders);
         for (String testProviderString : testProviderStrings) {
@@ -646,763 +404,24 @@
                     Boolean.parseBoolean(fragments[7]) /* supportsBearing */,
                     Integer.parseInt(fragments[8]) /* powerRequirement */,
                     Integer.parseInt(fragments[9]) /* accuracy */);
-            LocationProviderManager manager = getLocationProviderManager(name);
-            if (manager == null) {
-                manager = new LocationProviderManager(name);
-                mProviderManagers.add(manager);
-            }
-            manager.setMockProvider(
+            getOrAddLocationProviderManager(name).setMockProvider(
                     new MockProvider(properties, CallerIdentity.fromContext(mContext)));
         }
     }
 
-    private void initializeGnss() {
-        // Do not hold mLock when calling GnssManagerService#isGnssSupported() which calls into HAL.
-        if (GnssManagerService.isGnssSupported()) {
-            mGnssManagerService = new GnssManagerService(mContext, mInjector);
-            mGnssManagerService.onSystemReady();
+    private void onLocationModeChanged(int userId) {
+        boolean enabled = mInjector.getSettingsHelper().isLocationEnabled(userId);
+        LocationManager.invalidateLocalLocationEnabledCaches();
 
-            LocationProviderManager gnssManager = new LocationProviderManager(GPS_PROVIDER);
-            synchronized (mLock) {
-                mProviderManagers.add(gnssManager);
-            }
-            gnssManager.setRealProvider(mGnssManagerService.getGnssLocationProvider());
-
-            // bind to geofence proxy
-            IGpsGeofenceHardware gpsGeofenceHardware = mGnssManagerService.getGpsGeofenceProxy();
-            if (gpsGeofenceHardware != null) {
-                GeofenceProxy provider = GeofenceProxy.createAndBind(mContext, gpsGeofenceHardware);
-                if (provider == null) {
-                    Log.e(TAG, "unable to bind to GeofenceProxy");
-                }
-            }
-        }
-    }
-
-    private void onUserChanged(@UserIdInt int userId, @UserListener.UserChange int change) {
-        switch (change) {
-            case UserListener.CURRENT_USER_CHANGED:
-                synchronized (mLock) {
-                    for (LocationProviderManager manager : mProviderManagers) {
-                        manager.onEnabledChangedLocked(userId);
-                    }
-                }
-                break;
-            case UserListener.USER_STARTED:
-                synchronized (mLock) {
-                    for (LocationProviderManager manager : mProviderManagers) {
-                        manager.onUserStarted(userId);
-                    }
-                }
-                break;
-            case UserListener.USER_STOPPED:
-                synchronized (mLock) {
-                    for (LocationProviderManager manager : mProviderManagers) {
-                        manager.onUserStopped(userId);
-                    }
-                }
-                break;
-        }
-    }
-
-    /**
-     * Location provider manager, manages a LocationProvider.
-     */
-    class LocationProviderManager implements MockableLocationProvider.Listener {
-
-        private final String mName;
-
-        private final LocationFudger mLocationFudger;
-
-        // if the provider is enabled for a given user id - null or not present means unknown
-        @GuardedBy("mLock")
-        private final SparseArray<Boolean> mEnabled;
-
-        // last location for a given user
-        @GuardedBy("mLock")
-        private final SparseArray<Location> mLastLocation;
-
-        // last coarse location for a given user
-        @GuardedBy("mLock")
-        private final SparseArray<Location> mLastCoarseLocation;
-
-        // acquiring mLock makes operations on mProvider atomic, but is otherwise unnecessary
-        protected final MockableLocationProvider mProvider;
-
-        LocationProviderManager(String name) {
-            mName = name;
-            mLocationFudger = new LocationFudger(mSettingsHelper.getCoarseLocationAccuracyM());
-            mEnabled = new SparseArray<>(2);
-            mLastLocation = new SparseArray<>(2);
-            mLastCoarseLocation = new SparseArray<>(2);
-
-            // initialize last since this lets our reference escape
-            mProvider = new MockableLocationProvider(mLock, this);
+        if (D) {
+            Log.d(TAG, "[u" + userId + "] location enabled = " + enabled);
         }
 
-        public String getName() {
-            return mName;
-        }
-
-        public boolean hasProvider() {
-            return mProvider.getProvider() != null;
-        }
-
-        public void setRealProvider(AbstractLocationProvider provider) {
-            mProvider.setRealProvider(provider);
-        }
-
-        public void setMockProvider(@Nullable MockProvider provider) {
-            synchronized (mLock) {
-                mProvider.setMockProvider(provider);
-
-                // when removing a mock provider, also clear any mock last locations and reset the
-                // location fudger. the mock provider could have been used to infer the current
-                // location fudger offsets.
-                if (provider == null) {
-                    for (int i = 0; i < mLastLocation.size(); i++) {
-                        Location lastLocation = mLastLocation.valueAt(i);
-                        if (lastLocation != null && lastLocation.isFromMockProvider()) {
-                            mLastLocation.setValueAt(i, null);
-                        }
-                    }
-
-                    for (int i = 0; i < mLastCoarseLocation.size(); i++) {
-                        Location lastCoarseLocation = mLastCoarseLocation.valueAt(i);
-                        if (lastCoarseLocation != null && lastCoarseLocation.isFromMockProvider()) {
-                            mLastCoarseLocation.setValueAt(i, null);
-                        }
-                    }
-
-                    mLocationFudger.resetOffsets();
-                }
-            }
-        }
-
-        @Nullable
-        public CallerIdentity getProviderIdentity() {
-            return mProvider.getState().identity;
-        }
-
-        @Nullable
-        public ProviderProperties getProperties() {
-            return mProvider.getState().properties;
-        }
-
-        @Nullable
-        public Location getLastLocation(int userId, @PermissionLevel int permissionlevel) {
-            synchronized (mLock) {
-                switch (permissionlevel) {
-                    case PERMISSION_COARSE:
-                        return mLastCoarseLocation.get(userId);
-                    case PERMISSION_FINE:
-                        return mLastLocation.get(userId);
-                    default:
-                        throw new AssertionError();
-                }
-            }
-        }
-
-        public void injectLastLocation(Location location, int userId) {
-            synchronized (mLock) {
-                if (mLastLocation.get(userId) == null) {
-                    setLastLocation(location, userId);
-                }
-            }
-        }
-
-        private void setLastLocation(Location location, int userId) {
-            synchronized (mLock) {
-                mLastLocation.put(userId, location);
-
-                // update last coarse interval only if enough time has passed
-                long timeDeltaMs = Long.MAX_VALUE;
-                Location coarseLocation = mLastCoarseLocation.get(userId);
-                if (coarseLocation != null) {
-                    timeDeltaMs = NANOSECONDS.toMillis(location.getElapsedRealtimeNanos())
-                            - NANOSECONDS.toMillis(coarseLocation.getElapsedRealtimeNanos());
-                }
-                if (timeDeltaMs > FASTEST_COARSE_INTERVAL_MS) {
-                    mLastCoarseLocation.put(userId, mLocationFudger.createCoarse(location));
-                }
-            }
-        }
-
-        public void setMockProviderAllowed(boolean enabled) {
-            synchronized (mLock) {
-                if (!mProvider.isMock()) {
-                    throw new IllegalArgumentException(mName + " provider is not a test provider");
-                }
-
-                mProvider.setMockProviderAllowed(enabled);
-            }
-        }
-
-        public void setMockProviderLocation(Location location) {
-            synchronized (mLock) {
-                if (!mProvider.isMock()) {
-                    throw new IllegalArgumentException(mName + " provider is not a test provider");
-                }
-
-                String locationProvider = location.getProvider();
-                if (!TextUtils.isEmpty(locationProvider) && !mName.equals(locationProvider)) {
-                    // The location has an explicit provider that is different from the mock
-                    // provider name. The caller may be trying to fool us via b/33091107.
-                    EventLog.writeEvent(0x534e4554, "33091107", Binder.getCallingUid(),
-                            mName + "!=" + locationProvider);
-                }
-
-                mProvider.setMockProviderLocation(location);
-            }
-        }
-
-        public List<LocationRequest> getMockProviderRequests() {
-            synchronized (mLock) {
-                if (!mProvider.isMock()) {
-                    throw new IllegalArgumentException(mName + " provider is not a test provider");
-                }
-
-                return mProvider.getCurrentRequest().locationRequests;
-            }
-        }
-
-        public void setRequest(ProviderRequest request) {
-            mProvider.setRequest(request);
-        }
-
-        public void sendExtraCommand(int uid, int pid, String command, Bundle extras) {
-            mProvider.sendExtraCommand(uid, pid, command, extras);
-        }
-
-        @GuardedBy("mLock")
-        @Override
-        public void onReportLocation(Location location) {
-            // don't validate mock locations
-            if (!location.isFromMockProvider()) {
-                if (location.getLatitude() == 0 && location.getLongitude() == 0) {
-                    Log.w(TAG, "blocking 0,0 location from " + mName + " provider");
-                    return;
-                }
-            }
-
-            if (!location.isComplete()) {
-                Log.w(TAG, "blocking incomplete location from " + mName + " provider");
-                return;
-            }
-
-            // update last location if the provider is enabled or if servicing a bypass request
-            boolean locationSettingsIgnored = mProvider.getCurrentRequest().locationSettingsIgnored;
-            for (int userId : mUserInfoHelper.getRunningUserIds()) {
-                if (locationSettingsIgnored || isEnabled(userId)) {
-                    setLastLocation(location, userId);
-                }
-            }
-
-            handleLocationChangedLocked(this, location, mLocationFudger.createCoarse(location));
-        }
-
-        @GuardedBy("mLock")
-        @Override
-        public void onReportLocation(List<Location> locations) {
-            if (mGnssManagerService == null || !GPS_PROVIDER.equals(mName)) {
-                return;
-            }
-
-            mGnssManagerService.onReportLocation(locations);
-        }
-
-        @GuardedBy("mLock")
-        @Override
-        public void onStateChanged(State oldState, State newState) {
-            if (oldState.allowed != newState.allowed) {
-                if (D) {
-                    Log.d(TAG, mName + " provider allowed = " + newState.allowed);
-                }
-
-                onEnabledChangedLocked(UserHandle.USER_ALL);
-            }
-        }
-
-        public void onUserStarted(int userId) {
-            if (userId == UserHandle.USER_NULL) {
-                return;
-            } else if (userId == UserHandle.USER_ALL) {
-                for (int runningUserId : mUserInfoHelper.getRunningUserIds()) {
-                    onUserStarted(runningUserId);
-                }
-                return;
-            }
-
-            Preconditions.checkArgument(userId >= 0);
-
-            synchronized (mLock) {
-                // clear the user's prior enabled state to prevent broadcast of enabled state
-                // change. user starts should never result in a broadcast since the state has
-                // technically not changed.
-                mEnabled.put(userId, null);
-                onEnabledChangedLocked(userId);
-            }
-        }
-
-        public void onUserStopped(int userId) {
-            if (userId == UserHandle.USER_NULL) {
-                return;
-            } else if (userId == UserHandle.USER_ALL) {
-                mEnabled.clear();
-                mLastLocation.clear();
-                mLastCoarseLocation.clear();
-                return;
-            }
-
-            Preconditions.checkArgument(userId >= 0);
-
-            synchronized (mLock) {
-                mEnabled.remove(userId);
-                mLastLocation.remove(userId);
-                mLastCoarseLocation.remove(userId);
-            }
-        }
-
-        public boolean isEnabled(int userId) {
-            if (userId == UserHandle.USER_NULL) {
-                // used during initialization - ignore since many lower level operations (checking
-                // settings for instance) do not support the null user
-                return false;
-            }
-
-            Preconditions.checkArgument(userId >= 0);
-
-            synchronized (mLock) {
-                Boolean enabled = mEnabled.get(userId);
-                if (enabled == null) {
-                    // this generally shouldn't occur, but might be possible due to race conditions
-                    // on when we are notified of new users
-                    Log.w(TAG, mName + " provider saw user " + userId + " unexpectedly");
-                    onEnabledChangedLocked(userId);
-                    enabled = Objects.requireNonNull(mEnabled.get(userId));
-                }
-
-                return enabled;
-            }
-        }
-
-        @GuardedBy("mLock")
-        public void onEnabledChangedLocked(int userId) {
-            if (userId == UserHandle.USER_NULL) {
-                // used during initialization - ignore since many lower level operations (checking
-                // settings for instance) do not support the null user
-                return;
-            } else if (userId == UserHandle.USER_ALL) {
-                for (int runningUserId : mUserInfoHelper.getRunningUserIds()) {
-                    onEnabledChangedLocked(runningUserId);
-                }
-                return;
-            }
-
-            Preconditions.checkArgument(userId >= 0);
-
-            // if any property that contributes to "enabled" here changes state, it MUST result
-            // in a direct or indrect call to onEnabledChangedLocked. this allows the provider to
-            // guarantee that it will always eventually reach the correct state.
-            boolean enabled = mProvider.getState().allowed
-                    && mUserInfoHelper.isCurrentUserId(userId)
-                    && mSettingsHelper.isLocationEnabled(userId);
-
-            Boolean wasEnabled = mEnabled.get(userId);
-            if (wasEnabled != null && wasEnabled == enabled) {
-                return;
-            }
-
-            mEnabled.put(userId, enabled);
-
-            if (D) {
-                Log.d(TAG, "[u" + userId + "] " + mName + " provider enabled = " + enabled);
-            }
-
-            // clear last locations if we become disabled and if not servicing a bypass request
-            if (!enabled && !mProvider.getCurrentRequest().locationSettingsIgnored) {
-                mLastLocation.put(userId, null);
-                mLastCoarseLocation.put(userId, null);
-            }
-
-            // do not send change notifications if we just saw this user for the first time
-            if (wasEnabled != null) {
-                // fused and passive provider never get public updates for legacy reasons
-                if (!FUSED_PROVIDER.equals(mName) && !PASSIVE_PROVIDER.equals(mName)) {
-                    Intent intent = new Intent(PROVIDERS_CHANGED_ACTION)
-                            .putExtra(EXTRA_PROVIDER_NAME, mName)
-                            .putExtra(EXTRA_PROVIDER_ENABLED, enabled)
-                            .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY)
-                            .addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
-                    mContext.sendBroadcastAsUser(intent, UserHandle.of(userId));
-                }
-            }
-
-            updateProviderEnabledLocked(this, enabled);
-        }
-
-        public void dump(FileDescriptor fd, IndentingPrintWriter pw, String[] args) {
-            synchronized (mLock) {
-                pw.print(mName + " provider");
-                if (mProvider.isMock()) {
-                    pw.print(" [mock]");
-                }
-                pw.println(":");
-
-                pw.increaseIndent();
-
-                int[] userIds = mUserInfoHelper.getRunningUserIds();
-                for (int userId : userIds) {
-                    if (userIds.length != 1) {
-                        pw.println("user " + userId + ":");
-                        pw.increaseIndent();
-                    }
-                    pw.println("last location=" + mLastLocation.get(userId));
-                    pw.println("last coarse location=" + mLastCoarseLocation.get(userId));
-                    pw.println("enabled=" + isEnabled(userId));
-                    if (userIds.length != 1) {
-                        pw.decreaseIndent();
-                    }
-                }
-            }
-
-            mProvider.dump(fd, pw, args);
-
-            pw.decreaseIndent();
-        }
-    }
-
-    class PassiveLocationProviderManager extends LocationProviderManager {
-
-        private PassiveLocationProviderManager() {
-            super(PASSIVE_PROVIDER);
-        }
-
-        @Override
-        public void setRealProvider(AbstractLocationProvider provider) {
-            Preconditions.checkArgument(provider instanceof PassiveProvider);
-            super.setRealProvider(provider);
-        }
-
-        @Override
-        public void setMockProvider(@Nullable MockProvider provider) {
-            if (provider != null) {
-                throw new IllegalArgumentException("Cannot mock the passive provider");
-            }
-        }
-
-        public void updateLocation(Location location) {
-            synchronized (mLock) {
-                PassiveProvider passiveProvider = (PassiveProvider) mProvider.getProvider();
-                Preconditions.checkState(passiveProvider != null);
-
-                long identity = Binder.clearCallingIdentity();
-                try {
-                    passiveProvider.updateLocation(location);
-                } finally {
-                    Binder.restoreCallingIdentity(identity);
-                }
-            }
-        }
-    }
-
-    /**
-     * A wrapper class holding either an ILocationListener or a PendingIntent to receive
-     * location updates.
-     */
-    private final class Receiver extends LocationManagerServiceUtils.LinkedListenerBase implements
-            PendingIntent.OnFinished {
-        private static final long WAKELOCK_TIMEOUT_MILLIS = 60 * 1000;
-
-        private final ILocationListener mListener;
-        final PendingIntent mPendingIntent;
-        final WorkSource mWorkSource; // WorkSource for battery blame, or null to assign to caller.
-        private final boolean mHideFromAppOps; // True if AppOps should not monitor this receiver.
-        private final Object mKey;
-
-        final HashMap<String, UpdateRecord> mUpdateRecords = new HashMap<>();
-
-        // True if app ops has started monitoring this receiver for locations.
-        private boolean mOpMonitoring;
-        // True if app ops has started monitoring this receiver for high power (gps) locations.
-        private boolean mOpHighPowerMonitoring;
-        private int mPendingBroadcasts;
-        PowerManager.WakeLock mWakeLock;
-
-        private Receiver(ILocationListener listener, PendingIntent intent, CallerIdentity identity,
-                WorkSource workSource, boolean hideFromAppOps) {
-            super(identity);
-            mListener = listener;
-            mPendingIntent = intent;
-            if (listener != null) {
-                mKey = listener.asBinder();
-            } else {
-                mKey = intent;
-            }
-            if (workSource != null && workSource.isEmpty()) {
-                workSource = null;
-            }
-            mWorkSource = workSource;
-            mHideFromAppOps = hideFromAppOps;
-
-            updateMonitoring(true);
-
-            // construct/configure wakelock
-            mWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_KEY);
-            if (workSource == null) {
-                workSource = mCallerIdentity.addToWorkSource(null);
-            }
-            mWakeLock.setWorkSource(workSource);
-
-            // For a non-reference counted wakelock, each acquire will reset the timeout, and we
-            // only need to release it once.
-            mWakeLock.setReferenceCounted(false);
-        }
-
-        @Override
-        public boolean equals(Object otherObj) {
-            return (otherObj instanceof Receiver) && mKey.equals(((Receiver) otherObj).mKey);
-        }
-
-        @Override
-        public int hashCode() {
-            return mKey.hashCode();
-        }
-
-        @Override
-        public String toString() {
-            StringBuilder s = new StringBuilder();
-            s.append("Reciever[");
-            s.append(Integer.toHexString(System.identityHashCode(this)));
-            if (mListener != null) {
-                s.append(" listener");
-            } else {
-                s.append(" intent");
-            }
-            for (String p : mUpdateRecords.keySet()) {
-                s.append(" ").append(mUpdateRecords.get(p).toString());
-            }
-            s.append(" monitoring location: ").append(mOpMonitoring);
-            s.append("]");
-            return s.toString();
-        }
-
-        /**
-         * Update AppOp monitoring for this receiver.
-         *
-         * @param allow If true receiver is currently active, if false it's been removed.
-         */
-        public void updateMonitoring(boolean allow) {
-            if (mHideFromAppOps) {
-                return;
-            }
-
-            boolean requestingLocation = false;
-            boolean requestingHighPowerLocation = false;
-            if (allow) {
-                // See if receiver has any enabled update records.  Also note if any update records
-                // are high power (has a high power provider with an interval under a threshold).
-                for (UpdateRecord updateRecord : mUpdateRecords.values()) {
-                    LocationProviderManager manager = getLocationProviderManager(
-                            updateRecord.mProvider);
-                    if (manager == null) {
-                        continue;
-                    }
-                    if (!manager.isEnabled(UserHandle.getUserId(mCallerIdentity.getUid()))
-                            && !isSettingsExempt(updateRecord)) {
-                        continue;
-                    }
-
-                    requestingLocation = true;
-                    ProviderProperties properties = manager.getProperties();
-                    if (properties != null
-                            && properties.mPowerRequirement == Criteria.POWER_HIGH
-                            && updateRecord.mRequest.getInterval() < HIGH_POWER_INTERVAL_MS) {
-                        requestingHighPowerLocation = true;
-                        break;
-                    }
-                }
-            }
-
-            // First update monitoring of any location request (including high power).
-            mOpMonitoring = updateMonitoring(
-                    requestingLocation,
-                    mOpMonitoring,
-                    false);
-
-            // Now update monitoring of high power requests only.
-            mOpHighPowerMonitoring = updateMonitoring(
-                    requestingHighPowerLocation,
-                    mOpHighPowerMonitoring,
-                    true);
-        }
-
-        private boolean updateMonitoring(boolean allowMonitoring, boolean currentlyMonitoring,
-                boolean highPower) {
-            if (!currentlyMonitoring) {
-                if (allowMonitoring) {
-                    if (!highPower) {
-                        return mAppOpsHelper.startOpNoThrow(OP_MONITOR_LOCATION, mCallerIdentity);
-                    } else {
-                        return mAppOpsHelper.startOpNoThrow(OP_MONITOR_HIGH_POWER_LOCATION,
-                                mCallerIdentity);
-                    }
-                }
-            } else {
-                int permissionLevel = LocationPermissions.getPermissionLevel(mContext,
-                        mCallerIdentity.getUid(), mCallerIdentity.getPid());
-                if (!allowMonitoring || permissionLevel == PERMISSION_NONE
-                        || !mAppOpsHelper.checkOpNoThrow(
-                        LocationPermissions.asAppOp(permissionLevel), mCallerIdentity)) {
-                    if (!highPower) {
-                        mAppOpsHelper.finishOp(OP_MONITOR_LOCATION, mCallerIdentity);
-                    } else {
-                        mAppOpsHelper.finishOp(OP_MONITOR_HIGH_POWER_LOCATION, mCallerIdentity);
-                    }
-                    return false;
-                }
-            }
-
-            return currentlyMonitoring;
-        }
-
-        public boolean isListener() {
-            return mListener != null;
-        }
-
-        public boolean isPendingIntent() {
-            return mPendingIntent != null;
-        }
-
-        public ILocationListener getListener() {
-            if (mListener != null) {
-                return mListener;
-            }
-            throw new IllegalStateException("Request for non-existent listener");
-        }
-
-        public boolean callLocationChangedLocked(Location location,
-                LocationRequest locationRequest) {
-            if (mListener != null) {
-                try {
-                    mListener.onLocationChanged(new Location(location), new IRemoteCallback.Stub() {
-                        @Override
-                        public void sendResult(Bundle data) {
-                            synchronized (mLock) {
-                                decrementPendingBroadcastsLocked();
-                            }
-                        }
-                    });
-                    // call this after broadcasting so we do not increment
-                    // if we throw an exception.
-                    incrementPendingBroadcastsLocked();
-                } catch (RemoteException e) {
-                    return false;
-                }
-            } else {
-                Intent locationChanged = new Intent();
-                locationChanged.putExtra(KEY_LOCATION_CHANGED, new Location(location));
-                try {
-                    mPendingIntent.send(mContext, 0, locationChanged, this, mHandler,
-                            LocationPermissions.asPermission(
-                                    locationRequest.isCoarse() ? PERMISSION_COARSE
-                                            : PERMISSION_FINE),
-                            PendingIntentUtils.createDontSendToRestrictedAppsBundle(null));
-                    // call this after broadcasting so we do not increment
-                    // if we throw an exception.
-                    incrementPendingBroadcastsLocked();
-                } catch (PendingIntent.CanceledException e) {
-                    return false;
-                }
-            }
-            return true;
-        }
-
-        private boolean callProviderEnabledLocked(String provider, boolean enabled,
-                LocationRequest locationRequest) {
-            // First update AppOp monitoring.
-            // An app may get/lose location access as providers are enabled/disabled.
-            updateMonitoring(true);
-
-            if (mListener != null) {
-                try {
-                    mListener.onProviderEnabledChanged(provider, enabled);
-                } catch (RemoteException e) {
-                    return false;
-                }
-            } else {
-                Intent providerIntent = new Intent();
-                providerIntent.putExtra(KEY_PROVIDER_ENABLED, enabled);
-                try {
-                    mPendingIntent.send(mContext, 0, providerIntent, null, mHandler,
-                            LocationPermissions.asPermission(
-                                    locationRequest.isCoarse() ? PERMISSION_COARSE
-                                            : PERMISSION_FINE),
-                            PendingIntentUtils.createDontSendToRestrictedAppsBundle(null));
-                } catch (PendingIntent.CanceledException e) {
-                    return false;
-                }
-            }
-            return true;
-        }
-
-        @Override
-        public void binderDied() {
-            synchronized (mLock) {
-                removeUpdatesLocked(this);
-                clearPendingBroadcastsLocked();
-            }
-        }
-
-        @Override
-        public void onSendFinished(PendingIntent pendingIntent, Intent intent,
-                int resultCode, String resultData, Bundle resultExtras) {
-            synchronized (mLock) {
-                decrementPendingBroadcastsLocked();
-            }
-        }
-
-        // this must be called while synchronized by caller in a synchronized block
-        // containing the sending of the broadcaset
-        private void incrementPendingBroadcastsLocked() {
-            mPendingBroadcasts++;
-            // so wakelock calls will succeed
-            long identity = Binder.clearCallingIdentity();
-            try {
-                mWakeLock.acquire(WAKELOCK_TIMEOUT_MILLIS);
-            } finally {
-                Binder.restoreCallingIdentity(identity);
-            }
-        }
-
-        private void decrementPendingBroadcastsLocked() {
-            if (--mPendingBroadcasts == 0) {
-                // so wakelock calls will succeed
-                long identity = Binder.clearCallingIdentity();
-                try {
-                    if (mWakeLock.isHeld()) {
-                        mWakeLock.release();
-                    }
-                } finally {
-                    Binder.restoreCallingIdentity(identity);
-                }
-            }
-        }
-
-        public void clearPendingBroadcastsLocked() {
-            if (mPendingBroadcasts > 0) {
-                mPendingBroadcasts = 0;
-                // so wakelock calls will succeed
-                long identity = Binder.clearCallingIdentity();
-                try {
-                    if (mWakeLock.isHeld()) {
-                        mWakeLock.release();
-                    }
-                } finally {
-                    Binder.restoreCallingIdentity(identity);
-                }
-            }
-        }
+        Intent intent = new Intent(LocationManager.MODE_CHANGED_ACTION)
+                .putExtra(LocationManager.EXTRA_LOCATION_ENABLED, enabled)
+                .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY)
+                .addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+        mContext.sendBroadcastAsUser(intent, UserHandle.of(userId));
     }
 
     @Override
@@ -1459,17 +478,6 @@
         }
     }
 
-    @Nullable
-    LocationProviderManager getLocationProviderManager(String providerName) {
-        for (LocationProviderManager manager : mProviderManagers) {
-            if (providerName.equals(manager.getName())) {
-                return manager;
-            }
-        }
-
-        return null;
-    }
-
     @Override
     public List<String> getAllProviders() {
         ArrayList<String> providers = new ArrayList<>(mProviderManagers.size());
@@ -1532,397 +540,16 @@
         return null;
     }
 
-    @GuardedBy("mLock")
-    private void updateProviderEnabledLocked(LocationProviderManager manager, boolean enabled) {
-        ArrayList<Receiver> deadReceivers = null;
-        ArrayList<UpdateRecord> records = mRecordsByProvider.get(manager.getName());
-        if (records != null) {
-            for (UpdateRecord record : records) {
-                if (!mUserInfoHelper.isCurrentUserId(
-                        UserHandle.getUserId(record.mReceiver.mCallerIdentity.getUid()))) {
-                    continue;
-                }
-
-                // requests that ignore location settings will never provide notifications
-                if (isSettingsExempt(record)) {
-                    continue;
-                }
-
-                // Sends a notification message to the receiver
-                if (!record.mReceiver.callProviderEnabledLocked(manager.getName(), enabled,
-                        record.mRequest)) {
-                    if (deadReceivers == null) {
-                        deadReceivers = new ArrayList<>();
-                    }
-                    deadReceivers.add(record.mReceiver);
-                }
-            }
-        }
-
-        if (deadReceivers != null) {
-            for (int i = deadReceivers.size() - 1; i >= 0; i--) {
-                removeUpdatesLocked(deadReceivers.get(i));
-            }
-        }
-
-        applyRequirementsLocked(manager);
-    }
-
-    @GuardedBy("mLock")
-    private void applyRequirementsLocked(String providerName) {
-        LocationProviderManager manager = getLocationProviderManager(providerName);
-        if (manager != null) {
-            applyRequirementsLocked(manager);
-        }
-    }
-
-    @GuardedBy("mLock")
-    private void applyRequirementsLocked(LocationProviderManager manager) {
-        ArrayList<UpdateRecord> records = mRecordsByProvider.get(manager.getName());
-        ProviderRequest.Builder providerRequest = new ProviderRequest.Builder();
-
-        // if provider is not active, it should not respond to requests
-
-        if (mProviderManagers.contains(manager) && records != null && !records.isEmpty()) {
-            long backgroundThrottleInterval = mSettingsHelper.getBackgroundThrottleIntervalMs();
-
-            ArrayList<LocationRequest> requests = new ArrayList<>(records.size());
-
-            final boolean isForegroundOnlyMode =
-                    mBatterySaverMode == PowerManager.LOCATION_MODE_FOREGROUND_ONLY;
-            final boolean shouldThrottleRequests =
-                    mBatterySaverMode
-                            == PowerManager.LOCATION_MODE_THROTTLE_REQUESTS_WHEN_SCREEN_OFF
-                            && !mPowerManager.isInteractive();
-            // initialize the low power mode to true and set to false if any of the records requires
-            providerRequest.setLowPowerMode(true);
-            for (UpdateRecord record : records) {
-                CallerIdentity identity = record.mReceiver.mCallerIdentity;
-                if (!mUserInfoHelper.isCurrentUserId(identity.getUserId())) {
-                    continue;
-                }
-
-                if (!mAppOpsHelper.checkOpNoThrow(LocationPermissions.asAppOp(
-                        record.mRequest.isCoarse() ? PERMISSION_COARSE : PERMISSION_FINE),
-                        identity)) {
-                    continue;
-                }
-                final boolean isBatterySaverDisablingLocation = shouldThrottleRequests
-                        || (isForegroundOnlyMode && !record.mIsForegroundUid);
-                if (!manager.isEnabled(identity.getUserId()) || isBatterySaverDisablingLocation) {
-                    if (isSettingsExempt(record)) {
-                        providerRequest.setLocationSettingsIgnored(true);
-                        providerRequest.setLowPowerMode(false);
-                    } else {
-                        continue;
-                    }
-                }
-
-                LocationRequest locationRequest = record.mRealRequest;
-                long interval = locationRequest.getInterval();
-
-
-                // if we're forcing location, don't apply any throttling
-                if (!providerRequest.isLocationSettingsIgnored() && !isThrottlingExempt(
-                        record.mReceiver.mCallerIdentity)) {
-                    if (!record.mIsForegroundUid) {
-                        interval = Math.max(interval, backgroundThrottleInterval);
-                    }
-                    if (interval != locationRequest.getInterval()) {
-                        locationRequest = new LocationRequest(locationRequest);
-                        locationRequest.setInterval(interval);
-                    }
-                }
-
-                record.mRequest = locationRequest;
-                requests.add(locationRequest);
-                if (!locationRequest.isLowPowerMode()) {
-                    providerRequest.setLowPowerMode(false);
-                }
-                if (interval < providerRequest.getInterval()) {
-                    providerRequest.setInterval(interval);
-                }
-            }
-
-            providerRequest.setLocationRequests(requests);
-
-            if (providerRequest.getInterval() < Long.MAX_VALUE) {
-                // calculate who to blame for power
-                // This is somewhat arbitrary. We pick a threshold interval
-                // that is slightly higher that the minimum interval, and
-                // spread the blame across all applications with a request
-                // under that threshold.
-                // TODO: overflow
-                long thresholdInterval = (providerRequest.getInterval() + 1000) * 3 / 2;
-                for (UpdateRecord record : records) {
-                    if (mUserInfoHelper.isCurrentUserId(
-                            UserHandle.getUserId(record.mReceiver.mCallerIdentity.getUid()))) {
-                        LocationRequest locationRequest = record.mRequest;
-
-                        // Don't assign battery blame for update records whose
-                        // client has no permission to receive location data.
-                        if (!providerRequest.getLocationRequests().contains(locationRequest)) {
-                            continue;
-                        }
-
-                        if (locationRequest.getInterval() <= thresholdInterval) {
-                            if (record.mReceiver.mWorkSource != null
-                                    && isValidWorkSource(record.mReceiver.mWorkSource)) {
-                                providerRequest.getWorkSource().add(record.mReceiver.mWorkSource);
-                            } else {
-                                // Assign blame to caller if there's no WorkSource associated with
-                                // the request or if it's invalid.
-                                providerRequest.getWorkSource().add(
-                                        record.mReceiver.mCallerIdentity.getUid(),
-                                        record.mReceiver.mCallerIdentity.getPackageName());
-                            }
-                        }
-                    }
-                }
-            }
-        }
-
-        manager.setRequest(providerRequest.build());
-    }
-
-    /**
-     * Whether a given {@code WorkSource} associated with a Location request is valid.
-     */
-    private static boolean isValidWorkSource(WorkSource workSource) {
-        if (workSource.size() > 0) {
-            // If the WorkSource has one or more non-chained UIDs, make sure they're accompanied
-            // by tags.
-            return workSource.getPackageName(0) != null;
-        } else {
-            // For now, make sure callers have supplied an attribution tag for use with
-            // AppOpsManager. This might be relaxed in the future.
-            final List<WorkChain> workChains = workSource.getWorkChains();
-            return workChains != null && !workChains.isEmpty()
-                    && workChains.get(0).getAttributionTag() != null;
-        }
-    }
-
     @Override
     public String[] getBackgroundThrottlingWhitelist() {
-        return mSettingsHelper.getBackgroundThrottlePackageWhitelist().toArray(new String[0]);
+        return mInjector.getSettingsHelper().getBackgroundThrottlePackageWhitelist().toArray(
+                new String[0]);
     }
 
     @Override
     public String[] getIgnoreSettingsWhitelist() {
-        return mSettingsHelper.getIgnoreSettingsPackageWhitelist().toArray(new String[0]);
-    }
-
-    private boolean isThrottlingExempt(CallerIdentity callerIdentity) {
-        if (callerIdentity.getUid() == Process.SYSTEM_UID) {
-            return true;
-        }
-
-        if (mSettingsHelper.getBackgroundThrottlePackageWhitelist().contains(
-                callerIdentity.getPackageName())) {
-            return true;
-        }
-
-        return mLocalService.isProvider(null, callerIdentity);
-
-    }
-
-    private boolean isSettingsExempt(UpdateRecord record) {
-        if (!record.mRealRequest.isLocationSettingsIgnored()) {
-            return false;
-        }
-
-        if (mSettingsHelper.getIgnoreSettingsPackageWhitelist().contains(
-                record.mReceiver.mCallerIdentity.getPackageName())) {
-            return true;
-        }
-
-        return mLocalService.isProvider(null, record.mReceiver.mCallerIdentity);
-    }
-
-    private class UpdateRecord {
-        final String mProvider;
-        private final LocationRequest mRealRequest;  // original request from client
-        LocationRequest mRequest;  // possibly throttled version of the request
-        private final Receiver mReceiver;
-        private boolean mIsForegroundUid;
-        private Location mLastFixBroadcast;
-        private Throwable mStackTrace;  // for debugging only
-        private long mExpirationRealtimeMs;
-
-        /**
-         * Note: must be constructed with lock held.
-         */
-        private UpdateRecord(String provider, LocationRequest request, Receiver receiver) {
-            if (Build.IS_DEBUGGABLE) {
-                Preconditions.checkState(Thread.holdsLock(mLock));
-            }
-            mExpirationRealtimeMs = request.getExpirationRealtimeMs(SystemClock.elapsedRealtime());
-            mProvider = provider;
-            mRealRequest = request;
-            mRequest = request;
-            mReceiver = receiver;
-            mIsForegroundUid = mAppForegroundHelper.isAppForeground(
-                    mReceiver.mCallerIdentity.getUid());
-
-            if (D && receiver.mCallerIdentity.getPid() == Process.myPid()) {
-                mStackTrace = new Throwable();
-            }
-
-            ArrayList<UpdateRecord> records = mRecordsByProvider.computeIfAbsent(provider,
-                    k -> new ArrayList<>());
-            if (!records.contains(this)) {
-                records.add(this);
-            }
-
-            // Update statistics for historical location requests by package/provider
-            mRequestStatistics.startRequesting(
-                    mReceiver.mCallerIdentity.getPackageName(),
-                    mReceiver.mCallerIdentity.getAttributionTag(),
-                    provider, request.getInterval(), mIsForegroundUid);
-        }
-
-        /**
-         * Method to be called when record changes foreground/background
-         */
-        private void updateForeground(boolean isForeground) {
-            mIsForegroundUid = isForeground;
-            mRequestStatistics.updateForeground(
-                    mReceiver.mCallerIdentity.getPackageName(),
-                    mReceiver.mCallerIdentity.getAttributionTag(),
-                    mProvider, isForeground);
-        }
-
-        /**
-         * Method to be called when a record will no longer be used.
-         */
-        private void disposeLocked(boolean removeReceiver) {
-            if (Build.IS_DEBUGGABLE) {
-                Preconditions.checkState(Thread.holdsLock(mLock));
-            }
-
-            CallerIdentity identity = mReceiver.mCallerIdentity;
-            mRequestStatistics.stopRequesting(identity.getPackageName(),
-                    identity.getAttributionTag(),
-                    mProvider);
-
-            mLocationUsageLogger.logLocationApiUsage(
-                    LocationStatsEnums.USAGE_ENDED,
-                    LocationStatsEnums.API_REQUEST_LOCATION_UPDATES,
-                    identity.getPackageName(),
-                    mRealRequest,
-                    mReceiver.isListener(),
-                    mReceiver.isPendingIntent(),
-                    /* geofence= */ null,
-                    mAppForegroundHelper.isAppForeground(mReceiver.mCallerIdentity.getUid()));
-
-            // remove from mRecordsByProvider
-            ArrayList<UpdateRecord> globalRecords = mRecordsByProvider.get(this.mProvider);
-            if (globalRecords != null) {
-                globalRecords.remove(this);
-            }
-
-            if (!removeReceiver) return;  // the caller will handle the rest
-
-            // remove from Receiver#mUpdateRecords
-            HashMap<String, UpdateRecord> receiverRecords = mReceiver.mUpdateRecords;
-            receiverRecords.remove(this.mProvider);
-
-            // and also remove the Receiver if it has no more update records
-            if (receiverRecords.size() == 0) {
-                removeUpdatesLocked(mReceiver);
-            }
-        }
-
-        @Override
-        public String toString() {
-            StringBuilder b = new StringBuilder("UpdateRecord[");
-            b.append(mProvider).append(" ");
-            b.append(mReceiver.mCallerIdentity).append(" ");
-            if (!mIsForegroundUid) {
-                b.append("(background) ");
-            }
-            b.append(mRealRequest).append(" ").append(mReceiver.mWorkSource);
-
-            if (mStackTrace != null) {
-                ByteArrayOutputStream tmp = new ByteArrayOutputStream();
-                mStackTrace.printStackTrace(new PrintStream(tmp));
-                b.append("\n\n").append(tmp.toString()).append("\n");
-            }
-
-            b.append("]");
-            return b.toString();
-        }
-    }
-
-    @GuardedBy("mLock")
-    private Receiver getReceiverLocked(ILocationListener listener, CallerIdentity identity,
-            WorkSource workSource, boolean hideFromAppOps) {
-        IBinder binder = listener.asBinder();
-        Receiver receiver = mReceivers.get(binder);
-        if (receiver == null && identity != null) {
-            receiver = new Receiver(listener, null, identity, workSource,
-                    hideFromAppOps);
-            if (!receiver.linkToListenerDeathNotificationLocked(
-                    receiver.getListener().asBinder())) {
-                return null;
-            }
-            mReceivers.put(binder, receiver);
-        }
-        return receiver;
-    }
-
-    @GuardedBy("mLock")
-    private Receiver getReceiverLocked(PendingIntent intent, CallerIdentity identity,
-            WorkSource workSource, boolean hideFromAppOps) {
-        Receiver receiver = mReceivers.get(intent);
-        if (receiver == null && identity != null) {
-            receiver = new Receiver(null, intent, identity, workSource,
-                    hideFromAppOps);
-            mReceivers.put(intent, receiver);
-        }
-        return receiver;
-    }
-
-    /**
-     * Creates a LocationRequest based upon the supplied LocationRequest that to meets resolution
-     * and consistency requirements.
-     *
-     * @param request the LocationRequest from which to create a sanitized version
-     * @return a version of request that meets the given resolution and consistency requirements
-     * @hide
-     */
-    private LocationRequest createSanitizedRequest(LocationRequest request,
-            boolean callerHasLocationHardwarePermission, int permissionLevel) {
-        LocationRequest sanitizedRequest = new LocationRequest(request);
-        if (!callerHasLocationHardwarePermission) {
-            // allow setting low power mode only for callers with location hardware permission
-            sanitizedRequest.setLowPowerMode(false);
-        }
-        if (permissionLevel < PERMISSION_FINE) {
-            sanitizedRequest.setCoarse(true);
-            switch (sanitizedRequest.getQuality()) {
-                case LocationRequest.ACCURACY_FINE:
-                    sanitizedRequest.setQuality(LocationRequest.ACCURACY_BLOCK);
-                    break;
-                case LocationRequest.POWER_HIGH:
-                    sanitizedRequest.setQuality(LocationRequest.POWER_LOW);
-                    break;
-            }
-            // throttle
-            if (sanitizedRequest.getInterval() < FASTEST_COARSE_INTERVAL_MS) {
-                sanitizedRequest.setInterval(FASTEST_COARSE_INTERVAL_MS);
-            }
-            if (sanitizedRequest.getFastestInterval() < FASTEST_COARSE_INTERVAL_MS) {
-                sanitizedRequest.setFastestInterval(FASTEST_COARSE_INTERVAL_MS);
-            }
-        } else {
-            sanitizedRequest.setCoarse(false);
-        }
-        // make getFastestInterval() the minimum of interval and fastest interval
-        if (sanitizedRequest.getFastestInterval() > sanitizedRequest.getInterval()) {
-            sanitizedRequest.setFastestInterval(request.getInterval());
-        }
-        return sanitizedRequest;
+        return mInjector.getSettingsHelper().getIgnoreSettingsPackageWhitelist().toArray(
+                new String[0]);
     }
 
     @Override
@@ -1930,45 +557,24 @@
             String packageName, String attributionTag, String listenerId) {
         CallerIdentity identity = CallerIdentity.fromBinder(mContext, packageName, attributionTag,
                 listenerId);
-        int permissionLevel = LocationPermissions.getCallingOrSelfPermissionLevel(mContext);
-        LocationPermissions.enforceLocationPermission(Binder.getCallingUid(), permissionLevel,
+        int permissionLevel = LocationPermissions.getPermissionLevel(mContext, identity.getUid(),
+                identity.getPid());
+        LocationPermissions.enforceLocationPermission(identity.getUid(), permissionLevel,
                 PERMISSION_COARSE);
 
-        WorkSource workSource = request.getWorkSource();
-        if (workSource != null && !workSource.isEmpty()) {
-            mContext.enforceCallingOrSelfPermission(
-                    permission.UPDATE_DEVICE_STATS, null);
+        // clients in the system process should have an attribution tag set
+        if (identity.getPid() == Process.myPid() && attributionTag == null) {
+            Log.w(TAG, "system location request with no attribution tag",
+                    new IllegalArgumentException());
         }
-        boolean hideFromAppOps = request.getHideFromAppOps();
-        if (hideFromAppOps) {
-            mContext.enforceCallingOrSelfPermission(
-                    permission.UPDATE_APP_OPS_STATS, null);
-        }
-        if (request.isLocationSettingsIgnored()) {
-            mContext.enforceCallingOrSelfPermission(
-                    permission.WRITE_SECURE_SETTINGS, null);
-        }
-        boolean callerHasLocationHardwarePermission =
-                mContext.checkCallingPermission(permission.LOCATION_HARDWARE)
-                        == PERMISSION_GRANTED;
-        LocationRequest sanitizedRequest = createSanitizedRequest(request,
-                callerHasLocationHardwarePermission,
-                permissionLevel);
 
-        mLocationUsageLogger.logLocationApiUsage(
-                LocationStatsEnums.USAGE_STARTED,
-                LocationStatsEnums.API_REQUEST_LOCATION_UPDATES,
-                packageName, request, true, false,
-                /* geofence= */ null,
-                mAppForegroundHelper.isAppForeground(identity.getUid()));
+        request = validateAndSanitizeLocationRequest(request, permissionLevel);
 
-        synchronized (mLock) {
-            Receiver receiver = getReceiverLocked(Objects.requireNonNull(listener), identity,
-                    workSource, hideFromAppOps);
-            if (receiver != null) {
-                requestLocationUpdatesLocked(sanitizedRequest, receiver);
-            }
-        }
+        LocationProviderManager manager = getLocationProviderManager(request.getProvider());
+        Preconditions.checkArgument(manager != null,
+                "provider \"" + request.getProvider() + "\" does not exist");
+
+        manager.registerLocationRequest(request, identity, permissionLevel, listener);
     }
 
     @Override
@@ -1976,248 +582,154 @@
             String packageName, String attributionTag) {
         CallerIdentity identity = CallerIdentity.fromBinder(mContext, packageName, attributionTag,
                 AppOpsManager.toReceiverId(pendingIntent));
-        int permissionLevel = LocationPermissions.getCallingOrSelfPermissionLevel(mContext);
-        LocationPermissions.enforceLocationPermission(Binder.getCallingUid(), permissionLevel,
+        int permissionLevel = LocationPermissions.getPermissionLevel(mContext, identity.getUid(),
+                identity.getPid());
+        LocationPermissions.enforceLocationPermission(identity.getUid(), permissionLevel,
                 PERMISSION_COARSE);
 
+        // clients in the system process must have an attribution tag set
+        Preconditions.checkArgument(identity.getPid() != Process.myPid() || attributionTag != null);
+
+        request = validateAndSanitizeLocationRequest(request, permissionLevel);
+
+        LocationProviderManager manager = getLocationProviderManager(request.getProvider());
+        Preconditions.checkArgument(manager != null,
+                "provider \"" + request.getProvider() + "\" does not exist");
+
+        manager.registerLocationRequest(request, identity, permissionLevel, pendingIntent);
+    }
+
+    private LocationRequest validateAndSanitizeLocationRequest(LocationRequest request,
+            @PermissionLevel int permissionLevel) {
+        Objects.requireNonNull(request.getProvider());
+
         WorkSource workSource = request.getWorkSource();
         if (workSource != null && !workSource.isEmpty()) {
             mContext.enforceCallingOrSelfPermission(
-                    permission.UPDATE_DEVICE_STATS, null);
+                    permission.UPDATE_DEVICE_STATS,
+                    "setting a work source requires " + permission.UPDATE_DEVICE_STATS);
         }
-        boolean hideFromAppOps = request.getHideFromAppOps();
-        if (hideFromAppOps) {
+        if (request.getHideFromAppOps()) {
             mContext.enforceCallingOrSelfPermission(
-                    permission.UPDATE_APP_OPS_STATS, null);
+                    permission.UPDATE_APP_OPS_STATS,
+                    "hiding from app ops requires " + permission.UPDATE_APP_OPS_STATS);
         }
         if (request.isLocationSettingsIgnored()) {
             mContext.enforceCallingOrSelfPermission(
-                    permission.WRITE_SECURE_SETTINGS, null);
-        }
-        boolean callerHasLocationHardwarePermission =
-                mContext.checkCallingPermission(permission.LOCATION_HARDWARE)
-                        == PERMISSION_GRANTED;
-        LocationRequest sanitizedRequest = createSanitizedRequest(request,
-                callerHasLocationHardwarePermission,
-                permissionLevel);
-
-        mLocationUsageLogger.logLocationApiUsage(
-                LocationStatsEnums.USAGE_STARTED,
-                LocationStatsEnums.API_REQUEST_LOCATION_UPDATES,
-                packageName, request, true, false,
-                /* geofence= */ null,
-                mAppForegroundHelper.isAppForeground(identity.getUid()));
-
-        synchronized (mLock) {
-            Receiver receiver = getReceiverLocked(Objects.requireNonNull(pendingIntent), identity,
-                    workSource, hideFromAppOps);
-            if (receiver != null) {
-                requestLocationUpdatesLocked(sanitizedRequest, receiver);
-            }
-        }
-    }
-
-    @GuardedBy("mLock")
-    private void requestLocationUpdatesLocked(LocationRequest request, Receiver receiver) {
-        String provider = request.getProvider();
-
-        LocationProviderManager manager = getLocationProviderManager(provider);
-        if (manager == null) {
-            throw new IllegalArgumentException("provider doesn't exist: " + provider);
+                    permission.WRITE_SECURE_SETTINGS,
+                    "ignoring location settings requires " + permission.WRITE_SECURE_SETTINGS);
         }
 
-        UpdateRecord record = new UpdateRecord(provider, request, receiver);
-
-        UpdateRecord oldRecord = receiver.mUpdateRecords.put(provider, record);
-        if (oldRecord != null) {
-            oldRecord.disposeLocked(false);
+        LocationRequest sanitized = new LocationRequest(request);
+        if (mContext.checkCallingPermission(permission.LOCATION_HARDWARE) != PERMISSION_GRANTED) {
+            sanitized.setLowPowerMode(false);
         }
-
-        long identity = Binder.clearCallingIdentity();
-        try {
-            int userId = UserHandle.getUserId(receiver.mCallerIdentity.getUid());
-            if (!manager.isEnabled(userId) && !isSettingsExempt(record)) {
-                // Notify the listener that updates are currently disabled - but only if the request
-                // does not ignore location settings
-                receiver.callProviderEnabledLocked(provider, false, request);
+        if (permissionLevel < PERMISSION_FINE) {
+            switch (sanitized.getQuality()) {
+                case LocationRequest.ACCURACY_FINE:
+                    sanitized.setQuality(LocationRequest.ACCURACY_BLOCK);
+                    break;
+                case LocationRequest.POWER_HIGH:
+                    sanitized.setQuality(LocationRequest.POWER_LOW);
+                    break;
             }
 
-            applyRequirementsLocked(provider);
-
-            // Update the monitoring here just in case multiple location requests were added to the
-            // same receiver (this request may be high power and the initial might not have been).
-            receiver.updateMonitoring(true);
-        } finally {
-            Binder.restoreCallingIdentity(identity);
+            if (sanitized.getInterval() < FASTEST_COARSE_INTERVAL_MS) {
+                sanitized.setInterval(FASTEST_COARSE_INTERVAL_MS);
+            }
+            if (sanitized.getFastestInterval() < FASTEST_COARSE_INTERVAL_MS) {
+                sanitized.setFastestInterval(FASTEST_COARSE_INTERVAL_MS);
+            }
         }
+        if (sanitized.getFastestInterval() > sanitized.getInterval()) {
+            sanitized.setFastestInterval(request.getInterval());
+        }
+        if (sanitized.getWorkSource() != null) {
+            if (sanitized.getWorkSource().isEmpty()) {
+                sanitized.setWorkSource(null);
+            } else if (sanitized.getWorkSource().getPackageName(0) == null) {
+                Log.w(TAG, "received (and ignoring) illegal worksource with no package name");
+                sanitized.setWorkSource(null);
+            } else {
+                List<WorkChain> workChains = sanitized.getWorkSource().getWorkChains();
+                if (workChains != null && !workChains.isEmpty() && workChains.get(
+                        0).getAttributionTag() == null) {
+                    Log.w(TAG,
+                            "received (and ignoring) illegal worksource with no attribution tag");
+                    sanitized.setWorkSource(null);
+                }
+            }
+        }
+
+        return sanitized;
     }
 
     @Override
     public void unregisterLocationListener(ILocationListener listener) {
-        synchronized (mLock) {
-            Receiver receiver = getReceiverLocked(Objects.requireNonNull(listener), null, null,
-                    false);
-            if (receiver != null) {
-                removeUpdatesLocked(receiver);
-            }
+        for (LocationProviderManager manager : mProviderManagers) {
+            manager.unregisterLocationRequest(listener);
         }
     }
 
     @Override
     public void unregisterLocationPendingIntent(PendingIntent pendingIntent) {
-        synchronized (mLock) {
-            Receiver receiver = getReceiverLocked(Objects.requireNonNull(pendingIntent), null, null,
-                    false);
-            if (receiver != null) {
-                removeUpdatesLocked(receiver);
-            }
-        }
-    }
-
-    @GuardedBy("mLock")
-    private void removeUpdatesLocked(Receiver receiver) {
-        if (D) Log.i(TAG, "remove " + Integer.toHexString(System.identityHashCode(receiver)));
-
-        if (mReceivers.remove(receiver.mKey) != null && receiver.isListener()) {
-            receiver.unlinkFromListenerDeathNotificationLocked(
-                    receiver.getListener().asBinder());
-            receiver.clearPendingBroadcastsLocked();
-        }
-
-        receiver.updateMonitoring(false);
-
-        // Record which providers were associated with this listener
-        HashSet<String> providers = new HashSet<>();
-        HashMap<String, UpdateRecord> oldRecords = receiver.mUpdateRecords;
-        if (oldRecords != null) {
-            // Call dispose() on the obsolete update records.
-            for (UpdateRecord record : oldRecords.values()) {
-                // Update statistics for historical location requests by package/provider
-                record.disposeLocked(false);
-            }
-            // Accumulate providers
-            providers.addAll(oldRecords.keySet());
-        }
-
-        // update provider
-        for (String provider : providers) {
-            applyRequirementsLocked(provider);
+        for (LocationProviderManager manager : mProviderManagers) {
+            manager.unregisterLocationRequest(pendingIntent);
         }
     }
 
     @Override
     public Location getLastLocation(LocationRequest request, String packageName,
             String attributionTag) {
-        // unsafe is ok because app ops will verify the package name
-        CallerIdentity identity = CallerIdentity.fromBinderUnsafe(packageName,   attributionTag);
-        int permissionLevel = LocationPermissions.getCallingOrSelfPermissionLevel(mContext);
-        LocationPermissions.enforceLocationPermission(Binder.getCallingUid(), permissionLevel,
+        CallerIdentity identity = CallerIdentity.fromBinder(mContext, packageName, attributionTag);
+        int permissionLevel = LocationPermissions.getPermissionLevel(mContext, identity.getUid(),
+                identity.getPid());
+        LocationPermissions.enforceLocationPermission(identity.getUid(), permissionLevel,
                 PERMISSION_COARSE);
 
-        if (mSettingsHelper.isLocationPackageBlacklisted(identity.getUserId(),
-                identity.getPackageName())) {
-            return null;
-        }
-        if (!mUserInfoHelper.isCurrentUserId(identity.getUserId())) {
+        // clients in the system process must have an attribution tag set
+        Preconditions.checkArgument(identity.getPid() != Process.myPid() || attributionTag != null);
+
+        request = validateAndSanitizeLocationRequest(request, permissionLevel);
+
+        LocationProviderManager manager = getLocationProviderManager(request.getProvider());
+        if (manager == null) {
             return null;
         }
 
-        synchronized (mLock) {
-            LocationProviderManager manager = getLocationProviderManager(request.getProvider());
-            if (manager == null) {
-                return null;
-            }
-            if (!manager.isEnabled(identity.getUserId()) && !request.isLocationSettingsIgnored()) {
-                return null;
-            }
+        Location location = manager.getLastLocation(request, identity, permissionLevel);
 
-            // appops check should always be right before delivery
-            if (!mAppOpsHelper.noteOpNoThrow(LocationPermissions.asAppOp(permissionLevel),
-                    identity)) {
-                return null;
-            }
-
-            Location location = manager.getLastLocation(identity.getUserId(), permissionLevel);
-
-            // make a defensive copy - the client could be in the same process as us
-            return location != null ? new Location(location) : null;
+        // lastly - note app ops
+        if (!mInjector.getAppOpsHelper().noteOpNoThrow(LocationPermissions.asAppOp(permissionLevel),
+                identity)) {
+            return null;
         }
+
+        return location;
     }
 
     @Override
     public void getCurrentLocation(LocationRequest request,
-            ICancellationSignal remoteCancellationSignal, ILocationCallback callback,
+            ICancellationSignal cancellationTransport, ILocationCallback consumer,
             String packageName, String attributionTag, String listenerId) {
-        // unsafe is ok because app ops will verify the package name
-        CallerIdentity identity = CallerIdentity.fromBinderUnsafe(packageName, attributionTag,
+        CallerIdentity identity = CallerIdentity.fromBinder(mContext, packageName, attributionTag,
                 listenerId);
-        int permissionLevel = LocationPermissions.getCallingOrSelfPermissionLevel(mContext);
-        LocationPermissions.enforceLocationPermission(Binder.getCallingUid(), permissionLevel,
+        int permissionLevel = LocationPermissions.getPermissionLevel(mContext, identity.getUid(),
+                identity.getPid());
+        LocationPermissions.enforceLocationPermission(identity.getUid(), permissionLevel,
                 PERMISSION_COARSE);
 
-        request = createSanitizedRequest(request, false, permissionLevel);
-        request.setNumUpdates(1);
-        if (request.getExpireIn() > GET_CURRENT_LOCATION_MAX_TIMEOUT_MS) {
-            request.setExpireIn(GET_CURRENT_LOCATION_MAX_TIMEOUT_MS);
-        }
+        // clients in the system process must have an attribution tag set
+        Preconditions.checkState(identity.getPid() != Process.myPid() || attributionTag != null);
 
-        GetCurrentLocationTransport transport = new GetCurrentLocationTransport(callback);
+        request = validateAndSanitizeLocationRequest(request, permissionLevel);
 
-        if (mSettingsHelper.isLocationPackageBlacklisted(identity.getUserId(),
-                identity.getPackageName())) {
-            transport.deliverResult(null);
-            return;
-        }
-        if (!mUserInfoHelper.isCurrentUserId(identity.getUserId())) {
-            transport.deliverResult(null);
-            return;
-        }
+        LocationProviderManager manager = getLocationProviderManager(request.getProvider());
+        Preconditions.checkArgument(manager != null,
+                "provider \"" + request.getProvider() + "\" does not exist");
 
-        Location lastLocation;
-        synchronized (mLock) {
-            LocationProviderManager manager = getLocationProviderManager(request.getProvider());
-            if (manager == null) {
-                transport.deliverResult(null);
-                return;
-            }
-            if (!manager.isEnabled(identity.getUserId()) && !request.isLocationSettingsIgnored()) {
-                transport.deliverResult(null);
-                return;
-            }
-
-            lastLocation = manager.getLastLocation(identity.getUserId(), permissionLevel);
-        }
-
-        if (lastLocation != null) {
-            long locationAgeMs = NANOSECONDS.toMillis(
-                    SystemClock.elapsedRealtimeNanos() - lastLocation.getElapsedRealtimeNanos());
-
-            if (locationAgeMs < MAX_CURRENT_LOCATION_AGE_MS) {
-                // appops check should always be right before delivery
-                if (mAppOpsHelper.noteOpNoThrow(LocationPermissions.asAppOp(permissionLevel),
-                        identity)) {
-                    transport.deliverResult(lastLocation);
-                } else {
-                    transport.deliverResult(null);
-                }
-                return;
-            }
-
-            if (!mAppForegroundHelper.isAppForeground(Binder.getCallingUid())) {
-                if (locationAgeMs < mSettingsHelper.getBackgroundThrottleIntervalMs()) {
-                    // not allowed to request new locations, so we can't return anything
-                    transport.deliverResult(null);
-                    return;
-                }
-            }
-        }
-
-        registerLocationListener(request, transport, packageName, attributionTag, listenerId);
-        CancellationSignal cancellationSignal = CancellationSignal.fromTransport(
-                remoteCancellationSignal);
-        if (cancellationSignal != null) {
-            cancellationSignal.setOnCancelListener(() -> unregisterLocationListener(transport));
-        }
+        manager.getCurrentLocation(request, identity, permissionLevel, cancellationTransport,
+                consumer);
     }
 
     @Override
@@ -2228,8 +740,13 @@
                 return null;
             }
 
-            Location location = gpsManager.getLastLocation(UserHandle.getCallingUserId(),
-                    PERMISSION_FINE);
+            // create a location request that works in almost all circumstances
+            LocationRequest request = LocationRequest.createFromDeprecatedProvider(GPS_PROVIDER, 0,
+                    0, true);
+
+            // use our own identity rather than the caller
+            CallerIdentity identity = CallerIdentity.fromContext(mContext);
+            Location location = gpsManager.getLastLocation(request, identity, PERMISSION_FINE);
             if (location == null) {
                 return null;
             }
@@ -2248,11 +765,9 @@
         Preconditions.checkArgument(location.isComplete());
 
         int userId = UserHandle.getCallingUserId();
-        synchronized (mLock) {
-            LocationProviderManager manager = getLocationProviderManager(location.getProvider());
-            if (manager != null && manager.isEnabled(userId)) {
-                manager.injectLastLocation(Objects.requireNonNull(location), userId);
-            }
+        LocationProviderManager manager = getLocationProviderManager(location.getProvider());
+        if (manager != null && manager.isEnabled(userId)) {
+            manager.injectLastLocation(Objects.requireNonNull(location), userId);
         }
     }
 
@@ -2357,12 +872,11 @@
                     Objects.requireNonNull(command), extras);
         }
 
-        mLocationUsageLogger.logLocationApiUsage(
+        mInjector.getLocationUsageLogger().logLocationApiUsage(
                 LocationStatsEnums.USAGE_STARTED,
                 LocationStatsEnums.API_SEND_EXTRA_COMMAND,
                 provider);
-
-        mLocationUsageLogger.logLocationApiUsage(
+        mInjector.getLocationUsageLogger().logLocationApiUsage(
                 LocationStatsEnums.USAGE_ENDED,
                 LocationStatsEnums.API_SEND_EXTRA_COMMAND,
                 provider);
@@ -2385,7 +899,7 @@
             if (provider != null && !provider.equals(manager.getName())) {
                 continue;
             }
-            CallerIdentity identity = manager.getProviderIdentity();
+            CallerIdentity identity = manager.getIdentity();
             if (identity == null) {
                 continue;
             }
@@ -2406,7 +920,7 @@
             return Collections.emptyList();
         }
 
-        CallerIdentity identity = manager.getProviderIdentity();
+        CallerIdentity identity = manager.getIdentity();
         if (identity == null) {
             return Collections.emptyList();
         }
@@ -2454,164 +968,26 @@
 
         mContext.enforceCallingOrSelfPermission(permission.WRITE_SECURE_SETTINGS, null);
 
-        invalidateLocalLocationEnabledCaches();
-        mSettingsHelper.setLocationEnabled(enabled, userId);
+        LocationManager.invalidateLocalLocationEnabledCaches();
+        mInjector.getSettingsHelper().setLocationEnabled(enabled, userId);
     }
 
     @Override
     public boolean isLocationEnabledForUser(int userId) {
         userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
                 userId, false, false, "isLocationEnabledForUser", null);
-        return mSettingsHelper.isLocationEnabled(userId);
+        return mInjector.getSettingsHelper().isLocationEnabled(userId);
     }
 
     @Override
     public boolean isProviderEnabledForUser(String provider, int userId) {
-        // Fused provider is accessed indirectly via criteria rather than the provider-based APIs,
+        // fused provider is accessed indirectly via criteria rather than the provider-based APIs,
         // so we discourage its use
         if (FUSED_PROVIDER.equals(provider)) return false;
 
         return mLocalService.isProviderEnabledForUser(provider, userId);
     }
 
-    @GuardedBy("mLock")
-    private static boolean shouldBroadcastSafeLocked(
-            Location loc, Location lastLoc, UpdateRecord record, long now) {
-        // Always broadcast the first update
-        if (lastLoc == null) {
-            return true;
-        }
-
-        // Check whether sufficient time has passed
-        long minTime = record.mRealRequest.getFastestInterval();
-        long deltaMs = NANOSECONDS.toMillis(
-                loc.getElapsedRealtimeNanos() - lastLoc.getElapsedRealtimeNanos());
-        if (deltaMs < minTime - MAX_PROVIDER_SCHEDULING_JITTER_MS) {
-            return false;
-        }
-
-        // Check whether sufficient distance has been traveled
-        double minDistance = record.mRealRequest.getSmallestDisplacement();
-        if (minDistance > 0.0) {
-            if (loc.distanceTo(lastLoc) <= minDistance) {
-                return false;
-            }
-        }
-
-        // Check whether sufficient number of udpates is left
-        if (record.mRealRequest.getNumUpdates() <= 0) {
-            return false;
-        }
-
-        // Check whether the expiry date has passed
-        return record.mExpirationRealtimeMs >= now;
-    }
-
-    @GuardedBy("mLock")
-    private void handleLocationChangedLocked(LocationProviderManager manager, Location fineLocation,
-            Location coarseLocation) {
-        if (!mProviderManagers.contains(manager)) {
-            Log.w(TAG, "received location from unknown provider: " + manager.getName());
-            return;
-        }
-
-        // notify passive provider
-        if (manager != mPassiveManager) {
-            mPassiveManager.updateLocation(fineLocation);
-        }
-
-        long now = SystemClock.elapsedRealtime();
-
-        ArrayList<UpdateRecord> records = mRecordsByProvider.get(manager.getName());
-        if (records == null || records.size() == 0) return;
-
-        ArrayList<Receiver> deadReceivers = null;
-        ArrayList<UpdateRecord> deadUpdateRecords = null;
-
-        // Broadcast location to all listeners
-        for (UpdateRecord r : records) {
-            Receiver receiver = r.mReceiver;
-            CallerIdentity identity = receiver.mCallerIdentity;
-            boolean receiverDead = false;
-
-
-            if (!manager.isEnabled(identity.getUserId()) && !isSettingsExempt(r)) {
-                continue;
-            }
-
-            if (!mUserInfoHelper.isCurrentUserId(identity.getUserId())
-                    && !mLocalService.isProvider(null, identity)) {
-                continue;
-            }
-
-            if (mSettingsHelper.isLocationPackageBlacklisted(identity.getUserId(),
-                    identity.getPackageName())) {
-                continue;
-            }
-
-            int permissionLevel = r.mRequest.isCoarse() ? PERMISSION_COARSE : PERMISSION_FINE;
-
-            Location location;
-            switch (permissionLevel) {
-                case PERMISSION_COARSE:
-                    location = coarseLocation;
-                    break;
-                case PERMISSION_FINE:
-                    location = fineLocation;
-                    break;
-                default:
-                    throw new AssertionError();
-            }
-
-            if (shouldBroadcastSafeLocked(location, r.mLastFixBroadcast, r, now)) {
-                r.mLastFixBroadcast = location;
-
-                // appops check should always be right before delivery
-                if (!mAppOpsHelper.noteOpNoThrow(LocationPermissions.asAppOp(permissionLevel),
-                        receiver.mCallerIdentity)) {
-                    continue;
-                }
-
-                if (!receiver.callLocationChangedLocked(location, r.mRequest)) {
-                    receiverDead = true;
-                }
-                r.mRealRequest.decrementNumUpdates();
-            }
-
-            // track expired records
-            if (r.mRealRequest.getNumUpdates() <= 0 || r.mExpirationRealtimeMs < now) {
-                if (deadUpdateRecords == null) {
-                    deadUpdateRecords = new ArrayList<>();
-                }
-                deadUpdateRecords.add(r);
-            }
-            // track dead receivers
-            if (receiverDead) {
-                if (deadReceivers == null) {
-                    deadReceivers = new ArrayList<>();
-                }
-                if (!deadReceivers.contains(receiver)) {
-                    deadReceivers.add(receiver);
-                }
-            }
-        }
-
-        // remove dead records and receivers outside the loop
-        if (deadReceivers != null) {
-            for (Receiver receiver : deadReceivers) {
-                removeUpdatesLocked(receiver);
-            }
-        }
-        if (deadUpdateRecords != null) {
-            for (UpdateRecord r : deadUpdateRecords) {
-                r.disposeLocked(true);
-            }
-            applyRequirementsLocked(manager);
-        }
-    }
-
-    // Geocoder
-
     @Override
     public boolean geocoderIsPresent() {
         return mGeocodeProvider != null;
@@ -2637,7 +1013,6 @@
             double lowerLeftLatitude, double lowerLeftLongitude,
             double upperRightLatitude, double upperRightLongitude, int maxResults,
             GeocoderParams params, IGeocodeListener listener) {
-
         if (mGeocodeProvider != null) {
             mGeocodeProvider.getFromLocationName(locationName, lowerLeftLatitude,
                     lowerLeftLongitude, upperRightLatitude, upperRightLongitude,
@@ -2651,35 +1026,24 @@
         }
     }
 
-    // Mock Providers
-
     @Override
     public void addTestProvider(String provider, ProviderProperties properties,
             String packageName, String attributionTag) {
         // unsafe is ok because app ops will verify the package name
-        CallerIdentity identity = CallerIdentity.fromBinderUnsafe(packageName,
-                attributionTag);
-        if (!mAppOpsHelper.noteOp(OP_MOCK_LOCATION, identity)) {
+        CallerIdentity identity = CallerIdentity.fromBinderUnsafe(packageName, attributionTag);
+        if (!mInjector.getAppOpsHelper().noteOp(AppOpsManager.OP_MOCK_LOCATION, identity)) {
             return;
         }
 
-        synchronized (mLock) {
-            LocationProviderManager manager = getLocationProviderManager(provider);
-            if (manager == null) {
-                manager = new LocationProviderManager(provider);
-                mProviderManagers.add(manager);
-            }
-
-            manager.setMockProvider(new MockProvider(properties, identity));
-        }
+        getOrAddLocationProviderManager(provider).setMockProvider(
+                new MockProvider(properties, identity));
     }
 
     @Override
     public void removeTestProvider(String provider, String packageName, String attributionTag) {
         // unsafe is ok because app ops will verify the package name
-        CallerIdentity identity = CallerIdentity.fromBinderUnsafe(packageName,
-                attributionTag);
-        if (!mAppOpsHelper.noteOp(OP_MOCK_LOCATION, identity)) {
+        CallerIdentity identity = CallerIdentity.fromBinderUnsafe(packageName, attributionTag);
+        if (!mInjector.getAppOpsHelper().noteOp(AppOpsManager.OP_MOCK_LOCATION, identity)) {
             return;
         }
 
@@ -2691,7 +1055,7 @@
 
             manager.setMockProvider(null);
             if (!manager.hasProvider()) {
-                mProviderManagers.remove(manager);
+                removeLocationProviderManager(manager);
             }
         }
     }
@@ -2702,7 +1066,7 @@
         // unsafe is ok because app ops will verify the package name
         CallerIdentity identity = CallerIdentity.fromBinderUnsafe(packageName,
                 attributionTag);
-        if (!mAppOpsHelper.noteOp(OP_MOCK_LOCATION, identity)) {
+        if (!mInjector.getAppOpsHelper().noteOp(AppOpsManager.OP_MOCK_LOCATION, identity)) {
             return;
         }
 
@@ -2723,7 +1087,7 @@
         // unsafe is ok because app ops will verify the package name
         CallerIdentity identity = CallerIdentity.fromBinderUnsafe(packageName,
                 attributionTag);
-        if (!mAppOpsHelper.noteOp(OP_MOCK_LOCATION, identity)) {
+        if (!mInjector.getAppOpsHelper().noteOp(AppOpsManager.OP_MOCK_LOCATION, identity)) {
             return;
         }
 
@@ -2777,57 +1141,32 @@
 
         ipw.println("User Info:");
         ipw.increaseIndent();
-        mUserInfoHelper.dump(fd, ipw, args);
+        mInjector.getUserInfoHelper().dump(fd, ipw, args);
         ipw.decreaseIndent();
 
         ipw.println("Location Settings:");
         ipw.increaseIndent();
-        mSettingsHelper.dump(fd, ipw, args);
+        mInjector.getSettingsHelper().dump(fd, ipw, args);
         ipw.decreaseIndent();
 
+        ipw.println("Historical Records by Provider:");
+        ipw.increaseIndent();
+        TreeMap<PackageProviderKey, PackageStatistics> sorted = new TreeMap<>(
+                mInjector.getLocationRequestStatistics().statistics);
+        for (Map.Entry<PackageProviderKey, PackageStatistics> entry
+                : sorted.entrySet()) {
+            ipw.println(entry.getKey() + ": " + entry.getValue());
+        }
+        ipw.decreaseIndent();
+
+        mInjector.getLocationRequestStatistics().history.dump(ipw);
+
         synchronized (mLock) {
-            ipw.println("Battery Saver Location Mode: "
-                    + locationPowerSaveModeToString(mBatterySaverMode));
-
-            if (dumpFilter == null) {
-                ipw.println("Location Listeners:");
-                ipw.increaseIndent();
-                for (Receiver receiver : mReceivers.values()) {
-                    ipw.println(receiver);
-                }
-                ipw.decreaseIndent();
-
-                ipw.println("Active Records by Provider:");
-                ipw.increaseIndent();
-                for (Map.Entry<String, ArrayList<UpdateRecord>> entry :
-                        mRecordsByProvider.entrySet()) {
-                    ipw.println(entry.getKey() + ":");
-                    ipw.increaseIndent();
-                    for (UpdateRecord record : entry.getValue()) {
-                        ipw.println(record);
-                    }
-                    ipw.decreaseIndent();
-                }
-                ipw.decreaseIndent();
-
-            ipw.println("Historical Records by Provider:");
-            ipw.increaseIndent();
-            TreeMap<PackageProviderKey, PackageStatistics> sorted = new TreeMap<>(
-                    mRequestStatistics.statistics);
-            for (Map.Entry<PackageProviderKey, PackageStatistics> entry
-                    : sorted.entrySet()) {
-                ipw.println(entry.getKey() + ": " + entry.getValue());
-            }
-            ipw.decreaseIndent();
-
-                mRequestStatistics.history.dump(ipw);
-
-                if (mExtraLocationControllerPackage != null) {
-                    ipw.println(
-                            "Location Controller Extra Package: " + mExtraLocationControllerPackage
-                                    + (mExtraLocationControllerPackageEnabled ? " [enabled]"
-                                    : "[disabled]"));
-                }
+            if (mExtraLocationControllerPackage != null) {
+                ipw.println(
+                        "Location Controller Extra Package: " + mExtraLocationControllerPackage
+                                + (mExtraLocationControllerPackageEnabled ? " [enabled]"
+                                : "[disabled]"));
             }
         }
 
@@ -2857,47 +1196,6 @@
         }
     }
 
-    private class GetCurrentLocationTransport extends ILocationListener.Stub {
-
-        private final Executor mExecutor;
-        private final ILocationCallback mCallback;
-
-        GetCurrentLocationTransport(ILocationCallback callback) {
-            mExecutor = FgThread.getExecutor();
-            mCallback = callback;
-        }
-
-        @Override
-        public void onLocationChanged(Location location, IRemoteCallback onCompleteCallback) {
-            mExecutor.execute(() -> {
-                deliverResult(location);
-                try {
-                    onCompleteCallback.sendResult(null);
-                } catch (RemoteException e) {
-                    e.rethrowFromSystemServer();
-                }
-            });
-            unregisterLocationListener(this);
-        }
-
-        @Override
-        public void onProviderEnabledChanged(String provider, boolean enabled)
-                throws RemoteException {
-            if (!enabled) {
-                deliverResult(null);
-                unregisterLocationListener(this);
-            }
-        }
-
-        public void deliverResult(@Nullable Location location) {
-            try {
-                mCallback.onLocation(location);
-            } catch (RemoteException e) {
-                // do nothing
-            }
-        }
-    }
-
     private class LocalService extends LocationManagerInternal {
 
         LocalService() {}
@@ -2916,17 +1214,28 @@
         }
 
         @Override
-        public boolean isProvider(String provider, CallerIdentity identity) {
-            for (LocationProviderManager manager : mProviderManagers) {
-                if (provider != null && !provider.equals(manager.getName())) {
-                    continue;
-                }
-                if (identity.equals(manager.getProviderIdentity())) {
-                    return true;
-                }
-            }
+        public void addProviderEnabledListener(String provider, ProviderEnabledListener listener) {
+            LocationProviderManager manager = Objects.requireNonNull(
+                    getLocationProviderManager(provider));
+            manager.addEnabledListener(listener);
+        }
 
-            return false;
+        @Override
+        public void removeProviderEnabledListener(String provider,
+                ProviderEnabledListener listener) {
+            LocationProviderManager manager = Objects.requireNonNull(
+                    getLocationProviderManager(provider));
+            manager.removeEnabledListener(listener);
+        }
+
+        @Override
+        public boolean isProvider(String provider, CallerIdentity identity) {
+            LocationProviderManager manager = getLocationProviderManager(provider);
+            if (manager == null) {
+                return false;
+            } else {
+                return identity.equals(manager.getIdentity());
+            }
         }
 
         @Override
@@ -2935,6 +1244,13 @@
                 mGnssManagerService.sendNiResponse(notifId, userResponse);
             }
         }
+
+        @Override
+        public void reportGnssBatchLocations(List<Location> locations) {
+            if (mGnssManagerService != null) {
+                mGnssManagerService.onReportLocation(locations);
+            }
+        }
     }
 
     private static class SystemInjector implements Injector {
diff --git a/services/core/java/com/android/server/location/LocationManagerServiceUtils.java b/services/core/java/com/android/server/location/LocationManagerServiceUtils.java
deleted file mode 100644
index b9d86c8..0000000
--- a/services/core/java/com/android/server/location/LocationManagerServiceUtils.java
+++ /dev/null
@@ -1,73 +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.server.location;
-
-import android.annotation.NonNull;
-import android.location.util.identity.CallerIdentity;
-import android.os.IBinder;
-import android.os.RemoteException;
-
-import java.util.NoSuchElementException;
-
-/**
- * Shared utilities for LocationManagerService and GnssManager.
- */
-public class LocationManagerServiceUtils {
-
-    /**
-     * Skeleton class of listener that can be linked to a binder.
-     */
-    public abstract static class LinkedListenerBase implements IBinder.DeathRecipient {
-        protected final CallerIdentity mCallerIdentity;
-
-        LinkedListenerBase(@NonNull CallerIdentity callerIdentity) {
-            mCallerIdentity = callerIdentity;
-        }
-
-        @Override
-        public String toString() {
-            return mCallerIdentity.toString();
-        }
-
-        public CallerIdentity getCallerIdentity() {
-            return mCallerIdentity;
-        }
-
-        /**
-         * Link listener (i.e. callback) to a binder, so that it will be called upon binder's death.
-         */
-        public boolean linkToListenerDeathNotificationLocked(IBinder binder) {
-            try {
-                binder.linkToDeath(this, 0 /* flags */);
-                return true;
-            } catch (RemoteException e) {
-                return false;
-            }
-        }
-
-        /**
-         * Unlink death listener (i.e. callback) from binder.
-         */
-        public void unlinkFromListenerDeathNotificationLocked(IBinder binder) {
-            try {
-                binder.unlinkToDeath(this, 0 /* flags */);
-            } catch (NoSuchElementException e) {
-                // ignore
-            }
-        }
-    }
-}
diff --git a/services/core/java/com/android/server/location/LocationProviderManager.java b/services/core/java/com/android/server/location/LocationProviderManager.java
new file mode 100644
index 0000000..d4f8c7e
--- /dev/null
+++ b/services/core/java/com/android/server/location/LocationProviderManager.java
@@ -0,0 +1,1951 @@
+/*
+ * 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;
+
+import static android.app.AlarmManager.ELAPSED_REALTIME_WAKEUP;
+import static android.app.AlarmManager.WINDOW_EXACT;
+import static android.location.LocationManager.FUSED_PROVIDER;
+import static android.location.LocationManager.GPS_PROVIDER;
+import static android.location.LocationManager.KEY_LOCATION_CHANGED;
+import static android.location.LocationManager.KEY_PROVIDER_ENABLED;
+import static android.location.LocationManager.PASSIVE_PROVIDER;
+import static android.os.IPowerManager.LOCATION_MODE_NO_CHANGE;
+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_THROTTLE_REQUESTS_WHEN_SCREEN_OFF;
+
+import static com.android.server.location.LocationManagerService.D;
+import static com.android.server.location.LocationManagerService.TAG;
+import static com.android.server.location.LocationPermissions.PERMISSION_COARSE;
+import static com.android.server.location.LocationPermissions.PERMISSION_FINE;
+import static com.android.server.location.LocationPermissions.PERMISSION_NONE;
+
+import static java.util.concurrent.TimeUnit.NANOSECONDS;
+
+import android.annotation.Nullable;
+import android.app.AlarmManager;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.location.Criteria;
+import android.location.ILocationCallback;
+import android.location.ILocationListener;
+import android.location.Location;
+import android.location.LocationManager;
+import android.location.LocationManagerInternal;
+import android.location.LocationManagerInternal.ProviderEnabledListener;
+import android.location.LocationRequest;
+import android.location.util.identity.CallerIdentity;
+import android.os.Binder;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.CancellationSignal;
+import android.os.IBinder;
+import android.os.ICancellationSignal;
+import android.os.IRemoteCallback;
+import android.os.PowerManager;
+import android.os.PowerManager.LocationPowerSaveMode;
+import android.os.Process;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.os.UserHandle;
+import android.os.WorkSource;
+import android.stats.location.LocationStatsEnums;
+import android.text.TextUtils;
+import android.util.ArraySet;
+import android.util.EventLog;
+import android.util.IndentingPrintWriter;
+import android.util.Log;
+import android.util.SparseArray;
+import android.util.SparseBooleanArray;
+import android.util.TimeUtils;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.location.ProviderProperties;
+import com.android.internal.location.ProviderRequest;
+import com.android.internal.util.Preconditions;
+import com.android.server.FgThread;
+import com.android.server.LocalServices;
+import com.android.server.PendingIntentUtils;
+import com.android.server.location.LocationPermissions.PermissionLevel;
+import com.android.server.location.listeners.ListenerMultiplexer;
+import com.android.server.location.listeners.RemovableListenerRegistration;
+import com.android.server.location.util.AppForegroundHelper;
+import com.android.server.location.util.AppForegroundHelper.AppForegroundListener;
+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.LocationPermissionsHelper.LocationPermissionsListener;
+import com.android.server.location.util.LocationPowerSaveModeHelper;
+import com.android.server.location.util.LocationPowerSaveModeHelper.LocationPowerSaveModeChangedListener;
+import com.android.server.location.util.LocationUsageLogger;
+import com.android.server.location.util.ScreenInteractiveHelper;
+import com.android.server.location.util.ScreenInteractiveHelper.ScreenInteractiveChangedListener;
+import com.android.server.location.util.SettingsHelper;
+import com.android.server.location.util.SettingsHelper.GlobalSettingChangedListener;
+import com.android.server.location.util.SettingsHelper.UserSettingChangedListener;
+import com.android.server.location.util.UserInfoHelper;
+import com.android.server.location.util.UserInfoHelper.UserListener;
+
+import java.io.FileDescriptor;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Objects;
+
+class LocationProviderManager extends
+        ListenerMultiplexer<Object, LocationRequest, LocationProviderManager.LocationTransport,
+                LocationProviderManager.Registration, ProviderRequest> implements
+        AbstractLocationProvider.Listener {
+
+    // fastest interval at which clients may receive coarse locations
+    public static final long FASTEST_COARSE_INTERVAL_MS = 10 * 60 * 1000;
+
+    private static final String WAKELOCK_TAG = "*location*";
+    private static final long WAKELOCK_TIMEOUT_MS = 30 * 1000;
+
+    // maximum interval to be considered "high power" request
+    private static final long MAX_HIGH_POWER_INTERVAL_MS = 5 * 60 * 1000;
+
+    // maximum age of a location before it is no longer considered "current"
+    private static final long MAX_CURRENT_LOCATION_AGE_MS = 10 * 1000;
+
+    // max timeout allowed for getting the current location
+    private static final long GET_CURRENT_LOCATION_MAX_TIMEOUT_MS = 30 * 1000;
+
+    // maximum jitter allowed for fastest interval evaluation
+    private static final int MAX_FASTEST_INTERVAL_JITTER_MS = 100;
+
+    protected interface LocationTransport {
+
+        void deliverOnLocationChanged(Location location, @Nullable Runnable onCompleteCallback)
+                throws Exception;
+    }
+
+    protected interface ProviderTransport {
+
+        void deliverOnProviderEnabledChanged(String provider, boolean enabled) throws Exception;
+    }
+
+    protected static final class LocationListenerTransport implements LocationTransport,
+            ProviderTransport {
+
+        private final ILocationListener mListener;
+
+        protected LocationListenerTransport(ILocationListener listener) {
+            mListener = Objects.requireNonNull(listener);
+        }
+
+        @Override
+        public void deliverOnLocationChanged(Location location,
+                @Nullable Runnable onCompleteCallback)
+                throws RemoteException {
+            mListener.onLocationChanged(location,
+                    onCompleteCallback == null ? null : new IRemoteCallback.Stub() {
+                        @Override
+                        public void sendResult(Bundle data) {
+                            onCompleteCallback.run();
+                        }
+                    });
+        }
+
+        @Override
+        public void deliverOnProviderEnabledChanged(String provider, boolean enabled)
+                throws RemoteException {
+            mListener.onProviderEnabledChanged(provider, enabled);
+        }
+    }
+
+    protected static final class LocationPendingIntentTransport implements LocationTransport,
+            ProviderTransport {
+
+        private final Context mContext;
+        private final PendingIntent mPendingIntent;
+
+        public LocationPendingIntentTransport(Context context, PendingIntent pendingIntent) {
+            mContext = context;
+            mPendingIntent = pendingIntent;
+        }
+
+        @Override
+        public void deliverOnLocationChanged(Location location,
+                @Nullable Runnable onCompleteCallback)
+                throws PendingIntent.CanceledException {
+            mPendingIntent.send(mContext, 0, new Intent().putExtra(KEY_LOCATION_CHANGED, location),
+                    onCompleteCallback != null ? (pI, i, rC, rD, rE) -> onCompleteCallback.run()
+                            : null, null, null,
+                    PendingIntentUtils.createDontSendToRestrictedAppsBundle(null));
+        }
+
+        @Override
+        public void deliverOnProviderEnabledChanged(String provider, boolean enabled)
+                throws PendingIntent.CanceledException {
+            mPendingIntent.send(mContext, 0, new Intent().putExtra(KEY_PROVIDER_ENABLED, enabled),
+                    null, null, null,
+                    PendingIntentUtils.createDontSendToRestrictedAppsBundle(null));
+        }
+    }
+
+    protected static final class GetCurrentLocationTransport implements LocationTransport {
+
+        private final ILocationCallback mCallback;
+
+        protected GetCurrentLocationTransport(ILocationCallback callback) {
+            mCallback = Objects.requireNonNull(callback);
+        }
+
+        @Override
+        public void deliverOnLocationChanged(Location location,
+                @Nullable Runnable onCompleteCallback)
+                throws RemoteException {
+            // ILocationCallback doesn't currently support completion callbacks
+            Preconditions.checkState(onCompleteCallback == null);
+            mCallback.onLocation(location);
+        }
+    }
+
+    protected abstract class Registration extends
+            RemovableListenerRegistration<LocationRequest, LocationTransport> {
+
+        @PermissionLevel protected final int mPermissionLevel;
+        private final WorkSource mWorkSource;
+
+        // we cache these values because checking/calculating on the fly is more expensive
+        private boolean mPermitted;
+        private boolean mForeground;
+        @Nullable private LocationRequest mProviderLocationRequest;
+        private boolean mIsUsingHighPower;
+
+        protected Registration(LocationRequest request, CallerIdentity identity,
+                LocationTransport transport, @PermissionLevel int permissionLevel) {
+            super(TAG, Objects.requireNonNull(request), identity, transport);
+
+            Preconditions.checkArgument(permissionLevel > PERMISSION_NONE);
+            mPermissionLevel = permissionLevel;
+
+            if (request.getWorkSource() != null && !request.getWorkSource().isEmpty()) {
+                mWorkSource = request.getWorkSource();
+            } else {
+                mWorkSource = identity.addToWorkSource(null);
+            }
+        }
+
+        @GuardedBy("mLock")
+        @Override
+        protected final void onRemovableListenerRegister() {
+            if (Build.IS_DEBUGGABLE) {
+                Preconditions.checkState(Thread.holdsLock(mLock));
+            }
+
+            if (D) {
+                Log.d(TAG, mName + " provider added registration from " + getIdentity() + " -> "
+                        + getRequest());
+            }
+
+            // initialization order is important as there are ordering dependencies
+            mPermitted = mLocationPermissionsHelper.hasLocationPermissions(mPermissionLevel,
+                    getIdentity());
+            mForeground = mAppForegroundHelper.isAppForeground(getIdentity().getUid());
+            mProviderLocationRequest = calculateProviderLocationRequest();
+            mIsUsingHighPower = isUsingHighPower();
+
+            onProviderListenerRegister();
+        }
+
+        @GuardedBy("mLock")
+        @Override
+        protected final void onRemovableListenerUnregister() {
+            if (Build.IS_DEBUGGABLE) {
+                Preconditions.checkState(Thread.holdsLock(mLock));
+            }
+
+            onProviderListenerUnregister();
+
+            if (D) {
+                Log.d(TAG, mName + " provider removed registration from " + getIdentity());
+            }
+        }
+
+        /**
+         * Subclasses may override this instead of {@link #onRemovableListenerRegister()}.
+         */
+        @GuardedBy("mLock")
+        protected void onProviderListenerRegister() {}
+
+        /**
+         * Subclasses may override this instead of {@link #onRemovableListenerUnregister()}.
+         */
+        @GuardedBy("mLock")
+        protected void onProviderListenerUnregister() {}
+
+        @Override
+        protected final ListenerOperation<LocationTransport> onActive() {
+            if (!getRequest().getHideFromAppOps()) {
+                mLocationAttributionHelper.reportLocationStart(getIdentity(), getName(), getKey());
+            }
+            onHighPowerUsageChanged();
+            return null;
+        }
+
+        @Override
+        protected final void onInactive() {
+            onHighPowerUsageChanged();
+            if (!getRequest().getHideFromAppOps()) {
+                mLocationAttributionHelper.reportLocationStop(getIdentity(), getName(), getKey());
+            }
+        }
+
+        @Override
+        public final LocationRequest getRequest() {
+            return Objects.requireNonNull(mProviderLocationRequest);
+        }
+
+        public final boolean isForeground() {
+            return mForeground;
+        }
+
+        public final boolean isPermitted() {
+            return mPermitted;
+        }
+
+        @Override
+        protected final LocationProviderManager getOwner() {
+            return LocationProviderManager.this;
+        }
+
+        protected final WorkSource getWorkSource() {
+            return mWorkSource;
+        }
+
+        @GuardedBy("mLock")
+        private void onHighPowerUsageChanged() {
+            boolean isUsingHighPower = isUsingHighPower();
+            if (isUsingHighPower != mIsUsingHighPower) {
+                mIsUsingHighPower = isUsingHighPower;
+
+                if (!getRequest().getHideFromAppOps()) {
+                    if (mIsUsingHighPower) {
+                        mLocationAttributionHelper.reportHighPowerLocationStart(
+                                getIdentity(), getName(), getKey());
+                    } else {
+                        mLocationAttributionHelper.reportHighPowerLocationStop(
+                                getIdentity(), getName(), getKey());
+                    }
+                }
+            }
+        }
+
+        @GuardedBy("mLock")
+        private boolean isUsingHighPower() {
+            if (Build.IS_DEBUGGABLE) {
+                Preconditions.checkState(Thread.holdsLock(mLock));
+            }
+
+            return isActive()
+                    && getRequest().getInterval() < MAX_HIGH_POWER_INTERVAL_MS
+                    && getProperties().mPowerRequirement == Criteria.POWER_HIGH;
+        }
+
+        @GuardedBy("mLock")
+        final boolean onLocationPermissionsChanged(String packageName) {
+            if (getIdentity().getPackageName().equals(packageName)) {
+                return onLocationPermissionsChanged();
+            }
+
+            return false;
+        }
+
+        @GuardedBy("mLock")
+        final boolean onLocationPermissionsChanged(int uid) {
+            if (getIdentity().getUid() == uid) {
+                return onLocationPermissionsChanged();
+            }
+
+            return false;
+        }
+
+        @GuardedBy("mLock")
+        private boolean onLocationPermissionsChanged() {
+            if (Build.IS_DEBUGGABLE) {
+                Preconditions.checkState(Thread.holdsLock(mLock));
+            }
+
+            boolean permitted = mLocationPermissionsHelper.hasLocationPermissions(mPermissionLevel,
+                    getIdentity());
+            if (permitted != mPermitted) {
+                if (D) {
+                    Log.v(TAG, mName + " provider package " + getIdentity().getPackageName()
+                            + " permitted = " + permitted);
+                }
+
+                mPermitted = permitted;
+                return true;
+            }
+
+            return false;
+        }
+
+        @GuardedBy("mLock")
+        final boolean onForegroundChanged(int uid, boolean foreground) {
+            if (Build.IS_DEBUGGABLE) {
+                Preconditions.checkState(Thread.holdsLock(mLock));
+            }
+
+            if (getIdentity().getUid() == uid && foreground != mForeground) {
+                if (D) {
+                    Log.v(TAG, mName + " provider uid " + uid + " foreground = " + foreground);
+                }
+
+                mForeground = foreground;
+
+                // note that onProviderLocationRequestChanged() is always called
+                return onProviderLocationRequestChanged()
+                        || mLocationPowerSaveModeHelper.getLocationPowerSaveMode()
+                        == LOCATION_MODE_FOREGROUND_ONLY;
+            }
+
+            return false;
+        }
+
+        @GuardedBy("mLock")
+        final boolean onProviderLocationRequestChanged() {
+            if (Build.IS_DEBUGGABLE) {
+                Preconditions.checkState(Thread.holdsLock(mLock));
+            }
+
+            LocationRequest newRequest = calculateProviderLocationRequest();
+            if (!mProviderLocationRequest.equals(newRequest)) {
+                LocationRequest oldRequest = mProviderLocationRequest;
+                mProviderLocationRequest = newRequest;
+                onHighPowerUsageChanged();
+                updateService();
+
+                // if location settings ignored has changed then the active state may have changed
+                return oldRequest.isLocationSettingsIgnored()
+                        != newRequest.isLocationSettingsIgnored();
+            }
+
+            return false;
+        }
+
+        private LocationRequest calculateProviderLocationRequest() {
+            LocationRequest newRequest = new LocationRequest(super.getRequest());
+
+            if (newRequest.isLocationSettingsIgnored()) {
+                // if we are not currently allowed use location settings ignored, disable it
+                if (!mSettingsHelper.getIgnoreSettingsPackageWhitelist().contains(
+                        getIdentity().getPackageName()) && !mLocationManagerInternal.isProvider(
+                        null, getIdentity())) {
+                    newRequest.setLocationSettingsIgnored(false);
+                }
+            }
+
+            if (!newRequest.isLocationSettingsIgnored() && !isThrottlingExempt()) {
+                // throttle in the background
+                if (!mForeground) {
+                    newRequest.setInterval(Math.max(newRequest.getInterval(),
+                            mSettingsHelper.getBackgroundThrottleIntervalMs()));
+                }
+            }
+
+            return newRequest;
+        }
+
+        private boolean isThrottlingExempt() {
+            if (mSettingsHelper.getBackgroundThrottlePackageWhitelist().contains(
+                    getIdentity().getPackageName())) {
+                return true;
+            }
+
+            return mLocationManagerInternal.isProvider(null, getIdentity());
+        }
+
+        @Nullable
+        abstract ListenerOperation<LocationTransport> acceptLocationChange(Location fineLocation);
+
+        @Override
+        public String toString() {
+            StringBuilder builder = new StringBuilder();
+            builder.append(getIdentity());
+
+            ArraySet<String> flags = new ArraySet<>(2);
+            if (!isForeground()) {
+                flags.add("bg");
+            }
+            if (!isPermitted()) {
+                flags.add("na");
+            }
+            if (!flags.isEmpty()) {
+                builder.append(" ").append(flags);
+            }
+
+            if (mPermissionLevel == PERMISSION_COARSE) {
+                builder.append(" (COARSE)");
+            }
+
+            builder.append(" ").append(getRequest());
+            return builder.toString();
+        }
+    }
+
+    protected abstract class LocationRegistration extends Registration implements
+            AlarmManager.OnAlarmListener, ProviderEnabledListener {
+
+        private final PowerManager.WakeLock mWakeLock;
+
+        private volatile ProviderTransport mProviderTransport;
+        @Nullable private Location mLastLocation = null;
+        private int mNumLocationsDelivered = 0;
+        private long mExpirationRealtimeMs = Long.MAX_VALUE;
+
+        protected <TTransport extends LocationTransport & ProviderTransport> LocationRegistration(
+                LocationRequest request, CallerIdentity identity, TTransport transport,
+                @PermissionLevel int permissionLevel) {
+            super(request, identity, transport, permissionLevel);
+            mProviderTransport = transport;
+            mWakeLock = Objects.requireNonNull(mContext.getSystemService(PowerManager.class))
+                    .newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_TAG);
+            mWakeLock.setReferenceCounted(true);
+            mWakeLock.setWorkSource(getWorkSource());
+        }
+
+        @Override
+        protected void onListenerUnregister() {
+            mProviderTransport = null;
+        }
+
+        @GuardedBy("mLock")
+        @Override
+        protected final void onProviderListenerRegister() {
+            mExpirationRealtimeMs = getRequest().getExpirationRealtimeMs(
+                    SystemClock.elapsedRealtime());
+
+            // add alarm for expiration
+            if (mExpirationRealtimeMs < SystemClock.elapsedRealtime()) {
+                remove();
+            } else if (mExpirationRealtimeMs < Long.MAX_VALUE) {
+                AlarmManager alarmManager = Objects.requireNonNull(
+                        mContext.getSystemService(AlarmManager.class));
+                alarmManager.set(ELAPSED_REALTIME_WAKEUP, mExpirationRealtimeMs, WINDOW_EXACT,
+                        0, this, FgThread.getHandler(), getWorkSource());
+            }
+
+            // start listening for provider enabled/disabled events
+            addEnabledListener(this);
+
+            onLocationListenerRegister();
+
+            // if the provider is currently disabled, let the client know immediately
+            int userId = getIdentity().getUserId();
+            if (!isEnabled(userId)) {
+                onProviderEnabledChanged(mName, userId, false);
+            }
+        }
+
+        @GuardedBy("mLock")
+        @Override
+        protected final void onProviderListenerUnregister() {
+            // stop listening for provider enabled/disabled events
+            removeEnabledListener(this);
+
+            // remove alarm for expiration
+            if (mExpirationRealtimeMs < Long.MAX_VALUE) {
+                AlarmManager alarmManager = Objects.requireNonNull(
+                        mContext.getSystemService(AlarmManager.class));
+                alarmManager.cancel(this);
+            }
+
+            onLocationListenerUnregister();
+        }
+
+        /**
+         * Subclasses may override this instead of {@link #onRemovableListenerRegister()}.
+         */
+        @GuardedBy("mLock")
+        protected void onLocationListenerRegister() {}
+
+        /**
+         * Subclasses may override this instead of {@link #onRemovableListenerUnregister()}.
+         */
+        @GuardedBy("mLock")
+        protected void onLocationListenerUnregister() {}
+
+        @Override
+        public void onAlarm() {
+            if (D) {
+                Log.d(TAG, "removing " + getIdentity() + " from " + mName
+                        + " provider due to expiration at " + TimeUtils.formatRealtime(
+                        mExpirationRealtimeMs));
+            }
+
+            synchronized (mLock) {
+                remove();
+            }
+        }
+
+        @GuardedBy("mLock")
+        @Nullable
+        @Override
+        ListenerOperation<LocationTransport> acceptLocationChange(Location fineLocation) {
+            if (Build.IS_DEBUGGABLE) {
+                Preconditions.checkState(Thread.holdsLock(mLock));
+            }
+
+            // check expiration time - alarm is not guaranteed to go off at the right time,
+            // especially for short intervals
+            if (SystemClock.elapsedRealtime() >= mExpirationRealtimeMs) {
+                remove();
+                return null;
+            }
+
+            Location location;
+            switch (mPermissionLevel) {
+                case PERMISSION_FINE:
+                    location = fineLocation;
+                    break;
+                case PERMISSION_COARSE:
+                    location = mLocationFudger.createCoarse(fineLocation);
+                    break;
+                default:
+                    // shouldn't be possible to have a client added without location permissions
+                    throw new AssertionError();
+            }
+
+            if (mLastLocation != null) {
+                // check fastest interval
+                long deltaMs = NANOSECONDS.toMillis(
+                        location.getElapsedRealtimeNanos()
+                                - mLastLocation.getElapsedRealtimeNanos());
+                if (deltaMs < getRequest().getFastestInterval() - MAX_FASTEST_INTERVAL_JITTER_MS) {
+                    return null;
+                }
+
+                // check smallest displacement
+                double smallestDisplacement = getRequest().getSmallestDisplacement();
+                if (smallestDisplacement > 0.0 && location.distanceTo(mLastLocation)
+                        <= smallestDisplacement) {
+                    return null;
+                }
+            }
+
+            // note app ops
+            if (!mAppOpsHelper.noteOpNoThrow(LocationPermissions.asAppOp(mPermissionLevel),
+                    getIdentity())) {
+                if (D) {
+                    Log.w(TAG, "noteOp denied for " + getIdentity());
+                }
+                return null;
+            }
+
+            // update last location
+            mLastLocation = location;
+
+            return new ListenerOperation<LocationTransport>() {
+                @Override
+                public void onPreExecute() {
+                    // don't acquire a wakelock for mock locations to prevent abuse
+                    if (!location.isFromMockProvider()) {
+                        mWakeLock.acquire(WAKELOCK_TIMEOUT_MS);
+                    }
+                }
+
+                @Override
+                public void operate(LocationTransport listener) throws Exception {
+                    // if delivering to the same process, make a copy of the location first (since
+                    // location is mutable)
+                    Location deliveryLocation;
+                    if (getIdentity().getPid() == Process.myPid()) {
+                        deliveryLocation = new Location(location);
+                    } else {
+                        deliveryLocation = location;
+                    }
+
+                    listener.deliverOnLocationChanged(deliveryLocation,
+                            location.isFromMockProvider() ? null : mWakeLock::release);
+                }
+
+                @Override
+                public void onPostExecute(boolean success) {
+                    if (!success && !location.isFromMockProvider()) {
+                        mWakeLock.release();
+                    }
+
+                    if (success) {
+                        // check num updates - if successful then this function will always be run
+                        // from the same thread, and no additional synchronization is necessary
+                        boolean remove = ++mNumLocationsDelivered >= getRequest().getNumUpdates();
+                        if (remove) {
+                            if (D) {
+                                Log.d(TAG, "removing " + getIdentity() + " from " + mName
+                                        + " provider due to number of updates");
+                            }
+
+                            synchronized (mLock) {
+                                remove();
+                            }
+                        }
+                    }
+                }
+            };
+        }
+
+        @Override
+        public void onProviderEnabledChanged(String provider, int userId, boolean enabled) {
+            Preconditions.checkState(mName.equals(provider));
+
+            if (userId != getIdentity().getUserId()) {
+                return;
+            }
+
+            // we choose not to hold a wakelock for provider enabled changed events
+            executeSafely(getExecutor(), () -> mProviderTransport,
+                    listener -> listener.deliverOnProviderEnabledChanged(mName, enabled));
+        }
+    }
+
+    protected final class LocationListenerRegistration extends LocationRegistration implements
+            IBinder.DeathRecipient {
+
+        protected LocationListenerRegistration(LocationRequest request, CallerIdentity identity,
+                LocationListenerTransport transport, @PermissionLevel int permissionLevel) {
+            super(request, identity, transport, permissionLevel);
+        }
+
+        @GuardedBy("mLock")
+        @Override
+        protected void onLocationListenerRegister() {
+            try {
+                ((IBinder) getKey()).linkToDeath(this, 0);
+            } catch (RemoteException e) {
+                remove();
+            }
+        }
+
+        @GuardedBy("mLock")
+        @Override
+        protected void onLocationListenerUnregister() {
+            ((IBinder) getKey()).unlinkToDeath(this, 0);
+        }
+
+        @Override
+        public void binderDied() {
+            try {
+                if (D) {
+                    Log.d(TAG, mName + " provider client died: " + getIdentity());
+                }
+
+                synchronized (mLock) {
+                    remove();
+                }
+            } catch (RuntimeException e) {
+                // the caller may swallow runtime exceptions, so we rethrow as assertion errors to
+                // ensure the crash is seen
+                throw new AssertionError(e);
+            }
+        }
+    }
+
+    protected final class LocationPendingIntentRegistration extends LocationRegistration implements
+            PendingIntent.CancelListener {
+
+        protected LocationPendingIntentRegistration(LocationRequest request,
+                CallerIdentity identity, LocationPendingIntentTransport transport,
+                @PermissionLevel int permissionLevel) {
+            super(request, identity, transport, permissionLevel);
+        }
+
+        @GuardedBy("mLock")
+        @Override
+        protected void onLocationListenerRegister() {
+            ((PendingIntent) getKey()).registerCancelListener(this);
+        }
+
+        @GuardedBy("mLock")
+        @Override
+        protected void onLocationListenerUnregister() {
+            ((PendingIntent) getKey()).unregisterCancelListener(this);
+        }
+
+        @Override
+        public void onCancelled(PendingIntent intent) {
+            synchronized (mLock) {
+                remove();
+            }
+        }
+    }
+
+    protected final class GetCurrentLocationListenerRegistration extends Registration implements
+            IBinder.DeathRecipient, ProviderEnabledListener, AlarmManager.OnAlarmListener {
+
+        private volatile LocationTransport mTransport;
+
+        private long mExpirationRealtimeMs = Long.MAX_VALUE;
+
+        protected GetCurrentLocationListenerRegistration(LocationRequest request,
+                CallerIdentity identity, LocationTransport transport, int permissionLevel) {
+            super(request, identity, transport, permissionLevel);
+            mTransport = transport;
+        }
+
+        @GuardedBy("mLock")
+        void deliverLocation(@Nullable Location location) {
+            executeSafely(getExecutor(), () -> mTransport, acceptLocationChange(location));
+        }
+
+        @Override
+        protected void onListenerUnregister() {
+            mTransport = null;
+        }
+
+        @GuardedBy("mLock")
+        @Override
+        protected void onProviderListenerRegister() {
+            mExpirationRealtimeMs = getRequest().getExpirationRealtimeMs(
+                    SystemClock.elapsedRealtime());
+
+            // add alarm for expiration
+            if (mExpirationRealtimeMs < Long.MAX_VALUE) {
+                AlarmManager alarmManager = Objects.requireNonNull(
+                        mContext.getSystemService(AlarmManager.class));
+                alarmManager.set(ELAPSED_REALTIME_WAKEUP, mExpirationRealtimeMs, WINDOW_EXACT,
+                        0, this, FgThread.getHandler(), getWorkSource());
+            }
+
+            try {
+                ((IBinder) getKey()).linkToDeath(this, 0);
+            } catch (RemoteException e) {
+                remove();
+            }
+
+            // start listening for provider enabled/disabled events
+            addEnabledListener(this);
+
+            // if the provider is currently disabled fail immediately
+            int userId = getIdentity().getUserId();
+            if (!getRequest().isLocationSettingsIgnored() && !isEnabled(userId)) {
+                deliverLocation(null);
+            }
+        }
+
+        @GuardedBy("mLock")
+        @Override
+        protected void onProviderListenerUnregister() {
+            // stop listening for provider enabled/disabled events
+            removeEnabledListener(this);
+
+            // remove alarm for expiration
+            if (mExpirationRealtimeMs < Long.MAX_VALUE) {
+                AlarmManager alarmManager = Objects.requireNonNull(
+                        mContext.getSystemService(AlarmManager.class));
+                alarmManager.cancel(this);
+            }
+
+            ((IBinder) getKey()).unlinkToDeath(this, 0);
+        }
+
+        @Override
+        public void onAlarm() {
+            if (D) {
+                Log.d(TAG, "removing " + getIdentity() + " from " + mName
+                        + " provider due to expiration at " + TimeUtils.formatRealtime(
+                        mExpirationRealtimeMs));
+            }
+
+            synchronized (mLock) {
+                deliverLocation(null);
+            }
+        }
+
+        @GuardedBy("mLock")
+        @Nullable
+        @Override
+        ListenerOperation<LocationTransport> acceptLocationChange(@Nullable Location fineLocation) {
+            if (Build.IS_DEBUGGABLE) {
+                Preconditions.checkState(Thread.holdsLock(mLock));
+            }
+
+            // lastly - note app ops
+            Location location;
+            if (fineLocation == null) {
+                location = null;
+            } else if (!mAppOpsHelper.noteOpNoThrow(LocationPermissions.asAppOp(mPermissionLevel),
+                    getIdentity())) {
+                if (D) {
+                    Log.w(TAG, "noteOp denied for " + getIdentity());
+                }
+                location = null;
+            } else {
+                switch (mPermissionLevel) {
+                    case PERMISSION_FINE:
+                        location = fineLocation;
+                        break;
+                    case PERMISSION_COARSE:
+                        location = mLocationFudger.createCoarse(fineLocation);
+                        break;
+                    default:
+                        // shouldn't be possible to have a client added without location permissions
+                        throw new AssertionError();
+                }
+            }
+
+            return listener -> {
+                // if delivering to the same process, make a copy of the location first (since
+                // location is mutable)
+                Location deliveryLocation = location;
+                if (getIdentity().getPid() == Process.myPid() && location != null) {
+                    deliveryLocation = new Location(location);
+                }
+
+                // we currently don't hold a wakelock for getCurrentLocation deliveries
+                listener.deliverOnLocationChanged(deliveryLocation, null);
+
+                synchronized (mLock) {
+                    remove();
+                }
+            };
+        }
+
+        @Override
+        public void onProviderEnabledChanged(String provider, int userId, boolean enabled) {
+            Preconditions.checkState(mName.equals(provider));
+
+            if (userId != getIdentity().getUserId()) {
+                return;
+            }
+
+            // if the provider is disabled we give up on current location immediately
+            if (!getRequest().isLocationSettingsIgnored() && !enabled) {
+                synchronized (mLock) {
+                    deliverLocation(null);
+                }
+            }
+        }
+
+        @Override
+        public void binderDied() {
+            try {
+                if (D) {
+                    Log.d(TAG, mName + " provider client died: " + getIdentity());
+                }
+
+                synchronized (mLock) {
+                    remove();
+                }
+            } catch (RuntimeException e) {
+                // the caller may swallow runtime exceptions, so we rethrow as assertion errors to
+                // ensure the crash is seen
+                throw new AssertionError(e);
+            }
+        }
+    }
+
+    protected final Object mLock = new Object();
+
+    protected final String mName;
+    @Nullable private final PassiveLocationProviderManager mPassiveManager;
+
+    protected final Context mContext;
+
+    @GuardedBy("mLock")
+    private boolean mStarted;
+
+    // maps of user id to value
+    @GuardedBy("mLock")
+    private final SparseBooleanArray mEnabled; // null or not present means unknown
+    @GuardedBy("mLock")
+    private final SparseArray<LastLocation> mLastLocations;
+
+    @GuardedBy("mLock")
+    private final ArrayList<ProviderEnabledListener> mEnabledListeners;
+
+    protected final LocationManagerInternal mLocationManagerInternal;
+    protected final SettingsHelper mSettingsHelper;
+    protected final UserInfoHelper mUserInfoHelper;
+    protected final AppOpsHelper mAppOpsHelper;
+    protected final LocationPermissionsHelper mLocationPermissionsHelper;
+    protected final AppForegroundHelper mAppForegroundHelper;
+    protected final LocationPowerSaveModeHelper mLocationPowerSaveModeHelper;
+    protected final ScreenInteractiveHelper mScreenInteractiveHelper;
+    protected final LocationAttributionHelper mLocationAttributionHelper;
+    protected final LocationUsageLogger mLocationUsageLogger;
+    protected final LocationFudger mLocationFudger;
+    protected final LocationRequestStatistics mLocationRequestStatistics;
+
+    private final UserListener mUserChangedListener = this::onUserChanged;
+    private final UserSettingChangedListener mLocationEnabledChangedListener =
+            this::onLocationEnabledChanged;
+    private final GlobalSettingChangedListener mBackgroundThrottlePackageWhitelistChangedListener =
+            this::onBackgroundThrottlePackageWhitelistChanged;
+    private final UserSettingChangedListener mLocationPackageBlacklistChangedListener =
+            this::onLocationPackageBlacklistChanged;
+    private final LocationPermissionsListener mLocationPermissionsListener =
+            new LocationPermissionsListener() {
+                @Override
+                public void onLocationPermissionsChanged(String packageName) {
+                    LocationProviderManager.this.onLocationPermissionsChanged(packageName);
+                }
+
+                @Override
+                public void onLocationPermissionsChanged(int uid) {
+                    LocationProviderManager.this.onLocationPermissionsChanged(uid);
+                }
+            };
+    private final AppForegroundListener mAppForegroundChangedListener =
+            this::onAppForegroundChanged;
+    private final GlobalSettingChangedListener mBackgroundThrottleIntervalChangedListener =
+            this::onBackgroundThrottleIntervalChanged;
+    private final GlobalSettingChangedListener mIgnoreSettingsPackageWhitelistChangedListener =
+            this::onIgnoreSettingsWhitelistChanged;
+    private final LocationPowerSaveModeChangedListener mLocationPowerSaveModeChangedListener =
+            this::onLocationPowerSaveModeChanged;
+    private final ScreenInteractiveChangedListener mScreenInteractiveChangedListener =
+            this::onScreenInteractiveChanged;
+
+    // acquiring mLock makes operations on mProvider atomic, but is otherwise unnecessary
+    protected final MockableLocationProvider mProvider;
+
+    LocationProviderManager(Context context, Injector injector, String name,
+            @Nullable PassiveLocationProviderManager passiveManager) {
+        mContext = context;
+        mName = Objects.requireNonNull(name);
+        mPassiveManager = passiveManager;
+        mStarted = false;
+        mEnabled = new SparseBooleanArray(2);
+        mLastLocations = new SparseArray<>(2);
+
+        mEnabledListeners = new ArrayList<>();
+
+        mLocationManagerInternal = Objects.requireNonNull(
+                LocalServices.getService(LocationManagerInternal.class));
+        mSettingsHelper = injector.getSettingsHelper();
+        mUserInfoHelper = injector.getUserInfoHelper();
+        mAppOpsHelper = injector.getAppOpsHelper();
+        mLocationPermissionsHelper = injector.getLocationPermissionsHelper();
+        mAppForegroundHelper = injector.getAppForegroundHelper();
+        mLocationPowerSaveModeHelper = injector.getLocationPowerSaveModeHelper();
+        mScreenInteractiveHelper = injector.getScreenInteractiveHelper();
+        mLocationAttributionHelper = injector.getLocationAttributionHelper();
+        mLocationUsageLogger = injector.getLocationUsageLogger();
+        mLocationRequestStatistics = injector.getLocationRequestStatistics();
+        mLocationFudger = new LocationFudger(mSettingsHelper.getCoarseLocationAccuracyM());
+
+        // initialize last since this lets our reference escape
+        mProvider = new MockableLocationProvider(mLock, this);
+    }
+
+    public void startManager() {
+        synchronized (mLock) {
+            mStarted = true;
+
+            mUserInfoHelper.addListener(mUserChangedListener);
+            mSettingsHelper.addOnLocationEnabledChangedListener(mLocationEnabledChangedListener);
+
+            // initialize enabled state
+            onUserStarted(UserHandle.USER_ALL);
+        }
+    }
+
+    public void stopManager() {
+        synchronized (mLock) {
+            mUserInfoHelper.removeListener(mUserChangedListener);
+            mSettingsHelper.removeOnLocationEnabledChangedListener(mLocationEnabledChangedListener);
+
+            // notify and remove all listeners
+            onUserStopped(UserHandle.USER_ALL);
+            removeRegistrationIf(key -> true);
+            mEnabledListeners.clear();
+
+            mStarted = false;
+        }
+    }
+
+    public String getName() {
+        return mName;
+    }
+
+    @Nullable
+    public CallerIdentity getIdentity() {
+        return mProvider.getState().identity;
+    }
+
+    @Nullable
+    public ProviderProperties getProperties() {
+        return mProvider.getState().properties;
+    }
+
+    public boolean hasProvider() {
+        return mProvider.getProvider() != null;
+    }
+
+    public boolean isEnabled(int userId) {
+        if (userId == UserHandle.USER_NULL) {
+            return false;
+        }
+
+        Preconditions.checkArgument(userId >= 0);
+
+        synchronized (mLock) {
+            int index = mEnabled.indexOfKey(userId);
+            if (index < 0) {
+                // this generally shouldn't occur, but might be possible due to race conditions
+                // on when we are notified of new users
+                Log.w(TAG, mName + " provider saw user " + userId + " unexpectedly");
+                onEnabledChanged(userId);
+                index = mEnabled.indexOfKey(userId);
+            }
+
+            return mEnabled.valueAt(index);
+        }
+    }
+
+    public void addEnabledListener(ProviderEnabledListener listener) {
+        synchronized (mLock) {
+            Preconditions.checkState(mStarted);
+            mEnabledListeners.add(listener);
+        }
+    }
+
+    public void removeEnabledListener(ProviderEnabledListener listener) {
+        synchronized (mLock) {
+            Preconditions.checkState(mStarted);
+            mEnabledListeners.remove(listener);
+        }
+    }
+
+    public void setRealProvider(AbstractLocationProvider provider) {
+        synchronized (mLock) {
+            Preconditions.checkState(mStarted);
+            mProvider.setRealProvider(provider);
+        }
+    }
+
+    public void setMockProvider(@Nullable MockProvider provider) {
+        synchronized (mLock) {
+            Preconditions.checkState(mStarted);
+            mProvider.setMockProvider(provider);
+
+            // when removing a mock provider, also clear any mock last locations and reset the
+            // location fudger. the mock provider could have been used to infer the current
+            // location fudger offsets.
+            if (provider == null) {
+                final int lastLocationSize = mLastLocations.size();
+                for (int i = 0; i < lastLocationSize; i++) {
+                    mLastLocations.valueAt(i).clearMock();
+                }
+
+                mLocationFudger.resetOffsets();
+            }
+        }
+    }
+
+    public void setMockProviderAllowed(boolean enabled) {
+        synchronized (mLock) {
+            if (!mProvider.isMock()) {
+                throw new IllegalArgumentException(mName + " provider is not a test provider");
+            }
+
+            mProvider.setMockProviderAllowed(enabled);
+        }
+    }
+
+    public void setMockProviderLocation(Location location) {
+        synchronized (mLock) {
+            if (!mProvider.isMock()) {
+                throw new IllegalArgumentException(mName + " provider is not a test provider");
+            }
+
+            String locationProvider = location.getProvider();
+            if (!TextUtils.isEmpty(locationProvider) && !mName.equals(locationProvider)) {
+                // The location has an explicit provider that is different from the mock
+                // provider name. The caller may be trying to fool us via b/33091107.
+                EventLog.writeEvent(0x534e4554, "33091107", Binder.getCallingUid(),
+                        mName + "!=" + locationProvider);
+            }
+
+            mProvider.setMockProviderLocation(location);
+        }
+    }
+
+    public List<LocationRequest> getMockProviderRequests() {
+        synchronized (mLock) {
+            if (!mProvider.isMock()) {
+                throw new IllegalArgumentException(mName + " provider is not a test provider");
+            }
+
+            return mProvider.getCurrentRequest().locationRequests;
+        }
+    }
+
+    @Nullable
+    public Location getLastLocation(LocationRequest request, CallerIdentity identity,
+            @PermissionLevel int permissionLevel) {
+        Preconditions.checkArgument(mName.equals(request.getProvider()));
+
+        if (mSettingsHelper.isLocationPackageBlacklisted(identity.getUserId(),
+                identity.getPackageName())) {
+            return null;
+        }
+        if (!mUserInfoHelper.isCurrentUserId(identity.getUserId())) {
+            return null;
+        }
+        if (!request.isLocationSettingsIgnored() && !isEnabled(identity.getUserId())) {
+            return null;
+        }
+
+        Location location = getLastLocation(identity.getUserId(), permissionLevel,
+                request.isLocationSettingsIgnored());
+
+        // we don't note op here because we don't know what the client intends to do with the
+        // location, the client is responsible for noting if necessary
+
+        if (identity.getPid() == Process.myPid() && location != null) {
+            // if delivering to the same process, make a copy of the location first (since
+            // location is mutable)
+            return new Location(location);
+        } else {
+            return location;
+        }
+    }
+
+    @Nullable
+    private Location getLastLocation(int userId, @PermissionLevel int permissionLevel,
+            boolean ignoreLocationSettings) {
+        synchronized (mLock) {
+            LastLocation lastLocation = mLastLocations.get(userId);
+            if (lastLocation == null) {
+                return null;
+            }
+            return lastLocation.get(permissionLevel, ignoreLocationSettings);
+        }
+    }
+
+    public void injectLastLocation(Location location, int userId) {
+        synchronized (mLock) {
+            if (getLastLocation(userId, PERMISSION_FINE, false) == null) {
+                setLastLocation(location, userId);
+            }
+        }
+    }
+
+    private void setLastLocation(Location location, int userId) {
+        if (userId == UserHandle.USER_ALL) {
+            final int[] runningUserIds = mUserInfoHelper.getRunningUserIds();
+            for (int i = 0; i < runningUserIds.length; i++) {
+                setLastLocation(location, runningUserIds[i]);
+            }
+            return;
+        }
+
+        Preconditions.checkArgument(userId >= 0);
+
+        synchronized (mLock) {
+            LastLocation lastLocation = mLastLocations.get(userId);
+            if (lastLocation == null) {
+                lastLocation = new LastLocation();
+                mLastLocations.put(userId, lastLocation);
+            }
+
+            Location coarseLocation = mLocationFudger.createCoarse(location);
+            if (isEnabled(userId)) {
+                lastLocation.set(location, coarseLocation);
+            }
+            lastLocation.setBypass(location, coarseLocation);
+        }
+    }
+
+    public void getCurrentLocation(LocationRequest request, CallerIdentity identity,
+            int permissionLevel, ICancellationSignal cancellationTransport,
+            ILocationCallback callback) {
+        Preconditions.checkArgument(mName.equals(request.getProvider()));
+
+        if (request.getExpireIn() > GET_CURRENT_LOCATION_MAX_TIMEOUT_MS) {
+            request.setExpireIn(GET_CURRENT_LOCATION_MAX_TIMEOUT_MS);
+        }
+
+        GetCurrentLocationListenerRegistration registration =
+                new GetCurrentLocationListenerRegistration(
+                        request,
+                        identity,
+                        new GetCurrentLocationTransport(callback),
+                        permissionLevel);
+
+        synchronized (mLock) {
+            Location lastLocation = getLastLocation(request, identity, permissionLevel);
+            if (lastLocation != null) {
+                long locationAgeMs = NANOSECONDS.toMillis(
+                        SystemClock.elapsedRealtimeNanos()
+                                - lastLocation.getElapsedRealtimeNanos());
+                if (locationAgeMs < MAX_CURRENT_LOCATION_AGE_MS) {
+                    registration.deliverLocation(lastLocation);
+                    return;
+                }
+
+                if (!mAppForegroundHelper.isAppForeground(Binder.getCallingUid())
+                        && locationAgeMs < mSettingsHelper.getBackgroundThrottleIntervalMs()) {
+                    registration.deliverLocation(null);
+                    return;
+                }
+            }
+
+            // if last location isn't good enough then we add a location request
+            addRegistration(callback.asBinder(), registration);
+            CancellationSignal cancellationSignal = CancellationSignal.fromTransport(
+                    cancellationTransport);
+            if (cancellationSignal != null) {
+                cancellationSignal.setOnCancelListener(
+                        () -> {
+                            synchronized (mLock) {
+                                removeRegistration(callback.asBinder(), registration);
+                            }
+                        });
+            }
+        }
+    }
+
+    public void sendExtraCommand(int uid, int pid, String command, Bundle extras) {
+        mProvider.sendExtraCommand(uid, pid, command, extras);
+    }
+
+    public void registerLocationRequest(LocationRequest request, CallerIdentity identity,
+            @PermissionLevel int permissionLevel, ILocationListener listener) {
+        Preconditions.checkArgument(mName.equals(request.getProvider()));
+
+        synchronized (mLock) {
+            addRegistration(
+                    listener.asBinder(),
+                    new LocationListenerRegistration(
+                            request,
+                            identity,
+                            new LocationListenerTransport(listener),
+                            permissionLevel));
+        }
+    }
+
+    public void registerLocationRequest(LocationRequest request, CallerIdentity identity,
+            @PermissionLevel int permissionLevel, PendingIntent pendingIntent) {
+        Preconditions.checkArgument(mName.equals(request.getProvider()));
+
+        synchronized (mLock) {
+            addRegistration(
+                    pendingIntent,
+                    new LocationPendingIntentRegistration(
+                            request,
+                            identity,
+                            new LocationPendingIntentTransport(mContext, pendingIntent),
+                            permissionLevel));
+        }
+    }
+
+    public void unregisterLocationRequest(ILocationListener listener) {
+        synchronized (mLock) {
+            removeRegistration(listener.asBinder());
+        }
+    }
+
+    public void unregisterLocationRequest(PendingIntent pendingIntent) {
+        synchronized (mLock) {
+            removeRegistration(pendingIntent);
+        }
+    }
+
+    @GuardedBy("mLock")
+    @Override
+    protected void onRegister() {
+        if (Build.IS_DEBUGGABLE) {
+            Preconditions.checkState(Thread.holdsLock(mLock));
+        }
+
+        mSettingsHelper.addOnBackgroundThrottleIntervalChangedListener(
+                mBackgroundThrottleIntervalChangedListener);
+        mSettingsHelper.addOnBackgroundThrottlePackageWhitelistChangedListener(
+                mBackgroundThrottlePackageWhitelistChangedListener);
+        mSettingsHelper.addOnLocationPackageBlacklistChangedListener(
+                mLocationPackageBlacklistChangedListener);
+        mSettingsHelper.addOnIgnoreSettingsPackageWhitelistChangedListener(
+                mIgnoreSettingsPackageWhitelistChangedListener);
+        mLocationPermissionsHelper.addListener(mLocationPermissionsListener);
+        mAppForegroundHelper.addListener(mAppForegroundChangedListener);
+        mLocationPowerSaveModeHelper.addListener(mLocationPowerSaveModeChangedListener);
+        mScreenInteractiveHelper.addListener(mScreenInteractiveChangedListener);
+    }
+
+    @GuardedBy("mLock")
+    @Override
+    protected void onUnregister() {
+        if (Build.IS_DEBUGGABLE) {
+            Preconditions.checkState(Thread.holdsLock(mLock));
+        }
+
+        mSettingsHelper.removeOnBackgroundThrottleIntervalChangedListener(
+                mBackgroundThrottleIntervalChangedListener);
+        mSettingsHelper.removeOnBackgroundThrottlePackageWhitelistChangedListener(
+                mBackgroundThrottlePackageWhitelistChangedListener);
+        mSettingsHelper.removeOnLocationPackageBlacklistChangedListener(
+                mLocationPackageBlacklistChangedListener);
+        mSettingsHelper.removeOnIgnoreSettingsPackageWhitelistChangedListener(
+                mIgnoreSettingsPackageWhitelistChangedListener);
+        mLocationPermissionsHelper.removeListener(mLocationPermissionsListener);
+        mAppForegroundHelper.removeListener(mAppForegroundChangedListener);
+        mLocationPowerSaveModeHelper.removeListener(mLocationPowerSaveModeChangedListener);
+        mScreenInteractiveHelper.removeListener(mScreenInteractiveChangedListener);
+    }
+
+    @GuardedBy("mLock")
+    @Override
+    protected void onRegistrationAdded(Object key, Registration registration) {
+        if (Build.IS_DEBUGGABLE) {
+            Preconditions.checkState(Thread.holdsLock(mLock));
+        }
+
+        mLocationUsageLogger.logLocationApiUsage(
+                LocationStatsEnums.USAGE_STARTED,
+                LocationStatsEnums.API_REQUEST_LOCATION_UPDATES,
+                registration.getIdentity().getPackageName(),
+                registration.getRequest(),
+                key instanceof PendingIntent,
+                key instanceof IBinder,
+                /* geofence= */ null,
+                registration.isForeground());
+
+        mLocationRequestStatistics.startRequesting(
+                registration.getIdentity().getPackageName(),
+                registration.getIdentity().getAttributionTag(),
+                mName,
+                registration.getRequest().getInterval(),
+                registration.isForeground());
+    }
+
+    @GuardedBy("mLock")
+    @Override
+    protected void onRegistrationRemoved(Object key, Registration registration) {
+        if (Build.IS_DEBUGGABLE) {
+            Preconditions.checkState(Thread.holdsLock(mLock));
+        }
+
+        mLocationRequestStatistics.stopRequesting(
+                registration.getIdentity().getPackageName(),
+                registration.getIdentity().getAttributionTag(),
+                mName);
+
+        mLocationUsageLogger.logLocationApiUsage(
+                LocationStatsEnums.USAGE_ENDED,
+                LocationStatsEnums.API_REQUEST_LOCATION_UPDATES,
+                registration.getIdentity().getPackageName(),
+                registration.getRequest(),
+                key instanceof PendingIntent,
+                key instanceof IBinder,
+                /* geofence= */ null,
+                registration.isForeground());
+    }
+
+    @GuardedBy("mLock")
+    @Override
+    protected boolean registerWithService(ProviderRequest mergedRequest) {
+        if (Build.IS_DEBUGGABLE) {
+            Preconditions.checkState(Thread.holdsLock(mLock));
+        }
+
+        mProvider.setRequest(mergedRequest);
+        return true;
+    }
+
+    @GuardedBy("mLock")
+    @Override
+    protected boolean reregisterWithService(ProviderRequest oldRequest,
+            ProviderRequest newRequest) {
+        return registerWithService(newRequest);
+    }
+
+    @GuardedBy("mLock")
+    @Override
+    protected void unregisterWithService() {
+        if (Build.IS_DEBUGGABLE) {
+            Preconditions.checkState(Thread.holdsLock(mLock));
+        }
+        mProvider.setRequest(ProviderRequest.EMPTY_REQUEST);
+    }
+
+    @GuardedBy("mLock")
+    @Override
+    protected boolean isActive(Registration registration) {
+        if (Build.IS_DEBUGGABLE) {
+            Preconditions.checkState(Thread.holdsLock(mLock));
+        }
+
+        CallerIdentity identity = registration.getIdentity();
+
+        if (!registration.isPermitted()) {
+            return false;
+        }
+
+        if (!registration.getRequest().isLocationSettingsIgnored()) {
+            if (!isEnabled(identity.getUserId())) {
+                return false;
+            }
+
+            switch (mLocationPowerSaveModeHelper.getLocationPowerSaveMode()) {
+                case LOCATION_MODE_FOREGROUND_ONLY:
+                    if (!registration.isForeground()) {
+                        return false;
+                    }
+                    break;
+                case LOCATION_MODE_GPS_DISABLED_WHEN_SCREEN_OFF:
+                    if (!GPS_PROVIDER.equals(mName)) {
+                        break;
+                    }
+                    // fall through
+                case LOCATION_MODE_THROTTLE_REQUESTS_WHEN_SCREEN_OFF:
+                    // fall through
+                case LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF:
+                    if (!mScreenInteractiveHelper.isInteractive()) {
+                        return false;
+                    }
+                    break;
+                case LOCATION_MODE_NO_CHANGE:
+                    // fall through
+                default:
+                    break;
+            }
+        }
+
+        return !mSettingsHelper.isLocationPackageBlacklisted(identity.getUserId(),
+                identity.getPackageName());
+    }
+
+    @GuardedBy("mLock")
+    @Override
+    protected ProviderRequest mergeRequests(Collection<Registration> registrations) {
+        if (Build.IS_DEBUGGABLE) {
+            Preconditions.checkState(Thread.holdsLock(mLock));
+        }
+
+        ProviderRequest.Builder providerRequest = new ProviderRequest.Builder();
+        // initialize the low power mode to true and set to false if any of the records requires
+        providerRequest.setLowPowerMode(true);
+
+        ArrayList<Registration> providerRegistrations = new ArrayList<>(registrations.size());
+        for (Registration registration : registrations) {
+            LocationRequest locationRequest = registration.getRequest();
+
+            if (locationRequest.isLocationSettingsIgnored()) {
+                providerRequest.setLocationSettingsIgnored(true);
+            }
+            if (!locationRequest.isLowPowerMode()) {
+                providerRequest.setLowPowerMode(false);
+            }
+
+            providerRequest.setInterval(
+                    Math.min(locationRequest.getInterval(), providerRequest.getInterval()));
+            providerRegistrations.add(registration);
+        }
+
+        // collect contributing location requests
+        ArrayList<LocationRequest> providerRequests = new ArrayList<>(providerRegistrations.size());
+        final int registrationsSize = providerRegistrations.size();
+        for (int i = 0; i < registrationsSize; i++) {
+            providerRequests.add(providerRegistrations.get(i).getRequest());
+        }
+
+        providerRequest.setLocationRequests(providerRequests);
+
+        // calculate who to blame for power in a somewhat arbitrary fashion. we pick a threshold
+        // interval slightly higher that the minimum interval, and spread the blame across all
+        // contributing registrations under that threshold.
+        long thresholdIntervalMs = (providerRequest.getInterval() + 1000) * 3 / 2;
+        if (thresholdIntervalMs < 0) {
+            // handle overflow
+            thresholdIntervalMs = Long.MAX_VALUE;
+        }
+        for (int i = 0; i < registrationsSize; i++) {
+            LocationRequest request = providerRegistrations.get(i).getRequest();
+            if (request.getInterval() <= thresholdIntervalMs) {
+                providerRequest.getWorkSource().add(providerRegistrations.get(i).getWorkSource());
+            }
+        }
+
+        return providerRequest.build();
+    }
+
+    private void onUserChanged(int userId, int change) {
+        synchronized (mLock) {
+            switch (change) {
+                case UserListener.CURRENT_USER_CHANGED:
+                    onEnabledChanged(userId);
+                    break;
+                case UserListener.USER_STARTED:
+                    onUserStarted(userId);
+                    break;
+                case UserListener.USER_STOPPED:
+                    onUserStopped(userId);
+                    break;
+            }
+        }
+    }
+
+    private void onLocationEnabledChanged(int userId) {
+        synchronized (mLock) {
+            onEnabledChanged(userId);
+        }
+    }
+
+    private void onScreenInteractiveChanged(boolean screenInteractive) {
+        synchronized (mLock) {
+            switch (mLocationPowerSaveModeHelper.getLocationPowerSaveMode()) {
+                case LOCATION_MODE_GPS_DISABLED_WHEN_SCREEN_OFF:
+                    if (!GPS_PROVIDER.equals(mName)) {
+                        break;
+                    }
+                    // fall through
+                case LOCATION_MODE_THROTTLE_REQUESTS_WHEN_SCREEN_OFF:
+                    // fall through
+                case LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF:
+                    updateService();
+                    break;
+                default:
+                    break;
+            }
+        }
+    }
+
+    private void onBackgroundThrottlePackageWhitelistChanged() {
+        synchronized (mLock) {
+            updateRegistrations(Registration::onProviderLocationRequestChanged);
+        }
+    }
+
+    private void onBackgroundThrottleIntervalChanged() {
+        synchronized (mLock) {
+            updateRegistrations(Registration::onProviderLocationRequestChanged);
+        }
+    }
+
+    private void onLocationPowerSaveModeChanged(@LocationPowerSaveMode int locationPowerSaveMode) {
+        synchronized (mLock) {
+            // this is rare, just assume everything has changed to keep it simple
+            updateRegistrations(registration -> true);
+        }
+    }
+
+    private void onAppForegroundChanged(int uid, boolean foreground) {
+        synchronized (mLock) {
+            updateRegistrations(registration -> registration.onForegroundChanged(uid, foreground));
+        }
+    }
+
+    private void onIgnoreSettingsWhitelistChanged() {
+        synchronized (mLock) {
+            updateRegistrations(Registration::onProviderLocationRequestChanged);
+        }
+    }
+
+    private void onLocationPackageBlacklistChanged(int userId) {
+        synchronized (mLock) {
+            updateRegistrations(registration -> registration.getIdentity().getUserId() == userId);
+        }
+    }
+
+    private void onLocationPermissionsChanged(String packageName) {
+        synchronized (mLock) {
+            updateRegistrations(
+                    registration -> registration.onLocationPermissionsChanged(packageName));
+        }
+    }
+
+    private void onLocationPermissionsChanged(int uid) {
+        synchronized (mLock) {
+            updateRegistrations(registration -> registration.onLocationPermissionsChanged(uid));
+        }
+    }
+
+    @GuardedBy("mLock")
+    @Override
+    public void onStateChanged(
+            AbstractLocationProvider.State oldState, AbstractLocationProvider.State newState) {
+        if (Build.IS_DEBUGGABLE) {
+            Preconditions.checkState(Thread.holdsLock(mLock));
+        }
+
+        if (oldState.allowed != newState.allowed) {
+            onEnabledChanged(UserHandle.USER_ALL);
+        }
+    }
+
+    @GuardedBy("mLock")
+    @Override
+    public void onReportLocation(Location location) {
+        if (Build.IS_DEBUGGABLE) {
+            Preconditions.checkState(Thread.holdsLock(mLock));
+        }
+
+        // don't validate mock locations
+        if (!location.isFromMockProvider()) {
+            if (location.getLatitude() == 0 && location.getLongitude() == 0) {
+                Log.w(TAG, "blocking 0,0 location from " + mName + " provider");
+                return;
+            }
+        }
+
+        if (!location.isComplete()) {
+            Log.w(TAG, "blocking incomplete location from " + mName + " provider");
+            return;
+        }
+
+        // update last location
+        setLastLocation(location, UserHandle.USER_ALL);
+
+        // notify passive provider
+        if (mPassiveManager != null) {
+            mPassiveManager.updateLocation(location);
+        }
+
+        // attempt listener delivery
+        deliverToListeners(registration -> {
+            return registration.acceptLocationChange(location);
+        });
+    }
+
+    @GuardedBy("mLock")
+    @Override
+    public void onReportLocation(List<Location> locations) {
+        if (!GPS_PROVIDER.equals(mName)) {
+            return;
+        }
+
+        mLocationManagerInternal.reportGnssBatchLocations(locations);
+    }
+
+    @GuardedBy("mLock")
+    private void onUserStarted(int userId) {
+        if (Build.IS_DEBUGGABLE) {
+            Preconditions.checkState(Thread.holdsLock(mLock));
+        }
+
+        if (userId == UserHandle.USER_NULL) {
+            return;
+        }
+
+        if (userId == UserHandle.USER_ALL) {
+            // clear the user's prior enabled state to prevent broadcast of enabled state change
+            mEnabled.clear();
+            onEnabledChanged(UserHandle.USER_ALL);
+        } else {
+            Preconditions.checkArgument(userId >= 0);
+
+            // clear the user's prior enabled state to prevent broadcast of enabled state change
+            mEnabled.delete(userId);
+            onEnabledChanged(userId);
+        }
+    }
+
+    @GuardedBy("mLock")
+    private void onUserStopped(int userId) {
+        if (Build.IS_DEBUGGABLE) {
+            Preconditions.checkState(Thread.holdsLock(mLock));
+        }
+
+        if (userId == UserHandle.USER_NULL) {
+            return;
+        }
+
+        if (userId == UserHandle.USER_ALL) {
+            onEnabledChanged(UserHandle.USER_ALL);
+            mEnabled.clear();
+            mLastLocations.clear();
+        } else {
+            Preconditions.checkArgument(userId >= 0);
+
+            onEnabledChanged(userId);
+            mEnabled.delete(userId);
+            mLastLocations.remove(userId);
+        }
+    }
+
+    @GuardedBy("mLock")
+    private void onEnabledChanged(int userId) {
+        if (Build.IS_DEBUGGABLE) {
+            Preconditions.checkState(Thread.holdsLock(mLock));
+        }
+
+        if (userId == UserHandle.USER_NULL) {
+            // used during initialization - ignore since many lower level operations (checking
+            // settings for instance) do not support the null user
+            return;
+        } else if (userId == UserHandle.USER_ALL) {
+            final int[] runningUserIds = mUserInfoHelper.getRunningUserIds();
+            for (int i = 0; i < runningUserIds.length; i++) {
+                onEnabledChanged(runningUserIds[i]);
+            }
+            return;
+        }
+
+        Preconditions.checkArgument(userId >= 0);
+
+        boolean enabled = mStarted
+                && mProvider.getState().allowed
+                && mUserInfoHelper.isCurrentUserId(userId)
+                && mSettingsHelper.isLocationEnabled(userId);
+
+        int index = mEnabled.indexOfKey(userId);
+        Boolean wasEnabled = index < 0 ? null : mEnabled.valueAt(index);
+        if (wasEnabled != null && wasEnabled == enabled) {
+            return;
+        }
+
+        mEnabled.put(userId, enabled);
+
+        if (D) {
+            Log.d(TAG, "[u" + userId + "] " + mName + " provider enabled = " + enabled);
+        }
+
+        // clear last locations if we become disabled
+        if (!enabled) {
+            LastLocation lastLocation = mLastLocations.get(userId);
+            if (lastLocation != null) {
+                lastLocation.clearLocations();
+            }
+        }
+
+        // do not send change notifications if we just saw this user for the first time
+        if (wasEnabled != null) {
+            // fused and passive provider never get public updates for legacy reasons
+            if (!FUSED_PROVIDER.equals(mName) && !PASSIVE_PROVIDER.equals(mName)) {
+                Intent intent = new Intent(LocationManager.PROVIDERS_CHANGED_ACTION)
+                        .putExtra(LocationManager.EXTRA_PROVIDER_NAME, mName)
+                        .putExtra(LocationManager.EXTRA_PROVIDER_ENABLED, enabled)
+                        .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY)
+                        .addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+                mContext.sendBroadcastAsUser(intent, UserHandle.of(userId));
+            }
+
+            // send updates to internal listeners - since we expect listener changes to be more
+            // frequent than enabled changes, we use copy-on-read instead of copy-on-write
+            if (!mEnabledListeners.isEmpty()) {
+                ProviderEnabledListener[] listeners = mEnabledListeners.toArray(
+                        new ProviderEnabledListener[0]);
+                FgThread.getHandler().post(() -> {
+                    for (int i = 0; i < listeners.length; i++) {
+                        listeners[i].onProviderEnabledChanged(mName, userId, enabled);
+                    }
+                });
+            }
+        }
+
+        // update active state of affected registrations
+        updateRegistrations(registration -> registration.getIdentity().getUserId() == userId);
+    }
+
+    public void dump(FileDescriptor fd, IndentingPrintWriter ipw, String[] args) {
+        synchronized (mLock) {
+            ipw.print(mName + " provider");
+            if (mProvider.isMock()) {
+                ipw.print(" [mock]");
+            }
+            ipw.println(":");
+            ipw.increaseIndent();
+
+            super.dump(fd, ipw, args);
+
+            int[] userIds = mUserInfoHelper.getRunningUserIds();
+            for (int userId : userIds) {
+                if (userIds.length != 1) {
+                    ipw.println("user " + userId + ":");
+                    ipw.increaseIndent();
+                }
+                ipw.println("last location=" + getLastLocation(userId, PERMISSION_FINE, false));
+                ipw.println("enabled=" + isEnabled(userId));
+                if (userIds.length != 1) {
+                    ipw.decreaseIndent();
+                }
+            }
+        }
+
+        mProvider.dump(fd, ipw, args);
+
+        ipw.decreaseIndent();
+    }
+
+    private static class LastLocation {
+
+        @Nullable private Location mFineLocation;
+        @Nullable private Location mCoarseLocation;
+        @Nullable private Location mFineBypassLocation;
+        @Nullable private Location mCoarseBypassLocation;
+
+        public void clearMock() {
+            if (mFineLocation != null && mFineLocation.isFromMockProvider()) {
+                mFineLocation = null;
+                mCoarseLocation = null;
+            }
+            if (mFineBypassLocation != null && mFineBypassLocation.isFromMockProvider()) {
+                mFineBypassLocation = null;
+                mCoarseBypassLocation = null;
+            }
+        }
+
+        public void clearLocations() {
+            mFineLocation = null;
+            mCoarseLocation = null;
+        }
+
+        @Nullable
+        public Location get(@PermissionLevel int permissionLevel, boolean ignoreLocationSettings) {
+            switch (permissionLevel) {
+                case PERMISSION_FINE:
+                    if (ignoreLocationSettings) {
+                        return mFineBypassLocation;
+                    } else {
+                        return mFineLocation;
+                    }
+                case PERMISSION_COARSE:
+                    if (ignoreLocationSettings) {
+                        return mCoarseBypassLocation;
+                    } else {
+                        return mCoarseLocation;
+                    }
+                default:
+                    // shouldn't be possible to have a client added without location permissions
+                    throw new AssertionError();
+            }
+        }
+
+        public void set(Location location, Location coarseLocation) {
+            mFineLocation = location;
+            mCoarseLocation = calculateNextCoarse(mCoarseLocation, coarseLocation);
+        }
+
+        public void setBypass(Location location, Location coarseLocation) {
+            mFineBypassLocation = location;
+            mCoarseBypassLocation = calculateNextCoarse(mCoarseBypassLocation, coarseLocation);
+        }
+
+        private Location calculateNextCoarse(@Nullable Location oldCoarse, Location newCoarse) {
+            if (oldCoarse == null) {
+                return newCoarse;
+            }
+            // update last coarse interval only if enough time has passed
+            long timeDeltaMs = NANOSECONDS.toMillis(newCoarse.getElapsedRealtimeNanos())
+                        - NANOSECONDS.toMillis(oldCoarse.getElapsedRealtimeNanos());
+            if (timeDeltaMs > FASTEST_COARSE_INTERVAL_MS) {
+                return newCoarse;
+            } else {
+                return oldCoarse;
+            }
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/location/MockProvider.java b/services/core/java/com/android/server/location/MockProvider.java
index c5bd5e6..fc88f14 100644
--- a/services/core/java/com/android/server/location/MockProvider.java
+++ b/services/core/java/com/android/server/location/MockProvider.java
@@ -40,9 +40,8 @@
 
     public MockProvider(ProviderProperties properties, CallerIdentity identity) {
         // using a direct executor is ok because this class has no locks that could deadlock
-        super(DIRECT_EXECUTOR);
+        super(DIRECT_EXECUTOR, identity);
         setProperties(properties);
-        setIdentity(identity);
     }
 
     /** Sets the allowed state of this mock provider. */
diff --git a/services/core/java/com/android/server/location/MockableLocationProvider.java b/services/core/java/com/android/server/location/MockableLocationProvider.java
index 54af1c8..d8d435a 100644
--- a/services/core/java/com/android/server/location/MockableLocationProvider.java
+++ b/services/core/java/com/android/server/location/MockableLocationProvider.java
@@ -241,7 +241,6 @@
             if (getState().properties != null) {
                 pw.println("properties=" + getState().properties);
             }
-            pw.println("request=" + mRequest);
         }
 
         if (provider != null) {
diff --git a/services/core/java/com/android/server/location/PassiveLocationProviderManager.java b/services/core/java/com/android/server/location/PassiveLocationProviderManager.java
new file mode 100644
index 0000000..d4999ab
--- /dev/null
+++ b/services/core/java/com/android/server/location/PassiveLocationProviderManager.java
@@ -0,0 +1,60 @@
+/*
+ * 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;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.location.Location;
+import android.location.LocationManager;
+import android.os.Binder;
+
+import com.android.internal.util.Preconditions;
+import com.android.server.location.util.Injector;
+
+class PassiveLocationProviderManager extends LocationProviderManager {
+
+    PassiveLocationProviderManager(Context context, Injector injector) {
+        super(context, injector, LocationManager.PASSIVE_PROVIDER, null);
+    }
+
+    @Override
+    public void setRealProvider(AbstractLocationProvider provider) {
+        Preconditions.checkArgument(provider instanceof PassiveProvider);
+        super.setRealProvider(provider);
+    }
+
+    @Override
+    public void setMockProvider(@Nullable MockProvider provider) {
+        if (provider != null) {
+            throw new IllegalArgumentException("Cannot mock the passive provider");
+        }
+    }
+
+    public void updateLocation(Location location) {
+        synchronized (mLock) {
+            PassiveProvider passiveProvider = (PassiveProvider) mProvider.getProvider();
+            Preconditions.checkState(passiveProvider != null);
+
+            long identity = Binder.clearCallingIdentity();
+            try {
+                passiveProvider.updateLocation(location);
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
+    }
+}
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 0b7968b..1b599b0 100644
--- a/services/core/java/com/android/server/location/gnss/GnssListenerMultiplexer.java
+++ b/services/core/java/com/android/server/location/gnss/GnssListenerMultiplexer.java
@@ -16,17 +16,21 @@
 
 package com.android.server.location.gnss;
 
+import static android.location.LocationManager.GPS_PROVIDER;
+
 import static com.android.server.location.LocationPermissions.PERMISSION_FINE;
 import static com.android.server.location.gnss.GnssManagerService.TAG;
 
 import android.annotation.Nullable;
 import android.location.LocationManagerInternal;
+import android.location.LocationManagerInternal.ProviderEnabledListener;
 import android.location.util.identity.CallerIdentity;
 import android.os.IBinder;
 import android.os.IInterface;
 import android.os.Process;
 import android.util.ArraySet;
 
+import com.android.internal.util.Preconditions;
 import com.android.server.LocalServices;
 import com.android.server.location.listeners.BinderListenerRegistration;
 import com.android.server.location.listeners.ListenerMultiplexer;
@@ -161,8 +165,8 @@
     protected final LocationManagerInternal mLocationManagerInternal;
 
     private final UserListener mUserChangedListener = this::onUserChanged;
-    private final SettingsHelper.UserSettingChangedListener mLocationEnabledChangedListener =
-            this::onLocationEnabledChanged;
+    private final ProviderEnabledListener mProviderEnabledChangedListener =
+            this::onProviderEnabledChanged;
     private final SettingsHelper.GlobalSettingChangedListener
             mBackgroundThrottlePackageWhitelistChangedListener =
             this::onBackgroundThrottlePackageWhitelistChanged;
@@ -233,12 +237,11 @@
         }
 
         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.isPermitted()
                 && (registration.isForeground() || isBackgroundRestrictionExempt(identity))
                 && mUserInfoHelper.isCurrentUserId(identity.getUserId())
-                && mSettingsHelper.isLocationEnabled(identity.getUserId())
+                && mLocationManagerInternal.isProviderEnabledForUser(GPS_PROVIDER,
+                identity.getUserId())
                 && !mSettingsHelper.isLocationPackageBlacklisted(identity.getUserId(),
                 identity.getPackageName());
     }
@@ -263,7 +266,8 @@
         }
 
         mUserInfoHelper.addListener(mUserChangedListener);
-        mSettingsHelper.addOnLocationEnabledChangedListener(mLocationEnabledChangedListener);
+        mLocationManagerInternal.addProviderEnabledListener(GPS_PROVIDER,
+                mProviderEnabledChangedListener);
         mSettingsHelper.addOnBackgroundThrottlePackageWhitelistChangedListener(
                 mBackgroundThrottlePackageWhitelistChangedListener);
         mSettingsHelper.addOnLocationPackageBlacklistChangedListener(
@@ -279,7 +283,8 @@
         }
 
         mUserInfoHelper.removeListener(mUserChangedListener);
-        mSettingsHelper.removeOnLocationEnabledChangedListener(mLocationEnabledChangedListener);
+        mLocationManagerInternal.removeProviderEnabledListener(GPS_PROVIDER,
+                mProviderEnabledChangedListener);
         mSettingsHelper.removeOnBackgroundThrottlePackageWhitelistChangedListener(
                 mBackgroundThrottlePackageWhitelistChangedListener);
         mSettingsHelper.removeOnLocationPackageBlacklistChangedListener(
@@ -294,7 +299,8 @@
         }
     }
 
-    private void onLocationEnabledChanged(int userId) {
+    private void onProviderEnabledChanged(String provider, int userId, boolean enabled) {
+        Preconditions.checkState(GPS_PROVIDER.equals(provider));
         updateRegistrations(registration -> registration.getIdentity().getUserId() == userId);
     }
 
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 528cf8a..f94de9b 100644
--- a/services/core/java/com/android/server/location/listeners/ListenerMultiplexer.java
+++ b/services/core/java/com/android/server/location/listeners/ListenerMultiplexer.java
@@ -104,18 +104,18 @@
      * should return true if a matching call to {@link #unregisterWithService()} is required to
      * unregister (ie, if registration succeeds).
      *
-     * @see #reregisterWithService(Object)
+     * @see #reregisterWithService(Object, Object)
      */
-    protected abstract boolean registerWithService(TMergedRequest mergedRequest);
+    protected abstract boolean registerWithService(TMergedRequest newRequest);
 
     /**
      * Invoked when the service already has a request, and it is being replaced with a new request.
      * The default implementation unregisters first, then registers with the new merged request, but
      * this may be overridden by subclasses in order to reregister more efficiently.
      */
-    protected boolean reregisterWithService(TMergedRequest mergedRequest) {
+    protected boolean reregisterWithService(TMergedRequest oldRequest, TMergedRequest newRequest) {
         unregisterWithService();
-        return registerWithService(mergedRequest);
+        return registerWithService(newRequest);
     }
 
     /**
@@ -368,6 +368,7 @@
                     mCurrentRequest = null;
                     if (mServiceRegistered) {
                         mServiceRegistered = false;
+                        mCurrentRequest = null;
                         unregisterWithService();
                     }
                     return;
@@ -376,11 +377,15 @@
                 TMergedRequest merged = mergeRequests(actives);
                 if (!mServiceRegistered || !Objects.equals(merged, mCurrentRequest)) {
                     if (mServiceRegistered) {
-                        mServiceRegistered = reregisterWithService(merged);
+                        mServiceRegistered = reregisterWithService(mCurrentRequest, merged);
                     } else {
                         mServiceRegistered = registerWithService(merged);
                     }
-                    mCurrentRequest = merged;
+                    if (mServiceRegistered) {
+                        mCurrentRequest = merged;
+                    } else {
+                        mCurrentRequest = null;
+                    }
                 }
             } finally {
                 Binder.restoreCallingIdentity(identity);
@@ -389,29 +394,6 @@
     }
 
     /**
-     * Evaluates the given predicate for all registrations, and forces an {@link #updateService()}
-     * if any predicate returns true for an active registration. The predicate will always be
-     * evaluated for all registrations, even inactive registrations, or if it has already returned
-     * true for a prior registration.
-     */
-    protected final void updateService(Predicate<TRegistration> predicate) {
-        synchronized (mRegistrations) {
-            boolean updateService = false;
-            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;
-                }
-            }
-
-            if (updateService) {
-                updateService();
-            }
-        }
-    }
-
-    /**
      * Begins buffering calls to {@link #updateService()} until {@link UpdateServiceLock#close()}
      * is called. This is useful to prevent extra work when combining multiple calls (for example,
      * buffering {@code updateService()} until after multiple adds/removes/updates occur.
diff --git a/services/core/java/com/android/server/location/listeners/ListenerRegistration.java b/services/core/java/com/android/server/location/listeners/ListenerRegistration.java
index 0bdd131..ac56c51 100644
--- a/services/core/java/com/android/server/location/listeners/ListenerRegistration.java
+++ b/services/core/java/com/android/server/location/listeners/ListenerRegistration.java
@@ -24,6 +24,7 @@
 import android.location.util.identity.CallerIdentity;
 import android.os.Process;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.listeners.ListenerExecutor;
 import com.android.server.FgThread;
 
@@ -39,6 +40,9 @@
  */
 public class ListenerRegistration<TRequest, TListener> implements ListenerExecutor {
 
+    @VisibleForTesting
+    public static final Executor IN_PROCESS_EXECUTOR = FgThread.getExecutor();
+
     private final Executor mExecutor;
     private final @Nullable TRequest mRequest;
     private final CallerIdentity mIdentity;
@@ -55,9 +59,9 @@
             // there's a slight loophole here for pending intents - pending intent callbacks can
             // always be run on the direct executor since they're always asynchronous, but honestly
             // you shouldn't be using pending intent callbacks within the same process anyways
-            mExecutor =  FgThread.getExecutor();
+            mExecutor = IN_PROCESS_EXECUTOR;
         } else {
-            mExecutor =  DIRECT_EXECUTOR;
+            mExecutor = DIRECT_EXECUTOR;
         }
 
         mRequest = request;
@@ -73,7 +77,7 @@
     /**
      * Returns the request associated with this listener, or null if one wasn't supplied.
      */
-    public final @Nullable TRequest getRequest() {
+    public @Nullable TRequest getRequest() {
         return mRequest;
     }
 
@@ -107,7 +111,7 @@
      */
     protected void onInactive() {}
 
-    final boolean isActive() {
+    public final boolean isActive() {
         return mActive;
     }
 
@@ -120,7 +124,7 @@
         return false;
     }
 
-    final boolean isRegistered() {
+    public final boolean isRegistered() {
         return mListener != null;
     }
 
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 6a815ea..0698cca 100644
--- a/services/core/java/com/android/server/location/listeners/RemovableListenerRegistration.java
+++ b/services/core/java/com/android/server/location/listeners/RemovableListenerRegistration.java
@@ -39,7 +39,7 @@
     protected RemovableListenerRegistration(String tag, @Nullable TRequest request,
             CallerIdentity callerIdentity, TListener listener) {
         super(request, callerIdentity, listener);
-        mTag = tag;
+        mTag = Objects.requireNonNull(tag);
     }
 
     /**
diff --git a/services/core/java/com/android/server/pm/ApexManager.java b/services/core/java/com/android/server/pm/ApexManager.java
index 249b6801..07527c2 100644
--- a/services/core/java/com/android/server/pm/ApexManager.java
+++ b/services/core/java/com/android/server/pm/ApexManager.java
@@ -270,11 +270,12 @@
     abstract boolean revertActiveSessions();
 
     /**
-     * Abandons the staged session with the given sessionId.
+     * Abandons the staged session with the given sessionId. Client should handle {@code false}
+     * return value carefully as failure here can leave device in inconsistent state.
      *
-     * @return {@code true} upon success, {@code false} if any remote exception occurs
+     * @return {@code true} upon success, {@code false} if any exception occurs
      */
-    abstract boolean abortStagedSession(int sessionId) throws PackageManagerException;
+    abstract boolean abortStagedSession(int sessionId);
 
     /**
      * Uninstalls given {@code apexPackage}.
@@ -753,17 +754,13 @@
         }
 
         @Override
-        boolean abortStagedSession(int sessionId) throws PackageManagerException {
+        boolean abortStagedSession(int sessionId) {
             try {
                 waitForApexService().abortStagedSession(sessionId);
                 return true;
-            } catch (RemoteException re) {
-                Slog.e(TAG, "Unable to contact apexservice", re);
-                return false;
             } catch (Exception e) {
-                throw new PackageManagerException(
-                        PackageInstaller.SessionInfo.STAGED_SESSION_VERIFICATION_FAILED,
-                        "Failed to abort staged session : " + e.getMessage());
+                Slog.e(TAG, e.getMessage(), e);
+                return false;
             }
         }
 
@@ -1122,7 +1119,7 @@
         }
 
         @Override
-        boolean abortStagedSession(int sessionId) throws PackageManagerException {
+        boolean abortStagedSession(int sessionId) {
             throw new UnsupportedOperationException();
         }
 
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 7765f18..ca8a68b 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -3321,7 +3321,8 @@
     /** {@hide} */
     void setStagedSessionReady() {
         synchronized (mLock) {
-            if (mDestroyed) return; // Do not allow destroyed staged session to change state
+            // Do not allow destroyed/failed staged session to change state
+            if (mDestroyed || mStagedSessionFailed) return;
             mStagedSessionReady = true;
             mStagedSessionApplied = false;
             mStagedSessionFailed = false;
@@ -3332,10 +3333,10 @@
     }
 
     /** {@hide} */
-    void setStagedSessionFailed(@StagedSessionErrorCode int errorCode,
-                                String errorMessage) {
+    void setStagedSessionFailed(@StagedSessionErrorCode int errorCode, String errorMessage) {
         synchronized (mLock) {
-            if (mDestroyed) return; // Do not allow destroyed staged session to change state
+            // Do not allow destroyed/failed staged session to change state
+            if (mDestroyed || mStagedSessionFailed) return;
             mStagedSessionReady = false;
             mStagedSessionApplied = false;
             mStagedSessionFailed = true;
@@ -3350,7 +3351,8 @@
     /** {@hide} */
     void setStagedSessionApplied() {
         synchronized (mLock) {
-            if (mDestroyed) return; // Do not allow destroyed staged session to change state
+            // Do not allow destroyed/failed staged session to change state
+            if (mDestroyed || mStagedSessionFailed) return;
             mStagedSessionReady = false;
             mStagedSessionApplied = true;
             mStagedSessionFailed = false;
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index a726c8d..dbdcc4f 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -23643,8 +23643,20 @@
         }
     }
 
-    void onNewUserCreated(final int userId) {
-        mPermissionManager.onNewUserCreated(userId);
+    void onNewUserCreated(@UserIdInt int userId, boolean convertedFromPreCreated) {
+        if (DEBUG_PERMISSIONS) {
+            Slog.d(TAG, "onNewUserCreated(id=" + userId
+                    + ", convertedFromPreCreated=" + convertedFromPreCreated + ")");
+        }
+        if (!convertedFromPreCreated) {
+            mPermissionManager.onNewUserCreated(userId);
+            return;
+        }
+        if (!readPermissionStateForUser(userId)) {
+            // Could not read the existing permissions, re-grant them.
+            Slog.i(TAG, "re-granting permissions for pre-created user " + userId);
+            mPermissionManager.onNewUserCreated(userId);
+        }
     }
 
     boolean readPermissionStateForUser(@UserIdInt int userId) {
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index 89ed3c7..700f7be 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -260,7 +260,8 @@
     private static final int PACKAGE_MATCH_FLAGS =
             PackageManager.MATCH_DIRECT_BOOT_AWARE
                     | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
-                    | PackageManager.MATCH_UNINSTALLED_PACKAGES;
+                    | PackageManager.MATCH_UNINSTALLED_PACKAGES
+                    | PackageManager.MATCH_DISABLED_COMPONENTS;
 
     private static final int SYSTEM_APP_MASK =
             ApplicationInfo.FLAG_SYSTEM | ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
@@ -278,12 +279,6 @@
         }
     };
 
-    private static Predicate<ResolveInfo> ACTIVITY_NOT_SYSTEM_NOR_ENABLED = (ri) -> {
-        final ApplicationInfo ai = ri.activityInfo.applicationInfo;
-        final boolean isSystemApp = ai != null && (ai.flags & SYSTEM_APP_MASK) != 0;
-        return !isSystemApp && !ri.activityInfo.enabled;
-    };
-
     private static Predicate<ResolveInfo> ACTIVITY_NOT_INSTALLED = (ri) ->
             !isInstalled(ri.activityInfo);
 
@@ -3685,10 +3680,8 @@
         final long start = getStatStartTime();
         final long token = injectClearCallingIdentity();
         try {
-            return mIPackageManager.getPackageInfo(
-                    packageName, PACKAGE_MATCH_FLAGS | PackageManager.MATCH_DISABLED_COMPONENTS
-                            | (getSignatures ? PackageManager.GET_SIGNING_CERTIFICATES : 0),
-                    userId);
+            return mIPackageManager.getPackageInfo(packageName, PACKAGE_MATCH_FLAGS
+                    | (getSignatures ? PackageManager.GET_SIGNING_CERTIFICATES : 0), userId);
         } catch (RemoteException e) {
             // Shouldn't happen.
             Slog.wtf(TAG, "RemoteException", e);
@@ -3721,8 +3714,7 @@
         final long start = getStatStartTime();
         final long token = injectClearCallingIdentity();
         try {
-            return mIPackageManager.getApplicationInfo(packageName,
-                    PACKAGE_MATCH_FLAGS | PackageManager.MATCH_DISABLED_COMPONENTS, userId);
+            return mIPackageManager.getApplicationInfo(packageName, PACKAGE_MATCH_FLAGS, userId);
         } catch (RemoteException e) {
             // Shouldn't happen.
             Slog.wtf(TAG, "RemoteException", e);
@@ -3753,9 +3745,8 @@
         final long start = getStatStartTime();
         final long token = injectClearCallingIdentity();
         try {
-            return mIPackageManager.getActivityInfo(activity, (PACKAGE_MATCH_FLAGS
-                    | PackageManager.MATCH_DISABLED_COMPONENTS | PackageManager.GET_META_DATA),
-                    userId);
+            return mIPackageManager.getActivityInfo(activity,
+                    PACKAGE_MATCH_FLAGS | PackageManager.GET_META_DATA, userId);
         } catch (RemoteException e) {
             // Shouldn't happen.
             Slog.wtf(TAG, "RemoteException", e);
@@ -3800,8 +3791,7 @@
     List<PackageInfo> injectGetPackagesWithUninstalled(@UserIdInt int userId)
             throws RemoteException {
         final ParceledListSlice<PackageInfo> parceledList =
-                mIPackageManager.getInstalledPackages(
-                        PACKAGE_MATCH_FLAGS | PackageManager.MATCH_DISABLED_COMPONENTS, userId);
+                mIPackageManager.getInstalledPackages(PACKAGE_MATCH_FLAGS, userId);
         if (parceledList == null) {
             return Collections.emptyList();
         }
@@ -3836,6 +3826,41 @@
         return (ai != null) && ((ai.flags & flags) == flags);
     }
 
+    // Due to b/38267327, ActivityInfo.enabled may not reflect the current state of the component
+    // and we need to check the enabled state via PackageManager.getComponentEnabledSetting.
+    private boolean isEnabled(@Nullable ActivityInfo ai, int userId) {
+        if (ai == null) {
+            return false;
+        }
+
+        int enabledFlag;
+        final long token = injectClearCallingIdentity();
+        try {
+            enabledFlag = mIPackageManager.getComponentEnabledSetting(
+                    ai.getComponentName(), userId);
+        } catch (RemoteException e) {
+            // Shouldn't happen.
+            Slog.wtf(TAG, "RemoteException", e);
+            return false;
+        } finally {
+            injectRestoreCallingIdentity(token);
+        }
+
+        if ((enabledFlag == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT && ai.enabled)
+                || enabledFlag == PackageManager.COMPONENT_ENABLED_STATE_ENABLED) {
+            return true;
+        }
+        return false;
+    }
+
+    private static boolean isSystem(@Nullable ActivityInfo ai) {
+        return (ai != null) && isSystem(ai.applicationInfo);
+    }
+
+    private static boolean isSystem(@Nullable ApplicationInfo ai) {
+        return (ai != null) && (ai.flags & SYSTEM_APP_MASK) != 0;
+    }
+
     private static boolean isInstalled(@Nullable ApplicationInfo ai) {
         return (ai != null) && ai.enabled && (ai.flags & ApplicationInfo.FLAG_INSTALLED) != 0;
     }
@@ -3900,12 +3925,6 @@
         return intent;
     }
 
-    private static boolean isSystemApp(@Nullable final ApplicationInfo ai) {
-        final int systemAppMask =
-                ApplicationInfo.FLAG_SYSTEM | ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
-        return ai != null && ((ai.flags & systemAppMask) != 0);
-    }
-
     /**
      * Same as queryIntentActivitiesAsUser, except it makes sure the package is installed,
      * and only returns exported activities.
@@ -3938,7 +3957,10 @@
         }
         // Make sure the package is installed.
         resolved.removeIf(ACTIVITY_NOT_INSTALLED);
-        resolved.removeIf(ACTIVITY_NOT_SYSTEM_NOR_ENABLED);
+        resolved.removeIf((ri) -> {
+            final ActivityInfo ai = ri.activityInfo;
+            return !isSystem(ai) && !isEnabled(ai, userId);
+        });
         if (exportedOnly) {
             resolved.removeIf(ACTIVITY_NOT_EXPORTED);
         }
diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java
index 89bdb3e..f9bf54a 100644
--- a/services/core/java/com/android/server/pm/StagingManager.java
+++ b/services/core/java/com/android/server/pm/StagingManager.java
@@ -322,9 +322,6 @@
         }
         final long activeVersion = activePackage.applicationInfo.longVersionCode;
         if (activeVersion != session.params.requiredInstalledVersionCode) {
-            if (!mApexManager.abortStagedSession(session.sessionId)) {
-                Slog.e(TAG, "Failed to abort apex session " + session.sessionId);
-            }
             throw new PackageManagerException(
                     SessionInfo.STAGED_SESSION_VERIFICATION_FAILED,
                     "Installed version of APEX package " + activePackage.packageName
@@ -338,14 +335,11 @@
             throws PackageManagerException {
         final long activeVersion = activePackage.applicationInfo.longVersionCode;
         final long newVersionCode = newPackage.applicationInfo.longVersionCode;
-        boolean isAppDebuggable = (activePackage.applicationInfo.flags
+        final boolean isAppDebuggable = (activePackage.applicationInfo.flags
                 & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
         final boolean allowsDowngrade = PackageManagerServiceUtils.isDowngradePermitted(
                 session.params.installFlags, isAppDebuggable);
         if (activeVersion > newVersionCode && !allowsDowngrade) {
-            if (!mApexManager.abortStagedSession(session.sessionId)) {
-                Slog.e(TAG, "Failed to abort apex session " + session.sessionId);
-            }
             throw new PackageManagerException(
                     SessionInfo.STAGED_SESSION_VERIFICATION_FAILED,
                     "Downgrade of APEX package " + newPackage.packageName
@@ -835,37 +829,6 @@
         return null;
     }
 
-    private void verifyApksInSession(PackageInstallerSession session)
-            throws PackageManagerException {
-
-        final PackageInstallerSession apksToVerify = extractApksInSession(
-                session,  /* preReboot */ true);
-        if (apksToVerify == null) {
-            return;
-        }
-
-        final LocalIntentReceiverAsync receiver = new LocalIntentReceiverAsync(
-                (Intent result) -> {
-                    int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
-                            PackageInstaller.STATUS_FAILURE);
-                    if (status != PackageInstaller.STATUS_SUCCESS) {
-                        final String errorMessage = result.getStringExtra(
-                                PackageInstaller.EXTRA_STATUS_MESSAGE);
-                        Slog.e(TAG, "Failure to verify APK staged session "
-                                + session.sessionId + " [" + errorMessage + "]");
-                        session.setStagedSessionFailed(
-                                SessionInfo.STAGED_SESSION_VERIFICATION_FAILED, errorMessage);
-                        mPreRebootVerificationHandler.onPreRebootVerificationComplete(
-                                session.sessionId);
-                        return;
-                    }
-                    mPreRebootVerificationHandler.notifyPreRebootVerification_Apk_Complete(
-                            session.sessionId);
-                });
-
-        apksToVerify.commit(receiver.getIntentSender(), false);
-    }
-
     private void installApksInSession(@NonNull PackageInstallerSession session)
             throws PackageManagerException {
 
@@ -908,10 +871,21 @@
         mPreRebootVerificationHandler.startPreRebootVerification(session.sessionId);
     }
 
-    private int parentOrOwnSessionId(PackageInstallerSession session) {
+    private int getSessionIdForParentOrSelf(PackageInstallerSession session) {
         return session.hasParentSessionId() ? session.getParentSessionId() : session.sessionId;
     }
 
+    private PackageInstallerSession getParentSessionOrSelf(PackageInstallerSession session) {
+        return session.hasParentSessionId()
+                ? getStagedSession(session.getParentSessionId())
+                : session;
+    }
+
+    private boolean isRollback(PackageInstallerSession session) {
+        final PackageInstallerSession root = getParentSessionOrSelf(session);
+        return root.params.installReason == PackageManager.INSTALL_REASON_ROLLBACK;
+    }
+
     /**
      * <p> Check if the session provided is non-overlapping with the active staged sessions.
      *
@@ -937,6 +911,8 @@
         boolean supportsCheckpoint = ((StorageManager) mContext.getSystemService(
                 Context.STORAGE_SERVICE)).isCheckpointSupported();
 
+        final boolean isRollback = isRollback(session);
+
         synchronized (mStagedSessions) {
             for (int i = 0; i < mStagedSessions.size(); i++) {
                 final PackageInstallerSession stagedSession = mStagedSessions.valueAt(i);
@@ -951,8 +927,8 @@
                 }
                 // Check if stagedSession has an active parent session or not
                 if (stagedSession.hasParentSessionId()) {
-                    int parentId = stagedSession.getParentSessionId();
-                    PackageInstallerSession parentSession = mStagedSessions.get(parentId);
+                    final int parentId = stagedSession.getParentSessionId();
+                    final PackageInstallerSession parentSession = mStagedSessions.get(parentId);
                     if (parentSession == null || parentSession.isStagedAndInTerminalState()
                             || parentSession.isDestroyed()) {
                         // Parent session has been abandoned or terminated already
@@ -968,21 +944,37 @@
                     continue;
                 }
 
-                // If session is not among the active sessions, then it cannot have same package
-                // name as any of the active sessions.
+                // New session cannot have same package name as one of the active sessions
                 if (session.getPackageName().equals(stagedSession.getPackageName())) {
-                    throw new PackageManagerException(
-                            PackageManager.INSTALL_FAILED_OTHER_STAGED_SESSION_IN_PROGRESS,
-                            "Package: " + session.getPackageName() + " in session: "
-                                    + session.sessionId + " has been staged already by session: "
-                                    + stagedSession.sessionId, null);
+                    if (isRollback) {
+                        // If the new session is a rollback, then it gets priority. The existing
+                        // session is failed to unblock rollback.
+                        final PackageInstallerSession root = getParentSessionOrSelf(stagedSession);
+                        if (!ensureActiveApexSessionIsAborted(root)) {
+                            Slog.e(TAG, "Failed to abort apex session " + root.sessionId);
+                            // Safe to ignore active apex session abort failure since session
+                            // will be marked failed on next step and staging directory for session
+                            // will be deleted.
+                        }
+                        root.setStagedSessionFailed(
+                                SessionInfo.STAGED_SESSION_OTHER_ERROR,
+                                "Session was blocking rollback session: " + session.sessionId);
+                        Slog.i(TAG, "Session " + root.sessionId + " is marked failed due to "
+                                + "blocking rollback session: " + session.sessionId);
+                    } else {
+                        throw new PackageManagerException(
+                                PackageManager.INSTALL_FAILED_OTHER_STAGED_SESSION_IN_PROGRESS,
+                                "Package: " + session.getPackageName() + " in session: "
+                                        + session.sessionId + " has been staged already by session:"
+                                        + " " + stagedSession.sessionId, null);
+                    }
                 }
 
                 // Staging multiple root sessions is not allowed if device doesn't support
                 // checkpoint. If session and stagedSession do not have common ancestor, they are
                 // from two different root sessions.
-                if (!supportsCheckpoint
-                        && parentOrOwnSessionId(session) != parentOrOwnSessionId(stagedSession)) {
+                if (!supportsCheckpoint && getSessionIdForParentOrSelf(session)
+                        != getSessionIdForParentOrSelf(stagedSession)) {
                     throw new PackageManagerException(
                             PackageManager.INSTALL_FAILED_OTHER_STAGED_SESSION_IN_PROGRESS,
                             "Cannot stage multiple sessions without checkpoint support", null);
@@ -1042,23 +1034,11 @@
 
         // A session could be marked ready once its pre-reboot verification ends
         if (session.isStagedSessionReady()) {
-            if (sessionContainsApex(session)) {
-                try {
-                    ApexSessionInfo apexSession =
-                            mApexManager.getStagedSessionInfo(session.sessionId);
-                    if (apexSession == null || isApexSessionFinalized(apexSession)) {
-                        Slog.w(TAG,
-                                "Cannot abort session " + session.sessionId
-                                        + " because it is not active.");
-                    } else {
-                        mApexManager.abortStagedSession(session.sessionId);
-                    }
-                } catch (Exception e) {
-                    // Failed to contact apexd service. The apex might still be staged. We can still
-                    // safely cleanup the staged session since pre-reboot verification is complete.
-                    // Also, cleaning up the stageDir prevents the apex from being activated.
-                    Slog.w(TAG, "Could not contact apexd to abort staged session " + sessionId);
-                }
+            if (!ensureActiveApexSessionIsAborted(session)) {
+                // Failed to ensure apex session is aborted, so it can still be staged. We can still
+                // safely cleanup the staged session since pre-reboot verification is complete.
+                // Also, cleaning up the stageDir prevents the apex from being activated.
+                Slog.e(TAG, "Failed to abort apex session " + session.sessionId);
             }
         }
 
@@ -1068,6 +1048,22 @@
         return true;
     }
 
+    /**
+     * Ensure that there is no active apex session staged in apexd for the given session.
+     *
+     * @return returns true if it is ensured that there is no active apex session, otherwise false
+     */
+    private boolean ensureActiveApexSessionIsAborted(PackageInstallerSession session) {
+        if (!sessionContainsApex(session)) {
+            return true;
+        }
+        final ApexSessionInfo apexSession = mApexManager.getStagedSessionInfo(session.sessionId);
+        if (apexSession == null || isApexSessionFinalized(apexSession)) {
+            return true;
+        }
+        return mApexManager.abortStagedSession(session.sessionId);
+    }
+
     private boolean isApexSessionFinalized(ApexSessionInfo session) {
         /* checking if the session is in a final state, i.e., not active anymore */
         return session.isUnknown || session.isActivationFailed || session.isSuccess
@@ -1294,8 +1290,8 @@
                         + sessionId);
                 return;
             }
-            if (session.isDestroyed()) {
-                // No point in running verification on a destroyed session
+            if (session.isDestroyed() || session.isStagedSessionFailed()) {
+                // No point in running verification on a destroyed/failed session
                 onPreRebootVerificationComplete(sessionId);
                 return;
             }
@@ -1348,6 +1344,17 @@
             obtainMessage(MSG_PRE_REBOOT_VERIFICATION_START, sessionId, 0).sendToTarget();
         }
 
+        private void onPreRebootVerificationFailure(PackageInstallerSession session,
+                @SessionInfo.StagedSessionErrorCode int errorCode, String errorMessage) {
+            if (!ensureActiveApexSessionIsAborted(session)) {
+                Slog.e(TAG, "Failed to abort apex session " + session.sessionId);
+                // Safe to ignore active apex session abortion failure since session will be marked
+                // failed on next step and staging directory for session will be deleted.
+            }
+            session.setStagedSessionFailed(errorCode, errorMessage);
+            onPreRebootVerificationComplete(session.sessionId);
+        }
+
         // Things to do when pre-reboot verification completes for a particular sessionId
         private void onPreRebootVerificationComplete(int sessionId) {
             // Remove it from mVerificationRunning so that verification is considered complete
@@ -1432,8 +1439,7 @@
                         validateApexSignature(apexPackages.get(i));
                     }
                 } catch (PackageManagerException e) {
-                    session.setStagedSessionFailed(e.error, e.getMessage());
-                    onPreRebootVerificationComplete(session.sessionId);
+                    onPreRebootVerificationFailure(session, e.error, e.getMessage());
                     return;
                 }
 
@@ -1460,16 +1466,42 @@
             try {
                 Slog.d(TAG, "Running a pre-reboot verification for APKs in session "
                         + session.sessionId + " by performing a dry-run install");
-
                 // verifyApksInSession will notify the handler when APK verification is complete
                 verifyApksInSession(session);
-                // TODO(b/118865310): abort the session on apexd.
             } catch (PackageManagerException e) {
-                session.setStagedSessionFailed(e.error, e.getMessage());
-                onPreRebootVerificationComplete(session.sessionId);
+                onPreRebootVerificationFailure(session, e.error, e.getMessage());
             }
         }
 
+        private void verifyApksInSession(PackageInstallerSession session)
+                throws PackageManagerException {
+
+            final PackageInstallerSession apksToVerify = extractApksInSession(
+                    session,  /* preReboot */ true);
+            if (apksToVerify == null) {
+                return;
+            }
+
+            final LocalIntentReceiverAsync receiver = new LocalIntentReceiverAsync(
+                    (Intent result) -> {
+                        final int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
+                                PackageInstaller.STATUS_FAILURE);
+                        if (status != PackageInstaller.STATUS_SUCCESS) {
+                            final String errorMessage = result.getStringExtra(
+                                    PackageInstaller.EXTRA_STATUS_MESSAGE);
+                            Slog.e(TAG, "Failure to verify APK staged session "
+                                    + session.sessionId + " [" + errorMessage + "]");
+                            onPreRebootVerificationFailure(session,
+                                    SessionInfo.STAGED_SESSION_ACTIVATION_FAILED, errorMessage);
+                            return;
+                        }
+                        mPreRebootVerificationHandler.notifyPreRebootVerification_Apk_Complete(
+                                session.sessionId);
+                    });
+
+            apksToVerify.commit(receiver.getIntentSender(), false);
+        }
+
         /**
          * Pre-reboot verification state for wrapping up:
          * <p><ul>
@@ -1487,9 +1519,8 @@
             } catch (Exception e) {
                 // Failed to get hold of StorageManager
                 Slog.e(TAG, "Failed to get hold of StorageManager", e);
-                session.setStagedSessionFailed(SessionInfo.STAGED_SESSION_UNKNOWN,
+                onPreRebootVerificationFailure(session, SessionInfo.STAGED_SESSION_UNKNOWN,
                         "Failed to get hold of StorageManager");
-                onPreRebootVerificationComplete(session.sessionId);
                 return;
             }
 
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 8f11fd5..e3bee72 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -3497,7 +3497,7 @@
             }
 
             t.traceBegin("PM.onNewUserCreated-" + userId);
-            mPm.onNewUserCreated(userId);
+            mPm.onNewUserCreated(userId, /* convertedFromPreCreated= */ false);
             t.traceEnd();
             if (preCreate) {
                 // Must start user (which will be stopped right away, through
@@ -3570,10 +3570,7 @@
             writeUserListLP();
         }
         updateUserIds();
-        if (!mPm.readPermissionStateForUser(preCreatedUser.id)) {
-            // Could not read the existing permissions, re-grant them.
-            mPm.onNewUserCreated(preCreatedUser.id);
-        }
+        mPm.onNewUserCreated(preCreatedUser.id, /* convertedFromPreCreated= */ true);
         dispatchUserAdded(preCreatedUser);
         return preCreatedUser;
     }
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index b72ff2d..1b4fac6 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -70,7 +70,7 @@
 import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_TOP;
 import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_ALWAYS;
 import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_DEFAULT;
-import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_IF_WHITELISTED;
+import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_IF_ALLOWLISTED;
 import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_NEVER;
 import static android.content.pm.ActivityInfo.PERSIST_ACROSS_REBOOTS;
 import static android.content.pm.ActivityInfo.PERSIST_ROOT_ONLY;
@@ -168,7 +168,7 @@
 import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_FREE_RESIZE;
 import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_NONE;
 import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_WINDOWING_MODE_RESIZE;
-import static com.android.server.wm.ActivityTaskManagerService.getInputDispatchingTimeoutLocked;
+import static com.android.server.wm.ActivityTaskManagerService.getInputDispatchingTimeoutMillisLocked;
 import static com.android.server.wm.IdentifierProto.HASH_CODE;
 import static com.android.server.wm.IdentifierProto.TITLE;
 import static com.android.server.wm.IdentifierProto.USER_ID;
@@ -587,8 +587,8 @@
      */
     private boolean mOccludesParent;
 
-    // The input dispatching timeout for this application token in nanoseconds.
-    long mInputDispatchingTimeoutNanos;
+    // The input dispatching timeout for this application token in milliseconds.
+    long mInputDispatchingTimeoutMillis;
 
     private boolean mShowWhenLocked;
     private boolean mInheritShownWhenLocked;
@@ -1245,7 +1245,7 @@
         if (oldParent == null && newParent != null) {
             // First time we are adding the activity to the system.
             mVoiceInteraction = newTask.voiceSession != null;
-            mInputDispatchingTimeoutNanos = getInputDispatchingTimeoutLocked(this) * 1000000L;
+            mInputDispatchingTimeoutMillis = getInputDispatchingTimeoutMillisLocked(this);
 
             // TODO(b/36505427): Maybe this call should be moved inside
             // updateOverrideConfiguration()
@@ -1683,7 +1683,7 @@
         if (options != null) {
             final boolean useLockTask = options.getLockTaskMode();
             if (useLockTask && lockTaskLaunchMode == LOCK_TASK_LAUNCH_MODE_DEFAULT) {
-                lockTaskLaunchMode = LOCK_TASK_LAUNCH_MODE_IF_WHITELISTED;
+                lockTaskLaunchMode = LOCK_TASK_LAUNCH_MODE_IF_ALLOWLISTED;
             }
         }
         return lockTaskLaunchMode;
@@ -5651,8 +5651,9 @@
         } else {
             // In this case another process added windows using this activity token. So, we call the
             // generic service input dispatch timed out method so that the right process is blamed.
-            return mAtmService.mAmInternal.inputDispatchingTimedOut(
-                    windowPid, false /* aboveSystem */, reason) < 0;
+            long timeoutMillis = mAtmService.mAmInternal.inputDispatchingTimedOut(
+                    windowPid, false /* aboveSystem */, reason);
+            return timeoutMillis <= 0;
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
index ed1ea35..a05289f 100644
--- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
@@ -74,9 +74,9 @@
 import static com.android.server.wm.Task.ActivityState.PAUSED;
 import static com.android.server.wm.Task.ActivityState.PAUSING;
 import static com.android.server.wm.Task.FLAG_FORCE_HIDDEN_FOR_PINNED_TASK;
+import static com.android.server.wm.Task.LOCK_TASK_AUTH_ALLOWLISTED;
 import static com.android.server.wm.Task.LOCK_TASK_AUTH_LAUNCHABLE;
 import static com.android.server.wm.Task.LOCK_TASK_AUTH_LAUNCHABLE_PRIV;
-import static com.android.server.wm.Task.LOCK_TASK_AUTH_WHITELISTED;
 import static com.android.server.wm.Task.REPARENT_KEEP_STACK_AT_FRONT;
 import static com.android.server.wm.Task.TAG_CLEANUP;
 import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS;
@@ -788,7 +788,7 @@
             final LockTaskController lockTaskController = mService.getLockTaskController();
             if (task.mLockTaskAuth == LOCK_TASK_AUTH_LAUNCHABLE
                     || task.mLockTaskAuth == LOCK_TASK_AUTH_LAUNCHABLE_PRIV
-                    || (task.mLockTaskAuth == LOCK_TASK_AUTH_WHITELISTED
+                    || (task.mLockTaskAuth == LOCK_TASK_AUTH_ALLOWLISTED
                             && lockTaskController.getLockTaskModeState()
                                     == LOCK_TASK_MODE_LOCKED)) {
                 lockTaskController.startLockTaskMode(task, false, 0 /* blank UID */);
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
index 777ddda..2dc22ec 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
@@ -528,8 +528,8 @@
     public abstract void onActiveUidsCleared();
     public abstract void onUidProcStateChanged(int uid, int procState);
 
-    public abstract void onUidAddedToPendingTempWhitelist(int uid, String tag);
-    public abstract void onUidRemovedFromPendingTempWhitelist(int uid);
+    public abstract void onUidAddedToPendingTempAllowlist(int uid, String tag);
+    public abstract void onUidRemovedFromPendingTempAllowlist(int uid);
 
     /** Handle app crash event in {@link android.app.IActivityController} if there is one. */
     public abstract boolean handleAppCrashInActivityController(String processName, int pid,
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 5534b8c..627361d 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -50,6 +50,7 @@
 import static android.os.FactoryTest.FACTORY_TEST_HIGH_LEVEL;
 import static android.os.FactoryTest.FACTORY_TEST_LOW_LEVEL;
 import static android.os.FactoryTest.FACTORY_TEST_OFF;
+import static android.os.IInputConstants.DEFAULT_DISPATCHING_TIMEOUT_MILLIS;
 import static android.os.Process.FIRST_APPLICATION_UID;
 import static android.os.Process.SYSTEM_UID;
 import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
@@ -182,7 +183,6 @@
 import android.graphics.Bitmap;
 import android.graphics.Point;
 import android.graphics.Rect;
-import android.metrics.LogMaker;
 import android.net.Uri;
 import android.os.Binder;
 import android.os.Build;
@@ -237,12 +237,9 @@
 import com.android.internal.app.AssistUtils;
 import com.android.internal.app.IVoiceInteractor;
 import com.android.internal.app.ProcessMap;
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
 import com.android.internal.notification.SystemNotificationChannels;
 import com.android.internal.os.TransferPipe;
-import com.android.internal.os.logging.MetricsLoggerWrapper;
 import com.android.internal.policy.IKeyguardDismissCallback;
 import com.android.internal.policy.KeyguardDismissCallback;
 import com.android.internal.util.ArrayUtils;
@@ -314,10 +311,8 @@
     private static final String TAG_LOCKTASK = TAG + POSTFIX_LOCKTASK;
     private static final String TAG_CONFIGURATION = TAG + POSTFIX_CONFIGURATION;
 
-    // How long we wait until we timeout on key dispatching.
-    public static final int KEY_DISPATCHING_TIMEOUT_MS = 5 * 1000;
     // How long we wait until we timeout on key dispatching during instrumentation.
-    static final int INSTRUMENTATION_KEY_DISPATCHING_TIMEOUT_MS = 60 * 1000;
+    static final long INSTRUMENTATION_KEY_DISPATCHING_TIMEOUT_MILLIS = 60 * 1000;
     // How long we permit background activity starts after an activity in the process
     // started or finished.
     static final long ACTIVITY_BG_START_GRACE_PERIOD_MS = 10 * 1000;
@@ -385,7 +380,7 @@
     private AppOpsManager mAppOpsManager;
     /** All active uids in the system. */
     private final MirrorActiveUids mActiveUids = new MirrorActiveUids();
-    private final SparseArray<String> mPendingTempWhitelist = new SparseArray<>();
+    private final SparseArray<String> mPendingTempAllowlist = new SparseArray<>();
     /** All processes currently running that might have a window organized by name. */
     final ProcessMap<WindowProcessController> mProcessNames = new ProcessMap<>();
     /** All processes we currently have running mapped by pid and uid */
@@ -1111,7 +1106,7 @@
 
     @Override
     public int startActivityIntentSender(IApplicationThread caller, IIntentSender target,
-            IBinder whitelistToken, Intent fillInIntent, String resolvedType, IBinder resultTo,
+            IBinder allowlistToken, Intent fillInIntent, String resolvedType, IBinder resultTo,
             String resultWho, int requestCode, int flagsMask, int flagsValues, Bundle bOptions) {
         enforceNotIsolatedCaller("startActivityIntentSender");
         // Refuse possible leaked file descriptors
@@ -1134,7 +1129,7 @@
                 mAppSwitchesAllowedTime = 0;
             }
         }
-        return pir.sendInner(0, fillInIntent, resolvedType, whitelistToken, null, null,
+        return pir.sendInner(0, fillInIntent, resolvedType, allowlistToken, null, null,
                 resultTo, resultWho, requestCode, flagsMask, flagsValues, bOptions);
     }
 
@@ -3038,7 +3033,7 @@
         // system or a specific app.
         // * System-initiated requests will only start the pinned mode (screen pinning)
         // * App-initiated requests
-        //   - will put the device in fully locked mode (LockTask), if the app is whitelisted
+        //   - will put the device in fully locked mode (LockTask), if the app is allowlisted
         //   - will start the pinned mode, otherwise
         final int callingUid = Binder.getCallingUid();
         long ident = Binder.clearCallingIdentity();
@@ -3078,7 +3073,7 @@
                     "updateLockTaskPackages()");
         }
         synchronized (mGlobalLock) {
-            if (DEBUG_LOCKTASK) Slog.w(TAG_LOCKTASK, "Whitelisting " + userId + ":"
+            if (DEBUG_LOCKTASK) Slog.w(TAG_LOCKTASK, "Allowlisting " + userId + ":"
                     + Arrays.toString(packages));
             getLockTaskController().updateLockTaskPackages(userId, packages);
         }
@@ -4096,10 +4091,6 @@
                         final Task stack = r.getRootTask();
                         stack.setPictureInPictureAspectRatio(aspectRatio);
                         stack.setPictureInPictureActions(actions);
-                        MetricsLoggerWrapper.logPictureInPictureEnter(mContext,
-                                r.info.applicationInfo.uid, r.shortComponentName,
-                                r.supportsEnterPipOnTaskSwitch);
-                        logPictureInPictureArgs(params);
                     }
                 };
 
@@ -4143,7 +4134,6 @@
                             r.pictureInPictureArgs.getAspectRatio());
                     stack.setPictureInPictureActions(r.pictureInPictureArgs.getActions());
                 }
-                logPictureInPictureArgs(params);
             }
         } finally {
             Binder.restoreCallingIdentity(origId);
@@ -4157,18 +4147,6 @@
         return 3;
     }
 
-    private void logPictureInPictureArgs(PictureInPictureParams params) {
-        if (params.hasSetActions()) {
-            MetricsLogger.histogram(mContext, "tron_varz_picture_in_picture_actions_count",
-                    params.getActions().size());
-        }
-        if (params.hasSetAspectRatio()) {
-            LogMaker lm = new LogMaker(MetricsEvent.ACTION_PICTURE_IN_PICTURE_ASPECT_RATIO_CHANGED);
-            lm.addTaggedData(MetricsEvent.PICTURE_IN_PICTURE_ASPECT_RATIO, params.getAspectRatio());
-            MetricsLogger.action(lm);
-        }
-    }
-
     /**
      * Checks the state of the system and the activity associated with the given {@param token} to
      * verify that picture-in-picture is supported for that activity.
@@ -5376,15 +5354,18 @@
         }
     }
 
-    static long getInputDispatchingTimeoutLocked(ActivityRecord r) {
+    static long getInputDispatchingTimeoutMillisLocked(ActivityRecord r) {
         if (r == null || !r.hasProcess()) {
-            return KEY_DISPATCHING_TIMEOUT_MS;
+            return DEFAULT_DISPATCHING_TIMEOUT_MILLIS;
         }
-        return getInputDispatchingTimeoutLocked(r.app);
+        return getInputDispatchingTimeoutMillisLocked(r.app);
     }
 
-    private static long getInputDispatchingTimeoutLocked(WindowProcessController r) {
-        return r != null ? r.getInputDispatchingTimeout() : KEY_DISPATCHING_TIMEOUT_MS;
+    private static long getInputDispatchingTimeoutMillisLocked(WindowProcessController r) {
+        if (r == null) {
+            return DEFAULT_DISPATCHING_TIMEOUT_MILLIS;
+        }
+        return r.getInputDispatchingTimeoutMillis();
     }
 
     /**
@@ -5977,11 +5958,11 @@
     }
 
     /**
-     * @return whitelist tag for a uid from mPendingTempWhitelist, null if not currently on
-     * the whitelist
+     * @return allowlist tag for a uid from mPendingTempAllowlist, null if not currently on
+     * the allowlist
      */
-    String getPendingTempWhitelistTagForUidLocked(int uid) {
-        return mPendingTempWhitelist.get(uid);
+    String getPendingTempAllowlistTagForUidLocked(int uid) {
+        return mPendingTempAllowlist.get(uid);
     }
 
     void logAppTooSlow(WindowProcessController app, long startTime, String msg) {
@@ -7323,16 +7304,16 @@
         }
 
         @Override
-        public void onUidAddedToPendingTempWhitelist(int uid, String tag) {
+        public void onUidAddedToPendingTempAllowlist(int uid, String tag) {
             synchronized (mGlobalLockWithoutBoost) {
-                mPendingTempWhitelist.put(uid, tag);
+                mPendingTempAllowlist.put(uid, tag);
             }
         }
 
         @Override
-        public void onUidRemovedFromPendingTempWhitelist(int uid) {
+        public void onUidRemovedFromPendingTempAllowlist(int uid) {
             synchronized (mGlobalLockWithoutBoost) {
-                mPendingTempWhitelist.remove(uid);
+                mPendingTempAllowlist.remove(uid);
             }
         }
 
diff --git a/services/core/java/com/android/server/wm/DragState.java b/services/core/java/com/android/server/wm/DragState.java
index f840d92..22dd1d3 100644
--- a/services/core/java/com/android/server/wm/DragState.java
+++ b/services/core/java/com/android/server/wm/DragState.java
@@ -16,6 +16,8 @@
 
 package com.android.server.wm;
 
+import static android.os.IInputConstants.DEFAULT_DISPATCHING_TIMEOUT_MILLIS;
+
 import static com.android.server.wm.DragDropController.MSG_ANIMATION_END;
 import static com.android.server.wm.DragDropController.MSG_DRAG_END_TIMEOUT;
 import static com.android.server.wm.DragDropController.MSG_TEAR_DOWN_DRAG_AND_DROP_INPUT;
@@ -271,8 +273,7 @@
 
             mDragApplicationHandle = new InputApplicationHandle(new Binder());
             mDragApplicationHandle.name = "drag";
-            mDragApplicationHandle.dispatchingTimeoutNanos =
-                    WindowManagerService.DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS;
+            mDragApplicationHandle.dispatchingTimeoutMillis = DEFAULT_DISPATCHING_TIMEOUT_MILLIS;
 
             mDragWindowHandle = new InputWindowHandle(mDragApplicationHandle,
                     display.getDisplayId());
@@ -280,8 +281,7 @@
             mDragWindowHandle.token = mServerChannel.getToken();
             mDragWindowHandle.layoutParamsFlags = 0;
             mDragWindowHandle.layoutParamsType = WindowManager.LayoutParams.TYPE_DRAG;
-            mDragWindowHandle.dispatchingTimeoutNanos =
-                    WindowManagerService.DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS;
+            mDragWindowHandle.dispatchingTimeoutMillis = DEFAULT_DISPATCHING_TIMEOUT_MILLIS;
             mDragWindowHandle.visible = true;
             mDragWindowHandle.canReceiveKeys = false;
             mDragWindowHandle.hasFocus = true;
diff --git a/services/core/java/com/android/server/wm/InputConsumerImpl.java b/services/core/java/com/android/server/wm/InputConsumerImpl.java
index 852b3672..3b24584b 100644
--- a/services/core/java/com/android/server/wm/InputConsumerImpl.java
+++ b/services/core/java/com/android/server/wm/InputConsumerImpl.java
@@ -16,6 +16,8 @@
 
 package com.android.server.wm;
 
+import static android.os.IInputConstants.DEFAULT_DISPATCHING_TIMEOUT_MILLIS;
+
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.os.Binder;
@@ -69,16 +71,14 @@
 
         mApplicationHandle = new InputApplicationHandle(new Binder());
         mApplicationHandle.name = name;
-        mApplicationHandle.dispatchingTimeoutNanos =
-                WindowManagerService.DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS;
+        mApplicationHandle.dispatchingTimeoutMillis = DEFAULT_DISPATCHING_TIMEOUT_MILLIS;
 
         mWindowHandle = new InputWindowHandle(mApplicationHandle, displayId);
         mWindowHandle.name = name;
         mWindowHandle.token = mServerChannel.getToken();
         mWindowHandle.layoutParamsType = WindowManager.LayoutParams.TYPE_INPUT_CONSUMER;
         mWindowHandle.layoutParamsFlags = 0;
-        mWindowHandle.dispatchingTimeoutNanos =
-                WindowManagerService.DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS;
+        mWindowHandle.dispatchingTimeoutMillis = DEFAULT_DISPATCHING_TIMEOUT_MILLIS;
         mWindowHandle.visible = true;
         mWindowHandle.canReceiveKeys = false;
         mWindowHandle.hasFocus = false;
diff --git a/services/core/java/com/android/server/wm/InputManagerCallback.java b/services/core/java/com/android/server/wm/InputManagerCallback.java
index 9c4ac89..e166bfc 100644
--- a/services/core/java/com/android/server/wm/InputManagerCallback.java
+++ b/services/core/java/com/android/server/wm/InputManagerCallback.java
@@ -10,6 +10,7 @@
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 import static com.android.server.wm.WindowManagerService.H.ON_POINTER_DOWN_OUTSIDE_FOCUS;
 
+import android.annotation.Nullable;
 import android.os.Build;
 import android.os.Debug;
 import android.os.IBinder;
@@ -173,23 +174,23 @@
      */
     @Override
     public long notifyANR(InputApplicationHandle inputApplicationHandle, IBinder token,
-            String reason) {
+            @Nullable Integer pid, String reason) {
         final long startTime = SystemClock.uptimeMillis();
         try {
-            return notifyANRInner(inputApplicationHandle, token, reason);
+            return notifyANRInner(inputApplicationHandle, token, pid, reason);
         } finally {
             // Log the time because the method is called from InputDispatcher thread. It shouldn't
-            // take too long that may affect input response time.
+            // take too long because it blocks input while executing.
             Slog.d(TAG_WM, "notifyANR took " + (SystemClock.uptimeMillis() - startTime) + "ms");
         }
     }
 
     private long notifyANRInner(InputApplicationHandle inputApplicationHandle, IBinder token,
-            String reason) {
+            @Nullable Integer pid, String reason) {
         ActivityRecord activity = null;
         WindowState windowState = null;
         boolean aboveSystem = false;
-        int windowPid = INVALID_PID;
+        int windowPid = pid != null ? pid : INVALID_PID;
 
         preDumpIfLockTooSlow();
 
@@ -258,18 +259,14 @@
             if (!abort) {
                 // The activity manager declined to abort dispatching.
                 // Wait a bit longer and timeout again later.
-                return activity.mInputDispatchingTimeoutNanos;
+                return TimeUnit.MILLISECONDS.toNanos(activity.mInputDispatchingTimeoutMillis);
             }
         } else if (windowState != null || windowPid != INVALID_PID) {
             // Notify the activity manager about the timeout and let it decide whether
             // to abort dispatching or keep waiting.
-            long timeout = mService.mAmInternal.inputDispatchingTimedOut(windowPid, aboveSystem,
-                    reason);
-            if (timeout >= 0) {
-                // The activity manager declined to abort dispatching.
-                // Wait a bit longer and timeout again later.
-                return timeout * 1000000L; // nanoseconds
-            }
+            long timeoutMillis =
+                    mService.mAmInternal.inputDispatchingTimedOut(windowPid, aboveSystem, reason);
+            return TimeUnit.MILLISECONDS.toNanos(timeoutMillis);
         }
         return 0; // abort dispatching
     }
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index 20f1b9f..791f471 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -269,7 +269,7 @@
         flags = child.getSurfaceTouchableRegion(inputWindowHandle, flags);
         inputWindowHandle.layoutParamsFlags = flags;
         inputWindowHandle.layoutParamsType = type;
-        inputWindowHandle.dispatchingTimeoutNanos = child.getInputDispatchingTimeoutNanos();
+        inputWindowHandle.dispatchingTimeoutMillis = child.getInputDispatchingTimeoutMillis();
         inputWindowHandle.visible = isVisible;
         inputWindowHandle.canReceiveKeys = child.canReceiveKeys();
         inputWindowHandle.hasFocus = hasFocus;
@@ -385,7 +385,7 @@
         } else {
             final InputApplicationHandle handle = newApp.mInputApplicationHandle;
             handle.name = newApp.toString();
-            handle.dispatchingTimeoutNanos = newApp.mInputDispatchingTimeoutNanos;
+            handle.dispatchingTimeoutMillis = newApp.mInputDispatchingTimeoutMillis;
 
             mService.mInputManager.setFocusedApplication(mDisplayId, handle);
         }
@@ -570,8 +570,7 @@
             final String name, final int type, final boolean isVisible) {
         inputWindowHandle.name = name;
         inputWindowHandle.layoutParamsType = type;
-        inputWindowHandle.dispatchingTimeoutNanos =
-                WindowManagerService.DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS;
+        inputWindowHandle.dispatchingTimeoutMillis = 0; // it should never receive input
         inputWindowHandle.visible = isVisible;
         inputWindowHandle.canReceiveKeys = false;
         inputWindowHandle.hasFocus = false;
diff --git a/services/core/java/com/android/server/wm/Letterbox.java b/services/core/java/com/android/server/wm/Letterbox.java
index 28dcbcd..dccd3a6 100644
--- a/services/core/java/com/android/server/wm/Letterbox.java
+++ b/services/core/java/com/android/server/wm/Letterbox.java
@@ -16,6 +16,7 @@
 
 package com.android.server.wm;
 
+import static android.os.IInputConstants.DEFAULT_DISPATCHING_TIMEOUT_MILLIS;
 import static android.view.SurfaceControl.HIDDEN;
 
 import android.graphics.Point;
@@ -217,8 +218,7 @@
                     | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH
                     | WindowManager.LayoutParams.FLAG_SLIPPERY;
             mWindowHandle.layoutParamsType = WindowManager.LayoutParams.TYPE_INPUT_CONSUMER;
-            mWindowHandle.dispatchingTimeoutNanos =
-                    WindowManagerService.DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS;
+            mWindowHandle.dispatchingTimeoutMillis = DEFAULT_DISPATCHING_TIMEOUT_MILLIS;
             mWindowHandle.visible = true;
             mWindowHandle.ownerPid = Process.myPid();
             mWindowHandle.ownerUid = Process.myUid();
diff --git a/services/core/java/com/android/server/wm/LockTaskController.java b/services/core/java/com/android/server/wm/LockTaskController.java
index c7a438d..8ef57f7 100644
--- a/services/core/java/com/android/server/wm/LockTaskController.java
+++ b/services/core/java/com/android/server/wm/LockTaskController.java
@@ -33,11 +33,11 @@
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_LOCKTASK;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.wm.Task.LOCK_TASK_AUTH_ALLOWLISTED;
 import static com.android.server.wm.Task.LOCK_TASK_AUTH_DONT_LOCK;
 import static com.android.server.wm.Task.LOCK_TASK_AUTH_LAUNCHABLE;
 import static com.android.server.wm.Task.LOCK_TASK_AUTH_LAUNCHABLE_PRIV;
 import static com.android.server.wm.Task.LOCK_TASK_AUTH_PINNABLE;
-import static com.android.server.wm.Task.LOCK_TASK_AUTH_WHITELISTED;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -264,12 +264,12 @@
     }
 
     /**
-     * @return whether the requested task is allowed to be locked (either whitelisted, or declares
+     * @return whether the requested task is allowed to be locked (either allowlisted, or declares
      * lockTaskMode="always" in the manifest).
      */
-    boolean isTaskWhitelisted(Task task) {
+    boolean isTaskAllowlisted(Task task) {
         switch(task.mLockTaskAuth) {
-            case LOCK_TASK_AUTH_WHITELISTED:
+            case LOCK_TASK_AUTH_ALLOWLISTED:
             case LOCK_TASK_AUTH_LAUNCHABLE:
             case LOCK_TASK_AUTH_LAUNCHABLE_PRIV:
                 return true;
@@ -311,7 +311,7 @@
 
     private boolean isLockTaskModeViolationInternal(Task task, boolean isNewClearTask) {
         // TODO: Double check what's going on here. If the task is already in lock task mode, it's
-        // likely whitelisted, so will return false below.
+        // likely allowlisted, so will return false below.
         if (isTaskLocked(task) && !isNewClearTask) {
             // If the task is already at the top and won't be cleared, then allow the operation
             return false;
@@ -327,7 +327,7 @@
             return false;
         }
 
-        return !(isTaskWhitelisted(task) || mLockTaskModeTasks.isEmpty());
+        return !(isTaskAllowlisted(task) || mLockTaskModeTasks.isEmpty());
     }
 
     private boolean isRecentsAllowed(int userId) {
@@ -356,7 +356,7 @@
                 return false;
             default:
         }
-        return isPackageWhitelisted(userId, packageName);
+        return isPackageAllowlisted(userId, packageName);
     }
 
     private boolean isEmergencyCallTask(Task task) {
@@ -556,7 +556,7 @@
         if (!isSystemCaller) {
             task.mLockTaskUid = callingUid;
             if (task.mLockTaskAuth == LOCK_TASK_AUTH_PINNABLE) {
-                // startLockTask() called by app, but app is not part of lock task whitelist. Show
+                // startLockTask() called by app, but app is not part of lock task allowlist. Show
                 // app pinning request. We will come back here with isSystemCaller true.
                 if (DEBUG_LOCKTASK) Slog.w(TAG_LOCKTASK, "Mode default, asking user");
                 StatusBarManagerInternal statusBarManager = LocalServices.getService(
@@ -649,8 +649,8 @@
 
     /**
      * Update packages that are allowed to be launched in lock task mode.
-     * @param userId Which user this whitelist is associated with
-     * @param packages The whitelist of packages allowed in lock task mode
+     * @param userId Which user this allowlist is associated with
+     * @param packages The allowlist of packages allowed in lock task mode
      * @see #mLockTaskPackages
      */
     void updateLockTaskPackages(int userId, String[] packages) {
@@ -659,19 +659,19 @@
         boolean taskChanged = false;
         for (int taskNdx = mLockTaskModeTasks.size() - 1; taskNdx >= 0; --taskNdx) {
             final Task lockedTask = mLockTaskModeTasks.get(taskNdx);
-            final boolean wasWhitelisted = lockedTask.mLockTaskAuth == LOCK_TASK_AUTH_LAUNCHABLE
-                    || lockedTask.mLockTaskAuth == LOCK_TASK_AUTH_WHITELISTED;
+            final boolean wasAllowlisted = lockedTask.mLockTaskAuth == LOCK_TASK_AUTH_LAUNCHABLE
+                    || lockedTask.mLockTaskAuth == LOCK_TASK_AUTH_ALLOWLISTED;
             lockedTask.setLockTaskAuth();
-            final boolean isWhitelisted = lockedTask.mLockTaskAuth == LOCK_TASK_AUTH_LAUNCHABLE
-                    || lockedTask.mLockTaskAuth == LOCK_TASK_AUTH_WHITELISTED;
+            final boolean isAllowlisted = lockedTask.mLockTaskAuth == LOCK_TASK_AUTH_LAUNCHABLE
+                    || lockedTask.mLockTaskAuth == LOCK_TASK_AUTH_ALLOWLISTED;
 
             if (mLockTaskModeState != LOCK_TASK_MODE_LOCKED
                     || lockedTask.mUserId != userId
-                    || !wasWhitelisted || isWhitelisted) {
+                    || !wasAllowlisted || isAllowlisted) {
                 continue;
             }
 
-            // Terminate locked tasks that have recently lost whitelist authorization.
+            // Terminate locked tasks that have recently lost allowlist authorization.
             if (DEBUG_LOCKTASK) Slog.d(TAG_LOCKTASK, "onLockTaskPackagesUpdated: removing " +
                     lockedTask + " mLockTaskAuth()=" + lockedTask.lockTaskAuthToString());
             removeLockedTask(lockedTask);
@@ -697,17 +697,17 @@
         }
     }
 
-    boolean isPackageWhitelisted(int userId, String pkg) {
+    boolean isPackageAllowlisted(int userId, String pkg) {
         if (pkg == null) {
             return false;
         }
-        String[] whitelist;
-        whitelist = mLockTaskPackages.get(userId);
-        if (whitelist == null) {
+        String[] allowlist;
+        allowlist = mLockTaskPackages.get(userId);
+        if (allowlist == null) {
             return false;
         }
-        for (String whitelistedPkg : whitelist) {
-            if (pkg.equals(whitelistedPkg)) {
+        for (String allowlistedPkg : allowlist) {
+            if (pkg.equals(allowlistedPkg)) {
                 return true;
             }
         }
diff --git a/services/core/java/com/android/server/wm/PolicyControl.java b/services/core/java/com/android/server/wm/PolicyControl.java
index 0f92bc8..61b6e0b 100644
--- a/services/core/java/com/android/server/wm/PolicyControl.java
+++ b/services/core/java/com/android/server/wm/PolicyControl.java
@@ -196,40 +196,40 @@
         private static final String ALL = "*";
         private static final String APPS = "apps";
 
-        private final ArraySet<String> mWhitelist;
-        private final ArraySet<String> mBlacklist;
+        private final ArraySet<String> mAllowlist;
+        private final ArraySet<String> mDenylist;
 
-        private Filter(ArraySet<String> whitelist, ArraySet<String> blacklist) {
-            mWhitelist = whitelist;
-            mBlacklist = blacklist;
+        private Filter(ArraySet<String> allowlist, ArraySet<String> denylist) {
+            mAllowlist = allowlist;
+            mDenylist = denylist;
         }
 
         boolean matches(LayoutParams attrs) {
             if (attrs == null) return false;
             boolean isApp = attrs.type >= WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW
                     && attrs.type <= WindowManager.LayoutParams.LAST_APPLICATION_WINDOW;
-            if (isApp && mBlacklist.contains(APPS)) return false;
-            if (onBlacklist(attrs.packageName)) return false;
-            if (isApp && mWhitelist.contains(APPS)) return true;
-            return onWhitelist(attrs.packageName);
+            if (isApp && mDenylist.contains(APPS)) return false;
+            if (onDenylist(attrs.packageName)) return false;
+            if (isApp && mAllowlist.contains(APPS)) return true;
+            return onAllowlist(attrs.packageName);
         }
 
         boolean matches(String packageName) {
-            return !onBlacklist(packageName) && onWhitelist(packageName);
+            return !onDenylist(packageName) && onAllowlist(packageName);
         }
 
-        private boolean onBlacklist(String packageName) {
-            return mBlacklist.contains(packageName) || mBlacklist.contains(ALL);
+        private boolean onDenylist(String packageName) {
+            return mDenylist.contains(packageName) || mDenylist.contains(ALL);
         }
 
-        private boolean onWhitelist(String packageName) {
-            return mWhitelist.contains(ALL) || mWhitelist.contains(packageName);
+        private boolean onAllowlist(String packageName) {
+            return mAllowlist.contains(ALL) || mAllowlist.contains(packageName);
         }
 
         void dump(PrintWriter pw) {
             pw.print("Filter[");
-            dump("whitelist", mWhitelist, pw); pw.print(',');
-            dump("blacklist", mBlacklist, pw); pw.print(']');
+            dump("allowlist", mAllowlist, pw); pw.print(',');
+            dump("denylist", mDenylist, pw); pw.print(']');
         }
 
         private void dump(String name, ArraySet<String> set, PrintWriter pw) {
@@ -253,18 +253,18 @@
         // e.g. "com.package1", or "apps, com.android.keyguard" or "*"
         static Filter parse(String value) {
             if (value == null) return null;
-            ArraySet<String> whitelist = new ArraySet<String>();
-            ArraySet<String> blacklist = new ArraySet<String>();
+            ArraySet<String> allowlist = new ArraySet<String>();
+            ArraySet<String> denylist = new ArraySet<String>();
             for (String token : value.split(",")) {
                 token = token.trim();
                 if (token.startsWith("-") && token.length() > 1) {
                     token = token.substring(1);
-                    blacklist.add(token);
+                    denylist.add(token);
                 } else {
-                    whitelist.add(token);
+                    allowlist.add(token);
                 }
             }
-            return new Filter(whitelist, blacklist);
+            return new Filter(allowlist, denylist);
         }
     }
 }
diff --git a/services/core/java/com/android/server/wm/RecentTasks.java b/services/core/java/com/android/server/wm/RecentTasks.java
index ba2c0b6..df53563 100644
--- a/services/core/java/com/android/server/wm/RecentTasks.java
+++ b/services/core/java/com/android/server/wm/RecentTasks.java
@@ -655,7 +655,8 @@
         }
         for (int i = mTasks.size() - 1; i >= 0; --i) {
             final Task task = mTasks.get(i);
-            if (task.mUserId == userId && !mService.getLockTaskController().isTaskWhitelisted(task)) {
+            if (task.mUserId == userId
+                    && !mService.getLockTaskController().isTaskAllowlisted(task)) {
                 remove(task);
             }
         }
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index ac96d144..1cb483c 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -2571,7 +2571,7 @@
         mDisplayAccessUIDs.clear();
         for (int displayNdx = getChildCount() - 1; displayNdx >= 0; --displayNdx) {
             final DisplayContent displayContent = getChildAt(displayNdx);
-            // Only bother calculating the whitelist for private displays
+            // Only bother calculating the allowlist for private displays
             if (displayContent.isPrivate()) {
                 mDisplayAccessUIDs.append(
                         displayContent.mDisplayId, displayContent.getPresentUIDs());
diff --git a/services/core/java/com/android/server/wm/SafeActivityOptions.java b/services/core/java/com/android/server/wm/SafeActivityOptions.java
index b71ecbb..ede6708 100644
--- a/services/core/java/com/android/server/wm/SafeActivityOptions.java
+++ b/services/core/java/com/android/server/wm/SafeActivityOptions.java
@@ -233,10 +233,10 @@
             Slog.w(TAG, msg);
             throw new SecurityException(msg);
         }
-        // Check if someone tries to launch an unwhitelisted activity into LockTask mode.
+        // Check if someone tries to launch an unallowlisted activity into LockTask mode.
         final boolean lockTaskMode = options.getLockTaskMode();
         if (aInfo != null && lockTaskMode
-                && !supervisor.mService.getLockTaskController().isPackageWhitelisted(
+                && !supervisor.mService.getLockTaskController().isPackageAllowlisted(
                         UserHandle.getUserId(callingUid), aInfo.packageName)) {
             final String msg = "Permission Denial: starting " + getIntentString(intent)
                     + " from " + callerApp + " (pid=" + callingPid
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index f362005..be0815b0 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -45,7 +45,7 @@
 import static android.content.pm.ActivityInfo.FLAG_SHOW_FOR_ALL_USERS;
 import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_ALWAYS;
 import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_DEFAULT;
-import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_IF_WHITELISTED;
+import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_IF_ALLOWLISTED;
 import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_NEVER;
 import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_LANDSCAPE_ONLY;
 import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_PORTRAIT_ONLY;
@@ -210,7 +210,6 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.app.IVoiceInteractor;
-import com.android.internal.os.logging.MetricsLoggerWrapper;
 import com.android.internal.util.XmlUtils;
 import com.android.internal.util.function.pooled.PooledConsumer;
 import com.android.internal.util.function.pooled.PooledFunction;
@@ -411,7 +410,7 @@
     /** Starts in LOCK_TASK_MODE_LOCKED automatically. Can start over existing lockTask task. */
     final static int LOCK_TASK_AUTH_LAUNCHABLE = 2;
     /** Can enter lockTask without user approval. Can start over existing lockTask task. */
-    final static int LOCK_TASK_AUTH_WHITELISTED = 3;
+    final static int LOCK_TASK_AUTH_ALLOWLISTED = 3;
     /** Priv-app that starts in LOCK_TASK_MODE_LOCKED automatically. Can start over existing
      * lockTask task. */
     final static int LOCK_TASK_AUTH_LAUNCHABLE_PRIV = 4;
@@ -1686,7 +1685,7 @@
             getDisplayArea().addStackReferenceIfNeeded((Task) child);
         }
 
-        // Make sure the list of display UID whitelists is updated
+        // Make sure the list of display UID allowlists is updated
         // now that this record is in a new task.
         mRootWindowContainer.updateUIDsPresentOnDisplay();
 
@@ -1903,7 +1902,7 @@
             case LOCK_TASK_AUTH_DONT_LOCK: return "LOCK_TASK_AUTH_DONT_LOCK";
             case LOCK_TASK_AUTH_PINNABLE: return "LOCK_TASK_AUTH_PINNABLE";
             case LOCK_TASK_AUTH_LAUNCHABLE: return "LOCK_TASK_AUTH_LAUNCHABLE";
-            case LOCK_TASK_AUTH_WHITELISTED: return "LOCK_TASK_AUTH_WHITELISTED";
+            case LOCK_TASK_AUTH_ALLOWLISTED: return "LOCK_TASK_AUTH_ALLOWLISTED";
             case LOCK_TASK_AUTH_LAUNCHABLE_PRIV: return "LOCK_TASK_AUTH_LAUNCHABLE_PRIV";
             default: return "unknown=" + mLockTaskAuth;
         }
@@ -1923,8 +1922,8 @@
         final LockTaskController lockTaskController = mAtmService.getLockTaskController();
         switch (r.lockTaskLaunchMode) {
             case LOCK_TASK_LAUNCH_MODE_DEFAULT:
-                mLockTaskAuth = lockTaskController.isPackageWhitelisted(mUserId, pkg)
-                        ? LOCK_TASK_AUTH_WHITELISTED : LOCK_TASK_AUTH_PINNABLE;
+                mLockTaskAuth = lockTaskController.isPackageAllowlisted(mUserId, pkg)
+                        ? LOCK_TASK_AUTH_ALLOWLISTED : LOCK_TASK_AUTH_PINNABLE;
                 break;
 
             case LOCK_TASK_LAUNCH_MODE_NEVER:
@@ -1935,8 +1934,8 @@
                 mLockTaskAuth = LOCK_TASK_AUTH_LAUNCHABLE_PRIV;
                 break;
 
-            case LOCK_TASK_LAUNCH_MODE_IF_WHITELISTED:
-                mLockTaskAuth = lockTaskController.isPackageWhitelisted(mUserId, pkg)
+            case LOCK_TASK_LAUNCH_MODE_IF_ALLOWLISTED:
+                mLockTaskAuth = lockTaskController.isPackageAllowlisted(mUserId, pkg)
                         ? LOCK_TASK_AUTH_LAUNCHABLE : LOCK_TASK_AUTH_PINNABLE;
                 break;
         }
@@ -2366,7 +2365,6 @@
     private boolean shouldStartChangeTransition(int prevWinMode, int newWinMode) {
         if (mWmService.mDisableTransitionAnimation
                 || !isVisible()
-                || getDisplayContent().mAppTransition.isTransitionSet()
                 || getSurfaceControl() == null
                 || !isLeafTask()) {
             return false;
@@ -7372,8 +7370,6 @@
             getDisplayArea().positionChildAt(POSITION_TOP, this, false /* includingParents */);
 
             mStackSupervisor.scheduleUpdatePictureInPictureModeIfNeeded(task, this);
-            MetricsLoggerWrapper.logPictureInPictureFullScreen(mAtmService.mContext,
-                    task.effectiveUid, task.realActivity.flattenToString());
         });
     }
 
diff --git a/services/core/java/com/android/server/wm/TaskPositioner.java b/services/core/java/com/android/server/wm/TaskPositioner.java
index c68b660..3fbc037 100644
--- a/services/core/java/com/android/server/wm/TaskPositioner.java
+++ b/services/core/java/com/android/server/wm/TaskPositioner.java
@@ -18,6 +18,7 @@
 
 import static android.app.ActivityTaskManager.RESIZE_MODE_USER;
 import static android.app.ActivityTaskManager.RESIZE_MODE_USER_FORCED;
+import static android.os.IInputConstants.DEFAULT_DISPATCHING_TIMEOUT_MILLIS;
 import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
 
 import static com.android.internal.policy.TaskResizingAlgorithm.CTRL_BOTTOM;
@@ -230,8 +231,8 @@
 
         mDragApplicationHandle = new InputApplicationHandle(new Binder());
         mDragApplicationHandle.name = TAG;
-        mDragApplicationHandle.dispatchingTimeoutNanos =
-                WindowManagerService.DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS;
+        mDragApplicationHandle.dispatchingTimeoutMillis = DEFAULT_DISPATCHING_TIMEOUT_MILLIS;
+
 
         mDragWindowHandle = new InputWindowHandle(mDragApplicationHandle,
                 displayContent.getDisplayId());
@@ -239,8 +240,7 @@
         mDragWindowHandle.token = mServerChannel.getToken();
         mDragWindowHandle.layoutParamsFlags = 0;
         mDragWindowHandle.layoutParamsType = WindowManager.LayoutParams.TYPE_DRAG;
-        mDragWindowHandle.dispatchingTimeoutNanos =
-                WindowManagerService.DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS;
+        mDragWindowHandle.dispatchingTimeoutMillis = DEFAULT_DISPATCHING_TIMEOUT_MILLIS;
         mDragWindowHandle.visible = true;
         mDragWindowHandle.canReceiveKeys = false;
         mDragWindowHandle.hasFocus = true;
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index ede92f0..0b1d6bc 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -35,6 +35,7 @@
 import static android.content.pm.PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT;
 import static android.content.pm.PackageManager.FEATURE_PC;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.os.IInputConstants.DEFAULT_DISPATCHING_TIMEOUT_MILLIS;
 import static android.os.Process.INVALID_UID;
 import static android.os.Process.SYSTEM_UID;
 import static android.os.Process.myPid;
@@ -366,9 +367,6 @@
     // proceding with safe mode detection.
     private static final int INPUT_DEVICES_READY_FOR_SAFE_MODE_DETECTION_TIMEOUT_MILLIS = 1000;
 
-    // Default input dispatching timeout in nanoseconds.
-    static final long DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS = 5000 * 1000000L;
-
     // Poll interval in milliseconds for watching boot animation finished.
     // TODO(b/159045990) Migrate to SystemService.waitForState with dedicated thread.
     private static final int BOOT_ANIMATION_POLL_INTERVAL = 50;
@@ -8090,7 +8088,7 @@
                 | LayoutParams.FLAG_SLIPPERY);
         h.layoutParamsFlags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | sanitizedFlags;
         h.layoutParamsType = type;
-        h.dispatchingTimeoutNanos = DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS;
+        h.dispatchingTimeoutMillis = DEFAULT_DISPATCHING_TIMEOUT_MILLIS;
         h.canReceiveKeys = false;
         h.hasFocus = false;
         h.hasWallpaper = false;
diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java
index df59c56..a58c564 100644
--- a/services/core/java/com/android/server/wm/WindowProcessController.java
+++ b/services/core/java/com/android/server/wm/WindowProcessController.java
@@ -19,6 +19,7 @@
 import static android.app.ActivityManager.PROCESS_STATE_NONEXISTENT;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
 import static android.os.Build.VERSION_CODES.Q;
+import static android.os.IInputConstants.DEFAULT_DISPATCHING_TIMEOUT_MILLIS;
 import static android.view.Display.INVALID_DISPLAY;
 
 import static com.android.server.am.ActivityManagerService.MY_PID;
@@ -30,8 +31,7 @@
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.ActivityTaskManagerService.ACTIVITY_BG_START_GRACE_PERIOD_MS;
-import static com.android.server.wm.ActivityTaskManagerService.INSTRUMENTATION_KEY_DISPATCHING_TIMEOUT_MS;
-import static com.android.server.wm.ActivityTaskManagerService.KEY_DISPATCHING_TIMEOUT_MS;
+import static com.android.server.wm.ActivityTaskManagerService.INSTRUMENTATION_KEY_DISPATCHING_TIMEOUT_MILLIS;
 import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_NONE;
 import static com.android.server.wm.Task.ActivityState.DESTROYED;
 import static com.android.server.wm.Task.ActivityState.DESTROYING;
@@ -41,6 +41,7 @@
 import static com.android.server.wm.Task.ActivityState.STARTED;
 import static com.android.server.wm.Task.ActivityState.STOPPING;
 
+
 import android.Manifest;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -1064,10 +1065,16 @@
         return RELAUNCH_REASON_NONE;
     }
 
-    public long getInputDispatchingTimeout() {
+    /**
+     * Get the current dispatching timeout. If instrumentation is currently taking place, return
+     * a longer value. Shorter timeout is returned otherwise.
+     * @return The timeout in milliseconds
+     */
+    public long getInputDispatchingTimeoutMillis() {
         synchronized (mAtm.mGlobalLock) {
             return isInstrumenting() || isUsingWrapper()
-                    ? INSTRUMENTATION_KEY_DISPATCHING_TIMEOUT_MS : KEY_DISPATCHING_TIMEOUT_MS;
+                    ? INSTRUMENTATION_KEY_DISPATCHING_TIMEOUT_MILLIS :
+                    DEFAULT_DISPATCHING_TIMEOUT_MILLIS;
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index ef78420..ab78e74 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -25,6 +25,7 @@
 import static android.app.WindowConfiguration.isSplitScreenWindowingMode;
 import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
 import static android.graphics.GraphicsProtos.dumpPointProto;
+import static android.os.IInputConstants.DEFAULT_DISPATCHING_TIMEOUT_MILLIS;
 import static android.os.PowerManager.DRAW_WAKE_LOCK;
 import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
 import static android.view.InsetsState.ITYPE_IME;
@@ -1635,10 +1636,10 @@
         }
     }
 
-    public long getInputDispatchingTimeoutNanos() {
+    public long getInputDispatchingTimeoutMillis() {
         return mActivityRecord != null
-                ? mActivityRecord.mInputDispatchingTimeoutNanos
-                : WindowManagerService.DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS;
+                ? mActivityRecord.mInputDispatchingTimeoutMillis
+                : DEFAULT_DISPATCHING_TIMEOUT_MILLIS;
     }
 
     @Override
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index 0e1b2f2..4b5f38c 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -99,6 +99,7 @@
         "libpowermanager",
         "libutils",
         "libui",
+        "libvibratorservice",
         "libinput",
         "libinputflinger",
         "libinputflinger_base",
diff --git a/services/core/jni/com_android_server_VibratorService.cpp b/services/core/jni/com_android_server_VibratorService.cpp
index 05aa359..74e2328 100644
--- a/services/core/jni/com_android_server_VibratorService.cpp
+++ b/services/core/jni/com_android_server_VibratorService.cpp
@@ -33,6 +33,8 @@
 #include <inttypes.h>
 #include <stdio.h>
 
+#include <vibratorservice/VibratorHalController.h>
+
 using android::hardware::Return;
 using android::hardware::Void;
 using android::hardware::vibrator::V1_0::EffectStrength;
@@ -226,8 +228,24 @@
     return val >= *iter.begin() && val <= *std::prev(iter.end());
 }
 
-static void vibratorInit(JNIEnv *env, jclass clazz)
-{
+static aidl::CompositeEffect effectFromJavaPrimitive(JNIEnv* env, jobject primitive) {
+    aidl::CompositeEffect effect;
+    effect.primitive = static_cast<aidl::CompositePrimitive>(
+            env->GetIntField(primitive, gPrimitiveClassInfo.id));
+    effect.scale = static_cast<float>(env->GetFloatField(primitive, gPrimitiveClassInfo.scale));
+    effect.delayMs = static_cast<int32_t>(env->GetIntField(primitive, gPrimitiveClassInfo.delay));
+    return effect;
+}
+
+static void destroyVibratorController(void* rawVibratorController) {
+    vibrator::HalController* vibratorController =
+            reinterpret_cast<vibrator::HalController*>(rawVibratorController);
+    if (vibratorController) {
+        delete vibratorController;
+    }
+}
+
+static jlong vibratorInit(JNIEnv* /* env */, jclass /* clazz */) {
     if (auto hal = getHal<aidl::IVibrator>()) {
         // IBinder::pingBinder isn't accessible as a pointer function
         // but getCapabilities can serve the same purpose
@@ -236,25 +254,26 @@
     } else {
         halCall(&V1_0::IVibrator::ping).isOk();
     }
+    std::unique_ptr<vibrator::HalController> controller =
+            std::make_unique<vibrator::HalController>();
+    controller->init();
+    return reinterpret_cast<jlong>(controller.release());
 }
 
-static jboolean vibratorExists(JNIEnv* /* env */, jclass /* clazz */)
-{
-    bool ok;
+static jlong vibratorGetFinalizer(JNIEnv* /* env */, jclass /* clazz */) {
+    return static_cast<jlong>(reinterpret_cast<uintptr_t>(&destroyVibratorController));
+}
 
-    if (auto hal = getHal<aidl::IVibrator>()) {
-        // IBinder::pingBinder isn't accessible as a pointer function
-        // but getCapabilities can serve the same purpose
-        int32_t cap;
-        ok = hal->call(&aidl::IVibrator::getCapabilities, &cap).isOk();
-    } else {
-        ok = halCall(&V1_0::IVibrator::ping).isOk();
+static jboolean vibratorExists(JNIEnv* env, jclass /* clazz */, jlong controllerPtr) {
+    vibrator::HalController* controller = reinterpret_cast<vibrator::HalController*>(controllerPtr);
+    if (controller == nullptr) {
+        ALOGE("vibratorExists failed because controller was not initialized");
+        return JNI_FALSE;
     }
-    return ok ? JNI_TRUE : JNI_FALSE;
+    return controller->ping().isOk() ? JNI_TRUE : JNI_FALSE;
 }
 
-static void vibratorOn(JNIEnv* /* env */, jclass /* clazz */, jlong timeout_ms)
-{
+static void vibratorOn(JNIEnv* /* env */, jclass /* clazz */, jlong timeout_ms) {
     if (auto hal = getHal<aidl::IVibrator>()) {
         auto status = hal->call(&aidl::IVibrator::on, timeout_ms, nullptr);
         if (!status.isOk()) {
@@ -268,93 +287,53 @@
     }
 }
 
-static void vibratorOff(JNIEnv* /* env */, jclass /* clazz */)
-{
-    if (auto hal = getHal<aidl::IVibrator>()) {
-        auto status = hal->call(&aidl::IVibrator::off);
-        if (!status.isOk()) {
-            ALOGE("vibratorOff command failed: %s", status.toString8().string());
-        }
-    } else {
-        Status retStatus = halCall(&V1_0::IVibrator::off).withDefault(Status::UNKNOWN_ERROR);
-        if (retStatus != Status::OK) {
-            ALOGE("vibratorOff command failed (%" PRIu32 ").", static_cast<uint32_t>(retStatus));
-        }
+static void vibratorOff(JNIEnv* env, jclass /* clazz */, jlong controllerPtr) {
+    vibrator::HalController* controller = reinterpret_cast<vibrator::HalController*>(controllerPtr);
+    if (controller == nullptr) {
+        ALOGE("vibratorOff failed because controller was not initialized");
+        return;
     }
+    controller->off();
 }
 
-static jlong vibratorSupportsAmplitudeControl(JNIEnv*, jclass) {
-    if (auto hal = getHal<aidl::IVibrator>()) {
-        int32_t cap = 0;
-        if (!hal->call(&aidl::IVibrator::getCapabilities, &cap).isOk()) {
-            return false;
-        }
-        return (cap & aidl::IVibrator::CAP_AMPLITUDE_CONTROL) > 0;
-    } else {
-        return halCall(&V1_0::IVibrator::supportsAmplitudeControl).withDefault(false);
+static void vibratorSetAmplitude(JNIEnv* env, jclass /* clazz */, jlong controllerPtr,
+                                 jint amplitude) {
+    vibrator::HalController* controller = reinterpret_cast<vibrator::HalController*>(controllerPtr);
+    if (controller == nullptr) {
+        ALOGE("vibratorSetAmplitude failed because controller was not initialized");
+        return;
     }
+    controller->setAmplitude(static_cast<int32_t>(amplitude));
 }
 
-static void vibratorSetAmplitude(JNIEnv*, jclass, jint amplitude) {
-    if (auto hal = getHal<aidl::IVibrator>()) {
-        auto status = hal->call(&aidl::IVibrator::IVibrator::setAmplitude, static_cast<float>(amplitude) / UINT8_MAX);
-        if (!status.isOk()) {
-            ALOGE("Failed to set vibrator amplitude: %s", status.toString8().string());
-        }
-    } else {
-        Status status = halCall(&V1_0::IVibrator::setAmplitude, static_cast<uint32_t>(amplitude))
-            .withDefault(Status::UNKNOWN_ERROR);
-        if (status != Status::OK) {
-            ALOGE("Failed to set vibrator amplitude (%" PRIu32 ").",
-                  static_cast<uint32_t>(status));
-        }
+static void vibratorSetExternalControl(JNIEnv* env, jclass /* clazz */, jlong controllerPtr,
+                                       jboolean enabled) {
+    vibrator::HalController* controller = reinterpret_cast<vibrator::HalController*>(controllerPtr);
+    if (controller == nullptr) {
+        ALOGE("vibratorSetExternalControl failed because controller was not initialized");
+        return;
     }
+    controller->setExternalControl(enabled);
 }
 
-static jboolean vibratorSupportsExternalControl(JNIEnv*, jclass) {
-    if (auto hal = getHal<aidl::IVibrator>()) {
-        int32_t cap = 0;
-        if (!hal->call(&aidl::IVibrator::getCapabilities, &cap).isOk()) {
-            return false;
-        }
-        return (cap & aidl::IVibrator::CAP_EXTERNAL_CONTROL) > 0;
-    } else {
-        return halCall(&V1_3::IVibrator::supportsExternalControl).withDefault(false);
-    }
-}
-
-static void vibratorSetExternalControl(JNIEnv*, jclass, jboolean enabled) {
-    if (auto hal = getHal<aidl::IVibrator>()) {
-        auto status = hal->call(&aidl::IVibrator::IVibrator::setExternalControl, enabled);
-        if (!status.isOk()) {
-            ALOGE("Failed to set vibrator external control: %s", status.toString8().string());
-        }
-    } else {
-        Status status = halCall(&V1_3::IVibrator::setExternalControl, static_cast<uint32_t>(enabled))
-            .withDefault(Status::UNKNOWN_ERROR);
-        if (status != Status::OK) {
-            ALOGE("Failed to set vibrator external control (%" PRIu32 ").",
-                static_cast<uint32_t>(status));
-        }
-    }
-}
-
-static jintArray vibratorGetSupportedEffects(JNIEnv *env, jclass) {
-    if (auto hal = getHal<aidl::IVibrator>()) {
-        std::vector<aidl::Effect> supportedEffects;
-        if (!hal->call(&aidl::IVibrator::getSupportedEffects, &supportedEffects).isOk()) {
-            return nullptr;
-        }
-        jintArray arr = env->NewIntArray(supportedEffects.size());
-        env->SetIntArrayRegion(arr, 0, supportedEffects.size(),
-                reinterpret_cast<jint*>(supportedEffects.data()));
-        return arr;
-    } else {
+static jintArray vibratorGetSupportedEffects(JNIEnv* env, jclass /* clazz */, jlong controllerPtr) {
+    vibrator::HalController* controller = reinterpret_cast<vibrator::HalController*>(controllerPtr);
+    if (controller == nullptr) {
+        ALOGE("vibratorGetSupportedEffects failed because controller was not initialized");
         return nullptr;
     }
+    auto result = controller->getSupportedEffects();
+    if (!result.isOk()) {
+        return nullptr;
+    }
+    std::vector<aidl::Effect> supportedEffects = result.value();
+    jintArray effects = env->NewIntArray(supportedEffects.size());
+    env->SetIntArrayRegion(effects, 0, supportedEffects.size(),
+                           reinterpret_cast<jint*>(supportedEffects.data()));
+    return effects;
 }
 
-static jlong vibratorPerformEffect(JNIEnv* env, jclass, jlong effect, jlong strength,
+static jlong vibratorPerformEffect(JNIEnv* env, jclass /* clazz */, jlong effect, jlong strength,
                                    jobject vibration, jboolean withCallback) {
     if (auto hal = getHal<aidl::IVibrator>()) {
         int32_t lengthMs;
@@ -420,17 +399,8 @@
     return -1;
 }
 
-static aidl::CompositeEffect effectFromJavaPrimitive(JNIEnv* env, jobject primitive) {
-    aidl::CompositeEffect effect;
-    effect.primitive = static_cast<aidl::CompositePrimitive>(
-            env->GetIntField(primitive, gPrimitiveClassInfo.id));
-    effect.scale = static_cast<float>(env->GetFloatField(primitive, gPrimitiveClassInfo.scale));
-    effect.delayMs = static_cast<int>(env->GetIntField(primitive, gPrimitiveClassInfo.delay));
-    return effect;
-}
-
-static void vibratorPerformComposedEffect(JNIEnv* env, jclass, jobjectArray composition,
-                                   jobject vibration) {
+static void vibratorPerformComposedEffect(JNIEnv* env, jclass /* clazz */, jobjectArray composition,
+                                          jobject vibration) {
     auto hal = getHal<aidl::IVibrator>();
     if (!hal) {
         return;
@@ -451,65 +421,71 @@
     }
 }
 
-static jlong vibratorGetCapabilities(JNIEnv*, jclass) {
-    if (auto hal = getHal<aidl::IVibrator>()) {
-        int32_t cap = 0;
-        if (!hal->call(&aidl::IVibrator::getCapabilities, &cap).isOk()) {
-            return 0;
-        }
-        return cap;
+static jlong vibratorGetCapabilities(JNIEnv* env, jclass /* clazz */, jlong controllerPtr) {
+    vibrator::HalController* controller = reinterpret_cast<vibrator::HalController*>(controllerPtr);
+    if (controller == nullptr) {
+        ALOGE("vibratorGetCapabilities failed because controller was not initialized");
+        return 0;
     }
-
-    return 0;
+    auto result = controller->getCapabilities();
+    return result.isOk() ? static_cast<jlong>(result.value()) : 0;
 }
 
-static void vibratorAlwaysOnEnable(JNIEnv* env, jclass, jlong id, jlong effect, jlong strength) {
-    auto status = halCall(&aidl::IVibrator::alwaysOnEnable, id,
-            static_cast<aidl::Effect>(effect), static_cast<aidl::EffectStrength>(strength));
-    if (!status.isOk()) {
-        ALOGE("vibratortAlwaysOnEnable command failed (%s).", status.toString8().string());
+static void vibratorAlwaysOnEnable(JNIEnv* env, jclass /* clazz */, jlong controllerPtr, jlong id,
+                                   jlong effect, jlong strength) {
+    vibrator::HalController* controller = reinterpret_cast<vibrator::HalController*>(controllerPtr);
+    if (controller == nullptr) {
+        ALOGE("vibratorAlwaysOnEnable failed because controller was not initialized");
+        return;
     }
+    controller->alwaysOnEnable(static_cast<int32_t>(id), static_cast<aidl::Effect>(effect),
+                               static_cast<aidl::EffectStrength>(strength));
 }
 
-static void vibratorAlwaysOnDisable(JNIEnv* env, jclass, jlong id) {
-    auto status = halCall(&aidl::IVibrator::alwaysOnDisable, id);
-    if (!status.isOk()) {
-        ALOGE("vibratorAlwaysOnDisable command failed (%s).", status.toString8().string());
+static void vibratorAlwaysOnDisable(JNIEnv* env, jclass /* clazz */, jlong controllerPtr,
+                                    jlong id) {
+    vibrator::HalController* controller = reinterpret_cast<vibrator::HalController*>(controllerPtr);
+    if (controller == nullptr) {
+        ALOGE("vibratorAlwaysOnDisable failed because controller was not initialized");
+        return;
     }
+    controller->alwaysOnDisable(static_cast<int32_t>(id));
 }
 
 static const JNINativeMethod method_table[] = {
-        {"vibratorExists", "()Z", (void*)vibratorExists},
-        {"vibratorInit", "()V", (void*)vibratorInit},
+        {"vibratorInit", "()J", (void*)vibratorInit},
+        {"vibratorGetFinalizer", "()J", (void*)vibratorGetFinalizer},
+        {"vibratorExists", "(J)Z", (void*)vibratorExists},
         {"vibratorOn", "(J)V", (void*)vibratorOn},
-        {"vibratorOff", "()V", (void*)vibratorOff},
-        {"vibratorSupportsAmplitudeControl", "()Z", (void*)vibratorSupportsAmplitudeControl},
-        {"vibratorSetAmplitude", "(I)V", (void*)vibratorSetAmplitude},
+        {"vibratorOff", "(J)V", (void*)vibratorOff},
+        {"vibratorSetAmplitude", "(JI)V", (void*)vibratorSetAmplitude},
         {"vibratorPerformEffect", "(JJLcom/android/server/VibratorService$Vibration;Z)J",
          (void*)vibratorPerformEffect},
         {"vibratorPerformComposedEffect",
          "([Landroid/os/VibrationEffect$Composition$PrimitiveEffect;Lcom/android/server/"
          "VibratorService$Vibration;)V",
          (void*)vibratorPerformComposedEffect},
-        {"vibratorGetSupportedEffects", "()[I", (void*)vibratorGetSupportedEffects},
-        {"vibratorSupportsExternalControl", "()Z", (void*)vibratorSupportsExternalControl},
-        {"vibratorSetExternalControl", "(Z)V", (void*)vibratorSetExternalControl},
-        {"vibratorGetCapabilities", "()J", (void*)vibratorGetCapabilities},
-        {"vibratorAlwaysOnEnable", "(JJJ)V", (void*)vibratorAlwaysOnEnable},
-        {"vibratorAlwaysOnDisable", "(J)V", (void*)vibratorAlwaysOnDisable},
+        {"vibratorGetSupportedEffects", "(J)[I", (void*)vibratorGetSupportedEffects},
+        {"vibratorSetExternalControl", "(JZ)V", (void*)vibratorSetExternalControl},
+        {"vibratorGetCapabilities", "(J)J", (void*)vibratorGetCapabilities},
+        {"vibratorAlwaysOnEnable", "(JJJJ)V", (void*)vibratorAlwaysOnEnable},
+        {"vibratorAlwaysOnDisable", "(JJ)V", (void*)vibratorAlwaysOnDisable},
 };
 
 int register_android_server_VibratorService(JNIEnv *env) {
-    sMethodIdOnComplete = GetMethodIDOrDie(env,
-            FindClassOrDie(env, "com/android/server/VibratorService$Vibration"),
-            "onComplete", "()V");
-    jclass primitiveClass = FindClassOrDie(env,
-            "android/os/VibrationEffect$Composition$PrimitiveEffect");
+    sMethodIdOnComplete =
+            GetMethodIDOrDie(env,
+                             FindClassOrDie(env, "com/android/server/VibratorService$Vibration"),
+                             "onComplete", "()V");
+
+    jclass primitiveClass =
+            FindClassOrDie(env, "android/os/VibrationEffect$Composition$PrimitiveEffect");
     gPrimitiveClassInfo.id = GetFieldIDOrDie(env, primitiveClass, "id", "I");
     gPrimitiveClassInfo.scale = GetFieldIDOrDie(env, primitiveClass, "scale", "F");
     gPrimitiveClassInfo.delay = GetFieldIDOrDie(env, primitiveClass, "delay", "I");
-    return jniRegisterNativeMethods(env, "com/android/server/VibratorService",
-            method_table, NELEM(method_table));
+
+    return jniRegisterNativeMethods(env, "com/android/server/VibratorService", method_table,
+                                    NELEM(method_table));
 }
 
-};
+}; // namespace android
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index 7843663..0202c88 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -710,9 +710,8 @@
     jobject tokenObj = javaObjectForIBinder(env, token);
     jstring reasonObj = env->NewStringUTF(reason.c_str());
 
-    jlong newTimeout = env->CallLongMethod(mServiceObj,
-            gServiceClassInfo.notifyANR, inputApplicationHandleObj, tokenObj,
-                 reasonObj);
+    jlong newTimeout = env->CallLongMethod(mServiceObj, gServiceClassInfo.notifyANR,
+                                           inputApplicationHandleObj, tokenObj, reasonObj);
     if (checkAndClearExceptionFromCallback(env, "notifyANR")) {
         newTimeout = 0; // abort dispatch
     } else {
diff --git a/services/tests/mockingservicestests/AndroidManifest.xml b/services/tests/mockingservicestests/AndroidManifest.xml
index 44eb828..a398961 100644
--- a/services/tests/mockingservicestests/AndroidManifest.xml
+++ b/services/tests/mockingservicestests/AndroidManifest.xml
@@ -17,6 +17,8 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
         package="com.android.frameworks.mockingservicestests">
 
+    <uses-sdk android:targetSdkVersion="30" />
+
     <uses-permission android:name="android.permission.LOG_COMPAT_CHANGE"/>
     <uses-permission android:name="android.permission.READ_COMPAT_CHANGE_CONFIG"/>
     <uses-permission android:name="android.permission.CHANGE_CONFIGURATION" />
diff --git a/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsServiceTest.java
index 0a61c44..7a3a950 100644
--- a/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsServiceTest.java
@@ -130,7 +130,9 @@
     }
 
     @After
-    public void resetStaticMocks() {
+    public void tearDown() {
+        mAppOpsService.shutdown();
+
         mMockingSession.finishMocking();
     }
 
@@ -216,9 +218,8 @@
                 false);
         mAppOpsService.writeState();
 
-        // Create a new app ops service, and initialize its state from XML.
+        // Create a new app ops service which will initialize its state from XML.
         setupAppOpsService();
-        mAppOpsService.readState();
 
         // Query the state of the 2nd service.
         List<PackageOps> loggedOps = getLoggedOps();
@@ -233,9 +234,8 @@
         mAppOpsService.noteOperation(OP_READ_SMS, mMyUid, sMyPackageName, null, false, null, false);
         mAppOpsService.shutdown();
 
-        // Create a new app ops service, and initialize its state from XML.
+        // Create a new app ops service which will initialize its state from XML.
         setupAppOpsService();
-        mAppOpsService.readState();
 
         // Query the state of the 2nd service.
         List<PackageOps> loggedOps = getLoggedOps();
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/LocationProviderManagerTest.java b/services/tests/mockingservicestests/src/com/android/server/location/LocationProviderManagerTest.java
new file mode 100644
index 0000000..1cb004a
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/location/LocationProviderManagerTest.java
@@ -0,0 +1,971 @@
+/*
+ * 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;
+
+import static android.app.AlarmManager.ELAPSED_REALTIME_WAKEUP;
+import static android.app.AlarmManager.WINDOW_EXACT;
+import static android.app.AppOpsManager.OP_FINE_LOCATION;
+import static android.app.AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION;
+import static android.app.AppOpsManager.OP_MONITOR_LOCATION;
+import static android.location.Criteria.ACCURACY_COARSE;
+import static android.location.Criteria.ACCURACY_FINE;
+import static android.location.Criteria.POWER_HIGH;
+import static android.location.LocationManager.PASSIVE_PROVIDER;
+
+import static androidx.test.ext.truth.location.LocationSubject.assertThat;
+
+import static com.android.internal.util.ConcurrentUtils.DIRECT_EXECUTOR;
+import static com.android.server.location.LocationPermissions.PERMISSION_COARSE;
+import static com.android.server.location.LocationPermissions.PERMISSION_FINE;
+import static com.android.server.location.LocationUtils.createLocation;
+
+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.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.isNull;
+import static org.mockito.ArgumentMatchers.nullable;
+import static org.mockito.Mockito.after;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.timeout;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.MockitoAnnotations.initMocks;
+
+import android.app.AlarmManager;
+import android.app.AlarmManager.OnAlarmListener;
+import android.content.Context;
+import android.location.ILocationCallback;
+import android.location.ILocationListener;
+import android.location.Location;
+import android.location.LocationManagerInternal;
+import android.location.LocationManagerInternal.ProviderEnabledListener;
+import android.location.LocationRequest;
+import android.location.util.identity.CallerIdentity;
+import android.os.Bundle;
+import android.os.CancellationSignal;
+import android.os.Handler;
+import android.os.ICancellationSignal;
+import android.os.IRemoteCallback;
+import android.os.PowerManager;
+import android.os.Process;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.os.WorkSource;
+import android.platform.test.annotations.Presubmit;
+import android.util.Log;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.internal.location.ProviderProperties;
+import com.android.internal.location.ProviderRequest;
+import com.android.server.FgThread;
+import com.android.server.LocalServices;
+import com.android.server.location.listeners.ListenerRegistration;
+import com.android.server.location.util.FakeUserInfoHelper;
+import com.android.server.location.util.TestInjector;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.Collections;
+import java.util.Random;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+@Presubmit
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class LocationProviderManagerTest {
+
+    private static final String TAG = "LocationProviderManagerTest";
+
+    private static final long TIMEOUT_MS = 1000;
+
+    private static final int CURRENT_USER = FakeUserInfoHelper.DEFAULT_USERID;
+    private static final int OTHER_USER = CURRENT_USER + 10;
+
+    private static final String NAME = "test";
+    private static final ProviderProperties PROPERTIES = new ProviderProperties(false, false, false,
+            false, true, true, true, POWER_HIGH, ACCURACY_FINE);
+    private static final CallerIdentity IDENTITY = CallerIdentity.forTest(CURRENT_USER, 1,
+            "mypackage",
+            "attribution");
+
+    private Random mRandom;
+
+    @Mock
+    private LocationManagerInternal mInternal;
+    @Mock
+    private Context mContext;
+    @Mock
+    private AlarmManager mAlarmManager;
+    @Mock
+    private PowerManager mPowerManager;
+    @Mock
+    private PowerManager.WakeLock mWakeLock;
+
+    private TestInjector mInjector;
+    private PassiveLocationProviderManager mPassive;
+    private TestProvider mProvider;
+
+    private LocationProviderManager mManager;
+
+    @Before
+    public void setUp() {
+        initMocks(this);
+
+        long seed = System.currentTimeMillis();
+        Log.i(TAG, "location random seed: " + seed);
+
+        mRandom = new Random(seed);
+
+        LocalServices.addService(LocationManagerInternal.class, mInternal);
+
+        doReturn("android").when(mContext).getPackageName();
+        doReturn(mAlarmManager).when(mContext).getSystemService(AlarmManager.class);
+        doReturn(mPowerManager).when(mContext).getSystemService(PowerManager.class);
+        doReturn(mWakeLock).when(mPowerManager).newWakeLock(anyInt(), anyString());
+
+        mInjector = new TestInjector();
+        mInjector.getUserInfoHelper().startUser(OTHER_USER);
+
+        mPassive = new PassiveLocationProviderManager(mContext, mInjector);
+        mPassive.startManager();
+        mPassive.setRealProvider(new PassiveProvider(mContext));
+
+        mProvider = new TestProvider(PROPERTIES, IDENTITY);
+        mProvider.setProviderAllowed(true);
+
+        mManager = new LocationProviderManager(mContext, mInjector, NAME, mPassive);
+        mManager.startManager();
+        mManager.setRealProvider(mProvider);
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        LocalServices.removeServiceForTest(LocationManagerInternal.class);
+
+        // some test failures may leave the fg thread stuck, interrupt until we get out of it
+        CountDownLatch latch = new CountDownLatch(1);
+        FgThread.getExecutor().execute(latch::countDown);
+        int count = 0;
+        while (++count < 10 && !latch.await(10, TimeUnit.MILLISECONDS)) {
+            FgThread.get().getLooper().getThread().interrupt();
+        }
+    }
+
+    @Test
+    public void testProperties() {
+        assertThat(mManager.getName()).isEqualTo(NAME);
+        assertThat(mManager.getProperties()).isEqualTo(PROPERTIES);
+        assertThat(mManager.getIdentity()).isEqualTo(IDENTITY);
+        assertThat(mManager.hasProvider()).isTrue();
+
+        ProviderProperties newProperties = new ProviderProperties(true, true, true,
+                true, false, false, false, POWER_HIGH, ACCURACY_COARSE);
+        mProvider.setProperties(newProperties);
+        assertThat(mManager.getProperties()).isEqualTo(newProperties);
+
+        CallerIdentity newIdentity = CallerIdentity.forTest(OTHER_USER, 1, "otherpackage",
+                "otherattribution");
+        mProvider.setIdentity(newIdentity);
+        assertThat(mManager.getIdentity()).isEqualTo(newIdentity);
+
+        mManager.setRealProvider(null);
+        assertThat(mManager.hasProvider()).isFalse();
+    }
+
+    @Test
+    public void testRemoveProvider() {
+        mManager.setRealProvider(null);
+        assertThat(mManager.hasProvider()).isFalse();
+    }
+
+    @Test
+    public void testIsEnabled() {
+        assertThat(mManager.isEnabled(CURRENT_USER)).isTrue();
+
+        mInjector.getSettingsHelper().setLocationEnabled(false, CURRENT_USER);
+        assertThat(mManager.isEnabled(CURRENT_USER)).isFalse();
+
+        mInjector.getSettingsHelper().setLocationEnabled(true, CURRENT_USER);
+        mProvider.setAllowed(false);
+        assertThat(mManager.isEnabled(CURRENT_USER)).isFalse();
+
+        mProvider.setAllowed(true);
+        mInjector.getUserInfoHelper().setCurrentUserId(OTHER_USER);
+        assertThat(mManager.isEnabled(CURRENT_USER)).isFalse();
+        assertThat(mManager.isEnabled(OTHER_USER)).isTrue();
+
+        mInjector.getUserInfoHelper().setCurrentUserId(CURRENT_USER);
+        assertThat(mManager.isEnabled(CURRENT_USER)).isTrue();
+        assertThat(mManager.isEnabled(OTHER_USER)).isFalse();
+    }
+
+    @Test
+    public void testIsEnabledListener() {
+        ProviderEnabledListener listener = mock(ProviderEnabledListener.class);
+        mManager.addEnabledListener(listener);
+        verify(listener, never()).onProviderEnabledChanged(anyString(), anyInt(), anyBoolean());
+
+        mInjector.getSettingsHelper().setLocationEnabled(false, CURRENT_USER);
+        verify(listener, timeout(TIMEOUT_MS).times(1)).onProviderEnabledChanged(NAME, CURRENT_USER,
+                false);
+
+        mInjector.getSettingsHelper().setLocationEnabled(true, CURRENT_USER);
+        verify(listener, timeout(TIMEOUT_MS).times(1)).onProviderEnabledChanged(NAME, CURRENT_USER,
+                true);
+
+        mProvider.setAllowed(false);
+        verify(listener, timeout(TIMEOUT_MS).times(2)).onProviderEnabledChanged(NAME, CURRENT_USER,
+                false);
+
+        mProvider.setAllowed(true);
+        verify(listener, timeout(TIMEOUT_MS).times(2)).onProviderEnabledChanged(NAME, CURRENT_USER,
+                true);
+
+        mInjector.getUserInfoHelper().setCurrentUserId(OTHER_USER);
+        verify(listener, timeout(TIMEOUT_MS).times(3)).onProviderEnabledChanged(NAME, CURRENT_USER,
+                false);
+        verify(listener, timeout(TIMEOUT_MS).times(1)).onProviderEnabledChanged(NAME, OTHER_USER,
+                true);
+
+        mInjector.getUserInfoHelper().setCurrentUserId(CURRENT_USER);
+        verify(listener, timeout(TIMEOUT_MS).times(3)).onProviderEnabledChanged(NAME, CURRENT_USER,
+                true);
+        verify(listener, timeout(TIMEOUT_MS).times(1)).onProviderEnabledChanged(NAME, OTHER_USER,
+                false);
+
+        mManager.removeEnabledListener(listener);
+        mInjector.getSettingsHelper().setLocationEnabled(false, CURRENT_USER);
+        verifyNoMoreInteractions(listener);
+    }
+
+    @Test
+    public void testGetLastLocation_Fine() {
+        LocationRequest request = LocationRequest.createFromDeprecatedProvider(NAME, 0, 0, false);
+        assertThat(mManager.getLastLocation(request, IDENTITY, PERMISSION_FINE)).isNull();
+
+        Location loc = createLocation(NAME, mRandom);
+        mProvider.setProviderLocation(loc);
+        assertThat(mManager.getLastLocation(request, IDENTITY, PERMISSION_FINE)).isEqualTo(loc);
+    }
+
+    @Test
+    public void testGetLastLocation_Coarse() {
+        LocationRequest request = LocationRequest.createFromDeprecatedProvider(NAME, 0, 0, false);
+        assertThat(mManager.getLastLocation(request, IDENTITY, PERMISSION_FINE)).isNull();
+
+        Location loc = createLocation(NAME, mRandom);
+        mProvider.setProviderLocation(loc);
+        Location coarse = mManager.getLastLocation(request, IDENTITY, PERMISSION_COARSE);
+        assertThat(coarse).isNotEqualTo(loc);
+        assertThat(coarse).isNearby(loc, 5000);
+    }
+
+    @Test
+    public void testGetLastLocation_Bypass() {
+        LocationRequest request = LocationRequest.createFromDeprecatedProvider(NAME, 0, 0, false);
+        LocationRequest bypassRequest = LocationRequest.createFromDeprecatedProvider(NAME, 0, 0,
+                false).setLocationSettingsIgnored(true);
+        assertThat(mManager.getLastLocation(request, IDENTITY, PERMISSION_FINE)).isNull();
+        assertThat(mManager.getLastLocation(bypassRequest, IDENTITY, PERMISSION_FINE)).isNull();
+
+        Location loc = createLocation(NAME, mRandom);
+        mProvider.setProviderLocation(loc);
+        assertThat(mManager.getLastLocation(request, IDENTITY, PERMISSION_FINE)).isEqualTo(loc);
+        assertThat(mManager.getLastLocation(bypassRequest, IDENTITY, PERMISSION_FINE)).isEqualTo(
+                loc);
+
+        mProvider.setProviderAllowed(false);
+        assertThat(mManager.getLastLocation(request, IDENTITY, PERMISSION_FINE)).isNull();
+        assertThat(mManager.getLastLocation(bypassRequest, IDENTITY, PERMISSION_FINE)).isEqualTo(
+                loc);
+
+        loc = createLocation(NAME, mRandom);
+        mProvider.setProviderLocation(loc);
+        assertThat(mManager.getLastLocation(request, IDENTITY, PERMISSION_FINE)).isNull();
+        assertThat(mManager.getLastLocation(bypassRequest, IDENTITY, PERMISSION_FINE)).isEqualTo(
+                loc);
+
+        mProvider.setProviderAllowed(true);
+        assertThat(mManager.getLastLocation(request, IDENTITY, PERMISSION_FINE)).isNull();
+        assertThat(mManager.getLastLocation(bypassRequest, IDENTITY, PERMISSION_FINE)).isEqualTo(
+                loc);
+
+        loc = createLocation(NAME, mRandom);
+        mProvider.setProviderLocation(loc);
+        assertThat(mManager.getLastLocation(request, IDENTITY, PERMISSION_FINE)).isEqualTo(loc);
+        assertThat(mManager.getLastLocation(bypassRequest, IDENTITY, PERMISSION_FINE)).isEqualTo(
+                loc);
+    }
+
+    @Test
+    public void testGetLastLocation_ClearOnMockRemoval() {
+        MockProvider mockProvider = new MockProvider(PROPERTIES, IDENTITY);
+        mockProvider.setAllowed(true);
+        mManager.setMockProvider(mockProvider);
+
+        LocationRequest request = LocationRequest.createFromDeprecatedProvider(NAME, 0, 0, false);
+        Location loc = createLocation(NAME, mRandom);
+        mockProvider.setProviderLocation(loc);
+        assertThat(mManager.getLastLocation(request, IDENTITY, PERMISSION_FINE)).isEqualTo(loc);
+
+        mManager.setMockProvider(null);
+        assertThat(mManager.getLastLocation(request, IDENTITY, PERMISSION_FINE)).isNull();
+    }
+
+    @Test
+    public void testInjectLastLocation() {
+        Location loc1 = createLocation(NAME, mRandom);
+        mManager.injectLastLocation(loc1, CURRENT_USER);
+
+        LocationRequest request = LocationRequest.createFromDeprecatedProvider(NAME, 0, 0, false);
+        assertThat(mManager.getLastLocation(request, IDENTITY, PERMISSION_FINE)).isEqualTo(loc1);
+
+        Location loc2 = createLocation(NAME, mRandom);
+        mManager.injectLastLocation(loc2, CURRENT_USER);
+
+        assertThat(mManager.getLastLocation(request, IDENTITY, PERMISSION_FINE)).isEqualTo(loc1);
+    }
+
+    @Test
+    public void testPassive_Listener() throws Exception {
+        ILocationListener listener = createMockLocationListener();
+        LocationRequest request = LocationRequest.createFromDeprecatedProvider(PASSIVE_PROVIDER, 0,
+                0, false);
+        mPassive.registerLocationRequest(request, IDENTITY, PERMISSION_FINE, listener);
+
+        Location loc = createLocation(NAME, mRandom);
+        mProvider.setProviderLocation(loc);
+
+        ArgumentCaptor<Location> locationCaptor = ArgumentCaptor.forClass(Location.class);
+        verify(listener).onLocationChanged(locationCaptor.capture(),
+                nullable(IRemoteCallback.class));
+        assertThat(locationCaptor.getValue()).isEqualTo(loc);
+    }
+
+    @Test
+    public void testPassive_LastLocation() {
+        Location loc = createLocation(NAME, mRandom);
+        mProvider.setProviderLocation(loc);
+
+        LocationRequest request = LocationRequest.createFromDeprecatedProvider(PASSIVE_PROVIDER, 0,
+                0, false);
+        assertThat(mPassive.getLastLocation(request, IDENTITY, PERMISSION_FINE)).isEqualTo(loc);
+    }
+
+    @Test
+    public void testRegisterListener() throws Exception {
+        ArgumentCaptor<Location> locationCaptor = ArgumentCaptor.forClass(Location.class);
+
+        ILocationListener listener = createMockLocationListener();
+        mManager.registerLocationRequest(
+                LocationRequest.createFromDeprecatedProvider(NAME, 0, 0, false), IDENTITY,
+                PERMISSION_FINE, listener);
+
+        Location loc = createLocation(NAME, mRandom);
+        mProvider.setProviderLocation(loc);
+        verify(listener, times(1)).onLocationChanged(locationCaptor.capture(),
+                nullable(IRemoteCallback.class));
+        assertThat(locationCaptor.getValue()).isEqualTo(loc);
+
+        mInjector.getSettingsHelper().setLocationEnabled(false, CURRENT_USER);
+        verify(listener, timeout(TIMEOUT_MS).times(1)).onProviderEnabledChanged(NAME, false);
+        loc = createLocation(NAME, mRandom);
+        mProvider.setProviderLocation(loc);
+        verify(listener, times(1)).onLocationChanged(any(Location.class),
+                nullable(IRemoteCallback.class));
+
+        mInjector.getSettingsHelper().setLocationEnabled(true, CURRENT_USER);
+        verify(listener, timeout(TIMEOUT_MS).times(1)).onProviderEnabledChanged(NAME, true);
+
+        mProvider.setAllowed(false);
+        verify(listener, timeout(TIMEOUT_MS).times(2)).onProviderEnabledChanged(NAME, false);
+        loc = createLocation(NAME, mRandom);
+        mProvider.setProviderLocation(loc);
+        verify(listener, times(1)).onLocationChanged(any(Location.class),
+                nullable(IRemoteCallback.class));
+
+        mProvider.setAllowed(true);
+        verify(listener, timeout(TIMEOUT_MS).times(2)).onProviderEnabledChanged(NAME, true);
+
+        mInjector.getUserInfoHelper().setCurrentUserId(OTHER_USER);
+        verify(listener, timeout(TIMEOUT_MS).times(3)).onProviderEnabledChanged(NAME, false);
+        loc = createLocation(NAME, mRandom);
+        mProvider.setProviderLocation(loc);
+        verify(listener, times(1)).onLocationChanged(any(Location.class),
+                nullable(IRemoteCallback.class));
+
+        mInjector.getUserInfoHelper().setCurrentUserId(CURRENT_USER);
+        verify(listener, timeout(TIMEOUT_MS).times(3)).onProviderEnabledChanged(NAME, true);
+
+        loc = createLocation(NAME, mRandom);
+        mProvider.setProviderLocation(loc);
+        verify(listener, times(2)).onLocationChanged(locationCaptor.capture(),
+                nullable(IRemoteCallback.class));
+        assertThat(locationCaptor.getValue()).isEqualTo(loc);
+    }
+
+    @Test
+    public void testRegisterListener_SameProcess() throws Exception {
+        ArgumentCaptor<Location> locationCaptor = ArgumentCaptor.forClass(Location.class);
+
+        CallerIdentity identity = CallerIdentity.forTest(CURRENT_USER, Process.myPid(), "mypackage",
+                "attribution");
+
+        ILocationListener listener = createMockLocationListener();
+        mManager.registerLocationRequest(
+                LocationRequest.createFromDeprecatedProvider(NAME, 0, 0, false), identity,
+                PERMISSION_FINE, listener);
+
+        Location loc = createLocation(NAME, mRandom);
+        mProvider.setProviderLocation(loc);
+        verify(listener, timeout(TIMEOUT_MS).times(1)).onLocationChanged(locationCaptor.capture(),
+                nullable(IRemoteCallback.class));
+        assertThat(locationCaptor.getValue()).isEqualTo(loc);
+    }
+
+    @Test
+    public void testRegisterListener_Unregister() throws Exception {
+        ILocationListener listener = createMockLocationListener();
+        mManager.registerLocationRequest(
+                LocationRequest.createFromDeprecatedProvider(NAME, 0, 0, false), IDENTITY,
+                PERMISSION_FINE, listener);
+        mManager.unregisterLocationRequest(listener);
+
+        mProvider.setProviderLocation(createLocation(NAME, mRandom));
+        verify(listener, never()).onLocationChanged(any(Location.class),
+                nullable(IRemoteCallback.class));
+
+        mInjector.getSettingsHelper().setLocationEnabled(false, CURRENT_USER);
+        verify(listener, after(TIMEOUT_MS).never()).onProviderEnabledChanged(NAME, false);
+    }
+
+    @Test
+    public void testRegisterListener_Unregister_SameProcess() throws Exception {
+        CallerIdentity identity = CallerIdentity.forTest(CURRENT_USER, Process.myPid(), "mypackage",
+                "attribution");
+
+        ILocationListener listener = createMockLocationListener();
+        mManager.registerLocationRequest(
+                LocationRequest.createFromDeprecatedProvider(NAME, 0, 0, false), identity,
+                PERMISSION_FINE, listener);
+
+        CountDownLatch blocker = new CountDownLatch(1);
+        ListenerRegistration.IN_PROCESS_EXECUTOR.execute(() -> {
+            try {
+                blocker.await();
+            } catch (InterruptedException e) {
+                // do nothing
+            }
+        });
+
+        mProvider.setProviderLocation(createLocation(NAME, mRandom));
+        mManager.unregisterLocationRequest(listener);
+        blocker.countDown();
+        verify(listener, after(TIMEOUT_MS).never()).onLocationChanged(any(Location.class),
+                nullable(IRemoteCallback.class));
+    }
+
+    @Test
+    public void testRegisterListener_NumUpdates() throws Exception {
+        ILocationListener listener = createMockLocationListener();
+        LocationRequest request = LocationRequest.createFromDeprecatedProvider(NAME, 0, 0,
+                false).setNumUpdates(5);
+        mManager.registerLocationRequest(request, IDENTITY, PERMISSION_FINE, listener);
+
+        mProvider.setProviderLocation(createLocation(NAME, mRandom));
+        mProvider.setProviderLocation(createLocation(NAME, mRandom));
+        mProvider.setProviderLocation(createLocation(NAME, mRandom));
+        mProvider.setProviderLocation(createLocation(NAME, mRandom));
+        mProvider.setProviderLocation(createLocation(NAME, mRandom));
+        mProvider.setProviderLocation(createLocation(NAME, mRandom));
+
+        verify(listener, times(5)).onLocationChanged(any(Location.class),
+                nullable(IRemoteCallback.class));
+    }
+
+    @Test
+    public void testRegisterListener_ExpiringAlarm() throws Exception {
+        ILocationListener listener = createMockLocationListener();
+        LocationRequest request = LocationRequest.createFromDeprecatedProvider(NAME, 0, 0,
+                false).setExpireIn(5000);
+        mManager.registerLocationRequest(request, IDENTITY, PERMISSION_FINE, listener);
+        long baseTimeMs = SystemClock.elapsedRealtime();
+
+        ArgumentCaptor<Long> timeoutCapture = ArgumentCaptor.forClass(Long.class);
+        ArgumentCaptor<OnAlarmListener> listenerCapture = ArgumentCaptor.forClass(
+                OnAlarmListener.class);
+        verify(mAlarmManager).set(eq(ELAPSED_REALTIME_WAKEUP), timeoutCapture.capture(),
+                eq(WINDOW_EXACT), eq(0L), listenerCapture.capture(), any(Handler.class),
+                any(WorkSource.class));
+
+        assertThat(timeoutCapture.getValue()).isAtLeast(baseTimeMs + 4000);
+        assertThat(timeoutCapture.getValue()).isAtMost(baseTimeMs + 5000);
+        listenerCapture.getValue().onAlarm();
+
+        mProvider.setProviderLocation(createLocation(NAME, mRandom));
+        verify(listener, never()).onLocationChanged(any(Location.class),
+                nullable(IRemoteCallback.class));
+    }
+
+    @Test
+    public void testRegisterListener_ExpiringNoAlarm() throws Exception {
+        ILocationListener listener = createMockLocationListener();
+        LocationRequest request = LocationRequest.createFromDeprecatedProvider(NAME, 0, 0,
+                false).setExpireIn(25);
+        mManager.registerLocationRequest(request, IDENTITY, PERMISSION_FINE, listener);
+
+        Thread.sleep(25);
+
+        mProvider.setProviderLocation(createLocation(NAME, mRandom));
+        verify(listener, never()).onLocationChanged(any(Location.class),
+                nullable(IRemoteCallback.class));
+    }
+
+    @Test
+    public void testRegisterListener_AlreadyExpired() throws Exception {
+        ILocationListener listener = createMockLocationListener();
+        LocationRequest request = LocationRequest.createFromDeprecatedProvider(NAME, 0, 0,
+                false).setExpireIn(-1);
+        mManager.registerLocationRequest(request, IDENTITY, PERMISSION_FINE, listener);
+
+        mProvider.setProviderLocation(createLocation(NAME, mRandom));
+        verify(listener, never()).onLocationChanged(any(Location.class),
+                nullable(IRemoteCallback.class));
+    }
+
+    @Test
+    public void testRegisterListener_FastestInterval() throws Exception {
+        ILocationListener listener = createMockLocationListener();
+        LocationRequest request = LocationRequest.createFromDeprecatedProvider(NAME, 5000, 0,
+                false).setFastestInterval(5000);
+        mManager.registerLocationRequest(request, IDENTITY, PERMISSION_FINE, listener);
+
+        mProvider.setProviderLocation(createLocation(NAME, mRandom));
+        mProvider.setProviderLocation(createLocation(NAME, mRandom));
+
+        verify(listener, times(1)).onLocationChanged(any(Location.class),
+                nullable(IRemoteCallback.class));
+    }
+
+    @Test
+    public void testRegisterListener_SmallestDisplacement() throws Exception {
+        ILocationListener listener = createMockLocationListener();
+        LocationRequest request = LocationRequest.createFromDeprecatedProvider(NAME, 5000, 0,
+                false).setSmallestDisplacement(1f);
+        mManager.registerLocationRequest(request, IDENTITY, PERMISSION_FINE, listener);
+
+        Location loc = createLocation(NAME, mRandom);
+        mProvider.setProviderLocation(loc);
+        mProvider.setProviderLocation(loc);
+
+        verify(listener, times(1)).onLocationChanged(any(Location.class),
+                nullable(IRemoteCallback.class));
+    }
+
+    @Test
+    public void testRegisterListener_NoteOpFailure() throws Exception {
+        ILocationListener listener = createMockLocationListener();
+        LocationRequest request = LocationRequest.createFromDeprecatedProvider(NAME, 0, 0, false);
+        mManager.registerLocationRequest(request, IDENTITY, PERMISSION_FINE, listener);
+
+        mInjector.getAppOpsHelper().setAppOpAllowed(OP_FINE_LOCATION, IDENTITY.getPackageName(),
+                false);
+
+        mProvider.setProviderLocation(createLocation(NAME, mRandom));
+
+        verify(listener, never()).onLocationChanged(any(Location.class),
+                nullable(IRemoteCallback.class));
+    }
+
+    @Test
+    public void testRegisterListener_Wakelock() throws Exception {
+        CallerIdentity identity = CallerIdentity.forTest(CURRENT_USER, Process.myPid(), "mypackage",
+                "attribution");
+
+        ILocationListener listener = createMockLocationListener();
+        mManager.registerLocationRequest(
+                LocationRequest.createFromDeprecatedProvider(NAME, 0, 0, false), identity,
+                PERMISSION_FINE, listener);
+
+        CountDownLatch blocker = new CountDownLatch(1);
+        ListenerRegistration.IN_PROCESS_EXECUTOR.execute(() -> {
+            try {
+                blocker.await();
+            } catch (InterruptedException e) {
+                // do nothing
+            }
+        });
+
+        mProvider.setProviderLocation(createLocation(NAME, mRandom));
+        verify(mWakeLock).acquire(anyLong());
+        verify(mWakeLock, never()).release();
+
+        blocker.countDown();
+        verify(listener, timeout(TIMEOUT_MS)).onLocationChanged(any(Location.class),
+                nullable(IRemoteCallback.class));
+        verify(mWakeLock).acquire(anyLong());
+        verify(mWakeLock, timeout(TIMEOUT_MS)).release();
+    }
+
+    @Test
+    public void testGetCurrentLocation() throws Exception {
+        ArgumentCaptor<Location> locationCaptor = ArgumentCaptor.forClass(Location.class);
+
+        ILocationCallback listener = createMockGetCurrentLocationListener();
+        LocationRequest locationRequest = LocationRequest.createFromDeprecatedProvider(NAME, 0, 0,
+                false);
+        ICancellationSignal cancellationSignal = CancellationSignal.createTransport();
+        mManager.getCurrentLocation(locationRequest, IDENTITY,
+                PERMISSION_FINE, cancellationSignal, listener);
+
+        Location loc = createLocation(NAME, mRandom);
+        mProvider.setProviderLocation(loc);
+        mProvider.setProviderLocation(createLocation(NAME, mRandom));
+
+        verify(listener, times(1)).onLocation(locationCaptor.capture());
+        assertThat(locationCaptor.getValue()).isEqualTo(loc);
+    }
+
+    @Test
+    public void testGetCurrentLocation_Cancel() throws Exception {
+        ILocationCallback listener = createMockGetCurrentLocationListener();
+        LocationRequest locationRequest = LocationRequest.createFromDeprecatedProvider(NAME, 0, 0,
+                false);
+        ICancellationSignal cancellationSignal = CancellationSignal.createTransport();
+        mManager.getCurrentLocation(locationRequest, IDENTITY,
+                PERMISSION_FINE, cancellationSignal, listener);
+
+        cancellationSignal.cancel();
+        mProvider.setProviderLocation(createLocation(NAME, mRandom));
+
+        verify(listener, never()).onLocation(nullable(Location.class));
+    }
+
+    @Test
+    public void testGetCurrentLocation_ProviderDisabled() throws Exception {
+        ILocationCallback listener = createMockGetCurrentLocationListener();
+        LocationRequest locationRequest = LocationRequest.createFromDeprecatedProvider(NAME, 0, 0,
+                false);
+        ICancellationSignal cancellationSignal = CancellationSignal.createTransport();
+        mManager.getCurrentLocation(locationRequest, IDENTITY,
+                PERMISSION_FINE, cancellationSignal, listener);
+
+        mProvider.setProviderAllowed(false);
+        mProvider.setProviderAllowed(true);
+        mProvider.setProviderLocation(createLocation(NAME, mRandom));
+        verify(listener, times(1)).onLocation(isNull());
+    }
+
+    @Test
+    public void testGetCurrentLocation_ProviderAlreadyDisabled() throws Exception {
+        mProvider.setProviderAllowed(false);
+
+        ILocationCallback listener = createMockGetCurrentLocationListener();
+        LocationRequest locationRequest = LocationRequest.createFromDeprecatedProvider(NAME, 0, 0,
+                false);
+        ICancellationSignal cancellationSignal = CancellationSignal.createTransport();
+        mManager.getCurrentLocation(locationRequest, IDENTITY,
+                PERMISSION_FINE, cancellationSignal, listener);
+
+        mProvider.setProviderAllowed(true);
+        mProvider.setProviderLocation(createLocation(NAME, mRandom));
+        verify(listener, times(1)).onLocation(isNull());
+    }
+
+    @Test
+    public void testGetCurrentLocation_LastLocation() throws Exception {
+        ArgumentCaptor<Location> locationCaptor = ArgumentCaptor.forClass(Location.class);
+
+        Location loc = createLocation(NAME, mRandom);
+        mProvider.setProviderLocation(loc);
+
+        ILocationCallback listener = createMockGetCurrentLocationListener();
+        LocationRequest locationRequest = LocationRequest.createFromDeprecatedProvider(NAME, 0, 0,
+                false);
+        ICancellationSignal cancellationSignal = CancellationSignal.createTransport();
+        mManager.getCurrentLocation(locationRequest, IDENTITY,
+                PERMISSION_FINE, cancellationSignal, listener);
+
+        verify(listener, times(1)).onLocation(locationCaptor.capture());
+        assertThat(locationCaptor.getValue()).isEqualTo(loc);
+    }
+
+    @Test
+    public void testGetCurrentLocation_Timeout() throws Exception {
+        ILocationCallback listener = createMockGetCurrentLocationListener();
+        LocationRequest locationRequest = LocationRequest.createFromDeprecatedProvider(NAME, 0, 0,
+                false);
+        ICancellationSignal cancellationSignal = CancellationSignal.createTransport();
+        mManager.getCurrentLocation(locationRequest, IDENTITY,
+                PERMISSION_FINE, cancellationSignal, listener);
+
+        ArgumentCaptor<OnAlarmListener> listenerCapture = ArgumentCaptor.forClass(
+                OnAlarmListener.class);
+        verify(mAlarmManager).set(eq(ELAPSED_REALTIME_WAKEUP), anyLong(),
+                eq(WINDOW_EXACT), eq(0L), listenerCapture.capture(), any(Handler.class),
+                any(WorkSource.class));
+        listenerCapture.getValue().onAlarm();
+
+        verify(listener, times(1)).onLocation(isNull());
+    }
+
+    @Test
+    public void testLocationMonitoring() {
+        assertThat(mInjector.getAppOpsHelper().isAppOpStarted(OP_MONITOR_LOCATION,
+                IDENTITY.getPackageName())).isFalse();
+        assertThat(mInjector.getAppOpsHelper().isAppOpStarted(OP_MONITOR_HIGH_POWER_LOCATION,
+                IDENTITY.getPackageName())).isFalse();
+
+        ILocationListener listener = createMockLocationListener();
+        LocationRequest request = LocationRequest.createFromDeprecatedProvider(NAME, 0, 0, false);
+        mManager.registerLocationRequest(request, IDENTITY, PERMISSION_FINE, listener);
+
+        assertThat(mInjector.getAppOpsHelper().isAppOpStarted(OP_MONITOR_LOCATION,
+                IDENTITY.getPackageName())).isTrue();
+        assertThat(mInjector.getAppOpsHelper().isAppOpStarted(OP_MONITOR_HIGH_POWER_LOCATION,
+                IDENTITY.getPackageName())).isTrue();
+
+        mInjector.getAppForegroundHelper().setAppForeground(IDENTITY.getUid(), false);
+
+        assertThat(mInjector.getAppOpsHelper().isAppOpStarted(OP_MONITOR_LOCATION,
+                IDENTITY.getPackageName())).isTrue();
+        assertThat(mInjector.getAppOpsHelper().isAppOpStarted(OP_MONITOR_HIGH_POWER_LOCATION,
+                IDENTITY.getPackageName())).isFalse();
+
+        mManager.unregisterLocationRequest(listener);
+
+        assertThat(mInjector.getAppOpsHelper().isAppOpStarted(OP_MONITOR_LOCATION,
+                IDENTITY.getPackageName())).isFalse();
+        assertThat(mInjector.getAppOpsHelper().isAppOpStarted(OP_MONITOR_HIGH_POWER_LOCATION,
+                IDENTITY.getPackageName())).isFalse();
+    }
+
+    @Test
+    public void testProviderRequest() {
+        assertThat(mProvider.getRequest().reportLocation).isFalse();
+        assertThat(mProvider.getRequest().locationRequests).isEmpty();
+
+        ILocationListener listener1 = createMockLocationListener();
+        LocationRequest request1 = LocationRequest.createFromDeprecatedProvider(NAME, 5, 0, false);
+        mManager.registerLocationRequest(request1, IDENTITY, PERMISSION_FINE, listener1);
+
+        assertThat(mProvider.getRequest().reportLocation).isTrue();
+        assertThat(mProvider.getRequest().locationRequests).containsExactly(request1);
+        assertThat(mProvider.getRequest().locationSettingsIgnored).isFalse();
+        assertThat(mProvider.getRequest().interval).isEqualTo(5);
+        assertThat(mProvider.getRequest().lowPowerMode).isFalse();
+        assertThat(mProvider.getRequest().workSource).isNotNull();
+
+        ILocationListener listener2 = createMockLocationListener();
+        LocationRequest request2 = LocationRequest.createFromDeprecatedProvider(NAME, 1, 0,
+                false).setLowPowerMode(true);
+        mManager.registerLocationRequest(request2, IDENTITY, PERMISSION_FINE, listener2);
+
+        assertThat(mProvider.getRequest().reportLocation).isTrue();
+        assertThat(mProvider.getRequest().locationRequests).containsExactly(request1, request2);
+        assertThat(mProvider.getRequest().locationSettingsIgnored).isFalse();
+        assertThat(mProvider.getRequest().interval).isEqualTo(1);
+        assertThat(mProvider.getRequest().lowPowerMode).isFalse();
+        assertThat(mProvider.getRequest().workSource).isNotNull();
+
+        mManager.unregisterLocationRequest(listener1);
+
+        assertThat(mProvider.getRequest().reportLocation).isTrue();
+        assertThat(mProvider.getRequest().locationRequests).containsExactly(request2);
+        assertThat(mProvider.getRequest().locationSettingsIgnored).isFalse();
+        assertThat(mProvider.getRequest().interval).isEqualTo(1);
+        assertThat(mProvider.getRequest().lowPowerMode).isTrue();
+        assertThat(mProvider.getRequest().workSource).isNotNull();
+
+        mManager.unregisterLocationRequest(listener2);
+
+        assertThat(mProvider.getRequest().reportLocation).isFalse();
+        assertThat(mProvider.getRequest().locationRequests).isEmpty();
+    }
+
+    @Test
+    public void testProviderRequest_BackgroundThrottle() {
+        ILocationListener listener1 = createMockLocationListener();
+        LocationRequest request1 = LocationRequest.createFromDeprecatedProvider(NAME, 5, 0, false);
+        mManager.registerLocationRequest(request1, IDENTITY, PERMISSION_FINE, listener1);
+
+        assertThat(mProvider.getRequest().interval).isEqualTo(5);
+
+        mInjector.getAppForegroundHelper().setAppForeground(IDENTITY.getUid(), false);
+        assertThat(mProvider.getRequest().interval).isEqualTo(
+                mInjector.getSettingsHelper().getBackgroundThrottleIntervalMs());
+    }
+
+    @Test
+    public void testProviderRequest_IgnoreLocationSettings() {
+        mInjector.getSettingsHelper().setIgnoreSettingsPackageWhitelist(
+                Collections.singleton(IDENTITY.getPackageName()));
+
+        ILocationListener listener1 = createMockLocationListener();
+        LocationRequest request1 = LocationRequest.createFromDeprecatedProvider(NAME, 5, 0, false);
+        mManager.registerLocationRequest(request1, IDENTITY, PERMISSION_FINE, listener1);
+
+        assertThat(mProvider.getRequest().reportLocation).isTrue();
+        assertThat(mProvider.getRequest().interval).isEqualTo(5);
+        assertThat(mProvider.getRequest().locationSettingsIgnored).isFalse();
+
+        ILocationListener listener2 = createMockLocationListener();
+        LocationRequest request2 = LocationRequest.createFromDeprecatedProvider(NAME, 1, 0,
+                false).setLocationSettingsIgnored(true);
+        mManager.registerLocationRequest(request2, IDENTITY, PERMISSION_FINE, listener2);
+
+        assertThat(mProvider.getRequest().reportLocation).isTrue();
+        assertThat(mProvider.getRequest().interval).isEqualTo(1);
+        assertThat(mProvider.getRequest().locationSettingsIgnored).isTrue();
+    }
+
+    @Test
+    public void testProviderRequest_IgnoreLocationSettings_ProviderDisabled() {
+        mInjector.getSettingsHelper().setIgnoreSettingsPackageWhitelist(
+                Collections.singleton(IDENTITY.getPackageName()));
+
+        ILocationListener listener1 = createMockLocationListener();
+        LocationRequest request1 = LocationRequest.createFromDeprecatedProvider(NAME, 1, 0, false);
+        mManager.registerLocationRequest(request1, IDENTITY, PERMISSION_FINE, listener1);
+
+        ILocationListener listener2 = createMockLocationListener();
+        LocationRequest request2 = LocationRequest.createFromDeprecatedProvider(NAME, 5, 0,
+                false).setLocationSettingsIgnored(true);
+        mManager.registerLocationRequest(request2, IDENTITY, PERMISSION_FINE, listener2);
+
+        mInjector.getSettingsHelper().setLocationEnabled(false, IDENTITY.getUserId());
+
+        assertThat(mProvider.getRequest().reportLocation).isTrue();
+        assertThat(mProvider.getRequest().locationRequests).containsExactly(request2);
+        assertThat(mProvider.getRequest().interval).isEqualTo(5);
+        assertThat(mProvider.getRequest().locationSettingsIgnored).isTrue();
+    }
+
+    @Test
+    public void testProviderRequest_IgnoreLocationSettings_NoAllowlist() {
+        mInjector.getSettingsHelper().setIgnoreSettingsPackageWhitelist(
+                Collections.singleton(IDENTITY.getPackageName()));
+
+        ILocationListener listener = createMockLocationListener();
+        LocationRequest request = LocationRequest.createFromDeprecatedProvider(NAME, 1, 0,
+                false).setLocationSettingsIgnored(true);
+        mManager.registerLocationRequest(request, IDENTITY, PERMISSION_FINE, listener);
+
+        mInjector.getSettingsHelper().setIgnoreSettingsPackageWhitelist(Collections.emptySet());
+
+        assertThat(mProvider.getRequest().reportLocation).isTrue();
+        assertThat(mProvider.getRequest().interval).isEqualTo(1);
+        assertThat(mProvider.getRequest().locationSettingsIgnored).isFalse();
+    }
+
+    @Test
+    public void testProviderRequest_BackgroundThrottle_IgnoreLocationSettings() {
+        mInjector.getSettingsHelper().setIgnoreSettingsPackageWhitelist(
+                Collections.singleton(IDENTITY.getPackageName()));
+
+        ILocationListener listener1 = createMockLocationListener();
+        LocationRequest request1 = LocationRequest.createFromDeprecatedProvider(NAME, 5, 0,
+                false).setLocationSettingsIgnored(true);
+        mManager.registerLocationRequest(request1, IDENTITY, PERMISSION_FINE, listener1);
+
+        assertThat(mProvider.getRequest().interval).isEqualTo(5);
+
+        mInjector.getAppForegroundHelper().setAppForeground(IDENTITY.getUid(), false);
+        assertThat(mProvider.getRequest().interval).isEqualTo(5);
+    }
+
+    private ILocationListener createMockLocationListener() {
+        return spy(new ILocationListener.Stub() {
+            @Override
+            public void onLocationChanged(Location location, IRemoteCallback onCompleteCallback) {
+                if (onCompleteCallback != null) {
+                    try {
+                        onCompleteCallback.sendResult(null);
+                    } catch (RemoteException e) {
+                        e.rethrowFromSystemServer();
+                    }
+                }
+            }
+
+            @Override
+            public void onProviderEnabledChanged(String provider, boolean enabled) {
+            }
+        });
+    }
+
+    private ILocationCallback createMockGetCurrentLocationListener() {
+        return spy(new ILocationCallback.Stub() {
+            @Override
+            public void onLocation(Location location) {
+            }
+        });
+    }
+
+    private static class TestProvider extends AbstractLocationProvider {
+
+        private ProviderRequest mProviderRequest = ProviderRequest.EMPTY_REQUEST;
+
+        TestProvider(ProviderProperties properties, CallerIdentity identity) {
+            super(DIRECT_EXECUTOR, identity);
+            setProperties(properties);
+        }
+
+        public void setProviderAllowed(boolean allowed) {
+            setAllowed(allowed);
+        }
+
+        public void setProviderLocation(Location l) {
+            reportLocation(new Location(l));
+        }
+
+        public ProviderRequest getRequest() {
+            return mProviderRequest;
+        }
+
+        @Override
+        public void onSetRequest(ProviderRequest request) {
+            mProviderRequest = request;
+        }
+
+        @Override
+        protected void onExtraCommand(int uid, int pid, String command, Bundle extras) {
+        }
+
+        @Override
+        public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        }
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/VibratorServiceTest.java b/services/tests/servicestests/src/com/android/server/VibratorServiceTest.java
index c692253..1b6ac3c 100644
--- a/services/tests/servicestests/src/com/android/server/VibratorServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/VibratorServiceTest.java
@@ -183,13 +183,12 @@
 
     @Test
     public void hasAmplitudeControl_withAmplitudeControlSupport_returnsTrue() {
-        when(mNativeWrapperMock.vibratorSupportsAmplitudeControl()).thenReturn(true);
+        mockVibratorCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
         assertTrue(createService().hasAmplitudeControl());
     }
 
     @Test
     public void hasAmplitudeControl_withNoAmplitudeControlSupport_returnsFalse() {
-        when(mNativeWrapperMock.vibratorSupportsAmplitudeControl()).thenReturn(false);
         assertFalse(createService().hasAmplitudeControl());
     }
 
@@ -270,7 +269,7 @@
 
     @Test
     public void vibrate_withOneShotAndAmplitudeControl_turnsVibratorOnAndSetsAmplitude() {
-        when(mNativeWrapperMock.vibratorSupportsAmplitudeControl()).thenReturn(true);
+        mockVibratorCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
         VibratorService service = createService();
         Mockito.clearInvocations(mNativeWrapperMock);
 
@@ -340,7 +339,7 @@
     @Test
     public void vibrate_withWaveform_controlsVibratorAmplitudeDuringTotalVibrationTime()
             throws Exception {
-        when(mNativeWrapperMock.vibratorSupportsAmplitudeControl()).thenReturn(true);
+        mockVibratorCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
         VibratorService service = createService();
         Mockito.clearInvocations(mNativeWrapperMock);
 
@@ -511,7 +510,7 @@
         setVibrationIntensityUserSetting(Settings.System.RING_VIBRATION_INTENSITY,
                 Vibrator.VIBRATION_INTENSITY_OFF);
 
-        when(mNativeWrapperMock.vibratorSupportsAmplitudeControl()).thenReturn(true);
+        mockVibratorCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
         VibratorService service = createService();
         service.systemReady();
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java
index a137cde..044f819 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java
@@ -168,8 +168,8 @@
 
     @Test
     public void testStartLockTaskMode_once() throws Exception {
-        // GIVEN a task record with whitelisted auth
-        Task tr = getTask(Task.LOCK_TASK_AUTH_WHITELISTED);
+        // GIVEN a task record with allowlisted auth
+        Task tr = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED);
 
         // WHEN calling setLockTaskMode for LOCKED mode without resuming
         mLockTaskController.startLockTaskMode(tr, false, TEST_UID);
@@ -185,9 +185,9 @@
 
     @Test
     public void testStartLockTaskMode_twice() throws Exception {
-        // GIVEN two task records with whitelisted auth
-        Task tr1 = getTask(Task.LOCK_TASK_AUTH_WHITELISTED);
-        Task tr2 = getTask(Task.LOCK_TASK_AUTH_WHITELISTED);
+        // GIVEN two task records with allowlisted auth
+        Task tr1 = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED);
+        Task tr2 = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED);
 
         // WHEN calling setLockTaskMode for LOCKED mode on both tasks
         mLockTaskController.startLockTaskMode(tr1, false, TEST_UID);
@@ -205,7 +205,7 @@
 
     @Test
     public void testStartLockTaskMode_pinningRequest() {
-        // GIVEN a task record that is not whitelisted, i.e. with pinned auth
+        // GIVEN a task record that is not allowlisted, i.e. with pinned auth
         Task tr = getTask(Task.LOCK_TASK_AUTH_PINNABLE);
 
         // WHEN calling startLockTaskMode
@@ -236,23 +236,23 @@
 
     @Test
     public void testLockTaskViolation() {
-        // GIVEN one task record with whitelisted auth that is in lock task mode
-        Task tr = getTask(Task.LOCK_TASK_AUTH_WHITELISTED);
+        // GIVEN one task record with allowlisted auth that is in lock task mode
+        Task tr = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED);
         mLockTaskController.startLockTaskMode(tr, false, TEST_UID);
 
         // THEN it's not a lock task violation to try and launch this task without clearing
         assertFalse(mLockTaskController.isLockTaskModeViolation(tr, false));
 
-        // THEN it's a lock task violation to launch another task that is not whitelisted
+        // THEN it's a lock task violation to launch another task that is not allowlisted
         assertTrue(mLockTaskController.isLockTaskModeViolation(getTask(
                 Task.LOCK_TASK_AUTH_PINNABLE)));
         // THEN it's a lock task violation to launch another task that is disallowed from lock task
         assertTrue(mLockTaskController.isLockTaskModeViolation(getTask(
                 Task.LOCK_TASK_AUTH_DONT_LOCK)));
 
-        // THEN it's no a lock task violation to launch another task that is whitelisted
+        // THEN it's no a lock task violation to launch another task that is allowlisted
         assertFalse(mLockTaskController.isLockTaskModeViolation(getTask(
-                Task.LOCK_TASK_AUTH_WHITELISTED)));
+                Task.LOCK_TASK_AUTH_ALLOWLISTED)));
         assertFalse(mLockTaskController.isLockTaskModeViolation(getTask(
                 Task.LOCK_TASK_AUTH_LAUNCHABLE)));
         // THEN it's not a lock task violation to launch another task that is priv launchable
@@ -262,8 +262,8 @@
 
     @Test
     public void testLockTaskViolation_emergencyCall() {
-        // GIVEN one task record with whitelisted auth that is in lock task mode
-        Task tr = getTask(Task.LOCK_TASK_AUTH_WHITELISTED);
+        // GIVEN one task record with allowlisted auth that is in lock task mode
+        Task tr = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED);
         mLockTaskController.startLockTaskMode(tr, false, TEST_UID);
 
         // GIVEN tasks necessary for emergency calling
@@ -294,8 +294,8 @@
 
     @Test
     public void testStopLockTaskMode() throws Exception {
-        // GIVEN one task record with whitelisted auth that is in lock task mode
-        Task tr = getTask(Task.LOCK_TASK_AUTH_WHITELISTED);
+        // GIVEN one task record with allowlisted auth that is in lock task mode
+        Task tr = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED);
         mLockTaskController.startLockTaskMode(tr, false, TEST_UID);
 
         // WHEN the same caller calls stopLockTaskMode
@@ -311,8 +311,8 @@
 
     @Test(expected = SecurityException.class)
     public void testStopLockTaskMode_differentCaller() {
-        // GIVEN one task record with whitelisted auth that is in lock task mode
-        Task tr = getTask(Task.LOCK_TASK_AUTH_WHITELISTED);
+        // GIVEN one task record with allowlisted auth that is in lock task mode
+        Task tr = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED);
         mLockTaskController.startLockTaskMode(tr, false, TEST_UID);
 
         // WHEN a different caller calls stopLockTaskMode
@@ -323,8 +323,8 @@
 
     @Test
     public void testStopLockTaskMode_systemCaller() {
-        // GIVEN one task record with whitelisted auth that is in lock task mode
-        Task tr = getTask(Task.LOCK_TASK_AUTH_WHITELISTED);
+        // GIVEN one task record with allowlisted auth that is in lock task mode
+        Task tr = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED);
         mLockTaskController.startLockTaskMode(tr, false, TEST_UID);
 
         // WHEN system calls stopLockTaskMode
@@ -336,9 +336,9 @@
 
     @Test
     public void testStopLockTaskMode_twoTasks() throws Exception {
-        // GIVEN two task records with whitelisted auth that is in lock task mode
-        Task tr1 = getTask(Task.LOCK_TASK_AUTH_WHITELISTED);
-        Task tr2 = getTask(Task.LOCK_TASK_AUTH_WHITELISTED);
+        // GIVEN two task records with allowlisted auth that is in lock task mode
+        Task tr1 = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED);
+        Task tr2 = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED);
         mLockTaskController.startLockTaskMode(tr1, false, TEST_UID);
         mLockTaskController.startLockTaskMode(tr2, false, TEST_UID);
 
@@ -357,9 +357,9 @@
 
     @Test
     public void testStopLockTaskMode_rootTask() throws Exception {
-        // GIVEN two task records with whitelisted auth that is in lock task mode
-        Task tr1 = getTask(Task.LOCK_TASK_AUTH_WHITELISTED);
-        Task tr2 = getTask(Task.LOCK_TASK_AUTH_WHITELISTED);
+        // GIVEN two task records with allowlisted auth that is in lock task mode
+        Task tr1 = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED);
+        Task tr2 = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED);
         mLockTaskController.startLockTaskMode(tr1, false, TEST_UID);
         mLockTaskController.startLockTaskMode(tr2, false, TEST_UID);
 
@@ -405,9 +405,9 @@
 
     @Test
     public void testClearLockedTasks() throws Exception {
-        // GIVEN two task records with whitelisted auth that is in lock task mode
-        Task tr1 = getTask(Task.LOCK_TASK_AUTH_WHITELISTED);
-        Task tr2 = getTask(Task.LOCK_TASK_AUTH_WHITELISTED);
+        // GIVEN two task records with allowlisted auth that is in lock task mode
+        Task tr1 = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED);
+        Task tr2 = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED);
         mLockTaskController.startLockTaskMode(tr1, false, TEST_UID);
         mLockTaskController.startLockTaskMode(tr2, false, TEST_UID);
 
@@ -434,7 +434,7 @@
                 .thenReturn(DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED);
 
         // AND there is a task record
-        Task tr1 = getTask(Task.LOCK_TASK_AUTH_WHITELISTED);
+        Task tr1 = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED);
         mLockTaskController.startLockTaskMode(tr1, true, TEST_UID);
 
         // WHEN calling clearLockedTasks on the root task
@@ -454,7 +454,7 @@
                 .thenReturn(true);
 
         // AND there is a task record
-        Task tr1 = getTask(Task.LOCK_TASK_AUTH_WHITELISTED);
+        Task tr1 = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED);
         mLockTaskController.startLockTaskMode(tr1, true, TEST_UID);
 
         // WHEN calling clearLockedTasks on the root task
@@ -471,7 +471,7 @@
                 Settings.Secure.LOCK_TO_APP_EXIT_LOCKED, 1, mContext.getUserId());
 
         // AND there is a task record
-        Task tr1 = getTask(Task.LOCK_TASK_AUTH_WHITELISTED);
+        Task tr1 = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED);
         mLockTaskController.startLockTaskMode(tr1, true, TEST_UID);
 
         // WHEN calling clearLockedTasks on the root task
@@ -488,7 +488,7 @@
                 Settings.Secure.LOCK_TO_APP_EXIT_LOCKED, 0, mContext.getUserId());
 
         // AND there is a task record
-        Task tr1 = getTask(Task.LOCK_TASK_AUTH_WHITELISTED);
+        Task tr1 = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED);
         mLockTaskController.startLockTaskMode(tr1, true, TEST_UID);
 
         // WHEN calling clearLockedTasks on the root task
@@ -500,45 +500,45 @@
 
     @Test
     public void testUpdateLockTaskPackages() {
-        String[] whitelist1 = {TEST_PACKAGE_NAME, TEST_PACKAGE_NAME_2};
-        String[] whitelist2 = {TEST_PACKAGE_NAME};
+        String[] allowlist1 = {TEST_PACKAGE_NAME, TEST_PACKAGE_NAME_2};
+        String[] allowlist2 = {TEST_PACKAGE_NAME};
 
-        // No package is whitelisted initially
-        for (String pkg : whitelist1) {
-            assertFalse("Package shouldn't be whitelisted: " + pkg,
-                    mLockTaskController.isPackageWhitelisted(TEST_USER_ID, pkg));
-            assertFalse("Package shouldn't be whitelisted for user 0: " + pkg,
-                    mLockTaskController.isPackageWhitelisted(0, pkg));
+        // No package is allowlisted initially
+        for (String pkg : allowlist1) {
+            assertFalse("Package shouldn't be allowlisted: " + pkg,
+                    mLockTaskController.isPackageAllowlisted(TEST_USER_ID, pkg));
+            assertFalse("Package shouldn't be allowlisted for user 0: " + pkg,
+                    mLockTaskController.isPackageAllowlisted(0, pkg));
         }
 
-        // Apply whitelist
-        mLockTaskController.updateLockTaskPackages(TEST_USER_ID, whitelist1);
+        // Apply allowlist
+        mLockTaskController.updateLockTaskPackages(TEST_USER_ID, allowlist1);
 
-        // Assert the whitelist is applied to the correct user
-        for (String pkg : whitelist1) {
-            assertTrue("Package should be whitelisted: " + pkg,
-                    mLockTaskController.isPackageWhitelisted(TEST_USER_ID, pkg));
-            assertFalse("Package shouldn't be whitelisted for user 0: " + pkg,
-                    mLockTaskController.isPackageWhitelisted(0, pkg));
+        // Assert the allowlist is applied to the correct user
+        for (String pkg : allowlist1) {
+            assertTrue("Package should be allowlisted: " + pkg,
+                    mLockTaskController.isPackageAllowlisted(TEST_USER_ID, pkg));
+            assertFalse("Package shouldn't be allowlisted for user 0: " + pkg,
+                    mLockTaskController.isPackageAllowlisted(0, pkg));
         }
 
-        // Update whitelist
-        mLockTaskController.updateLockTaskPackages(TEST_USER_ID, whitelist2);
+        // Update allowlist
+        mLockTaskController.updateLockTaskPackages(TEST_USER_ID, allowlist2);
 
-        // Assert the new whitelist is applied
-        assertTrue("Package should remain whitelisted: " + TEST_PACKAGE_NAME,
-                mLockTaskController.isPackageWhitelisted(TEST_USER_ID, TEST_PACKAGE_NAME));
-        assertFalse("Package should no longer be whitelisted: " + TEST_PACKAGE_NAME_2,
-                mLockTaskController.isPackageWhitelisted(TEST_USER_ID, TEST_PACKAGE_NAME_2));
+        // Assert the new allowlist is applied
+        assertTrue("Package should remain allowlisted: " + TEST_PACKAGE_NAME,
+                mLockTaskController.isPackageAllowlisted(TEST_USER_ID, TEST_PACKAGE_NAME));
+        assertFalse("Package should no longer be allowlisted: " + TEST_PACKAGE_NAME_2,
+                mLockTaskController.isPackageAllowlisted(TEST_USER_ID, TEST_PACKAGE_NAME_2));
     }
 
     @Test
     public void testUpdateLockTaskPackages_taskRemoved() throws Exception {
-        // GIVEN two tasks which are whitelisted initially
+        // GIVEN two tasks which are allowlisted initially
         Task tr1 = getTaskForUpdate(TEST_PACKAGE_NAME, true);
         Task tr2 = getTaskForUpdate(TEST_PACKAGE_NAME_2, false);
-        String[] whitelist = {TEST_PACKAGE_NAME, TEST_PACKAGE_NAME_2};
-        mLockTaskController.updateLockTaskPackages(TEST_USER_ID, whitelist);
+        String[] allowlist = {TEST_PACKAGE_NAME, TEST_PACKAGE_NAME_2};
+        mLockTaskController.updateLockTaskPackages(TEST_USER_ID, allowlist);
 
         // GIVEN the tasks are launched into LockTask mode
         mLockTaskController.startLockTaskMode(tr1, false, TEST_UID);
@@ -548,9 +548,9 @@
         assertTrue(mLockTaskController.isTaskLocked(tr2));
         verifyLockTaskStarted(STATUS_BAR_MASK_LOCKED, DISABLE2_MASK);
 
-        // WHEN removing one package from whitelist
-        whitelist = new String[] {TEST_PACKAGE_NAME};
-        mLockTaskController.updateLockTaskPackages(TEST_USER_ID, whitelist);
+        // WHEN removing one package from allowlist
+        allowlist = new String[] {TEST_PACKAGE_NAME};
+        mLockTaskController.updateLockTaskPackages(TEST_USER_ID, allowlist);
 
         // THEN the task running that package should be stopped
         verify(tr2).performClearTaskLocked();
@@ -560,9 +560,9 @@
         assertTrue(mLockTaskController.isTaskLocked(tr1));
         verifyLockTaskStarted(STATUS_BAR_MASK_LOCKED, DISABLE2_MASK);
 
-        // WHEN removing the last package from whitelist
-        whitelist = new String[] {};
-        mLockTaskController.updateLockTaskPackages(TEST_USER_ID, whitelist);
+        // WHEN removing the last package from allowlist
+        allowlist = new String[] {};
+        mLockTaskController.updateLockTaskPackages(TEST_USER_ID, allowlist);
 
         // THEN the last task should be cleared, and the system should quit LockTask mode
         verify(tr1).performClearTaskLocked();
@@ -574,7 +574,7 @@
     @Test
     public void testUpdateLockTaskFeatures() throws Exception {
         // GIVEN a locked task
-        Task tr = getTask(Task.LOCK_TASK_AUTH_WHITELISTED);
+        Task tr = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED);
         mLockTaskController.startLockTaskMode(tr, false, TEST_UID);
 
         // THEN lock task mode should be started with default status bar masks
@@ -616,7 +616,7 @@
     @Test
     public void testUpdateLockTaskFeatures_differentUser() throws Exception {
         // GIVEN a locked task
-        Task tr = getTask(Task.LOCK_TASK_AUTH_WHITELISTED);
+        Task tr = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED);
         mLockTaskController.startLockTaskMode(tr, false, TEST_UID);
 
         // THEN lock task mode should be started with default status bar masks
@@ -638,7 +638,7 @@
     @Test
     public void testUpdateLockTaskFeatures_keyguard() {
         // GIVEN a locked task
-        Task tr = getTask(Task.LOCK_TASK_AUTH_WHITELISTED);
+        Task tr = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED);
         mLockTaskController.startLockTaskMode(tr, false, TEST_UID);
 
         // THEN keyguard should be disabled
@@ -704,7 +704,7 @@
                 TEST_USER_ID, TEST_PACKAGE_NAME, LOCK_TASK_LAUNCH_MODE_DEFAULT));
 
         // Start lock task mode
-        Task tr = getTask(Task.LOCK_TASK_AUTH_WHITELISTED);
+        Task tr = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED);
         mLockTaskController.startLockTaskMode(tr, false, TEST_UID);
 
         // WHEN LOCK_TASK_FEATURE_BLOCK_ACTIVITY_START_IN_TASK is not enabled
@@ -719,15 +719,15 @@
         assertTrue(mLockTaskController.isActivityAllowed(
                 TEST_USER_ID, TEST_PACKAGE_NAME, LOCK_TASK_LAUNCH_MODE_ALWAYS));
 
-        // unwhitelisted package should not be allowed
+        // unallowlisted package should not be allowed
         assertFalse(mLockTaskController.isActivityAllowed(
                 TEST_USER_ID, TEST_PACKAGE_NAME, LOCK_TASK_LAUNCH_MODE_DEFAULT));
 
-        // update the whitelist
-        String[] whitelist = new String[] { TEST_PACKAGE_NAME };
-        mLockTaskController.updateLockTaskPackages(TEST_USER_ID, whitelist);
+        // update the allowlist
+        String[] allowlist = new String[] { TEST_PACKAGE_NAME };
+        mLockTaskController.updateLockTaskPackages(TEST_USER_ID, allowlist);
 
-        // whitelisted package should be allowed
+        // allowlisted package should be allowed
         assertTrue(mLockTaskController.isActivityAllowed(
                 TEST_USER_ID, TEST_PACKAGE_NAME, LOCK_TASK_LAUNCH_MODE_DEFAULT));
 
@@ -755,17 +755,17 @@
     }
 
     /**
-     * @param isAppAware {@code true} if the app has marked if_whitelisted in its manifest
+     * @param isAppAware {@code true} if the app has marked if allowlisted in its manifest
      */
     private Task getTaskForUpdate(String pkg, boolean isAppAware) {
-        final int authIfWhitelisted = isAppAware
+        final int authIfAllowlisted = isAppAware
                 ? Task.LOCK_TASK_AUTH_LAUNCHABLE
-                : Task.LOCK_TASK_AUTH_WHITELISTED;
-        Task tr = getTask(pkg, authIfWhitelisted);
+                : Task.LOCK_TASK_AUTH_ALLOWLISTED;
+        Task tr = getTask(pkg, authIfAllowlisted);
         doAnswer((invocation) -> {
-            boolean isWhitelisted =
-                    mLockTaskController.isPackageWhitelisted(TEST_USER_ID, pkg);
-            tr.mLockTaskAuth = isWhitelisted ? authIfWhitelisted : Task.LOCK_TASK_AUTH_PINNABLE;
+            boolean isAllowlisted =
+                    mLockTaskController.isPackageAllowlisted(TEST_USER_ID, pkg);
+            tr.mLockTaskAuth = isAllowlisted ? authIfAllowlisted : Task.LOCK_TASK_AUTH_PINNABLE;
             return null;
         }).when(tr).setLockTaskAuth();
         return tr;
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/ColorFiltersMutateActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/ColorFiltersMutateActivity.java
index 51bae3a..08144c8 100644
--- a/tests/HwAccelerationTest/src/com/android/test/hwui/ColorFiltersMutateActivity.java
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/ColorFiltersMutateActivity.java
@@ -60,7 +60,7 @@
 
         static final String sSkSL =
                 "uniform float param1;\n"
-                + "void main(float x, float y, inout half4 color) {\n"
+                + "void main(float2 xy, inout half4 color) {\n"
                 + "color = half4(color.r, half(param1), color.b, 1.0);\n"
                 + "}\n";
 
diff --git a/tests/Input/Android.bp b/tests/Input/Android.bp
new file mode 100644
index 0000000..9d35cbc
--- /dev/null
+++ b/tests/Input/Android.bp
@@ -0,0 +1,12 @@
+android_test {
+    name: "InputTests",
+    srcs: ["src/**/*.kt"],
+    platform_apis: true,
+    certificate: "platform",
+    static_libs: [
+            "androidx.test.ext.junit",
+            "androidx.test.rules",
+            "android-support-test",
+            "ub-uiautomator",
+        ],
+}
diff --git a/tests/Input/AndroidManifest.xml b/tests/Input/AndroidManifest.xml
new file mode 100644
index 0000000..4195df7
--- /dev/null
+++ b/tests/Input/AndroidManifest.xml
@@ -0,0 +1,35 @@
+<?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.test.input">
+    <uses-permission android:name="android.permission.MONITOR_INPUT"/>
+    <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS"/>
+    <uses-permission android:name="android.permission.INJECT_EVENTS"/>
+
+    <application android:label="InputTest">
+
+        <activity android:name=".UnresponsiveGestureMonitorActivity"
+             android:label="Unresponsive gesture monitor"
+             android:process=":externalProcess">
+        </activity>
+
+
+    </application>
+    <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+                     android:targetPackage="com.android.test.input"
+                     android:label="Input Tests"/>
+</manifest>
diff --git a/tests/Input/AndroidTest.xml b/tests/Input/AndroidTest.xml
new file mode 100644
index 0000000..c62db1ea
--- /dev/null
+++ b/tests/Input/AndroidTest.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright 2020 Google Inc. All Rights Reserved.
+ -->
+<configuration description="Runs Input Tests">
+    <option name="test-tag" value="InputTests" />
+    <target_preparer class="com.android.tradefed.targetprep.DeviceSetup">
+        <!-- keeps the screen on during tests -->
+        <option name="screen-always-on" value="on" />
+        <!-- prevents the phone from restarting -->
+        <option name="force-skip-system-props" value="true" />
+    </target_preparer>
+    <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+        <option name="cleanup-apks" value="true"/>
+        <option name="test-file-name" value="InputTests.apk"/>
+
+    </target_preparer>
+    <test class="com.android.tradefed.testtype.AndroidJUnitTest">
+        <option name="package" value="com.android.test.input"/>
+        <option name="exclude-annotation" value="androidx.test.filters.FlakyTest" />
+        <option name="shell-timeout" value="660s" />
+        <option name="test-timeout" value="600s" />
+        <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
+    </test>
+</configuration>
diff --git a/tests/Input/src/com/android/test/input/AnrTest.kt b/tests/Input/src/com/android/test/input/AnrTest.kt
new file mode 100644
index 0000000..4da3eca
--- /dev/null
+++ b/tests/Input/src/com/android/test/input/AnrTest.kt
@@ -0,0 +1,115 @@
+/*
+ * 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.test.input
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.filters.MediumTest
+
+import android.graphics.Rect
+import android.os.SystemClock
+import android.provider.Settings
+import android.provider.Settings.Global.HIDE_ERROR_DIALOGS
+import android.support.test.uiautomator.By
+import android.support.test.uiautomator.UiDevice
+import android.support.test.uiautomator.UiObject2
+import android.support.test.uiautomator.Until
+import android.view.InputDevice
+import android.view.MotionEvent
+
+import org.junit.After
+import org.junit.Assert.fail
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/**
+ * This test makes sure that an unresponsive gesture monitor gets an ANR.
+ *
+ * The gesture monitor must be registered from a different process than the instrumented process.
+ * Otherwise, when the test runs, you will get:
+ * Test failed to run to completion.
+ * Reason: 'Instrumentation run failed due to 'keyDispatchingTimedOut''.
+ * Check device logcat for details
+ * RUNNER ERROR: Instrumentation run failed due to 'keyDispatchingTimedOut'
+ */
+@MediumTest
+@RunWith(AndroidJUnit4::class)
+class AnrTest {
+    companion object {
+        private const val TAG = "AnrTest"
+    }
+
+    val mInstrumentation = InstrumentationRegistry.getInstrumentation()
+    var mHideErrorDialogs = 0
+
+    @Before
+    fun setUp() {
+        val contentResolver = mInstrumentation.targetContext.contentResolver
+        mHideErrorDialogs = Settings.Global.getInt(contentResolver, HIDE_ERROR_DIALOGS, 0)
+        Settings.Global.putInt(contentResolver, HIDE_ERROR_DIALOGS, 0)
+    }
+
+    @After
+    fun tearDown() {
+        val contentResolver = mInstrumentation.targetContext.contentResolver
+        Settings.Global.putInt(contentResolver, HIDE_ERROR_DIALOGS, mHideErrorDialogs)
+    }
+
+    @Test
+    fun testGestureMonitorAnr() {
+        startUnresponsiveActivity()
+        val uiDevice: UiDevice = UiDevice.getInstance(mInstrumentation)
+        val obj: UiObject2? = uiDevice.wait(Until.findObject(
+                By.text("Unresponsive gesture monitor")), 10000)
+
+        if (obj == null) {
+            fail("Could not find unresponsive activity")
+            return
+        }
+
+        val rect: Rect = obj.visibleBounds
+        val downTime = SystemClock.uptimeMillis()
+        val downEvent = MotionEvent.obtain(downTime, downTime,
+                MotionEvent.ACTION_DOWN, rect.left.toFloat(), rect.top.toFloat(), 0 /* metaState */)
+        downEvent.source = InputDevice.SOURCE_TOUCHSCREEN
+
+        mInstrumentation.uiAutomation.injectInputEvent(downEvent, false /* sync*/)
+
+        // Todo: replace using timeout from android.hardware.input.IInputManager
+        SystemClock.sleep(5000) // default ANR timeout for gesture monitors
+
+        clickCloseAppOnAnrDialog()
+    }
+
+    private fun clickCloseAppOnAnrDialog() {
+        // Find anr dialog and kill app
+        val uiDevice: UiDevice = UiDevice.getInstance(mInstrumentation)
+        val closeAppButton: UiObject2? =
+                uiDevice.wait(Until.findObject(By.res("android:id/aerr_close")), 20000)
+        if (closeAppButton == null) {
+            fail("Could not find anr dialog")
+            return
+        }
+        closeAppButton.click()
+    }
+
+    private fun startUnresponsiveActivity() {
+        val flags = " -W -n "
+        val startCmd = "am start $flags com.android.test.input/.UnresponsiveGestureMonitorActivity"
+        mInstrumentation.uiAutomation.executeShellCommand(startCmd)
+    }
+}
\ No newline at end of file
diff --git a/tests/Input/src/com/android/test/input/UnresponsiveGestureMonitorActivity.kt b/tests/Input/src/com/android/test/input/UnresponsiveGestureMonitorActivity.kt
new file mode 100644
index 0000000..d83a457
--- /dev/null
+++ b/tests/Input/src/com/android/test/input/UnresponsiveGestureMonitorActivity.kt
@@ -0,0 +1,52 @@
+/**
+ * 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.test.input
+
+import android.app.Activity
+import android.hardware.input.InputManager
+import android.os.Bundle
+import android.os.Looper
+import android.util.Log
+import android.view.InputChannel
+import android.view.InputEvent
+import android.view.InputEventReceiver
+import android.view.InputMonitor
+
+class UnresponsiveReceiver(channel: InputChannel, looper: Looper) :
+        InputEventReceiver(channel, looper) {
+    companion object {
+        const val TAG = "UnresponsiveReceiver"
+    }
+    override fun onInputEvent(event: InputEvent) {
+        Log.i(TAG, "Received $event")
+        // Not calling 'finishInputEvent' in order to trigger the ANR
+    }
+}
+
+class UnresponsiveGestureMonitorActivity : Activity() {
+    companion object {
+        const val MONITOR_NAME = "unresponsive gesture monitor"
+    }
+    private lateinit var mInputEventReceiver: InputEventReceiver
+    private lateinit var mInputMonitor: InputMonitor
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        mInputMonitor = InputManager.getInstance().monitorGestureInput(MONITOR_NAME, displayId)
+        mInputEventReceiver = UnresponsiveReceiver(
+                mInputMonitor.getInputChannel(), Looper.myLooper())
+    }
+}
diff --git a/tests/StagedInstallTest/Android.bp b/tests/StagedInstallTest/Android.bp
index da6018e..530d0e4 100644
--- a/tests/StagedInstallTest/Android.bp
+++ b/tests/StagedInstallTest/Android.bp
@@ -29,6 +29,7 @@
         "compatibility-tradefed",
         "frameworks-base-hostutils",
         "module_test_util",
+        "cts-install-lib-host",
     ],
     data: [
         ":com.android.apex.cts.shim.v2_prebuilt",
diff --git a/tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java b/tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java
index 7cfbdc2..5285b04 100644
--- a/tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java
+++ b/tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java
@@ -22,6 +22,7 @@
 import static org.junit.Assert.fail;
 import static org.junit.Assume.assumeTrue;
 
+import com.android.cts.install.lib.host.InstallUtilsHost;
 import com.android.ddmlib.Log;
 import com.android.tests.rollback.host.AbandonSessionsRule;
 import com.android.tests.util.ModuleTestUtils;
@@ -49,6 +50,7 @@
     private static final String APK_A = "TestAppAv1.apk";
 
     private final ModuleTestUtils mTestUtils = new ModuleTestUtils(this);
+    private final InstallUtilsHost mHostUtils = new InstallUtilsHost(this);
 
     /**
      * Runs the given phase of a test by calling into the device.
@@ -93,7 +95,7 @@
     @Test
     public void testAdbStagedInstallWaitForReadyFlagWorks() throws Exception {
         assumeTrue("Device does not support updating APEX",
-                mTestUtils.isApexUpdateSupported());
+                mHostUtils.isApexUpdateSupported());
 
         File apexFile = mTestUtils.getTestFile(SHIM_V2);
         String output = getDevice().executeAdbCommand("install", "--staged",
@@ -107,7 +109,7 @@
     @Test
     public void testAdbStagedInstallNoWaitFlagWorks() throws Exception {
         assumeTrue("Device does not support updating APEX",
-                mTestUtils.isApexUpdateSupported());
+                mHostUtils.isApexUpdateSupported());
 
         File apexFile = mTestUtils.getTestFile(SHIM_V2);
         String output = getDevice().executeAdbCommand("install", "--staged",
@@ -122,7 +124,7 @@
     @Test
     public void testAdbInstallMultiPackageCommandWorks() throws Exception {
         assumeTrue("Device does not support updating APEX",
-                mTestUtils.isApexUpdateSupported());
+                mHostUtils.isApexUpdateSupported());
 
         File apexFile = mTestUtils.getTestFile(SHIM_V2);
         File apkFile = mTestUtils.getTestFile(APK_A);