Merge "Dependency.get(BG_HANDLER) is dead. Long live @BgHandler."
diff --git a/api/system-current.txt b/api/system-current.txt
index 412e095..ba4ba48 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -68,6 +68,7 @@
     field public static final String CRYPT_KEEPER = "android.permission.CRYPT_KEEPER";
     field public static final String DEVICE_POWER = "android.permission.DEVICE_POWER";
     field public static final String DISPATCH_PROVISIONING_MESSAGE = "android.permission.DISPATCH_PROVISIONING_MESSAGE";
+    field public static final String ENTER_CAR_MODE_PRIORITIZED = "android.permission.ENTER_CAR_MODE_PRIORITIZED";
     field public static final String FORCE_BACK = "android.permission.FORCE_BACK";
     field public static final String FORCE_STOP_PACKAGES = "android.permission.FORCE_STOP_PACKAGES";
     field public static final String GET_APP_OPS_STATS = "android.permission.GET_APP_OPS_STATS";
@@ -77,6 +78,7 @@
     field public static final String GRANT_PROFILE_OWNER_DEVICE_IDS_ACCESS = "android.permission.GRANT_PROFILE_OWNER_DEVICE_IDS_ACCESS";
     field public static final String GRANT_RUNTIME_PERMISSIONS = "android.permission.GRANT_RUNTIME_PERMISSIONS";
     field public static final String GRANT_RUNTIME_PERMISSIONS_TO_TELEPHONY_DEFAULTS = "android.permission.GRANT_RUNTIME_PERMISSIONS_TO_TELEPHONY_DEFAULTS";
+    field public static final String HANDLE_CAR_MODE_CHANGES = "android.permission.HANDLE_CAR_MODE_CHANGES";
     field public static final String HARDWARE_TEST = "android.permission.HARDWARE_TEST";
     field public static final String HDMI_CEC = "android.permission.HDMI_CEC";
     field public static final String HIDE_NON_SYSTEM_OVERLAY_WINDOWS = "android.permission.HIDE_NON_SYSTEM_OVERLAY_WINDOWS";
@@ -646,6 +648,15 @@
     method public boolean isStatusBarExpansionDisabled();
   }
 
+  public class UiModeManager {
+    method @RequiresPermission(android.Manifest.permission.ENTER_CAR_MODE_PRIORITIZED) public void enableCarMode(@IntRange(from=0) int, int);
+    field public static final String ACTION_ENTER_CAR_MODE_PRIORITIZED = "android.app.action.ENTER_CAR_MODE_PRIORITIZED";
+    field public static final String ACTION_EXIT_CAR_MODE_PRIORITIZED = "android.app.action.EXIT_CAR_MODE_PRIORITIZED";
+    field public static final int DEFAULT_PRIORITY = 0; // 0x0
+    field public static final String EXTRA_CALLING_PACKAGE = "android.app.extra.CALLING_PACKAGE";
+    field public static final String EXTRA_PRIORITY = "android.app.extra.PRIORITY";
+  }
+
   public final class Vr2dDisplayProperties implements android.os.Parcelable {
     ctor public Vr2dDisplayProperties(int, int, int);
     method public int describeContents();
diff --git a/api/test-current.txt b/api/test-current.txt
index 19e1212..e0e48d4 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -420,6 +420,7 @@
   }
 
   public class UiModeManager {
+    method @RequiresPermission("android.permission.ENTER_CAR_MODE_PRIORITIZED") public void enableCarMode(@IntRange(from=0) int, int);
     method public boolean isNightModeLocked();
     method public boolean isUiModeLocked();
   }
diff --git a/core/java/android/app/IUiModeManager.aidl b/core/java/android/app/IUiModeManager.aidl
index f2c9f61..a3e0845 100644
--- a/core/java/android/app/IUiModeManager.aidl
+++ b/core/java/android/app/IUiModeManager.aidl
@@ -25,7 +25,7 @@
      * Enables the car mode. Only the system can do this.
      * @hide
      */
-    void enableCarMode(int flags);
+    void enableCarMode(int flags, int priority, String callingPackage);
 
     /**
      * Disables the car mode.
@@ -34,6 +34,12 @@
     void disableCarMode(int flags);
 
     /**
+     * Disables car mode (the original version is marked unsupported app usage so cannot be changed
+     * for the time being).
+     */
+    void disableCarModeByCallingPackage(int flags, String callingPackage);
+
+    /**
      * Return the current running mode.
      */
     int getCurrentModeType();
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 69c174a..d0ba879 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -657,7 +657,7 @@
                 new CachedServiceFetcher<UiModeManager>() {
             @Override
             public UiModeManager createService(ContextImpl ctx) throws ServiceNotFoundException {
-                return new UiModeManager();
+                return new UiModeManager(ctx.getOuterContext());
             }});
 
         registerService(Context.USB_SERVICE, UsbManager.class,
diff --git a/core/java/android/app/UiModeManager.java b/core/java/android/app/UiModeManager.java
index 46316e1..8324787 100644
--- a/core/java/android/app/UiModeManager.java
+++ b/core/java/android/app/UiModeManager.java
@@ -17,6 +17,10 @@
 package android.app;
 
 import android.annotation.IntDef;
+import android.annotation.IntRange;
+import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
 import android.annotation.SystemService;
 import android.annotation.TestApi;
 import android.annotation.UnsupportedAppUsage;
@@ -68,6 +72,25 @@
      * of the broadcast to {@link Activity#RESULT_CANCELED}.
      */
     public static String ACTION_ENTER_CAR_MODE = "android.app.action.ENTER_CAR_MODE";
+
+    /**
+     * Broadcast sent when the device's UI has switched to car mode, either by being placed in a car
+     * dock or explicit action of the user.
+     * <p>
+     * In addition to the behavior for {@link #ACTION_ENTER_CAR_MODE}, this broadcast includes the
+     * package name of the app which requested to enter car mode in the
+     * {@link #EXTRA_CALLING_PACKAGE}.  If an app requested to enter car mode using
+     * {@link #enableCarMode(int, int)} and specified a priority this will be specified in the
+     * {@link #EXTRA_PRIORITY}.
+     *
+     * This is primarily intended to be received by other components of the Android OS.
+     * <p>
+     * Receiver requires permission: {@link android.Manifest.permission.HANDLE_CAR_MODE_CHANGES}
+     * @hide
+     */
+    @SystemApi
+    public static final String ACTION_ENTER_CAR_MODE_PRIORITIZED =
+            "android.app.action.ENTER_CAR_MODE_PRIORITIZED";
     
     /**
      * Broadcast sent when the device's UI has switch away from car mode back
@@ -75,6 +98,28 @@
      * when the user exits car mode.
      */
     public static String ACTION_EXIT_CAR_MODE = "android.app.action.EXIT_CAR_MODE";
+
+    /**
+     * Broadcast sent when the device's UI has switched away from car mode back to normal mode.
+     * Typically used by a car mode app, to dismiss itself when the user exits car mode.
+     * <p>
+     * In addition to the behavior for {@link #ACTION_EXIT_CAR_MODE}, this broadcast includes the
+     * package name of the app which requested to exit car mode in {@link #EXTRA_CALLING_PACKAGE}.
+     * If an app requested to enter car mode using {@link #enableCarMode(int, int)} and specified a
+     * priority this will be specified in the {@link #EXTRA_PRIORITY} when exiting car mode.
+     * <p>
+     * If {@link #DISABLE_CAR_MODE_ALL_PRIORITIES} is used when disabling car mode (i.e. this is
+     * initiated by the user via the persistent car mode notification), this broadcast is sent once
+     * for each priority level for which car mode is being disabled.
+     * <p>
+     * This is primarily intended to be received by other components of the Android OS.
+     * <p>
+     * Receiver requires permission: {@link android.Manifest.permission.HANDLE_CAR_MODE_CHANGES}
+     * @hide
+     */
+    @SystemApi
+    public static final String ACTION_EXIT_CAR_MODE_PRIORITIZED =
+            "android.app.action.EXIT_CAR_MODE_PRIORITIZED";
     
     /**
      * Broadcast sent when the device's UI has switched to desk mode,
@@ -97,6 +142,24 @@
      */
     public static String ACTION_EXIT_DESK_MODE = "android.app.action.EXIT_DESK_MODE";
 
+    /**
+     * String extra used with {@link #ACTION_ENTER_CAR_MODE_PRIORITIZED} and
+     * {@link #ACTION_EXIT_CAR_MODE_PRIORITIZED} to indicate the package name of the app which
+     * requested to enter or exit car mode.
+     * @hide
+     */
+    @SystemApi
+    public static final String EXTRA_CALLING_PACKAGE = "android.app.extra.CALLING_PACKAGE";
+
+    /**
+     * Integer extra used with {@link #ACTION_ENTER_CAR_MODE_PRIORITIZED} and
+     * {@link #ACTION_EXIT_CAR_MODE_PRIORITIZED} to indicate the priority level at which car mode
+     * is being disabled.
+     * @hide
+     */
+    @SystemApi
+    public static final String EXTRA_PRIORITY = "android.app.extra.PRIORITY";
+
     /** @hide */
     @IntDef(prefix = { "MODE_" }, value = {
             MODE_NIGHT_AUTO,
@@ -126,10 +189,21 @@
 
     private IUiModeManager mService;
 
+    /**
+     * Context required for getting the opPackageName of API caller; maybe be {@code null} if the
+     * old constructor marked with UnSupportedAppUsage is used.
+     */
+    private @Nullable Context mContext;
+
     @UnsupportedAppUsage
     /*package*/ UiModeManager() throws ServiceNotFoundException {
+        this(null /* context */);
+    }
+
+    /*package*/ UiModeManager(Context context) throws ServiceNotFoundException {
         mService = IUiModeManager.Stub.asInterface(
                 ServiceManager.getServiceOrThrow(Context.UI_MODE_SERVICE));
+        mContext = context;
     }
 
     /**
@@ -152,6 +226,14 @@
      */
     public static final int ENABLE_CAR_MODE_ALLOW_SLEEP = 0x0002;
 
+    /** @hide */
+    @IntDef(prefix = { "ENABLE_CAR_MODE_" }, value = {
+            ENABLE_CAR_MODE_GO_CAR_HOME,
+            ENABLE_CAR_MODE_ALLOW_SLEEP
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface EnableCarMode {}
+
     /**
      * Force device into car mode, like it had been placed in the car dock.
      * This will cause the device to switch to the car home UI as part of
@@ -159,9 +241,54 @@
      * @param flags Must be 0.
      */
     public void enableCarMode(int flags) {
+        enableCarMode(DEFAULT_PRIORITY, flags);
+    }
+
+    /**
+     * Force device into car mode, like it had been placed in the car dock.  This will cause the
+     * device to switch to the car home UI as part of the mode switch.
+     * <p>
+     * An app may request to enter car mode when the system is already in car mode.  The app may
+     * specify a "priority" when entering car mode.  The device will remain in car mode
+     * (i.e. {@link #getCurrentModeType()} is {@link Configuration#UI_MODE_TYPE_CAR}) as long as
+     * there is a priority level at which car mode have been enabled.  For example assume app A
+     * enters car mode at priority level 100, and then app B enters car mode at the default priority
+     * (0).  If app A exits car mode, the device will remain in car mode until app B exits car mode.
+     * <p>
+     * Specifying a priority level when entering car mode is important in cases where multiple apps
+     * on a device implement a car-mode {@link android.telecom.InCallService} (see
+     * {@link android.telecom.TelecomManager#METADATA_IN_CALL_SERVICE_CAR_MODE_UI}).  The
+     * {@link android.telecom.InCallService} associated with the highest priority app which entered
+     * car mode will be bound to by Telecom and provided with information about ongoing calls on
+     * the device.
+     * <p>
+     * System apps holding the required permission can enable car mode when the app determines the
+     * correct conditions exist for that app to be in car mode.  The device maker should ensure that
+     * where multiple apps exist on the device which can potentially enter car mode, appropriate
+     * priorities are used to ensure that calls delivered by the
+     * {@link android.telecom.InCallService} API are delivered to the highest priority app.
+     * If app A and app B can both potentially enable car mode, and it is desired that app B is the
+     * one which should receive call information, the priority for app B should be higher than the
+     * one for app A.
+     * <p>
+     * When an app uses a priority to enable car mode, they can disable car mode at the specified
+     * priority level using {@link #disableCarMode(int)}.  An app may only enable car mode at a
+     * single priority.
+     * <p>
+     * Public apps are assumed to enter/exit car mode at {@link #DEFAULT_PRIORITY}.
+     *
+     * @param priority The declared priority for the caller.
+     * @param flags Car mode flags.
+     * @hide
+     */
+    @SystemApi
+    @TestApi
+    @RequiresPermission(android.Manifest.permission.ENTER_CAR_MODE_PRIORITIZED)
+    public void enableCarMode(@IntRange(from = 0) int priority, @EnableCarMode int flags) {
         if (mService != null) {
             try {
-                mService.enableCarMode(flags);
+                mService.enableCarMode(flags, priority,
+                        mContext == null ? null : mContext.getOpPackageName());
             } catch (RemoteException e) {
                 throw e.rethrowFromSystemServer();
             }
@@ -176,15 +303,44 @@
      * being in car mode).
      */
     public static final int DISABLE_CAR_MODE_GO_HOME = 0x0001;
+
+    /**
+     * Flag for use with {@link #disableCarMode(int)}: Disables car mode at ALL priority levels.
+     * Primarily intended for use from {@link com.android.internal.app.DisableCarModeActivity} to
+     * provide the user with a means to exit car mode at all priority levels.
+     * @hide
+     */
+    public static final int DISABLE_CAR_MODE_ALL_PRIORITIES = 0x0002;
+
+    /** @hide */
+    @IntDef(prefix = { "DISABLE_CAR_MODE_" }, value = {
+            DISABLE_CAR_MODE_GO_HOME
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface DisableCarMode {}
+
+    /**
+     * The default priority used for entering car mode.
+     * <p>
+     * Callers of the {@link UiModeManager#enableCarMode(int)} priority will be assigned the
+     * default priority.
+     * <p>
+     * System apps can specify a priority other than the default priority when using
+     * {@link UiModeManager#enableCarMode(int, int)} to enable car mode.
+     * @hide
+     */
+    @SystemApi
+    public static final int DEFAULT_PRIORITY = 0;
     
     /**
      * Turn off special mode if currently in car mode.
-     * @param flags May be 0 or {@link #DISABLE_CAR_MODE_GO_HOME}.
+     * @param flags One of the disable car mode flags.
      */
-    public void disableCarMode(int flags) {
+    public void disableCarMode(@DisableCarMode int flags) {
         if (mService != null) {
             try {
-                mService.disableCarMode(flags);
+                mService.disableCarModeByCallingPackage(flags,
+                        mContext == null ? null : mContext.getOpPackageName());
             } catch (RemoteException e) {
                 throw e.rethrowFromSystemServer();
             }
diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java
index b04f781..e44d6ae 100644
--- a/core/java/android/content/ContextWrapper.java
+++ b/core/java/android/content/ContextWrapper.java
@@ -672,7 +672,6 @@
     /** @hide */
     @Override
     @Nullable
-    @SystemApi
     public Intent registerReceiverForAllUsers(@Nullable BroadcastReceiver receiver,
             @NonNull IntentFilter filter, @Nullable String broadcastPermission,
             @Nullable Handler scheduler) {
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 27402a4..187ab46 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -2584,8 +2584,7 @@
      * @return True when the TextView isFocused and has a valid zero-length selection (cursor).
      */
     private boolean shouldBlink() {
-        if (!isCursorVisible() || !mTextView.isFocused() ||
-                !mTextView.isVisibleToUser()) return false;
+        if (!isCursorVisible() || !mTextView.isFocused()) return false;
 
         final int start = mTextView.getSelectionStart();
         if (start < 0) return false;
diff --git a/core/java/com/android/internal/app/DisableCarModeActivity.java b/core/java/com/android/internal/app/DisableCarModeActivity.java
index 7943c61..d44312b 100644
--- a/core/java/com/android/internal/app/DisableCarModeActivity.java
+++ b/core/java/com/android/internal/app/DisableCarModeActivity.java
@@ -33,7 +33,9 @@
         try {
             IUiModeManager uiModeManager = IUiModeManager.Stub.asInterface(
                     ServiceManager.getService("uimode"));
-            uiModeManager.disableCarMode(UiModeManager.DISABLE_CAR_MODE_GO_HOME);
+            uiModeManager.disableCarModeByCallingPackage(UiModeManager.DISABLE_CAR_MODE_GO_HOME
+                            | UiModeManager.DISABLE_CAR_MODE_ALL_PRIORITIES,
+                    getOpPackageName());
         } catch (RemoteException e) {
             Log.e(TAG, "Failed to disable car mode", e);
         }
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index e1ca1f6..62001aa 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -108,6 +108,8 @@
 
     <protected-broadcast android:name="android.app.action.ENTER_CAR_MODE" />
     <protected-broadcast android:name="android.app.action.EXIT_CAR_MODE" />
+    <protected-broadcast android:name="android.app.action.ENTER_CAR_MODE_PRIVILEGED" />
+    <protected-broadcast android:name="android.app.action.EXIT_CAR_MODE_PRIVILEGED" />
     <protected-broadcast android:name="android.app.action.ENTER_DESK_MODE" />
     <protected-broadcast android:name="android.app.action.EXIT_DESK_MODE" />
     <protected-broadcast android:name="android.app.action.NEXT_ALARM_CLOCK_CHANGED" />
@@ -4346,6 +4348,21 @@
          it will be ignored.
         @hide -->
     <permission android:name="android.permission.MODIFY_DAY_NIGHT_MODE"
+      android:protectionLevel="signature|privileged" />
+
+    <!-- @SystemApi Allows entering or exiting car mode using a specified priority.
+        This permission is required to use UiModeManager while specifying a priority for the calling
+        app.  A device manufacturer uses this permission to prioritize the apps which can
+        potentially request to enter car-mode on a device to help establish the correct behavior
+        where multiple such apps are active at the same time.
+        @hide -->
+    <permission android:name="android.permission.ENTER_CAR_MODE_PRIORITIZED"
+                android:protectionLevel="signature|privileged" />
+
+    <!-- @SystemApi Required to receive ACTION_ENTER_CAR_MODE_PRIVILEGED or
+        ACTION_EXIT_CAR_MODE_PRIVILEGED.
+        @hide -->
+    <permission android:name="android.permission.HANDLE_CAR_MODE_CHANGES"
                 android:protectionLevel="signature|privileged" />
 
     <!-- The system process is explicitly the only one allowed to launch the
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index d2ce4e0..fe539e4 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -244,6 +244,7 @@
         <permission name="android.permission.BIND_CONNECTION_SERVICE"/>
         <permission name="android.permission.BIND_INCALL_SERVICE"/>
         <permission name="android.permission.CALL_PRIVILEGED"/>
+        <permission name="android.permission.HANDLE_CAR_MODE_CHANGES"/>
         <permission name="android.permission.INTERACT_ACROSS_USERS"/>
         <permission name="android.permission.MANAGE_USERS"/>
         <permission name="android.permission.MANAGE_ROLE_HOLDERS"/>
@@ -338,6 +339,8 @@
         <permission name="android.permission.SYSTEM_CAMERA" />
         <!-- Permission required to test ExplicitHealthCheckServiceImpl. -->
         <permission name="android.permission.BIND_EXPLICIT_HEALTH_CHECK_SERVICE"/>
+        <!-- Permission required for UiModeManager cts test. -->
+        <permission name="android.permission.ENTER_CAR_MODE_PRIORITIZED"/>
     </privapp-permissions>
 
     <privapp-permissions package="com.android.statementservice">
diff --git a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java
index d0f7c78..240e7d1 100644
--- a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java
+++ b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java
@@ -82,11 +82,6 @@
     public static final String CATEGORY_SPECIAL =
             "com.android.mediarouteprovider.CATEGORY_SPECIAL";
 
-    // system routes
-    private static final String DEFAULT_ROUTE_ID = "DEFAULT_ROUTE";
-    private static final String CATEGORY_LIVE_AUDIO = "android.media.intent.category.LIVE_AUDIO";
-    private static final String CATEGORY_LIVE_VIDEO = "android.media.intent.category.LIVE_VIDEO";
-
     private static final int TIMEOUT_MS = 5000;
 
     private Context mContext;
@@ -95,17 +90,13 @@
     private Executor mExecutor;
     private String mPackageName;
 
-    private static final List<String> CATEGORIES_ALL = new ArrayList();
-    private static final List<String> CATEGORIES_SPECIAL = new ArrayList();
-    private static final List<String> CATEGORIES_LIVE_AUDIO = new ArrayList<>();
+    private static final List<String> CONTROL_CATEGORIES_ALL = new ArrayList();
+    private static final List<String> CONTROL_CATEGORIES_SPECIAL = new ArrayList();
     static {
-        CATEGORIES_ALL.add(CATEGORY_SAMPLE);
-        CATEGORIES_ALL.add(CATEGORY_SPECIAL);
-        CATEGORIES_ALL.add(CATEGORY_LIVE_AUDIO);
+        CONTROL_CATEGORIES_ALL.add(CATEGORY_SAMPLE);
+        CONTROL_CATEGORIES_ALL.add(CATEGORY_SPECIAL);
 
-        CATEGORIES_SPECIAL.add(CATEGORY_SPECIAL);
-
-        CATEGORIES_LIVE_AUDIO.add(CATEGORY_LIVE_AUDIO);
+        CONTROL_CATEGORIES_SPECIAL.add(CATEGORY_SPECIAL);
     }
 
     @Before
@@ -162,7 +153,7 @@
         mRouter2.registerCallback(mExecutor, routerCallback);
 
         Map<String, MediaRoute2Info> routes =
-                waitAndGetRoutesWithManager(CATEGORIES_ALL);
+                waitAndGetRoutesWithManager(CONTROL_CATEGORIES_ALL);
 
         CountDownLatch latch = new CountDownLatch(1);
         MediaRouter2Manager.Callback callback = new MediaRouter2Manager.Callback() {
@@ -196,7 +187,7 @@
         mManager.registerCallback(mExecutor, mockCallback);
 
         Map<String, MediaRoute2Info> routes =
-                waitAndGetRoutesWithManager(CATEGORIES_SPECIAL);
+                waitAndGetRoutesWithManager(CONTROL_CATEGORIES_SPECIAL);
 
         Assert.assertEquals(1, routes.size());
         Assert.assertNotNull(routes.get(ROUTE_ID_SPECIAL_CATEGORY));
@@ -212,7 +203,7 @@
         MediaRouter2.Callback mockCallback = mock(MediaRouter2.Callback.class);
         mRouter2.registerCallback(mExecutor, mockCallback);
 
-        Map<String, MediaRoute2Info> routes = waitAndGetRoutes(CATEGORIES_SPECIAL);
+        Map<String, MediaRoute2Info> routes = waitAndGetRoutes(CONTROL_CATEGORIES_SPECIAL);
 
         Assert.assertEquals(1, routes.size());
         Assert.assertNotNull(routes.get(ROUTE_ID_SPECIAL_CATEGORY));
@@ -228,7 +219,7 @@
         mManager.registerCallback(mExecutor, managerCallback);
         mRouter2.registerCallback(mExecutor, routerCallback);
 
-        Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(CATEGORIES_ALL);
+        Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(CONTROL_CATEGORIES_ALL);
 
         MediaRoute2Info routeToSelect = routes.get(ROUTE_ID1);
         assertNotNull(routeToSelect);
@@ -251,7 +242,7 @@
 
         mRouter2.registerCallback(mExecutor, routerCallback);
 
-        Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(CATEGORIES_ALL);
+        Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(CONTROL_CATEGORIES_ALL);
 
         awaitOnRouteChangedManager(
                 () -> mManager.selectRoute(mPackageName, routes.get(ROUTE_ID1)),
@@ -273,7 +264,7 @@
 
     @Test
     public void testControlVolumeWithRouter() throws Exception {
-        Map<String, MediaRoute2Info> routes = waitAndGetRoutes(CATEGORIES_ALL);
+        Map<String, MediaRoute2Info> routes = waitAndGetRoutes(CONTROL_CATEGORIES_ALL);
 
         MediaRoute2Info volRoute = routes.get(ROUTE_ID_VARIABLE_VOLUME);
         int originalVolume = volRoute.getVolume();
@@ -295,7 +286,7 @@
         MediaRouter2.Callback mockCallback = mock(MediaRouter2.Callback.class);
 
         mRouter2.registerCallback(mExecutor, mockCallback);
-        Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(CATEGORIES_ALL);
+        Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(CONTROL_CATEGORIES_ALL);
 
         MediaRoute2Info volRoute = routes.get(ROUTE_ID_VARIABLE_VOLUME);
         int originalVolume = volRoute.getVolume();
@@ -318,7 +309,7 @@
     public void testVolumeHandling() throws Exception {
         MediaRouter2.Callback mockCallback = mock(MediaRouter2.Callback.class);
         mRouter2.registerCallback(mExecutor, mockCallback);
-        Map<String, MediaRoute2Info> routes = waitAndGetRoutes(CATEGORIES_ALL);
+        Map<String, MediaRoute2Info> routes = waitAndGetRoutes(CONTROL_CATEGORIES_ALL);
 
         MediaRoute2Info fixedVolumeRoute = routes.get(ROUTE_ID_FIXED_VOLUME);
         MediaRoute2Info variableVolumeRoute = routes.get(ROUTE_ID_VARIABLE_VOLUME);
@@ -330,13 +321,6 @@
         mRouter2.unregisterCallback(mockCallback);
     }
 
-    @Test
-    public void testDefaultRoute() throws Exception {
-        Map<String, MediaRoute2Info> routes = waitAndGetRoutes(CATEGORIES_LIVE_AUDIO);
-
-        assertNotNull(routes.get(DEFAULT_ROUTE_ID));
-    }
-
     Map<String, MediaRoute2Info> waitAndGetRoutes(List<String> controlCategories) throws Exception {
         CountDownLatch latch = new CountDownLatch(1);
         MediaRouter2.Callback callback = new MediaRouter2.Callback() {
@@ -433,7 +417,6 @@
     static Map<String, MediaRoute2Info> createRouteMap(List<MediaRoute2Info> routes) {
         Map<String, MediaRoute2Info> routeMap = new HashMap<>();
         for (MediaRoute2Info route : routes) {
-            // intentionally not route.getUniqueId() for convenience.
             routeMap.put(route.getId(), route);
         }
         return routeMap;
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
index 386626d..0c4db49 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
@@ -2563,8 +2563,12 @@
             // value defined in system property
             StringBuilder val = new StringBuilder();
             List<Integer> defaultNetworks = TelephonyProperties.default_network();
-            for (int phoneId = 0;
-                    phoneId < TelephonyManager.getDefault().getPhoneCount(); phoneId++) {
+            int phoneCount = 1;
+            TelephonyManager telephonyManager = mContext.getSystemService(TelephonyManager.class);
+            if (telephonyManager != null) {
+                phoneCount = telephonyManager.getSupportedModemCount();
+            }
+            for (int phoneId = 0; phoneId < phoneCount; phoneId++) {
                 int mode = defaultNetworks.size() <= phoneId
                         || defaultNetworks.get(phoneId) == null
                         ? RILConstants.PREFERRED_NETWORK_MODE : defaultNetworks.get(phoneId);
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index c59f342..1a658f4 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -215,6 +215,9 @@
     <!-- Permission required for CTS test - CrossProfileAppsHostSideTest -->
     <uses-permission android:name="android.permission.INTERACT_ACROSS_PROFILES"/>
 
+    <!-- Permission requried for CTS test - UiModeManagerTest -->
+    <uses-permission android:name="android.permission.ENTER_CAR_MODE_PRIORITIZED"/>
+
     <application android:label="@string/app_label"
                 android:theme="@android:style/Theme.DeviceDefault.DayNight"
                 android:defaultToDeviceProtectedStorage="true"
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index ab433d2..e9c20db 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -369,6 +369,10 @@
         <receiver android:name=".screenshot.GlobalScreenshot$DeleteScreenshotReceiver"
             android:exported="false" />
 
+        <!-- Callback for invoking a smart action from the screenshot notification. -->
+        <receiver android:name=".screenshot.GlobalScreenshot$SmartActionsReceiver"
+                  android:exported="false"/>
+
         <!-- started from UsbDeviceSettingsManager -->
         <activity android:name=".usb.UsbConfirmActivity"
             android:exported="true"
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
index 0d400fe..617dbdf 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
@@ -45,6 +45,8 @@
 import com.android.systemui.statusbar.phone.StatusBar;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 
+import java.util.concurrent.Executor;
+
 import dagger.Module;
 import dagger.Provides;
 
@@ -124,7 +126,9 @@
      * This method is overridden in vendor specific implementation of Sys UI.
      */
     public ScreenshotNotificationSmartActionsProvider
-            createScreenshotNotificationSmartActionsProvider() {
+            createScreenshotNotificationSmartActionsProvider(Context context,
+            Executor executor,
+            Handler uiHandler) {
         return new ScreenshotNotificationSmartActionsProvider();
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
index e790c1d..66cd919 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
@@ -75,6 +75,7 @@
 import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
 import com.android.systemui.R;
 import com.android.systemui.SystemUI;
+import com.android.systemui.SystemUIFactory;
 import com.android.systemui.dagger.qualifiers.MainResources;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.statusbar.phone.StatusBar;
@@ -126,8 +127,17 @@
         }
     }
 
-    static final String SCREENSHOT_URI_ID = "android:screenshot_uri_id";
+    // These strings are used for communicating the action invoked to
+    // ScreenshotNotificationSmartActionsProvider.
+    static final String EXTRA_ACTION_TYPE = "android:screenshot_action_type";
+    static final String EXTRA_ID = "android:screenshot_id";
+    static final String ACTION_TYPE_DELETE = "Delete";
+    static final String ACTION_TYPE_SHARE = "Share";
+    static final String ACTION_TYPE_EDIT = "Edit";
+    static final String EXTRA_SMART_ACTIONS_ENABLED = "android:smart_actions_enabled";
     static final String EXTRA_ACTION_INTENT = "android:screenshot_action_intent";
+
+    static final String SCREENSHOT_URI_ID = "android:screenshot_uri_id";
     static final String EXTRA_CANCEL_NOTIFICATION = "android:screenshot_cancel_notification";
     static final String EXTRA_DISALLOW_ENTER_PIP = "android:screenshot_disallow_enter_pip";
 
@@ -689,9 +699,9 @@
     }
 
     @VisibleForTesting
-    static CompletableFuture<List<Notification.Action>> getSmartActionsFuture(Context context,
+    static CompletableFuture<List<Notification.Action>> getSmartActionsFuture(String screenshotId,
             Bitmap image, ScreenshotNotificationSmartActionsProvider smartActionsProvider,
-            Handler handler, boolean smartActionsEnabled, boolean isManagedProfile) {
+            boolean smartActionsEnabled, boolean isManagedProfile) {
         if (!smartActionsEnabled) {
             Slog.i(TAG, "Screenshot Intelligence not enabled, returning empty list.");
             return CompletableFuture.completedFuture(Collections.emptyList());
@@ -705,6 +715,7 @@
 
         Slog.d(TAG, "Screenshot from a managed profile: " + isManagedProfile);
         CompletableFuture<List<Notification.Action>> smartActionsFuture;
+        long startTimeMs = SystemClock.uptimeMillis();
         try {
             ActivityManager.RunningTaskInfo runningTask =
                     ActivityManagerWrapper.getInstance().getRunningTask();
@@ -712,34 +723,74 @@
                     (runningTask != null && runningTask.topActivity != null)
                             ? runningTask.topActivity
                             : new ComponentName("", "");
-            smartActionsFuture = smartActionsProvider.getActions(image, context,
-                    THREAD_POOL_EXECUTOR,
-                    handler,
+            smartActionsFuture = smartActionsProvider.getActions(screenshotId, image,
                     componentName,
                     isManagedProfile);
         } catch (Throwable e) {
+            long waitTimeMs = SystemClock.uptimeMillis() - startTimeMs;
             smartActionsFuture = CompletableFuture.completedFuture(Collections.emptyList());
             Slog.e(TAG, "Failed to get future for screenshot notification smart actions.", e);
+            notifyScreenshotOp(screenshotId, smartActionsProvider,
+                    ScreenshotNotificationSmartActionsProvider.ScreenshotOp.REQUEST_SMART_ACTIONS,
+                    ScreenshotNotificationSmartActionsProvider.ScreenshotOpStatus.ERROR,
+                    waitTimeMs);
         }
         return smartActionsFuture;
     }
 
     @VisibleForTesting
-    static List<Notification.Action> getSmartActions(
-            CompletableFuture<List<Notification.Action>> smartActionsFuture, int timeoutMs) {
+    static List<Notification.Action> getSmartActions(String screenshotId,
+            CompletableFuture<List<Notification.Action>> smartActionsFuture, int timeoutMs,
+            ScreenshotNotificationSmartActionsProvider smartActionsProvider) {
+        long startTimeMs = SystemClock.uptimeMillis();
         try {
-            long startTimeMs = SystemClock.uptimeMillis();
             List<Notification.Action> actions = smartActionsFuture.get(timeoutMs,
                     TimeUnit.MILLISECONDS);
+            long waitTimeMs = SystemClock.uptimeMillis() - startTimeMs;
             Slog.d(TAG, String.format("Wait time for smart actions: %d ms",
-                    SystemClock.uptimeMillis() - startTimeMs));
+                    waitTimeMs));
+            notifyScreenshotOp(screenshotId, smartActionsProvider,
+                    ScreenshotNotificationSmartActionsProvider.ScreenshotOp.WAIT_FOR_SMART_ACTIONS,
+                    ScreenshotNotificationSmartActionsProvider.ScreenshotOpStatus.SUCCESS,
+                    waitTimeMs);
             return actions;
         } catch (Throwable e) {
-            Slog.e(TAG, "Failed to obtain screenshot notification smart actions.", e);
+            long waitTimeMs = SystemClock.uptimeMillis() - startTimeMs;
+            Slog.d(TAG, "Failed to obtain screenshot notification smart actions.", e);
+            ScreenshotNotificationSmartActionsProvider.ScreenshotOpStatus status =
+                    (e instanceof TimeoutException)
+                            ? ScreenshotNotificationSmartActionsProvider.ScreenshotOpStatus.TIMEOUT
+                            : ScreenshotNotificationSmartActionsProvider.ScreenshotOpStatus.ERROR;
+            notifyScreenshotOp(screenshotId, smartActionsProvider,
+                    ScreenshotNotificationSmartActionsProvider.ScreenshotOp.WAIT_FOR_SMART_ACTIONS,
+                    status, waitTimeMs);
             return Collections.emptyList();
         }
     }
 
+    static void notifyScreenshotOp(String screenshotId,
+            ScreenshotNotificationSmartActionsProvider smartActionsProvider,
+            ScreenshotNotificationSmartActionsProvider.ScreenshotOp op,
+            ScreenshotNotificationSmartActionsProvider.ScreenshotOpStatus status, long durationMs) {
+        try {
+            smartActionsProvider.notifyOp(screenshotId, op, status, durationMs);
+        } catch (Throwable e) {
+            Slog.e(TAG, "Error in notifyScreenshotOp: ", e);
+        }
+    }
+
+    static void notifyScreenshotAction(Context context, String screenshotId, String action,
+            boolean isSmartAction) {
+        try {
+            ScreenshotNotificationSmartActionsProvider provider =
+                    SystemUIFactory.getInstance().createScreenshotNotificationSmartActionsProvider(
+                            context, THREAD_POOL_EXECUTOR, new Handler());
+            provider.notifyAction(screenshotId, action, isSmartAction);
+        } catch (Throwable e) {
+            Slog.e(TAG, "Error in notifyScreenshotAction: ", e);
+        }
+    }
+
     /**
      * Receiver to proxy the share or edit intent, used to clean up the notification and send
      * appropriate signals to the system (ie. to dismiss the keyguard if necessary).
@@ -783,6 +834,13 @@
             } else {
                 startActivityRunnable.run();
             }
+
+            if (intent.getBooleanExtra(EXTRA_SMART_ACTIONS_ENABLED, false)) {
+                String actionType = Intent.ACTION_EDIT.equals(intent.getAction()) ? ACTION_TYPE_EDIT
+                        : ACTION_TYPE_SHARE;
+                notifyScreenshotAction(context, intent.getStringExtra(EXTRA_ID),
+                        actionType, false);
+            }
         }
     }
 
@@ -813,6 +871,29 @@
             // And delete the image from the media store
             final Uri uri = Uri.parse(intent.getStringExtra(SCREENSHOT_URI_ID));
             new DeleteImageInBackgroundTask(context).execute(uri);
+            if (intent.getBooleanExtra(EXTRA_SMART_ACTIONS_ENABLED, false)) {
+                notifyScreenshotAction(context, intent.getStringExtra(EXTRA_ID),
+                        ACTION_TYPE_DELETE,
+                        false);
+            }
+        }
+    }
+
+    /**
+     * Executes the smart action tapped by the user in the notification.
+     */
+    public static class SmartActionsReceiver extends BroadcastReceiver {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            PendingIntent actionIntent = intent.getParcelableExtra(EXTRA_ACTION_INTENT);
+            ActivityOptions opts = ActivityOptions.makeBasic();
+            context.startActivityAsUser(actionIntent.getIntent(), opts.toBundle(),
+                    UserHandle.CURRENT);
+
+            Slog.d(TAG, "Screenshot notification smart action is invoked.");
+            notifyScreenshotAction(context, intent.getStringExtra(EXTRA_ID),
+                    intent.getStringExtra(EXTRA_ACTION_TYPE),
+                    true);
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java b/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java
index e9dbe02..5e5cf74 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java
@@ -38,6 +38,7 @@
 import android.net.Uri;
 import android.os.AsyncTask;
 import android.os.Build;
+import android.os.Bundle;
 import android.os.Environment;
 import android.os.Handler;
 import android.os.ParcelFileDescriptor;
@@ -50,6 +51,7 @@
 import android.text.TextUtils;
 import android.util.Slog;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
 import com.android.internal.messages.nano.SystemMessageProto;
 import com.android.systemui.R;
@@ -69,9 +71,12 @@
 import java.time.ZoneOffset;
 import java.time.ZonedDateTime;
 import java.time.format.DateTimeFormatter;
+import java.util.ArrayList;
 import java.util.Date;
 import java.util.List;
 import java.util.Objects;
+import java.util.Random;
+import java.util.UUID;
 import java.util.concurrent.CompletableFuture;
 
 /**
@@ -81,6 +86,7 @@
     private static final String TAG = "SaveImageInBackgroundTask";
 
     private static final String SCREENSHOT_FILE_NAME_TEMPLATE = "Screenshot_%s.png";
+    private static final String SCREENSHOT_ID_TEMPLATE = "Screenshot_%s";
     private static final String SCREENSHOT_SHARE_SUBJECT_TEMPLATE = "Screenshot (%s)";
 
     private final GlobalScreenshot.SaveImageInBackgroundData mParams;
@@ -91,8 +97,10 @@
     private final Notification.BigPictureStyle mNotificationStyle;
     private final int mImageWidth;
     private final int mImageHeight;
-    private final Handler mHandler;
     private final ScreenshotNotificationSmartActionsProvider mSmartActionsProvider;
+    private final String mScreenshotId;
+    private final boolean mSmartActionsEnabled;
+    private final Random mRandom = new Random();
 
     SaveImageInBackgroundTask(Context context, GlobalScreenshot.SaveImageInBackgroundData data,
             NotificationManager nManager) {
@@ -103,11 +111,20 @@
         mImageTime = System.currentTimeMillis();
         String imageDate = new SimpleDateFormat("yyyyMMdd-HHmmss").format(new Date(mImageTime));
         mImageFileName = String.format(SCREENSHOT_FILE_NAME_TEMPLATE, imageDate);
+        mScreenshotId = String.format(SCREENSHOT_ID_TEMPLATE, UUID.randomUUID());
 
         // Initialize screenshot notification smart actions provider.
-        mHandler = new Handler();
-        mSmartActionsProvider =
-                SystemUIFactory.getInstance().createScreenshotNotificationSmartActionsProvider();
+        mSmartActionsEnabled = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_SYSTEMUI,
+                SystemUiDeviceConfigFlags.ENABLE_SCREENSHOT_NOTIFICATION_SMART_ACTIONS, false);
+        if (mSmartActionsEnabled) {
+            mSmartActionsProvider =
+                    SystemUIFactory.getInstance()
+                            .createScreenshotNotificationSmartActionsProvider(
+                                    context, THREAD_POOL_EXECUTOR, new Handler());
+        } else {
+            // If smart actions is not enabled use empty implementation.
+            mSmartActionsProvider = new ScreenshotNotificationSmartActionsProvider();
+        }
 
         // Create the large notification icon
         mImageWidth = data.image.getWidth();
@@ -201,6 +218,38 @@
         return info.isManagedProfile();
     }
 
+    private List<Notification.Action> buildSmartActions(
+            List<Notification.Action> actions, Context context) {
+        List<Notification.Action> broadcastActions = new ArrayList<>();
+        for (Notification.Action action : actions) {
+            // Proxy smart actions through {@link GlobalScreenshot.SmartActionsReceiver}
+            // for logging smart actions.
+            Bundle extras = action.getExtras();
+            String actionType = extras.getString(
+                    ScreenshotNotificationSmartActionsProvider.ACTION_TYPE,
+                    ScreenshotNotificationSmartActionsProvider.DEFAULT_ACTION_TYPE);
+            Intent intent = new Intent(context,
+                    GlobalScreenshot.SmartActionsReceiver.class).putExtra(
+                    GlobalScreenshot.EXTRA_ACTION_INTENT, action.actionIntent);
+            addIntentExtras(mScreenshotId, intent, actionType, mSmartActionsEnabled);
+            PendingIntent broadcastIntent = PendingIntent.getBroadcast(context,
+                    mRandom.nextInt(),
+                    intent,
+                    PendingIntent.FLAG_CANCEL_CURRENT);
+            broadcastActions.add(new Notification.Action.Builder(action.getIcon(), action.title,
+                    broadcastIntent).setContextual(true).addExtras(extras).build());
+        }
+        return broadcastActions;
+    }
+
+    private static void addIntentExtras(String screenshotId, Intent intent, String actionType,
+            boolean smartActionsEnabled) {
+        intent
+                .putExtra(GlobalScreenshot.EXTRA_ACTION_TYPE, actionType)
+                .putExtra(GlobalScreenshot.EXTRA_ID, screenshotId)
+                .putExtra(GlobalScreenshot.EXTRA_SMART_ACTIONS_ENABLED, smartActionsEnabled);
+    }
+
     /**
      * Generates a new hardware bitmap with specified values, copying the content from the
      * passed in bitmap.
@@ -227,16 +276,13 @@
 
         Context context = mParams.context;
         Bitmap image = mParams.image;
-        boolean smartActionsEnabled = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_SYSTEMUI,
-                SystemUiDeviceConfigFlags.ENABLE_SCREENSHOT_NOTIFICATION_SMART_ACTIONS, true);
-        CompletableFuture<List<Notification.Action>>
-                smartActionsFuture = GlobalScreenshot.getSmartActionsFuture(
-                context, image, mSmartActionsProvider, mHandler, smartActionsEnabled,
-                isManagedProfile(context));
-
         Resources r = context.getResources();
 
         try {
+            CompletableFuture<List<Notification.Action>> smartActionsFuture =
+                    GlobalScreenshot.getSmartActionsFuture(mScreenshotId, image,
+                            mSmartActionsProvider, mSmartActionsEnabled, isManagedProfile(context));
+
             // Save the screenshot to the MediaStore
             final MediaStore.PendingParams params = new MediaStore.PendingParams(
                     MediaStore.Images.Media.EXTERNAL_CONTENT_URI, mImageFileName, "image/png");
@@ -289,104 +335,11 @@
                 IoUtils.closeQuietly(session);
             }
 
-            // Note: Both the share and edit actions are proxied through ActionProxyReceiver in
-            // order to do some common work like dismissing the keyguard and sending
-            // closeSystemWindows
-
-            // Create a share intent, this will always go through the chooser activity first
-            // which should not trigger auto-enter PiP
-            String subjectDate = DateFormat.getDateTimeInstance().format(new Date(mImageTime));
-            String subject = String.format(SCREENSHOT_SHARE_SUBJECT_TEMPLATE, subjectDate);
-            Intent sharingIntent = new Intent(Intent.ACTION_SEND);
-            sharingIntent.setType("image/png");
-            sharingIntent.putExtra(Intent.EXTRA_STREAM, uri);
-            // Include URI in ClipData also, so that grantPermission picks it up.
-            // We don't use setData here because some apps interpret this as "to:".
-            ClipData clipdata = new ClipData(new ClipDescription("content",
-                    new String[]{ClipDescription.MIMETYPE_TEXT_PLAIN}),
-                    new ClipData.Item(uri));
-            sharingIntent.setClipData(clipdata);
-            sharingIntent.putExtra(Intent.EXTRA_SUBJECT, subject);
-            sharingIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
-
-            // Make sure pending intents for the system user are still unique across users
-            // by setting the (otherwise unused) request code to the current user id.
-            int requestCode = context.getUserId();
-
-            PendingIntent chooserAction = PendingIntent.getBroadcast(context, requestCode,
-                    new Intent(context, GlobalScreenshot.TargetChosenReceiver.class),
-                    PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT);
-            Intent sharingChooserIntent = Intent.createChooser(sharingIntent, null,
-                    chooserAction.getIntentSender())
-                    .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK)
-                    .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
-
-            // Create a share action for the notification
-            PendingIntent shareAction = PendingIntent.getBroadcastAsUser(context, requestCode,
-                    new Intent(context, GlobalScreenshot.ActionProxyReceiver.class)
-                            .putExtra(GlobalScreenshot.EXTRA_ACTION_INTENT, sharingChooserIntent)
-                            .putExtra(GlobalScreenshot.EXTRA_DISALLOW_ENTER_PIP, true)
-                            .setAction(Intent.ACTION_SEND),
-                    PendingIntent.FLAG_CANCEL_CURRENT, UserHandle.SYSTEM);
-            Notification.Action.Builder shareActionBuilder = new Notification.Action.Builder(
-                    R.drawable.ic_screenshot_share,
-                    r.getString(com.android.internal.R.string.share), shareAction);
-            mNotificationBuilder.addAction(shareActionBuilder.build());
-
-            // Create an edit intent, if a specific package is provided as the editor, then
-            // launch that directly
-            String editorPackage = context.getString(R.string.config_screenshotEditor);
-            Intent editIntent = new Intent(Intent.ACTION_EDIT);
-            if (!TextUtils.isEmpty(editorPackage)) {
-                editIntent.setComponent(ComponentName.unflattenFromString(editorPackage));
-            }
-            editIntent.setType("image/png");
-            editIntent.setData(uri);
-            editIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
-            editIntent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
-
-            // Create a edit action
-            PendingIntent editAction = PendingIntent.getBroadcastAsUser(context, requestCode,
-                    new Intent(context, GlobalScreenshot.ActionProxyReceiver.class)
-                            .putExtra(GlobalScreenshot.EXTRA_ACTION_INTENT, editIntent)
-                            .putExtra(GlobalScreenshot.EXTRA_CANCEL_NOTIFICATION,
-                                    editIntent.getComponent() != null)
-                            .setAction(Intent.ACTION_EDIT),
-                    PendingIntent.FLAG_CANCEL_CURRENT, UserHandle.SYSTEM);
-            Notification.Action.Builder editActionBuilder = new Notification.Action.Builder(
-                    R.drawable.ic_screenshot_edit,
-                    r.getString(com.android.internal.R.string.screenshot_edit), editAction);
-            mNotificationBuilder.addAction(editActionBuilder.build());
-            if (editAction != null && mParams.onEditReady != null) {
-                mParams.onEditReady.apply(editAction);
-            }
-
-            // Create a delete action for the notification
-            PendingIntent deleteAction = PendingIntent.getBroadcast(context, requestCode,
-                    new Intent(context, GlobalScreenshot.DeleteScreenshotReceiver.class)
-                            .putExtra(GlobalScreenshot.SCREENSHOT_URI_ID, uri.toString()),
-                    PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT);
-            Notification.Action.Builder deleteActionBuilder = new Notification.Action.Builder(
-                    R.drawable.ic_screenshot_delete,
-                    r.getString(com.android.internal.R.string.delete), deleteAction);
-            mNotificationBuilder.addAction(deleteActionBuilder.build());
+            populateNotificationActions(context, r, uri, smartActionsFuture, mNotificationBuilder);
 
             mParams.imageUri = uri;
             mParams.image = null;
             mParams.errorMsgResId = 0;
-
-            if (smartActionsEnabled) {
-                int timeoutMs = DeviceConfig.getInt(DeviceConfig.NAMESPACE_SYSTEMUI,
-                        SystemUiDeviceConfigFlags
-                                .SCREENSHOT_NOTIFICATION_SMART_ACTIONS_TIMEOUT_MS,
-                        1000);
-                List<Notification.Action> smartActions = GlobalScreenshot.getSmartActions(
-                        smartActionsFuture,
-                        timeoutMs);
-                for (Notification.Action action : smartActions) {
-                    mNotificationBuilder.addAction(action);
-                }
-            }
         } catch (Exception e) {
             // IOException/UnsupportedOperationException may be thrown if external storage is
             // not mounted
@@ -403,6 +356,115 @@
         return null;
     }
 
+    @VisibleForTesting
+    void populateNotificationActions(Context context, Resources r, Uri uri,
+            CompletableFuture<List<Notification.Action>> smartActionsFuture,
+            Notification.Builder notificationBuilder) {
+        // Note: Both the share and edit actions are proxied through ActionProxyReceiver in
+        // order to do some common work like dismissing the keyguard and sending
+        // closeSystemWindows
+
+        // Create a share intent, this will always go through the chooser activity first
+        // which should not trigger auto-enter PiP
+        String subjectDate = DateFormat.getDateTimeInstance().format(new Date(mImageTime));
+        String subject = String.format(SCREENSHOT_SHARE_SUBJECT_TEMPLATE, subjectDate);
+        Intent sharingIntent = new Intent(Intent.ACTION_SEND);
+        sharingIntent.setType("image/png");
+        sharingIntent.putExtra(Intent.EXTRA_STREAM, uri);
+        // Include URI in ClipData also, so that grantPermission picks it up.
+        // We don't use setData here because some apps interpret this as "to:".
+        ClipData clipdata = new ClipData(new ClipDescription("content",
+                new String[]{ClipDescription.MIMETYPE_TEXT_PLAIN}),
+                new ClipData.Item(uri));
+        sharingIntent.setClipData(clipdata);
+        sharingIntent.putExtra(Intent.EXTRA_SUBJECT, subject);
+        sharingIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+
+        // Make sure pending intents for the system user are still unique across users
+        // by setting the (otherwise unused) request code to the current user id.
+        int requestCode = context.getUserId();
+
+        PendingIntent chooserAction = PendingIntent.getBroadcast(context, requestCode,
+                new Intent(context, GlobalScreenshot.TargetChosenReceiver.class),
+                PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT);
+        Intent sharingChooserIntent = Intent.createChooser(sharingIntent, null,
+                chooserAction.getIntentSender())
+                .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK)
+                .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+
+        // Create a share action for the notification
+        PendingIntent shareAction = PendingIntent.getBroadcastAsUser(context, requestCode,
+                new Intent(context, GlobalScreenshot.ActionProxyReceiver.class)
+                        .putExtra(GlobalScreenshot.EXTRA_ACTION_INTENT, sharingChooserIntent)
+                        .putExtra(GlobalScreenshot.EXTRA_DISALLOW_ENTER_PIP, true)
+                        .putExtra(GlobalScreenshot.EXTRA_ID, mScreenshotId)
+                        .putExtra(GlobalScreenshot.EXTRA_SMART_ACTIONS_ENABLED,
+                                mSmartActionsEnabled)
+                        .setAction(Intent.ACTION_SEND),
+                PendingIntent.FLAG_CANCEL_CURRENT, UserHandle.SYSTEM);
+        Notification.Action.Builder shareActionBuilder = new Notification.Action.Builder(
+                R.drawable.ic_screenshot_share,
+                r.getString(com.android.internal.R.string.share), shareAction);
+        notificationBuilder.addAction(shareActionBuilder.build());
+
+        // Create an edit intent, if a specific package is provided as the editor, then
+        // launch that directly
+        String editorPackage = context.getString(R.string.config_screenshotEditor);
+        Intent editIntent = new Intent(Intent.ACTION_EDIT);
+        if (!TextUtils.isEmpty(editorPackage)) {
+            editIntent.setComponent(ComponentName.unflattenFromString(editorPackage));
+        }
+        editIntent.setType("image/png");
+        editIntent.setData(uri);
+        editIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+        editIntent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+
+        // Create a edit action
+        PendingIntent editAction = PendingIntent.getBroadcastAsUser(context, requestCode,
+                new Intent(context, GlobalScreenshot.ActionProxyReceiver.class)
+                        .putExtra(GlobalScreenshot.EXTRA_ACTION_INTENT, editIntent)
+                        .putExtra(GlobalScreenshot.EXTRA_CANCEL_NOTIFICATION,
+                                editIntent.getComponent() != null)
+                        .putExtra(GlobalScreenshot.EXTRA_ID, mScreenshotId)
+                        .putExtra(GlobalScreenshot.EXTRA_SMART_ACTIONS_ENABLED,
+                                mSmartActionsEnabled)
+                        .setAction(Intent.ACTION_EDIT),
+                PendingIntent.FLAG_CANCEL_CURRENT, UserHandle.SYSTEM);
+        Notification.Action.Builder editActionBuilder = new Notification.Action.Builder(
+                R.drawable.ic_screenshot_edit,
+                r.getString(com.android.internal.R.string.screenshot_edit), editAction);
+        notificationBuilder.addAction(editActionBuilder.build());
+        if (editAction != null && mParams.onEditReady != null) {
+            mParams.onEditReady.apply(editAction);
+        }
+
+        // Create a delete action for the notification
+        PendingIntent deleteAction = PendingIntent.getBroadcast(context, requestCode,
+                new Intent(context, GlobalScreenshot.DeleteScreenshotReceiver.class)
+                        .putExtra(GlobalScreenshot.SCREENSHOT_URI_ID, uri.toString())
+                        .putExtra(GlobalScreenshot.EXTRA_ID, mScreenshotId)
+                        .putExtra(GlobalScreenshot.EXTRA_SMART_ACTIONS_ENABLED,
+                                mSmartActionsEnabled),
+                PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT);
+        Notification.Action.Builder deleteActionBuilder = new Notification.Action.Builder(
+                R.drawable.ic_screenshot_delete,
+                r.getString(com.android.internal.R.string.delete), deleteAction);
+        notificationBuilder.addAction(deleteActionBuilder.build());
+
+        if (mSmartActionsEnabled) {
+            int timeoutMs = DeviceConfig.getInt(DeviceConfig.NAMESPACE_SYSTEMUI,
+                    SystemUiDeviceConfigFlags
+                            .SCREENSHOT_NOTIFICATION_SMART_ACTIONS_TIMEOUT_MS,
+                    1000);
+            List<Notification.Action> smartActions = buildSmartActions(
+                    GlobalScreenshot.getSmartActions(mScreenshotId, smartActionsFuture,
+                            timeoutMs, mSmartActionsProvider), context);
+            for (Notification.Action action : smartActions) {
+                notificationBuilder.addAction(action);
+            }
+        }
+    }
+
     @Override
     protected void onPostExecute(Void params) {
         if (mParams.errorMsgResId != 0) {
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsProvider.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsProvider.java
index fa23bf7..b6f5447 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsProvider.java
@@ -18,41 +18,84 @@
 
 import android.app.Notification;
 import android.content.ComponentName;
-import android.content.Context;
 import android.graphics.Bitmap;
-import android.os.Handler;
 import android.util.Log;
 
 import java.util.Collections;
 import java.util.List;
 import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.Executor;
 
 /**
  * This class can be overridden by a vendor-specific sys UI implementation,
  * in order to provide smart actions in the screenshot notification.
  */
 public class ScreenshotNotificationSmartActionsProvider {
+    /* Key provided in the notification action to get the type of smart action. */
+    public static final String ACTION_TYPE = "action_type";
+    public static final String DEFAULT_ACTION_TYPE = "Smart Action";
+
+    /* Define phases of screenshot execution. */
+    protected enum ScreenshotOp {
+        OP_UNKNOWN,
+        RETRIEVE_SMART_ACTIONS,
+        REQUEST_SMART_ACTIONS,
+        WAIT_FOR_SMART_ACTIONS
+    }
+
+    /* Enum to report success or failure for screenshot execution phases. */
+    protected enum ScreenshotOpStatus {
+        OP_STATUS_UNKNOWN,
+        SUCCESS,
+        ERROR,
+        TIMEOUT
+    }
+
     private static final String TAG = "ScreenshotActions";
 
     /**
      * Default implementation that returns an empty list.
      * This method is overridden in vendor-specific Sys UI implementation.
      *
+     * @param screenshotId     A generated random unique id for the screenshot.
      * @param bitmap           The bitmap of the screenshot. The bitmap config must be {@link
      *                         HARDWARE}.
-     * @param context          The current app {@link Context}.
-     * @param executor         A {@link Executor} that can be used to execute tasks in parallel.
-     * @param handler          A {@link Handler} to possibly run UI-thread code.
      * @param componentName    Contains package and activity class names where the screenshot was
      *                         taken. This is used as an additional signal to generate and rank more
      *                         relevant actions.
      * @param isManagedProfile The screenshot was taken for a work profile app.
      */
-    public CompletableFuture<List<Notification.Action>> getActions(Bitmap bitmap, Context context,
-            Executor executor, Handler handler, ComponentName componentName,
+    public CompletableFuture<List<Notification.Action>> getActions(
+            String screenshotId,
+            Bitmap bitmap,
+            ComponentName componentName,
             boolean isManagedProfile) {
         Log.d(TAG, "Returning empty smart action list.");
         return CompletableFuture.completedFuture(Collections.emptyList());
     }
+
+    /**
+     * Notify exceptions and latency encountered during generating smart actions.
+     * This method is overridden in vendor-specific Sys UI implementation.
+     *
+     * @param screenshotId Unique id of the screenshot.
+     * @param op           screenshot execution phase defined in {@link ScreenshotOp}
+     * @param status       {@link ScreenshotOpStatus} to report success or failure.
+     * @param durationMs   latency experienced in different phases of screenshots.
+     */
+    public void notifyOp(String screenshotId, ScreenshotOp op, ScreenshotOpStatus status,
+            long durationMs) {
+        Log.d(TAG, "Return without notify.");
+    }
+
+    /**
+     * Notify screenshot notification action invoked.
+     * This method is overridden in vendor-specific Sys UI implementation.
+     *
+     * @param screenshotId  Unique id of the screenshot.
+     * @param action        type of notification action invoked.
+     * @param isSmartAction whether action invoked was a smart action.
+     */
+    public void notifyAction(String screenshotId, String action, boolean isSmartAction) {
+        Log.d(TAG, "Return without notify.");
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsTest.java
index 99850e7..3f32c66b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsTest.java
@@ -16,8 +16,12 @@
 
 package com.android.systemui.screenshot;
 
+import static android.content.Context.NOTIFICATION_SERVICE;
+
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doThrow;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.times;
@@ -25,14 +29,20 @@
 import static org.mockito.Mockito.when;
 
 import android.app.Notification;
+import android.app.NotificationManager;
+import android.content.Intent;
 import android.graphics.Bitmap;
+import android.net.Uri;
+import android.os.Bundle;
 import android.os.Handler;
+import android.os.Looper;
 import android.testing.AndroidTestingRunner;
 
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.SystemUIFactory;
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.util.NotificationChannels;
 
 import org.junit.Assert;
 import org.junit.Before;
@@ -70,12 +80,12 @@
         when(bitmap.getConfig()).thenReturn(Bitmap.Config.HARDWARE);
         ScreenshotNotificationSmartActionsProvider smartActionsProvider = mock(
                 ScreenshotNotificationSmartActionsProvider.class);
-        when(smartActionsProvider.getActions(any(), any(), any(), any(), any(),
+        when(smartActionsProvider.getActions(any(), any(), any(),
                 eq(false))).thenThrow(
                 RuntimeException.class);
         CompletableFuture<List<Notification.Action>> smartActionsFuture =
-                GlobalScreenshot.getSmartActionsFuture(mContext, bitmap,
-                        smartActionsProvider, mHandler, true, false);
+                GlobalScreenshot.getSmartActionsFuture("", bitmap,
+                        smartActionsProvider, true, false);
         Assert.assertNotNull(smartActionsFuture);
         List<Notification.Action> smartActions = smartActionsFuture.get(5, TimeUnit.MILLISECONDS);
         Assert.assertEquals(Collections.emptyList(), smartActions);
@@ -92,10 +102,19 @@
         when(smartActionsFuture.get(timeoutMs, TimeUnit.MILLISECONDS)).thenThrow(
                 RuntimeException.class);
         List<Notification.Action> actions = GlobalScreenshot.getSmartActions(
-                smartActionsFuture, timeoutMs);
+                "", smartActionsFuture, timeoutMs, mSmartActionsProvider);
         Assert.assertEquals(Collections.emptyList(), actions);
     }
 
+    // Tests any exception thrown in notifying feedback does not affect regular screenshot flow.
+    @Test
+    public void testExceptionHandlingInNotifyingFeedback()
+            throws Exception {
+        doThrow(RuntimeException.class).when(mSmartActionsProvider).notifyOp(any(), any(), any(),
+                anyLong());
+        GlobalScreenshot.notifyScreenshotOp(null, mSmartActionsProvider, null, null, -1);
+    }
+
     // Tests for a non-hardware bitmap, ScreenshotNotificationSmartActionsProvider is never invoked
     // and a completed future is returned.
     @Test
@@ -104,9 +123,9 @@
         Bitmap bitmap = mock(Bitmap.class);
         when(bitmap.getConfig()).thenReturn(Bitmap.Config.RGB_565);
         CompletableFuture<List<Notification.Action>> smartActionsFuture =
-                GlobalScreenshot.getSmartActionsFuture(mContext, bitmap,
-                        mSmartActionsProvider, mHandler, true, true);
-        verify(mSmartActionsProvider, never()).getActions(any(), any(), any(), any(), any(),
+                GlobalScreenshot.getSmartActionsFuture("", bitmap,
+                        mSmartActionsProvider, true, true);
+        verify(mSmartActionsProvider, never()).getActions(any(), any(), any(),
                 eq(false));
         Assert.assertNotNull(smartActionsFuture);
         List<Notification.Action> smartActions = smartActionsFuture.get(5, TimeUnit.MILLISECONDS);
@@ -118,10 +137,10 @@
     public void testScreenshotNotificationSmartActionsProviderInvokedOnce() {
         Bitmap bitmap = mock(Bitmap.class);
         when(bitmap.getConfig()).thenReturn(Bitmap.Config.HARDWARE);
-        GlobalScreenshot.getSmartActionsFuture(mContext, bitmap, mSmartActionsProvider,
-                mHandler, true, true);
+        GlobalScreenshot.getSmartActionsFuture("", bitmap, mSmartActionsProvider,
+                true, true);
         verify(mSmartActionsProvider, times(1))
-                .getActions(any(), any(), any(), any(), any(), eq(true));
+                .getActions(any(), any(), any(), eq(true));
     }
 
     // Tests for a hardware bitmap, a completed future is returned.
@@ -131,13 +150,65 @@
         Bitmap bitmap = mock(Bitmap.class);
         when(bitmap.getConfig()).thenReturn(Bitmap.Config.HARDWARE);
         ScreenshotNotificationSmartActionsProvider actionsProvider =
-                SystemUIFactory.getInstance().createScreenshotNotificationSmartActionsProvider();
+                SystemUIFactory.getInstance().createScreenshotNotificationSmartActionsProvider(
+                        mContext, null, mHandler);
         CompletableFuture<List<Notification.Action>> smartActionsFuture =
-                GlobalScreenshot.getSmartActionsFuture(mContext, bitmap,
+                GlobalScreenshot.getSmartActionsFuture("", bitmap,
                         actionsProvider,
-                        mHandler, true, true);
+                        true, true);
         Assert.assertNotNull(smartActionsFuture);
         List<Notification.Action> smartActions = smartActionsFuture.get(5, TimeUnit.MILLISECONDS);
         Assert.assertEquals(smartActions.size(), 0);
     }
+
+    // Tests for notification action extras.
+    @Test
+    public void testNotificationActionExtras() {
+        if (Looper.myLooper() == null) {
+            Looper.prepare();
+        }
+        NotificationManager notificationManager =
+                (NotificationManager) mContext.getSystemService(NOTIFICATION_SERVICE);
+        GlobalScreenshot.SaveImageInBackgroundData
+                data = new GlobalScreenshot.SaveImageInBackgroundData();
+        data.context = mContext;
+        data.image = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
+        data.iconSize = 10;
+        data.finisher = null;
+        data.onEditReady = null;
+        data.previewWidth = 10;
+        data.previewheight = 10;
+        SaveImageInBackgroundTask task = new SaveImageInBackgroundTask(mContext, data,
+                notificationManager);
+        Notification.Builder notificationBuilder = new Notification.Builder(mContext,
+                NotificationChannels.SCREENSHOTS_HEADSUP);
+        task.populateNotificationActions(mContext, mContext.getResources(),
+                Uri.parse("Screenshot_123.png"),
+                CompletableFuture.completedFuture(Collections.emptyList()), notificationBuilder);
+
+        Notification notification = notificationBuilder.build();
+        Assert.assertEquals(notification.actions.length, 3);
+        boolean isShareFound = false;
+        boolean isEditFound = false;
+        boolean isDeleteFound = false;
+        for (Notification.Action action : notification.actions) {
+            Intent intent = action.actionIntent.getIntent();
+            Assert.assertNotNull(intent);
+            Bundle bundle = intent.getExtras();
+            Assert.assertTrue(bundle.containsKey(GlobalScreenshot.EXTRA_ID));
+            Assert.assertTrue(bundle.containsKey(GlobalScreenshot.EXTRA_SMART_ACTIONS_ENABLED));
+
+            if (action.title.equals(GlobalScreenshot.ACTION_TYPE_DELETE)) {
+                isDeleteFound = intent.getAction() == null;
+            } else if (action.title.equals(GlobalScreenshot.ACTION_TYPE_EDIT)) {
+                isEditFound = Intent.ACTION_EDIT.equals(intent.getAction());
+            } else if (action.title.equals(GlobalScreenshot.ACTION_TYPE_SHARE)) {
+                isShareFound = Intent.ACTION_SEND.equals(intent.getAction());
+            }
+        }
+
+        Assert.assertTrue(isEditFound);
+        Assert.assertTrue(isDeleteFound);
+        Assert.assertTrue(isShareFound);
+    }
 }
diff --git a/services/core/java/com/android/server/UiModeManagerService.java b/services/core/java/com/android/server/UiModeManagerService.java
index a517467..74e82bb 100644
--- a/services/core/java/com/android/server/UiModeManagerService.java
+++ b/services/core/java/com/android/server/UiModeManagerService.java
@@ -16,6 +16,7 @@
 
 package com.android.server;
 
+import android.annotation.IntRange;
 import android.annotation.Nullable;
 import android.app.Activity;
 import android.app.ActivityManager;
@@ -41,6 +42,7 @@
 import android.os.PowerManager;
 import android.os.PowerManager.ServiceType;
 import android.os.PowerManagerInternal;
+import android.os.Process;
 import android.os.RemoteException;
 import android.os.ResultReceiver;
 import android.os.ServiceManager;
@@ -52,6 +54,7 @@
 import android.service.dreams.Sandman;
 import android.service.vr.IVrManager;
 import android.service.vr.IVrStateCallbacks;
+import android.util.ArraySet;
 import android.util.Slog;
 
 import com.android.internal.R;
@@ -65,6 +68,9 @@
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
 
 final class UiModeManagerService extends SystemService {
     private static final String TAG = UiModeManager.class.getSimpleName();
@@ -80,6 +86,7 @@
     private int mLastBroadcastState = Intent.EXTRA_DOCK_STATE_UNDOCKED;
     private int mNightMode = UiModeManager.MODE_NIGHT_NO;
 
+    private Map<Integer, String> mCarModePackagePriority = new HashMap<>();
     private boolean mCarModeEnabled = false;
     private boolean mCharging = false;
     private boolean mPowerSave = false;
@@ -349,15 +356,25 @@
 
     private final IUiModeManager.Stub mService = new IUiModeManager.Stub() {
         @Override
-        public void enableCarMode(int flags) {
+        public void enableCarMode(@UiModeManager.EnableCarMode int flags,
+                @IntRange(from = 0) int priority, String callingPackage) {
             if (isUiModeLocked()) {
                 Slog.e(TAG, "enableCarMode while UI mode is locked");
                 return;
             }
+
+            if (priority != UiModeManager.DEFAULT_PRIORITY
+                    && getContext().checkCallingOrSelfPermission(
+                    android.Manifest.permission.ENTER_CAR_MODE_PRIORITIZED)
+                    != PackageManager.PERMISSION_GRANTED) {
+                throw new SecurityException("Enabling car mode with a priority requires "
+                        + "permission ENTER_CAR_MODE_PRIORITIZED");
+            }
+
             final long ident = Binder.clearCallingIdentity();
             try {
                 synchronized (mLock) {
-                    setCarModeLocked(true, flags);
+                    setCarModeLocked(true, flags, priority, callingPackage);
                     if (mSystemReady) {
                         updateLocked(flags, 0);
                     }
@@ -367,16 +384,49 @@
             }
         }
 
+        /**
+         * This method is only kept around for the time being; the AIDL has an UnsupportedAppUsage
+         * tag which means this method is technically considered part of the greylist "API".
+         * @param flags
+         */
         @Override
-        public void disableCarMode(int flags) {
+        public void disableCarMode(@UiModeManager.DisableCarMode int flags) {
+            disableCarModeByCallingPackage(flags, null /* callingPackage */);
+        }
+
+        /**
+         * Handles requests to disable car mode.
+         * @param flags Disable car mode flags
+         * @param callingPackage
+         */
+        @Override
+        public void disableCarModeByCallingPackage(@UiModeManager.DisableCarMode int flags,
+                String callingPackage) {
             if (isUiModeLocked()) {
                 Slog.e(TAG, "disableCarMode while UI mode is locked");
                 return;
             }
+
+            // If the caller is the system, we will allow the DISABLE_CAR_MODE_ALL_PRIORITIES car
+            // mode flag to be specified; this is so that the user can disable car mode at all
+            // priorities using the persistent notification.
+            boolean isSystemCaller = Binder.getCallingUid() == Process.SYSTEM_UID;
+            final int carModeFlags =
+                    isSystemCaller ? flags : flags & ~UiModeManager.DISABLE_CAR_MODE_ALL_PRIORITIES;
+
             final long ident = Binder.clearCallingIdentity();
             try {
                 synchronized (mLock) {
-                    setCarModeLocked(false, 0);
+                    // Determine if the caller has enabled car mode at a priority other than the
+                    // default one.  If they have, then attempt to disable at that priority.
+                    int priority = mCarModePackagePriority.entrySet()
+                            .stream()
+                            .filter(e -> e.getValue().equals(callingPackage))
+                            .findFirst()
+                            .map(Map.Entry::getKey)
+                            .orElse(UiModeManager.DEFAULT_PRIORITY);
+
+                    setCarModeLocked(false, carModeFlags, priority, callingPackage);
                     if (mSystemReady) {
                         updateLocked(0, flags);
                     }
@@ -477,19 +527,32 @@
         synchronized (mLock) {
             pw.println("Current UI Mode Service state:");
             pw.print("  mDockState="); pw.print(mDockState);
-                    pw.print(" mLastBroadcastState="); pw.println(mLastBroadcastState);
+            pw.print(" mLastBroadcastState="); pw.println(mLastBroadcastState);
+
             pw.print("  mNightMode="); pw.print(mNightMode); pw.print(" (");
-                    pw.print(Shell.nightModeToStr(mNightMode)); pw.print(") ");
-                    pw.print(" mNightModeLocked="); pw.print(mNightModeLocked);
-                    pw.print(" mCarModeEnabled="); pw.print(mCarModeEnabled);
-                    pw.print(" mComputedNightMode="); pw.print(mComputedNightMode);
-                    pw.print(" mCarModeEnableFlags="); pw.print(mCarModeEnableFlags);
-                    pw.print(" mEnableCarDockLaunch="); pw.println(mEnableCarDockLaunch);
+            pw.print(Shell.nightModeToStr(mNightMode)); pw.print(") ");
+            pw.print(" mNightModeLocked="); pw.println(mNightModeLocked);
+
+            pw.print("  mCarModeEnabled="); pw.print(mCarModeEnabled);
+            pw.print(" (carModeApps=");
+            for (Map.Entry<Integer, String> entry : mCarModePackagePriority.entrySet()) {
+                pw.print(entry.getKey());
+                pw.print(":");
+                pw.print(entry.getValue());
+                pw.print(" ");
+            }
+            pw.println("");
+            pw.print(" mComputedNightMode="); pw.print(mComputedNightMode);
+            pw.print(" mCarModeEnableFlags="); pw.print(mCarModeEnableFlags);
+            pw.print(" mEnableCarDockLaunch="); pw.println(mEnableCarDockLaunch);
+
             pw.print("  mCurUiMode=0x"); pw.print(Integer.toHexString(mCurUiMode));
-                    pw.print(" mUiModeLocked="); pw.print(mUiModeLocked);
-                    pw.print(" mSetUiMode=0x"); pw.println(Integer.toHexString(mSetUiMode));
+            pw.print(" mUiModeLocked="); pw.print(mUiModeLocked);
+            pw.print(" mSetUiMode=0x"); pw.println(Integer.toHexString(mSetUiMode));
+
             pw.print("  mHoldingConfiguration="); pw.print(mHoldingConfiguration);
-                    pw.print(" mSystemReady="); pw.println(mSystemReady);
+            pw.print(" mSystemReady="); pw.println(mSystemReady);
+
             if (mTwilightManager != null) {
                 // We may not have a TwilightManager.
                 pw.print("  mTwilightService.getLastTwilightState()=");
@@ -512,12 +575,32 @@
         }
     }
 
-    void setCarModeLocked(boolean enabled, int flags) {
-        if (mCarModeEnabled != enabled) {
-            mCarModeEnabled = enabled;
+    /**
+     * Updates the global car mode state.
+     * The device is considered to be in car mode if there exists an app at any priority level which
+     * has entered car mode.
+     *
+     * @param enabled {@code true} if the caller wishes to enable car mode, {@code false} otherwise.
+     * @param flags Flags used when enabling/disabling car mode.
+     * @param priority The priority level for entering or exiting car mode; defaults to
+     *                 {@link UiModeManager#DEFAULT_PRIORITY} for callers using
+     *                 {@link UiModeManager#enableCarMode(int)}.  Callers using
+     *                 {@link UiModeManager#enableCarMode(int, int)} may specify a priority.
+     * @param packageName The package name of the app which initiated the request to enable or
+     *                    disable car mode.
+     */
+    void setCarModeLocked(boolean enabled, int flags, int priority, String packageName) {
+        if (enabled) {
+            enableCarMode(priority, packageName);
+        } else {
+            disableCarMode(flags, priority, packageName);
+        }
+        boolean isCarModeNowEnabled = isCarModeEnabled();
 
+        if (mCarModeEnabled != isCarModeNowEnabled) {
+            mCarModeEnabled = isCarModeNowEnabled;
             // When exiting car mode, restore night mode from settings
-            if (!mCarModeEnabled) {
+            if (!isCarModeNowEnabled) {
                 Context context = getContext();
                 updateNightModeFromSettings(context,
                         context.getResources(),
@@ -527,11 +610,102 @@
         mCarModeEnableFlags = flags;
     }
 
+    /**
+     * Handles disabling car mode.
+     * <p>
+     * Car mode can be disabled at a priority level if any of the following is true:
+     * 1. The priority being disabled is the {@link UiModeManager#DEFAULT_PRIORITY}.
+     * 2. The priority level is enabled and the caller is the app who originally enabled it.
+     * 3. The {@link UiModeManager#DISABLE_CAR_MODE_ALL_PRIORITIES} flag was specified, meaning all
+     *    car mode priorities are disabled.
+     *
+     * @param flags Car mode flags.
+     * @param priority The priority level at which to disable car mode.
+     * @param packageName The calling package which initiated the request.
+     */
+    private void disableCarMode(int flags, int priority, String packageName) {
+        boolean isDisableAll = (flags & UiModeManager.DISABLE_CAR_MODE_ALL_PRIORITIES) != 0;
+        boolean isPriorityTracked = mCarModePackagePriority.keySet().contains(priority);
+        boolean isDefaultPriority = priority == UiModeManager.DEFAULT_PRIORITY;
+        boolean isChangeAllowed =
+                // Anyone can disable the default priority.
+                isDefaultPriority
+                // If priority was enabled, only enabling package can disable it.
+                || isPriorityTracked && mCarModePackagePriority.get(priority).equals(packageName)
+                // Disable all priorities flag can disable all regardless.
+                || isDisableAll;
+        if (isChangeAllowed) {
+            Slog.d(TAG, "disableCarMode: disabling, priority=" + priority
+                    + ", packageName=" + packageName);
+            if (isDisableAll) {
+                Set<Map.Entry<Integer, String>> entries =
+                        new ArraySet<>(mCarModePackagePriority.entrySet());
+                mCarModePackagePriority.clear();
+
+                for (Map.Entry<Integer, String> entry : entries) {
+                    notifyCarModeDisabled(entry.getKey(), entry.getValue());
+                }
+            } else {
+                mCarModePackagePriority.remove(priority);
+                notifyCarModeDisabled(priority, packageName);
+            }
+        }
+    }
+
+    /**
+     * Handles enabling car mode.
+     * <p>
+     * Car mode can be enabled at any priority if it has not already been enabled at that priority.
+     * The calling package is tracked for the first app which enters priority at the
+     * {@link UiModeManager#DEFAULT_PRIORITY}, though any app can disable it at that priority.
+     *
+     * @param priority The priority for enabling car mode.
+     * @param packageName The calling package which initiated the request.
+     */
+    private void enableCarMode(int priority, String packageName) {
+        boolean isPriorityTracked = mCarModePackagePriority.containsKey(priority);
+        boolean isPackagePresent = mCarModePackagePriority.containsValue(packageName);
+        if (!isPriorityTracked && !isPackagePresent) {
+            Slog.d(TAG, "enableCarMode: enabled at priority=" + priority + ", packageName="
+                    + packageName);
+            mCarModePackagePriority.put(priority, packageName);
+            notifyCarModeEnabled(priority, packageName);
+        } else {
+            Slog.d(TAG, "enableCarMode: car mode at priority " + priority + " already enabled.");
+        }
+
+    }
+
+    private void notifyCarModeEnabled(int priority, String packageName) {
+        Intent intent = new Intent(UiModeManager.ACTION_ENTER_CAR_MODE_PRIORITIZED);
+        intent.putExtra(UiModeManager.EXTRA_CALLING_PACKAGE, packageName);
+        intent.putExtra(UiModeManager.EXTRA_PRIORITY, priority);
+        getContext().sendBroadcastAsUser(intent, UserHandle.ALL,
+                android.Manifest.permission.HANDLE_CAR_MODE_CHANGES);
+    }
+
+    private void notifyCarModeDisabled(int priority, String packageName) {
+        Intent intent = new Intent(UiModeManager.ACTION_EXIT_CAR_MODE_PRIORITIZED);
+        intent.putExtra(UiModeManager.EXTRA_CALLING_PACKAGE, packageName);
+        intent.putExtra(UiModeManager.EXTRA_PRIORITY, priority);
+        getContext().sendBroadcastAsUser(intent, UserHandle.ALL,
+                android.Manifest.permission.HANDLE_CAR_MODE_CHANGES);
+    }
+
+    /**
+     * Determines if car mode is enabled at any priority level.
+     * @return {@code true} if car mode is enabled, {@code false} otherwise.
+     */
+    private boolean isCarModeEnabled() {
+        return mCarModePackagePriority.size() > 0;
+    }
+
     private void updateDockState(int newState) {
         synchronized (mLock) {
             if (newState != mDockState) {
                 mDockState = newState;
-                setCarModeLocked(mDockState == Intent.EXTRA_DOCK_STATE_CAR, 0);
+                setCarModeLocked(mDockState == Intent.EXTRA_DOCK_STATE_CAR, 0,
+                        UiModeManager.DEFAULT_PRIORITY, "" /* packageName */);
                 if (mSystemReady) {
                     updateLocked(UiModeManager.ENABLE_CAR_MODE_GO_CAR_HOME, 0);
                 }
diff --git a/services/core/java/com/android/server/media/MediaRoute2Provider.java b/services/core/java/com/android/server/media/MediaRoute2Provider.java
deleted file mode 100644
index 91c9253..0000000
--- a/services/core/java/com/android/server/media/MediaRoute2Provider.java
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * Copyright 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.media;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.content.ComponentName;
-import android.content.Intent;
-import android.media.MediaRoute2Info;
-import android.media.MediaRoute2ProviderInfo;
-
-import java.util.Objects;
-
-abstract class MediaRoute2Provider {
-    final ComponentName mComponentName;
-    final String mUniqueId;
-
-    private Callback mCallback;
-    private MediaRoute2ProviderInfo mProviderInfo;
-
-    MediaRoute2Provider(@NonNull ComponentName componentName) {
-        mComponentName = Objects.requireNonNull(componentName, "Component name must not be null.");
-        mUniqueId = componentName.flattenToShortString();
-    }
-
-    public void setCallback(MediaRoute2ProviderProxy.Callback callback) {
-        mCallback = callback;
-    }
-
-    public abstract void requestSelectRoute(String packageName, String routeId, int seq);
-    public abstract void unselectRoute(String packageName, String routeId);
-    public abstract void sendControlRequest(MediaRoute2Info route, Intent request);
-    public abstract void requestSetVolume(MediaRoute2Info route, int volume);
-    public abstract void requestUpdateVolume(MediaRoute2Info route, int delta);
-
-    @NonNull
-    public String getUniqueId() {
-        return mUniqueId;
-    }
-
-    @Nullable
-    public MediaRoute2ProviderInfo getProviderInfo() {
-        return mProviderInfo;
-    }
-
-    void setAndNotifyProviderInfo(MediaRoute2ProviderInfo info) {
-        //TODO: check if info is not updated
-        if (info == null) {
-            mProviderInfo = null;
-        } else {
-            mProviderInfo = new MediaRoute2ProviderInfo.Builder(info)
-                    .setUniqueId(mUniqueId)
-                    .build();
-        }
-        if (mCallback != null) {
-            mCallback.onProviderStateChanged(this);
-        }
-    }
-
-    public boolean hasComponentName(String packageName, String className) {
-        return mComponentName.getPackageName().equals(packageName)
-                && mComponentName.getClassName().equals(className);
-    }
-
-    public interface Callback {
-        void onProviderStateChanged(MediaRoute2Provider provider);
-    }
-}
diff --git a/services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java b/services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java
index 3b6580a..51a0df3 100644
--- a/services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java
+++ b/services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java
@@ -17,6 +17,7 @@
 package com.android.server.media;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -41,14 +42,20 @@
 /**
  * Maintains a connection to a particular media route provider service.
  */
-final class MediaRoute2ProviderProxy extends MediaRoute2Provider implements ServiceConnection {
+final class MediaRoute2ProviderProxy implements ServiceConnection {
     private static final String TAG = "MR2ProviderProxy";
     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
     private final Context mContext;
+    private final ComponentName mComponentName;
+    private final String mUniqueId;
     private final int mUserId;
     private final Handler mHandler;
 
+    private Callback mCallback;
+
+    private MediaRoute2ProviderInfo mProviderInfo;
+
     // Connection state
     private boolean mRunning;
     private boolean mBound;
@@ -57,8 +64,9 @@
 
     MediaRoute2ProviderProxy(@NonNull Context context, @NonNull ComponentName componentName,
             int userId) {
-        super(componentName);
         mContext = Objects.requireNonNull(context, "Context must not be null.");
+        mComponentName = Objects.requireNonNull(componentName, "Component name must not be null.");
+        mUniqueId = componentName.flattenToShortString();
         mUserId = userId;
         mHandler = new Handler();
     }
@@ -72,7 +80,10 @@
         pw.println(prefix + "  mConnectionReady=" + mConnectionReady);
     }
 
-    @Override
+    public void setCallback(Callback callback) {
+        mCallback = callback;
+    }
+
     public void requestSelectRoute(String packageName, String routeId, int seq) {
         if (mConnectionReady) {
             mActiveConnection.requestSelectRoute(packageName, routeId, seq);
@@ -80,7 +91,6 @@
         }
     }
 
-    @Override
     public void unselectRoute(String packageName, String routeId) {
         if (mConnectionReady) {
             mActiveConnection.unselectRoute(packageName, routeId);
@@ -88,7 +98,6 @@
         }
     }
 
-    @Override
     public void sendControlRequest(MediaRoute2Info route, Intent request) {
         if (mConnectionReady) {
             mActiveConnection.sendControlRequest(route.getId(), request);
@@ -96,7 +105,6 @@
         }
     }
 
-    @Override
     public void requestSetVolume(MediaRoute2Info route, int volume) {
         if (mConnectionReady) {
             mActiveConnection.requestSetVolume(route.getId(), volume);
@@ -104,7 +112,6 @@
         }
     }
 
-    @Override
     public void requestUpdateVolume(MediaRoute2Info route, int delta) {
         if (mConnectionReady) {
             mActiveConnection.requestUpdateVolume(route.getId(), delta);
@@ -112,6 +119,16 @@
         }
     }
 
+    @NonNull
+    public String getUniqueId() {
+        return mUniqueId;
+    }
+
+    @Nullable
+    public MediaRoute2ProviderInfo getProviderInfo() {
+        return mProviderInfo;
+    }
+
     public boolean hasComponentName(String packageName, String className) {
         return mComponentName.getPackageName().equals(packageName)
                 && mComponentName.getClassName().equals(className);
@@ -253,6 +270,20 @@
         setAndNotifyProviderInfo(info);
     }
 
+    private void setAndNotifyProviderInfo(MediaRoute2ProviderInfo info) {
+        //TODO: check if info is not updated
+        if (info == null) {
+            mProviderInfo = null;
+        } else {
+            mProviderInfo = new MediaRoute2ProviderInfo.Builder(info)
+                .setUniqueId(mUniqueId)
+                .build();
+        }
+        if (mCallback != null) {
+            mCallback.onProviderStateChanged(MediaRoute2ProviderProxy.this);
+        }
+    }
+
     private void disconnect() {
         if (mActiveConnection != null) {
             mConnectionReady = false;
@@ -267,6 +298,10 @@
         return "Service connection " + mComponentName.flattenToShortString();
     }
 
+    public interface Callback {
+        void onProviderStateChanged(@NonNull MediaRoute2ProviderProxy provider);
+    }
+
     private final class Connection implements DeathRecipient {
         private final IMediaRoute2Provider mProvider;
         private final ProviderClient mClient;
diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
index 7820cd7..adfb9cb 100644
--- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
+++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
@@ -728,15 +728,14 @@
 
     static final class UserHandler extends Handler implements
             MediaRoute2ProviderWatcher.Callback,
-            MediaRoute2Provider.Callback {
+            MediaRoute2ProviderProxy.Callback {
 
         private final WeakReference<MediaRouter2ServiceImpl> mServiceRef;
         private final UserRecord mUserRecord;
         private final MediaRoute2ProviderWatcher mWatcher;
 
         //TODO: Make this thread-safe.
-        private final SystemMediaRoute2Provider mSystemProvider;
-        private final ArrayList<MediaRoute2Provider> mMediaProviders =
+        private final ArrayList<MediaRoute2ProviderProxy> mMediaProviders =
                 new ArrayList<>();
         private final List<MediaRoute2ProviderInfo> mProviderInfos = new ArrayList<>();
 
@@ -747,8 +746,6 @@
             super(Looper.getMainLooper(), null, true);
             mServiceRef = new WeakReference<>(service);
             mUserRecord = userRecord;
-            mSystemProvider = new SystemMediaRoute2Provider(service.mContext, this);
-            mMediaProviders.add(mSystemProvider);
             mWatcher = new MediaRoute2ProviderWatcher(service.mContext, this,
                     this, mUserRecord.mUserId);
         }
@@ -780,7 +777,7 @@
         }
 
         @Override
-        public void onProviderStateChanged(@NonNull MediaRoute2Provider provider) {
+        public void onProviderStateChanged(@NonNull MediaRoute2ProviderProxy provider) {
             sendMessage(PooledLambda.obtainMessage(UserHandler::updateProvider, this, provider));
         }
 
@@ -793,7 +790,7 @@
                     controlHints, seq));
         }
 
-        private void updateProvider(MediaRoute2Provider provider) {
+        private void updateProvider(MediaRoute2ProviderProxy provider) {
             int providerIndex = getProviderInfoIndex(provider.getUniqueId());
             MediaRoute2ProviderInfo providerInfo = provider.getProviderInfo();
             MediaRoute2ProviderInfo prevInfo =
@@ -957,7 +954,7 @@
 
         private void requestSelectRoute(String clientPackageName, MediaRoute2Info route, int seq) {
             if (route != null) {
-                MediaRoute2Provider provider = findProvider(route.getProviderId());
+                MediaRoute2ProviderProxy provider = findProvider(route.getProviderId());
                 if (provider == null) {
                     Slog.w(TAG, "Ignoring to select route of unknown provider " + route);
                 } else {
@@ -968,7 +965,7 @@
 
         private void unselectRoute(String clientPackageName, MediaRoute2Info route) {
             if (route != null) {
-                MediaRoute2Provider provider = findProvider(route.getProviderId());
+                MediaRoute2ProviderProxy provider = findProvider(route.getProviderId());
                 if (provider == null) {
                     Slog.w(TAG, "Ignoring to unselect route of unknown provider " + route);
                 } else {
@@ -978,21 +975,21 @@
         }
 
         private void sendControlRequest(MediaRoute2Info route, Intent request) {
-            final MediaRoute2Provider provider = findProvider(route.getProviderId());
+            final MediaRoute2ProviderProxy provider = findProvider(route.getProviderId());
             if (provider != null) {
                 provider.sendControlRequest(route, request);
             }
         }
 
         private void requestSetVolume(MediaRoute2Info route, int volume) {
-            final MediaRoute2Provider provider = findProvider(route.getProviderId());
+            final MediaRoute2ProviderProxy provider = findProvider(route.getProviderId());
             if (provider != null) {
                 provider.requestSetVolume(route, volume);
             }
         }
 
         private void requestUpdateVolume(MediaRoute2Info route, int delta) {
-            final MediaRoute2Provider provider = findProvider(route.getProviderId());
+            final MediaRoute2ProviderProxy provider = findProvider(route.getProviderId());
             if (provider != null) {
                 provider.requestUpdateVolume(route, delta);
             }
@@ -1156,8 +1153,8 @@
             }
         }
 
-        private MediaRoute2Provider findProvider(String providerId) {
-            for (MediaRoute2Provider provider : mMediaProviders) {
+        private MediaRoute2ProviderProxy findProvider(String providerId) {
+            for (MediaRoute2ProviderProxy provider : mMediaProviders) {
                 if (TextUtils.equals(provider.getUniqueId(), providerId)) {
                     return provider;
                 }
diff --git a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
deleted file mode 100644
index 13ded61..0000000
--- a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
+++ /dev/null
@@ -1,202 +0,0 @@
-/*
- * Copyright 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.media;
-
-import android.annotation.NonNull;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.media.AudioManager;
-import android.media.AudioRoutesInfo;
-import android.media.IAudioRoutesObserver;
-import android.media.IAudioService;
-import android.media.MediaRoute2Info;
-import android.media.MediaRoute2ProviderInfo;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.text.TextUtils;
-import android.util.Log;
-
-import com.android.internal.R;
-
-/**
- * Provides routes for local playbacks such as phone speaker, wired headset, or Bluetooth speakers.
- */
-class SystemMediaRoute2Provider extends MediaRoute2Provider {
-    private static final String TAG = "MR2SystemProvider";
-    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
-
-    private static final String DEFAULT_ROUTE_ID = "DEFAULT_ROUTE";
-    private static final String BLUETOOTH_ROUTE_ID = "BLUETOOTH_ROUTE";
-
-    // TODO: Move these to a proper place
-    public static final String CATEGORY_LIVE_AUDIO = "android.media.intent.category.LIVE_AUDIO";
-    public static final String CATEGORY_LIVE_VIDEO = "android.media.intent.category.LIVE_VIDEO";
-
-    private final AudioManager mAudioManager;
-    private final IAudioService mAudioService;
-    private final Handler mHandler;
-    private final Context mContext;
-
-    private static ComponentName sComponentName = new ComponentName(
-            SystemMediaRoute2Provider.class.getPackageName$(),
-            SystemMediaRoute2Provider.class.getName());
-
-    //TODO: Clean up these when audio manager support multiple bt devices
-    MediaRoute2Info mDefaultRoute;
-    MediaRoute2Info mBluetoothA2dpRoute;
-    final AudioRoutesInfo mCurAudioRoutesInfo = new AudioRoutesInfo();
-
-    final IAudioRoutesObserver.Stub mAudioRoutesObserver = new IAudioRoutesObserver.Stub() {
-        @Override
-        public void dispatchAudioRoutesChanged(final AudioRoutesInfo newRoutes) {
-            mHandler.post(new Runnable() {
-                @Override public void run() {
-                    updateAudioRoutes(newRoutes);
-                }
-            });
-        }
-    };
-
-    SystemMediaRoute2Provider(Context context, Callback callback) {
-        super(sComponentName);
-        setCallback(callback);
-
-        mContext = context;
-        mHandler = new Handler(Looper.getMainLooper());
-
-        mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
-        mAudioService = IAudioService.Stub.asInterface(
-                ServiceManager.getService(Context.AUDIO_SERVICE));
-
-        initializeRoutes();
-    }
-
-    //TODO: implement method
-    @Override
-    public void requestSelectRoute(@NonNull String packageName, @NonNull String routeId, int seq) {
-        try {
-            mAudioService.setBluetoothA2dpOn(
-                    !TextUtils.equals(routeId, mDefaultRoute.getId()));
-        } catch (RemoteException ex) {
-            Log.e(TAG, "Error changing Bluetooth A2DP route");
-        }
-    }
-
-    //TODO: implement method
-    @Override
-    public void unselectRoute(@NonNull String packageName, @NonNull String routeId) {
-        // does nothing..?
-    }
-
-    //TODO: implement method
-    @Override
-    public void sendControlRequest(@NonNull MediaRoute2Info route, @NonNull Intent request) {
-    }
-
-    //TODO: implement method
-    @Override
-    public void requestSetVolume(MediaRoute2Info route, int volume) {
-    }
-
-    //TODO: implement method
-    @Override
-    public void requestUpdateVolume(MediaRoute2Info route, int delta) {
-    }
-
-    void initializeRoutes() {
-        //TODO: adds necessary info
-        mDefaultRoute = new MediaRoute2Info.Builder(
-                DEFAULT_ROUTE_ID,
-                mContext.getResources().getText(R.string.default_audio_route_name).toString())
-                .setVolumeHandling(mAudioManager.isVolumeFixed()
-                        ? MediaRoute2Info.PLAYBACK_VOLUME_FIXED
-                        : MediaRoute2Info.PLAYBACK_VOLUME_VARIABLE)
-                .setVolumeMax(mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC))
-                .setVolume(mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC))
-                .addSupportedCategory(CATEGORY_LIVE_AUDIO)
-                .addSupportedCategory(CATEGORY_LIVE_VIDEO)
-                .build();
-
-        AudioRoutesInfo newAudioRoutes = null;
-        try {
-            newAudioRoutes = mAudioService.startWatchingRoutes(mAudioRoutesObserver);
-        } catch (RemoteException e) {
-        }
-        if (newAudioRoutes != null) {
-            // This will select the active BT route if there is one and the current
-            // selected route is the default system route, or if there is no selected
-            // route yet.
-            updateAudioRoutes(newAudioRoutes);
-        }
-
-        publishRoutes();
-    }
-
-    void updateAudioRoutes(AudioRoutesInfo newRoutes) {
-        int name = R.string.default_audio_route_name;
-        mCurAudioRoutesInfo.mainType = newRoutes.mainType;
-        if ((newRoutes.mainType & AudioRoutesInfo.MAIN_HEADPHONES) != 0
-                || (newRoutes.mainType & AudioRoutesInfo.MAIN_HEADSET) != 0) {
-            name = com.android.internal.R.string.default_audio_route_name_headphones;
-        } else if ((newRoutes.mainType & AudioRoutesInfo.MAIN_DOCK_SPEAKERS) != 0) {
-            name = com.android.internal.R.string.default_audio_route_name_dock_speakers;
-        } else if ((newRoutes.mainType & AudioRoutesInfo.MAIN_HDMI) != 0) {
-            name = com.android.internal.R.string.default_audio_route_name_hdmi;
-        } else if ((newRoutes.mainType & AudioRoutesInfo.MAIN_USB) != 0) {
-            name = com.android.internal.R.string.default_audio_route_name_usb;
-        }
-
-        mDefaultRoute = new MediaRoute2Info.Builder(
-                DEFAULT_ROUTE_ID, mContext.getResources().getText(name).toString())
-                .setVolumeHandling(mAudioManager.isVolumeFixed()
-                        ? MediaRoute2Info.PLAYBACK_VOLUME_FIXED
-                        : MediaRoute2Info.PLAYBACK_VOLUME_VARIABLE)
-                .setVolumeMax(mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC))
-                .setVolume(mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC))
-                .addSupportedCategory(CATEGORY_LIVE_AUDIO)
-                .addSupportedCategory(CATEGORY_LIVE_VIDEO)
-                .build();
-
-        if (!TextUtils.equals(newRoutes.bluetoothName, mCurAudioRoutesInfo.bluetoothName)) {
-            mCurAudioRoutesInfo.bluetoothName = newRoutes.bluetoothName;
-            if (mCurAudioRoutesInfo.bluetoothName != null) {
-                //TODO: mark as bluetooth once MediaRoute2Info has device type
-                mBluetoothA2dpRoute = new MediaRoute2Info.Builder(BLUETOOTH_ROUTE_ID,
-                        mCurAudioRoutesInfo.bluetoothName.toString())
-                        .setDescription(mContext.getResources().getText(
-                                R.string.bluetooth_a2dp_audio_route_name).toString())
-                        .addSupportedCategory(CATEGORY_LIVE_AUDIO)
-                        .build();
-            } else if (mBluetoothA2dpRoute != null) {
-                mBluetoothA2dpRoute = null;
-            }
-        }
-
-        publishRoutes();
-    }
-    void publishRoutes() {
-        MediaRoute2ProviderInfo.Builder builder = new MediaRoute2ProviderInfo.Builder()
-                .addRoute(mDefaultRoute);
-        if (mBluetoothA2dpRoute != null) {
-            builder.addRoute(mBluetoothA2dpRoute);
-        }
-        setAndNotifyProviderInfo(builder.build());
-    }
-}
diff --git a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
index 21bdc43..3bc2838 100644
--- a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
+++ b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
@@ -698,12 +698,6 @@
     JNIEnv* env = getJniEnv();
 
     uint32_t listSize = getGnssSvInfoListSize(svStatus);
-    if (listSize > static_cast<uint32_t>(
-            android::hardware::gnss::V1_0::GnssMax::SVS_COUNT)) {
-        ALOGD("Too many satellites %u. Clamps to %u.", listSize,
-              static_cast<uint32_t>(android::hardware::gnss::V1_0::GnssMax::SVS_COUNT));
-        listSize = static_cast<uint32_t>(android::hardware::gnss::V1_0::GnssMax::SVS_COUNT);
-    }
 
     jintArray svidWithFlagArray = env->NewIntArray(listSize);
     jfloatArray cn0Array = env->NewFloatArray(listSize);