Merge "idmap2: replace std::pair<bool, T> with Result<T>"
diff --git a/api/current.txt b/api/current.txt
index 4f39e25..38bf3ab 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -11565,18 +11565,19 @@
     field public static final java.lang.String FEATURE_DEVICE_ADMIN = "android.software.device_admin";
     field public static final java.lang.String FEATURE_EMBEDDED = "android.hardware.type.embedded";
     field public static final java.lang.String FEATURE_ETHERNET = "android.hardware.ethernet";
-    field public static final java.lang.String FEATURE_FACE = "android.hardware.face";
+    field public static final java.lang.String FEATURE_FACE = "android.hardware.biometrics.face";
     field public static final java.lang.String FEATURE_FAKETOUCH = "android.hardware.faketouch";
     field public static final java.lang.String FEATURE_FAKETOUCH_MULTITOUCH_DISTINCT = "android.hardware.faketouch.multitouch.distinct";
     field public static final java.lang.String FEATURE_FAKETOUCH_MULTITOUCH_JAZZHAND = "android.hardware.faketouch.multitouch.jazzhand";
-    field public static final java.lang.String FEATURE_FINGERPRINT = "android.hardware.fingerprint";
+    field public static final java.lang.String FEATURE_FINGERPRINT = "android.hardware.biometrics.fingerprint";
+    field public static final java.lang.String FEATURE_FINGERPRINT_PRE_29 = "android.hardware.fingerprint";
     field public static final java.lang.String FEATURE_FREEFORM_WINDOW_MANAGEMENT = "android.software.freeform_window_management";
     field public static final java.lang.String FEATURE_GAMEPAD = "android.hardware.gamepad";
     field public static final java.lang.String FEATURE_HIFI_SENSORS = "android.hardware.sensor.hifi_sensors";
     field public static final java.lang.String FEATURE_HOME_SCREEN = "android.software.home_screen";
     field public static final java.lang.String FEATURE_INPUT_METHODS = "android.software.input_methods";
     field public static final java.lang.String FEATURE_IPSEC_TUNNELS = "android.software.ipsec_tunnels";
-    field public static final java.lang.String FEATURE_IRIS = "android.hardware.iris";
+    field public static final java.lang.String FEATURE_IRIS = "android.hardware.biometrics.iris";
     field public static final java.lang.String FEATURE_LEANBACK = "android.software.leanback";
     field public static final java.lang.String FEATURE_LEANBACK_ONLY = "android.software.leanback_only";
     field public static final java.lang.String FEATURE_LIVE_TV = "android.software.live_tv";
@@ -14861,6 +14862,7 @@
     method public int getAmbientShadowColor();
     method public int getBottom();
     method public float getCameraDistance();
+    method public boolean getClipToBounds();
     method public boolean getClipToOutline();
     method public float getElevation();
     method public int getHeight();
@@ -14881,6 +14883,7 @@
     method public float getTranslationY();
     method public float getTranslationZ();
     method public long getUniqueId();
+    method public boolean getUseCompositingLayer();
     method public int getWidth();
     method public boolean hasDisplayList();
     method public boolean hasIdentityMatrix();
@@ -25308,24 +25311,24 @@
     method public java.lang.Object clearNextDataSources();
     method public void clearPendingCommands();
     method public void close();
-    method public java.lang.Object deselectTrack(int);
+    method public java.lang.Object deselectTrack(android.media.DataSourceDesc, int);
     method public android.media.AudioAttributes getAudioAttributes();
     method public int getAudioSessionId();
-    method public long getBufferedPosition();
+    method public long getBufferedPosition(android.media.DataSourceDesc);
     method public android.media.DataSourceDesc getCurrentDataSource();
     method public long getCurrentPosition();
-    method public long getDuration();
+    method public long getDuration(android.media.DataSourceDesc);
     method public float getMaxPlayerVolume();
     method public android.os.PersistableBundle getMetrics();
     method public android.media.PlaybackParams getPlaybackParams();
     method public float getPlayerVolume();
     method public android.media.AudioDeviceInfo getPreferredDevice();
     method public android.media.AudioDeviceInfo getRoutedDevice();
-    method public int getSelectedTrack(int);
+    method public int getSelectedTrack(android.media.DataSourceDesc, int);
     method public int getState();
     method public android.media.SyncParams getSyncParams();
     method public android.media.MediaTimestamp getTimestamp();
-    method public java.util.List<android.media.MediaPlayer2.TrackInfo> getTrackInfo();
+    method public java.util.List<android.media.MediaPlayer2.TrackInfo> getTrackInfo(android.media.DataSourceDesc);
     method public android.media.VideoSize getVideoSize();
     method public boolean isLooping();
     method public java.lang.Object loopCurrent(boolean);
@@ -25338,7 +25341,7 @@
     method public void reset();
     method public java.lang.Object seekTo(long);
     method public java.lang.Object seekTo(long, int);
-    method public java.lang.Object selectTrack(int);
+    method public java.lang.Object selectTrack(android.media.DataSourceDesc, int);
     method public java.lang.Object setAudioAttributes(android.media.AudioAttributes);
     method public java.lang.Object setAudioSessionId(int);
     method public java.lang.Object setAuxEffectSendLevel(float);
@@ -41482,6 +41485,7 @@
     method protected void dump(java.lang.String, java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
     method public int getDesiredMinimumHeight();
     method public int getDesiredMinimumWidth();
+    method public android.content.Context getDisplayContext();
     method public android.view.SurfaceHolder getSurfaceHolder();
     method public boolean isPreview();
     method public boolean isVisible();
diff --git a/api/system-current.txt b/api/system-current.txt
index a83c32d..7d10b098 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -3713,6 +3713,7 @@
     method public boolean isWifiScannerSupported();
     method public void registerNetworkRequestMatchCallback(android.net.wifi.WifiManager.NetworkRequestMatchCallback, android.os.Handler);
     method public void save(android.net.wifi.WifiConfiguration, android.net.wifi.WifiManager.ActionListener);
+    method public void setDeviceMobilityState(int);
     method public boolean setWifiApConfiguration(android.net.wifi.WifiConfiguration);
     method public boolean startScan(android.os.WorkSource);
     method public void unregisterNetworkRequestMatchCallback(android.net.wifi.WifiManager.NetworkRequestMatchCallback);
@@ -3720,6 +3721,10 @@
     field public static final int CHANGE_REASON_CONFIG_CHANGE = 2; // 0x2
     field public static final int CHANGE_REASON_REMOVED = 1; // 0x1
     field public static final java.lang.String CONFIGURED_NETWORKS_CHANGED_ACTION = "android.net.wifi.CONFIGURED_NETWORKS_CHANGE";
+    field public static final int DEVICE_MOBILITY_STATE_HIGH_MVMT = 1; // 0x1
+    field public static final int DEVICE_MOBILITY_STATE_LOW_MVMT = 2; // 0x2
+    field public static final int DEVICE_MOBILITY_STATE_STATIONARY = 3; // 0x3
+    field public static final int DEVICE_MOBILITY_STATE_UNKNOWN = 0; // 0x0
     field public static final java.lang.String EXTRA_CHANGE_REASON = "changeReason";
     field public static final java.lang.String EXTRA_MULTIPLE_NETWORKS_CHANGED = "multipleChanges";
     field public static final java.lang.String EXTRA_PREVIOUS_WIFI_AP_STATE = "previous_wifi_state";
@@ -5827,6 +5832,7 @@
   public class SubscriptionManager {
     method public java.util.List<android.telephony.SubscriptionInfo> getAvailableSubscriptionInfoList();
     method public void requestEmbeddedSubscriptionInfoListRefresh();
+    method public void requestEmbeddedSubscriptionInfoListRefresh(int);
     method public void setDefaultDataSubId(int);
     method public void setDefaultSmsSubId(int);
     field public static final android.net.Uri ADVANCED_CALLING_ENABLED_CONTENT_URI;
diff --git a/api/test-current.txt b/api/test-current.txt
index d534501..46e7683 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -655,11 +655,6 @@
     method public android.media.BufferingParams.Builder setResumePlaybackMarkMs(int);
   }
 
-  public class MediaPlayer implements android.media.AudioRouting android.media.VolumeAutomation {
-    method public android.media.BufferingParams getBufferingParams();
-    method public void setBufferingParams(android.media.BufferingParams);
-  }
-
   public final class PlaybackParams implements android.os.Parcelable {
     method public int getAudioStretchMode();
     method public android.media.PlaybackParams setAudioStretchMode(int);
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 566017b..9d604bb 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -2288,21 +2288,28 @@
      * {@link #hasSystemFeature}: The device has biometric hardware to detect a fingerprint.
      */
     @SdkConstant(SdkConstantType.FEATURE)
-    public static final String FEATURE_FINGERPRINT = "android.hardware.fingerprint";
+    public static final String FEATURE_FINGERPRINT_PRE_29 = "android.hardware.fingerprint";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and
+     * {@link #hasSystemFeature}: The device has biometric hardware to detect a fingerprint.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_FINGERPRINT = "android.hardware.biometrics.fingerprint";
 
     /**
      * Feature for {@link #getSystemAvailableFeatures} and
      * {@link #hasSystemFeature}: The device has biometric hardware to perform face authentication.
      */
     @SdkConstant(SdkConstantType.FEATURE)
-    public static final String FEATURE_FACE = "android.hardware.face";
+    public static final String FEATURE_FACE = "android.hardware.biometrics.face";
 
     /**
      * Feature for {@link #getSystemAvailableFeatures} and
      * {@link #hasSystemFeature}: The device has biometric hardware to perform iris authentication.
      */
     @SdkConstant(SdkConstantType.FEATURE)
-    public static final String FEATURE_IRIS = "android.hardware.iris";
+    public static final String FEATURE_IRIS = "android.hardware.biometrics.iris";
 
     /**
      * Feature for {@link #getSystemAvailableFeatures} and
diff --git a/core/java/android/net/INetdEventCallback.aidl b/core/java/android/net/INetdEventCallback.aidl
index 4b1a08d..0877a1a4 100644
--- a/core/java/android/net/INetdEventCallback.aidl
+++ b/core/java/android/net/INetdEventCallback.aidl
@@ -45,6 +45,20 @@
             in String[] ipAddresses, int ipAddressesCount, long timestamp, int uid);
 
     /**
+     * Represents adding or removing a NAT64 prefix.
+     * This method must not block or perform long-running operations.
+     *
+     * @param netId the ID of the network the prefix was performed on.
+     * @param added true if the NAT64 prefix was added, or false if the NAT64 prefix was removed.
+     *        There is only one prefix at a time for each netId. If a prefix is added, it replaces
+     *        the previous-added prefix.
+     * @param prefixString the detected NAT64 prefix as a string literal.
+     * @param prefixLength the prefix length associated with this NAT64 prefix.
+     */
+    void onNat64PrefixEvent(int netId, boolean added, @utf8InCpp String prefixString,
+            int prefixLength);
+
+    /**
      * Represents a private DNS validation success or failure.
      * This method must not block or perform long-running operations.
      *
diff --git a/core/java/android/os/FileUtils.java b/core/java/android/os/FileUtils.java
index 428d9e1..e84a518 100644
--- a/core/java/android/os/FileUtils.java
+++ b/core/java/android/os/FileUtils.java
@@ -23,6 +23,7 @@
 import static android.os.ParcelFileDescriptor.MODE_TRUNCATE;
 import static android.os.ParcelFileDescriptor.MODE_WRITE_ONLY;
 import static android.system.OsConstants.F_OK;
+import static android.system.OsConstants.O_ACCMODE;
 import static android.system.OsConstants.O_APPEND;
 import static android.system.OsConstants.O_CREAT;
 import static android.system.OsConstants.O_RDONLY;
@@ -1259,11 +1260,11 @@
 
         int res = 0;
         if (mode.startsWith("rw")) {
-            res |= O_RDWR | O_CREAT;
+            res = O_RDWR | O_CREAT;
         } else if (mode.startsWith("w")) {
-            res |= O_WRONLY | O_CREAT;
+            res = O_WRONLY | O_CREAT;
         } else if (mode.startsWith("r")) {
-            res |= O_RDONLY;
+            res = O_RDONLY;
         } else {
             throw new IllegalArgumentException("Bad mode: " + mode);
         }
@@ -1279,12 +1280,12 @@
     /** {@hide} */
     public static String translateModePosixToString(int mode) {
         String res = "";
-        if ((mode & O_RDWR) == O_RDWR) {
-            res += "rw";
-        } else if ((mode & O_WRONLY) == O_WRONLY) {
-            res += "w";
-        } else if ((mode & O_RDONLY) == O_RDONLY) {
-            res += "r";
+        if ((mode & O_ACCMODE) == O_RDWR) {
+            res = "rw";
+        } else if ((mode & O_ACCMODE) == O_WRONLY) {
+            res = "w";
+        } else if ((mode & O_ACCMODE) == O_RDONLY) {
+            res = "r";
         } else {
             throw new IllegalArgumentException("Bad mode: " + mode);
         }
@@ -1300,12 +1301,12 @@
     /** {@hide} */
     public static int translateModePosixToPfd(int mode) {
         int res = 0;
-        if ((mode & O_RDWR) == O_RDWR) {
-            res |= MODE_READ_WRITE;
-        } else if ((mode & O_WRONLY) == O_WRONLY) {
-            res |= MODE_WRITE_ONLY;
-        } else if ((mode & O_RDONLY) == O_RDONLY) {
-            res |= MODE_READ_ONLY;
+        if ((mode & O_ACCMODE) == O_RDWR) {
+            res = MODE_READ_WRITE;
+        } else if ((mode & O_ACCMODE) == O_WRONLY) {
+            res = MODE_WRITE_ONLY;
+        } else if ((mode & O_ACCMODE) == O_RDONLY) {
+            res = MODE_READ_ONLY;
         } else {
             throw new IllegalArgumentException("Bad mode: " + mode);
         }
@@ -1325,11 +1326,11 @@
     public static int translateModePfdToPosix(int mode) {
         int res = 0;
         if ((mode & MODE_READ_WRITE) == MODE_READ_WRITE) {
-            res |= O_RDWR;
+            res = O_RDWR;
         } else if ((mode & MODE_WRITE_ONLY) == MODE_WRITE_ONLY) {
-            res |= O_WRONLY;
+            res = O_WRONLY;
         } else if ((mode & MODE_READ_ONLY) == MODE_READ_ONLY) {
-            res |= O_RDONLY;
+            res = O_RDONLY;
         } else {
             throw new IllegalArgumentException("Bad mode: " + mode);
         }
@@ -1428,4 +1429,3 @@
         }
     }
 }
-
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index a095b0d..f295b70 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -25,6 +25,7 @@
 import android.app.WallpaperColors;
 import android.app.WallpaperInfo;
 import android.app.WallpaperManager;
+import android.content.Context;
 import android.content.Intent;
 import android.content.res.TypedArray;
 import android.graphics.Bitmap;
@@ -52,12 +53,12 @@
 import android.view.InputDevice;
 import android.view.InputEvent;
 import android.view.InputEventReceiver;
+import android.view.InsetsState;
 import android.view.MotionEvent;
 import android.view.SurfaceHolder;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.WindowInsets;
-import android.view.InsetsState;
 import android.view.WindowManager;
 import android.view.WindowManagerGlobal;
 
@@ -211,7 +212,8 @@
         private final Supplier<Long> mClockFunction;
         private final Handler mHandler;
 
-        Display mDisplay;
+        private Display mDisplay;
+        private Context mDisplayContext;
         private int mDisplayState;
 
         final BaseSurfaceHolder mSurfaceHolder = new BaseSurfaceHolder() {
@@ -1038,6 +1040,7 @@
             mIWallpaperEngine.mDisplayManager.registerDisplayListener(mDisplayListener,
                     mCaller.getHandler());
             mDisplay = mIWallpaperEngine.mDisplay;
+            mDisplayContext = createDisplayContext(mDisplay);
             mDisplayState = mDisplay.getState();
 
             if (DEBUG) Log.v(TAG, "onCreate(): " + this);
@@ -1050,6 +1053,18 @@
         }
 
         /**
+         * The {@link Context} with resources that match the current display the wallpaper is on.
+         * For multiple display environment, multiple engines can be created to render on each
+         * display, but these displays may have different densities. Use this context to get the
+         * corresponding resources for currently display, avoiding the context of the service.
+         *
+         * @return A {@link Context} for current display.
+         */
+        public Context getDisplayContext() {
+            return mDisplayContext;
+        }
+
+        /**
          * Executes life cycle event and updates internal ambient mode state based on
          * message sent from handler.
          *
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index ade7577..8b97e0e 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -46,6 +46,7 @@
         DEFAULT_FLAGS.put("settings_mobile_network_v2", "true");
         DEFAULT_FLAGS.put("settings_network_and_internet_v2", "false");
         DEFAULT_FLAGS.put("settings_seamless_transfer", "false");
+        DEFAULT_FLAGS.put("settings_slice_injection", "false");
         DEFAULT_FLAGS.put("settings_systemui_theme", "true");
         DEFAULT_FLAGS.put("settings_wifi_dpp", "false");
         DEFAULT_FLAGS.put("settings_wifi_mac_randomization", "false");
diff --git a/core/java/com/android/server/net/BaseNetdEventCallback.java b/core/java/com/android/server/net/BaseNetdEventCallback.java
index 97247aa..a65214a 100644
--- a/core/java/com/android/server/net/BaseNetdEventCallback.java
+++ b/core/java/com/android/server/net/BaseNetdEventCallback.java
@@ -32,6 +32,12 @@
     }
 
     @Override
+    public void onNat64PrefixEvent(int netId, boolean added, String prefixString,
+            int prefixLength) {
+        // default no-op
+    }
+
+    @Override
     public void onPrivateDnsValidationEvent(int netId, String ipAddress,
             String hostname, boolean validated) {
         // default no-op
diff --git a/core/jni/android_view_RenderNode.cpp b/core/jni/android_view_RenderNode.cpp
index 752624b..0d75de9 100644
--- a/core/jni/android_view_RenderNode.cpp
+++ b/core/jni/android_view_RenderNode.cpp
@@ -338,6 +338,11 @@
     return renderNode->stagingProperties().hasOverlappingRendering();
 }
 
+static jboolean android_view_RenderNode_getClipToBounds(jlong renderNodePtr) {
+    RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
+    return renderNode->stagingProperties().getClipToBounds();
+}
+
 static jboolean android_view_RenderNode_getClipToOutline(jlong renderNodePtr) {
     RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
     return renderNode->stagingProperties().getOutline().getShouldClip();
@@ -409,6 +414,11 @@
     return !renderNode->stagingProperties().hasTransformMatrix();
 }
 
+static jint android_view_RenderNode_getLayerType(jlong renderNodePtr) {
+    RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
+    return static_cast<int>(renderNode->stagingProperties().layerProperties().type());
+}
+
 // ----------------------------------------------------------------------------
 // RenderProperties - computed getters
 // ----------------------------------------------------------------------------
@@ -623,10 +633,12 @@
 // ----------------------------------------------------------------------------
     { "nIsValid",              "(J)Z",   (void*) android_view_RenderNode_isValid },
     { "nSetLayerType",         "(JI)Z",  (void*) android_view_RenderNode_setLayerType },
+    { "nGetLayerType",         "(J)I",   (void*) android_view_RenderNode_getLayerType },
     { "nSetLayerPaint",        "(JJ)Z",  (void*) android_view_RenderNode_setLayerPaint },
     { "nSetStaticMatrix",      "(JJ)Z",  (void*) android_view_RenderNode_setStaticMatrix },
     { "nSetAnimationMatrix",   "(JJ)Z",  (void*) android_view_RenderNode_setAnimationMatrix },
     { "nSetClipToBounds",      "(JZ)Z",  (void*) android_view_RenderNode_setClipToBounds },
+    { "nGetClipToBounds",      "(J)Z",   (void*) android_view_RenderNode_getClipToBounds },
     { "nSetClipBounds",        "(JIIII)Z", (void*) android_view_RenderNode_setClipBounds },
     { "nSetClipBoundsEmpty",   "(J)Z",   (void*) android_view_RenderNode_setClipBoundsEmpty },
     { "nSetProjectBackwards",  "(JZ)Z",  (void*) android_view_RenderNode_setProjectBackwards },
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 7fa3e66..cc8927f 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1642,6 +1642,12 @@
     <permission android:name="android.permission.NETWORK_BYPASS_PRIVATE_DNS"
         android:protectionLevel="signature" />
 
+    <!-- #SystemApi @hide Allows device mobility state to be set so that Wifi scan interval can be increased
+         when the device is stationary in order to save power.
+         <p>Not for use by third-party applications. -->
+    <permission android:name="android.permission.WIFI_SET_DEVICE_MOBILITY_STATE"
+        android:protectionLevel="signature|privileged" />
+
     <!-- ======================================= -->
     <!-- Permissions for short range, peripheral networks -->
     <!-- ======================================= -->
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 9277dae..dd0b1ee 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1165,6 +1165,10 @@
     <!-- The default suggested battery % at which we enable battery saver automatically.  -->
     <integer name="config_lowBatteryAutoTriggerDefaultLevel">15</integer>
 
+    <!-- The app which will handle routine based automatic battery saver, if empty the UI for
+             routine based battery saver will be hidden -->
+    <string name="config_batterySaverScheduleProvider"></string>
+
     <!-- Close low battery warning when battery level reaches the lowBatteryWarningLevel
          plus this -->
     <integer name="config_lowBatteryCloseWarningBump">5</integer>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index d68681d..87fdc1f 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3463,6 +3463,7 @@
   <java-symbol type="integer" name="config_lowBatteryAutoTriggerDefaultLevel" />
   <java-symbol type="bool" name="config_batterySaverStickyBehaviourDisabled" />
   <java-symbol type="integer" name="config_dynamicPowerSavingsDefaultDisableThreshold" />
+  <java-symbol type="string" name="config_batterySaverScheduleProvider" />
 
   <!-- For car devices -->
   <java-symbol type="string" name="car_loading_profile" />
diff --git a/core/tests/coretests/src/android/os/FileUtilsTest.java b/core/tests/coretests/src/android/os/FileUtilsTest.java
index 55e21a7..514ea0c 100644
--- a/core/tests/coretests/src/android/os/FileUtilsTest.java
+++ b/core/tests/coretests/src/android/os/FileUtilsTest.java
@@ -517,6 +517,28 @@
     }
 
     @Test
+    public void testMalformedTransate_int() throws Exception {
+        try {
+            // The non-standard Linux access mode 3 should throw
+            // an IllegalArgumentException.
+            translateModePosixToPfd(O_RDWR | O_WRONLY);
+            fail();
+        } catch (IllegalArgumentException expected) {
+        }
+    }
+
+    @Test
+    public void testMalformedTransate_string() throws Exception {
+        try {
+            // The non-standard Linux access mode 3 should throw
+            // an IllegalArgumentException.
+            translateModePosixToString(O_RDWR | O_WRONLY);
+            fail();
+        } catch (IllegalArgumentException expected) {
+        }
+    }
+
+    @Test
     public void testTranslateMode_Invalid() throws Exception {
         try {
             translateModeStringToPosix("rwx");
diff --git a/data/etc/Android.mk b/data/etc/Android.mk
index 61ef426..a02c3ee 100644
--- a/data/etc/Android.mk
+++ b/data/etc/Android.mk
@@ -54,6 +54,7 @@
 LOCAL_MODULE_CLASS := ETC
 LOCAL_MODULE_RELATIVE_PATH := permissions
 LOCAL_MODULE_STEM := com.android.settings.xml
+LOCAL_PRODUCT_MODULE := true
 LOCAL_SRC_FILES := com.android.settings.xml
 include $(BUILD_PREBUILT)
 
diff --git a/graphics/java/android/graphics/RenderNode.java b/graphics/java/android/graphics/RenderNode.java
index d6f08b9..3b1d44b 100644
--- a/graphics/java/android/graphics/RenderNode.java
+++ b/graphics/java/android/graphics/RenderNode.java
@@ -446,7 +446,21 @@
     }
 
     /**
-     * Sets the clip bounds of the RenderNode.
+     * Gets whether or not a compositing layer is forced to be used. The default & recommended
+     * is false, as it is typically faster to avoid using compositing layers.
+     * See {@link #setUseCompositingLayer(boolean, Paint)}.
+     *
+     * @return true if a compositing layer is forced, false otherwise
+     */
+    public boolean getUseCompositingLayer() {
+        return nGetLayerType(mNativeRenderNode) != 0;
+    }
+
+    /**
+     * Sets the clip bounds of the RenderNode. If null, the clip bounds is removed from the
+     * RenderNode. If non-null, the RenderNode will be clipped to this rect. If
+     * {@link #setClipToBounds(boolean)} is true, then the RenderNode will be clipped to the
+     * intersection of this rectangle and the bounds of the render node.
      *
      * @param rect the bounds to clip to. If null, the clip bounds are reset
      * @return True if the clip bounds changed, false otherwise
@@ -460,16 +474,30 @@
     }
 
     /**
-     * Set whether the Render node should clip itself to its bounds. This property is controlled by
-     * the view's parent.
+     * Set whether the Render node should clip itself to its bounds. This defaults to true,
+     * and is useful to the renderer in enable quick-rejection of chunks of the tree as well as
+     * better partial invalidation support. Clipping can be further restricted or controlled
+     * through the combination of this property as well as {@link #setClipBounds(Rect)}, which
+     * allows for a different clipping rectangle to be used in addition to or instead of the
+     * {@link #setLeftTopRightBottom(int, int, int, int)} or the RenderNode.
      *
-     * @param clipToBounds true if the display list should clip to its bounds
+     * @param clipToBounds true if the display list should clip to its bounds, false otherwise.
      */
     public boolean setClipToBounds(boolean clipToBounds) {
         return nSetClipToBounds(mNativeRenderNode, clipToBounds);
     }
 
     /**
+     * Returns whether or not the RenderNode is clipping to its bounds. See
+     * {@link #setClipToBounds(boolean)} and {@link #setLeftTopRightBottom(int, int, int, int)}
+     *
+     * @return true if the render node clips to its bounds, false otherwise.
+     */
+    public boolean getClipToBounds() {
+        return nGetClipToBounds(mNativeRenderNode);
+    }
+
+    /**
      * Sets whether the RenderNode should be drawn immediately after the
      * closest ancestor RenderNode containing a projection receiver.
      *
@@ -1339,12 +1367,18 @@
     private static native boolean nSetLayerType(long renderNode, int layerType);
 
     @CriticalNative
+    private static native int nGetLayerType(long renderNode);
+
+    @CriticalNative
     private static native boolean nSetLayerPaint(long renderNode, long paint);
 
     @CriticalNative
     private static native boolean nSetClipToBounds(long renderNode, boolean clipToBounds);
 
     @CriticalNative
+    private static native boolean nGetClipToBounds(long renderNode);
+
+    @CriticalNative
     private static native boolean nSetClipBounds(long renderNode, int left, int top,
             int right, int bottom);
 
diff --git a/libs/hwui/RecordingCanvas.h b/libs/hwui/RecordingCanvas.h
index 35cf707..de8777b 100644
--- a/libs/hwui/RecordingCanvas.h
+++ b/libs/hwui/RecordingCanvas.h
@@ -65,6 +65,7 @@
     void applyColorTransform(ColorTransform transform);
 
     bool hasText() const { return mHasText; }
+    size_t usedSize() const { return fUsed; }
 
 private:
     friend class RecordingCanvas;
diff --git a/libs/hwui/RenderProperties.h b/libs/hwui/RenderProperties.h
index 04379ae..ddb7e4e 100644
--- a/libs/hwui/RenderProperties.h
+++ b/libs/hwui/RenderProperties.h
@@ -98,15 +98,15 @@
 
     LayerProperties& operator=(const LayerProperties& other);
 
+    // Strongly recommend using effectiveLayerType instead
+    LayerType type() const { return mType; }
+
 private:
     LayerProperties();
     ~LayerProperties();
     void reset();
     bool setColorFilter(SkColorFilter* filter);
 
-    // Private since external users should go through properties().effectiveLayerType()
-    LayerType type() const { return mType; }
-
     friend class RenderProperties;
 
     LayerType mType = LayerType::None;
diff --git a/libs/hwui/pipeline/skia/SkiaDisplayList.h b/libs/hwui/pipeline/skia/SkiaDisplayList.h
index d7879e7..45f3a4c 100644
--- a/libs/hwui/pipeline/skia/SkiaDisplayList.h
+++ b/libs/hwui/pipeline/skia/SkiaDisplayList.h
@@ -49,7 +49,7 @@
  */
 class SkiaDisplayList {
 public:
-    size_t getUsedSize() { return allocator.usedSize(); }
+    size_t getUsedSize() { return allocator.usedSize() + mDisplayList.usedSize(); }
 
     ~SkiaDisplayList() {
         /* Given that we are using a LinearStdAllocator to store some of the
diff --git a/libs/input/PointerController.cpp b/libs/input/PointerController.cpp
index 80d8e72..0a90f85 100644
--- a/libs/input/PointerController.cpp
+++ b/libs/input/PointerController.cpp
@@ -89,6 +89,10 @@
 
     mLocked.animationPending = false;
 
+    mLocked.displayWidth = -1;
+    mLocked.displayHeight = -1;
+    mLocked.displayOrientation = DISPLAY_ORIENTATION_0;
+
     mLocked.presentation = PRESENTATION_POINTER;
     mLocked.presentationChanged = false;
 
@@ -106,6 +110,15 @@
     mLocked.lastFrameUpdatedTime = 0;
 
     mLocked.buttonState = 0;
+
+    mPolicy->loadPointerIcon(&mLocked.pointerIcon);
+
+    loadResources();
+
+    if (mLocked.pointerIcon.isValid()) {
+        mLocked.pointerIconChanged = true;
+        updatePointerLocked();
+    }
 }
 
 PointerController::~PointerController() {
@@ -131,15 +144,23 @@
 
 bool PointerController::getBoundsLocked(float* outMinX, float* outMinY,
         float* outMaxX, float* outMaxY) const {
-
-    if (!mLocked.viewport.isValid()) {
+    if (mLocked.displayWidth <= 0 || mLocked.displayHeight <= 0) {
         return false;
     }
 
-    *outMinX = mLocked.viewport.logicalLeft;
-    *outMinY = mLocked.viewport.logicalTop;
-    *outMaxX = mLocked.viewport.logicalRight - 1;
-    *outMaxY = mLocked.viewport.logicalBottom - 1;
+    *outMinX = 0;
+    *outMinY = 0;
+    switch (mLocked.displayOrientation) {
+    case DISPLAY_ORIENTATION_90:
+    case DISPLAY_ORIENTATION_270:
+        *outMaxX = mLocked.displayHeight - 1;
+        *outMaxY = mLocked.displayWidth - 1;
+        break;
+    default:
+        *outMaxX = mLocked.displayWidth - 1;
+        *outMaxY = mLocked.displayHeight - 1;
+        break;
+    }
     return true;
 }
 
@@ -210,12 +231,6 @@
     *outY = mLocked.pointerY;
 }
 
-int32_t PointerController::getDisplayId() const {
-    AutoMutex _l(mLock);
-
-    return mLocked.viewport.displayId;
-}
-
 void PointerController::fade(Transition transition) {
     AutoMutex _l(mLock);
 
@@ -340,57 +355,48 @@
 void PointerController::reloadPointerResources() {
     AutoMutex _l(mLock);
 
-    loadResourcesLocked();
+    loadResources();
+
+    if (mLocked.presentation == PRESENTATION_POINTER) {
+        mLocked.additionalMouseResources.clear();
+        mLocked.animationResources.clear();
+        mPolicy->loadPointerIcon(&mLocked.pointerIcon);
+        mPolicy->loadAdditionalMouseResources(&mLocked.additionalMouseResources,
+                                              &mLocked.animationResources);
+    }
+
+    mLocked.presentationChanged = true;
     updatePointerLocked();
 }
 
-/**
- * The viewport values for deviceHeight and deviceWidth have already been adjusted for rotation,
- * so here we are getting the dimensions in the original, unrotated orientation (orientation 0).
- */
-static void getNonRotatedSize(const DisplayViewport& viewport, int32_t& width, int32_t& height) {
-    if (viewport.orientation == DISPLAY_ORIENTATION_90
-            || viewport.orientation == DISPLAY_ORIENTATION_270) {
-        width = viewport.deviceHeight;
-        height = viewport.deviceWidth;
-    } else {
-        width = viewport.deviceWidth;
-        height = viewport.deviceHeight;
-    }
-}
-
-void PointerController::setDisplayViewport(const DisplayViewport& viewport) {
+void PointerController::setDisplayViewport(int32_t width, int32_t height, int32_t orientation) {
     AutoMutex _l(mLock);
-    if (viewport == mLocked.viewport) {
-        return;
+
+    // Adjust to use the display's unrotated coordinate frame.
+    if (orientation == DISPLAY_ORIENTATION_90
+            || orientation == DISPLAY_ORIENTATION_270) {
+        int32_t temp = height;
+        height = width;
+        width = temp;
     }
 
-    const DisplayViewport oldViewport = mLocked.viewport;
-    mLocked.viewport = viewport;
-
-    int32_t oldDisplayWidth, oldDisplayHeight;
-    getNonRotatedSize(oldViewport, oldDisplayWidth, oldDisplayHeight);
-    int32_t newDisplayWidth, newDisplayHeight;
-    getNonRotatedSize(viewport, newDisplayWidth, newDisplayHeight);
-
-    // Reset cursor position to center if size or display changed.
-    if (oldViewport.displayId != viewport.displayId
-            || oldDisplayWidth != newDisplayWidth
-            || oldDisplayHeight != newDisplayHeight) {
+    if (mLocked.displayWidth != width || mLocked.displayHeight != height) {
+        mLocked.displayWidth = width;
+        mLocked.displayHeight = height;
 
         float minX, minY, maxX, maxY;
         if (getBoundsLocked(&minX, &minY, &maxX, &maxY)) {
             mLocked.pointerX = (minX + maxX) * 0.5f;
             mLocked.pointerY = (minY + maxY) * 0.5f;
-            // Reload icon resources for density may be changed.
-            loadResourcesLocked();
         } else {
             mLocked.pointerX = 0;
             mLocked.pointerY = 0;
         }
 
         fadeOutAndReleaseAllSpotsLocked();
-    } else if (oldViewport.orientation != viewport.orientation) {
+    }
+
+    if (mLocked.displayOrientation != orientation) {
         // Apply offsets to convert from the pixel top-left corner position to the pixel center.
         // This creates an invariant frame of reference that we can easily rotate when
         // taking into account that the pointer may be located at fractional pixel offsets.
@@ -399,37 +405,37 @@
         float temp;
 
         // Undo the previous rotation.
-        switch (oldViewport.orientation) {
+        switch (mLocked.displayOrientation) {
         case DISPLAY_ORIENTATION_90:
             temp = x;
-            x =  oldViewport.deviceHeight - y;
+            x = mLocked.displayWidth - y;
             y = temp;
             break;
         case DISPLAY_ORIENTATION_180:
-            x = oldViewport.deviceWidth - x;
-            y = oldViewport.deviceHeight - y;
+            x = mLocked.displayWidth - x;
+            y = mLocked.displayHeight - y;
             break;
         case DISPLAY_ORIENTATION_270:
             temp = x;
             x = y;
-            y = oldViewport.deviceWidth - temp;
+            y = mLocked.displayHeight - temp;
             break;
         }
 
         // Perform the new rotation.
-        switch (viewport.orientation) {
+        switch (orientation) {
         case DISPLAY_ORIENTATION_90:
             temp = x;
             x = y;
-            y = viewport.deviceHeight - temp;
+            y = mLocked.displayWidth - temp;
             break;
         case DISPLAY_ORIENTATION_180:
-            x = viewport.deviceWidth - x;
-            y = viewport.deviceHeight - y;
+            x = mLocked.displayWidth - x;
+            y = mLocked.displayHeight - y;
             break;
         case DISPLAY_ORIENTATION_270:
             temp = x;
-            x = viewport.deviceWidth - y;
+            x = mLocked.displayHeight - y;
             y = temp;
             break;
         }
@@ -438,6 +444,7 @@
         // and save the results.
         mLocked.pointerX = x - 0.5f;
         mLocked.pointerY = y - 0.5f;
+        mLocked.displayOrientation = orientation;
     }
 
     updatePointerLocked();
@@ -607,16 +614,11 @@
     mLooper->removeMessages(mHandler, MSG_INACTIVITY_TIMEOUT);
 }
 
-void PointerController::updatePointerLocked() REQUIRES(mLock) {
-    if (!mLocked.viewport.isValid()) {
-        return;
-    }
-
+void PointerController::updatePointerLocked() {
     mSpriteController->openTransaction();
 
     mLocked.pointerSprite->setLayer(Sprite::BASE_LAYER_POINTER);
     mLocked.pointerSprite->setPosition(mLocked.pointerX, mLocked.pointerY);
-    mLocked.pointerSprite->setDisplayId(mLocked.viewport.displayId);
 
     if (mLocked.pointerAlpha > 0) {
         mLocked.pointerSprite->setAlpha(mLocked.pointerAlpha);
@@ -727,18 +729,8 @@
     }
 }
 
-void PointerController::loadResourcesLocked() REQUIRES(mLock) {
+void PointerController::loadResources() {
     mPolicy->loadPointerResources(&mResources);
-
-    if (mLocked.presentation == PRESENTATION_POINTER) {
-        mLocked.additionalMouseResources.clear();
-        mLocked.animationResources.clear();
-        mPolicy->loadPointerIcon(&mLocked.pointerIcon);
-        mPolicy->loadAdditionalMouseResources(&mLocked.additionalMouseResources,
-                                              &mLocked.animationResources);
-    }
-
-    mLocked.pointerIconChanged = true;
 }
 
 
diff --git a/libs/input/PointerController.h b/libs/input/PointerController.h
index a32cc42..7f4e5a5 100644
--- a/libs/input/PointerController.h
+++ b/libs/input/PointerController.h
@@ -23,7 +23,6 @@
 #include <vector>
 
 #include <ui/DisplayInfo.h>
-#include <input/DisplayViewport.h>
 #include <input/Input.h>
 #include <PointerControllerInterface.h>
 #include <utils/BitSet.h>
@@ -97,7 +96,6 @@
     virtual int32_t getButtonState() const;
     virtual void setPosition(float x, float y);
     virtual void getPosition(float* outX, float* outY) const;
-    virtual int32_t getDisplayId() const;
     virtual void fade(Transition transition);
     virtual void unfade(Transition transition);
 
@@ -108,7 +106,7 @@
 
     void updatePointerIcon(int32_t iconId);
     void setCustomPointerIcon(const SpriteIcon& icon);
-    void setDisplayViewport(const DisplayViewport& viewport);
+    void setDisplayViewport(int32_t width, int32_t height, int32_t orientation);
     void setInactivityTimeout(InactivityTimeout inactivityTimeout);
     void reloadPointerResources();
 
@@ -158,7 +156,9 @@
         size_t animationFrameIndex;
         nsecs_t lastFrameUpdatedTime;
 
-        DisplayViewport viewport;
+        int32_t displayWidth;
+        int32_t displayHeight;
+        int32_t displayOrientation;
 
         InactivityTimeout inactivityTimeout;
 
@@ -182,7 +182,7 @@
 
         Vector<Spot*> spots;
         Vector<sp<Sprite> > recycledSprites;
-    } mLocked GUARDED_BY(mLock);
+    } mLocked;
 
     bool getBoundsLocked(float* outMinX, float* outMinY, float* outMaxX, float* outMaxY) const;
     void setPositionLocked(float x, float y);
@@ -207,7 +207,7 @@
     void fadeOutAndReleaseSpotLocked(Spot* spot);
     void fadeOutAndReleaseAllSpotsLocked();
 
-    void loadResourcesLocked();
+    void loadResources();
 };
 
 } // namespace android
diff --git a/libs/input/SpriteController.cpp b/libs/input/SpriteController.cpp
index c1868d3..eb2bc98 100644
--- a/libs/input/SpriteController.cpp
+++ b/libs/input/SpriteController.cpp
@@ -144,16 +144,13 @@
         }
     }
 
-    // Resize and/or reparent sprites if needed.
+    // Resize sprites if needed.
     SurfaceComposerClient::Transaction t;
     bool needApplyTransaction = false;
     for (size_t i = 0; i < numSprites; i++) {
         SpriteUpdate& update = updates.editItemAt(i);
-        if (update.state.surfaceControl == nullptr) {
-            continue;
-        }
 
-        if (update.state.wantSurfaceVisible()) {
+        if (update.state.surfaceControl != NULL && update.state.wantSurfaceVisible()) {
             int32_t desiredWidth = update.state.icon.bitmap.width();
             int32_t desiredHeight = update.state.icon.bitmap.height();
             if (update.state.surfaceWidth < desiredWidth
@@ -173,12 +170,6 @@
                 }
             }
         }
-
-        // If surface is a new one, we have to set right layer stack.
-        if (update.surfaceChanged || update.state.dirty & DIRTY_DISPLAY_ID) {
-            t.setLayerStack(update.state.surfaceControl, update.state.displayId);
-            needApplyTransaction = true;
-        }
     }
     if (needApplyTransaction) {
         t.apply();
@@ -245,7 +236,7 @@
         if (update.state.surfaceControl != NULL && (becomingVisible || becomingHidden
                 || (wantSurfaceVisibleAndDrawn && (update.state.dirty & (DIRTY_ALPHA
                         | DIRTY_POSITION | DIRTY_TRANSFORMATION_MATRIX | DIRTY_LAYER
-                        | DIRTY_VISIBILITY | DIRTY_HOTSPOT | DIRTY_DISPLAY_ID))))) {
+                        | DIRTY_VISIBILITY | DIRTY_HOTSPOT))))) {
             needApplyTransaction = true;
 
             if (wantSurfaceVisibleAndDrawn
@@ -454,15 +445,6 @@
     }
 }
 
-void SpriteController::SpriteImpl::setDisplayId(int32_t displayId) {
-    AutoMutex _l(mController->mLock);
-
-    if (mLocked.state.displayId != displayId) {
-        mLocked.state.displayId = displayId;
-        invalidateLocked(DIRTY_DISPLAY_ID);
-    }
-}
-
 void SpriteController::SpriteImpl::invalidateLocked(uint32_t dirty) {
     bool wasDirty = mLocked.state.dirty;
     mLocked.state.dirty |= dirty;
diff --git a/libs/input/SpriteController.h b/libs/input/SpriteController.h
index 5b216f5..31e43e9 100644
--- a/libs/input/SpriteController.h
+++ b/libs/input/SpriteController.h
@@ -125,9 +125,6 @@
 
     /* Sets the sprite transformation matrix. */
     virtual void setTransformationMatrix(const SpriteTransformationMatrix& matrix) = 0;
-
-    /* Sets the id of the display where the sprite should be shown. */
-    virtual void setDisplayId(int32_t displayId) = 0;
 };
 
 /*
@@ -173,7 +170,6 @@
         DIRTY_LAYER = 1 << 4,
         DIRTY_VISIBILITY = 1 << 5,
         DIRTY_HOTSPOT = 1 << 6,
-        DIRTY_DISPLAY_ID = 1 << 7,
     };
 
     /* Describes the state of a sprite.
@@ -184,7 +180,7 @@
     struct SpriteState {
         inline SpriteState() :
                 dirty(0), visible(false),
-                positionX(0), positionY(0), layer(0), alpha(1.0f), displayId(ADISPLAY_ID_DEFAULT),
+                positionX(0), positionY(0), layer(0), alpha(1.0f),
                 surfaceWidth(0), surfaceHeight(0), surfaceDrawn(false), surfaceVisible(false) {
         }
 
@@ -197,7 +193,6 @@
         int32_t layer;
         float alpha;
         SpriteTransformationMatrix transformationMatrix;
-        int32_t displayId;
 
         sp<SurfaceControl> surfaceControl;
         int32_t surfaceWidth;
@@ -230,7 +225,6 @@
         virtual void setLayer(int32_t layer);
         virtual void setAlpha(float alpha);
         virtual void setTransformationMatrix(const SpriteTransformationMatrix& matrix);
-        virtual void setDisplayId(int32_t displayId);
 
         inline const SpriteState& getStateLocked() const {
             return mLocked.state;
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index 18d36eb..0057875 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -19,7 +19,6 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.TestApi;
 import android.annotation.UnsupportedAppUsage;
 import android.app.ActivityThread;
 import android.content.ContentProvider;
@@ -1680,37 +1679,6 @@
     public native boolean isPlaying();
 
     /**
-     * Gets the current buffering management params used by the source component.
-     * Calling it only after {@code setDataSource} has been called.
-     * Each type of data source might have different set of default params.
-     *
-     * @return the current buffering management params used by the source component.
-     * @throws IllegalStateException if the internal player engine has not been
-     * initialized, or {@code setDataSource} has not been called.
-     * @hide
-     */
-    @NonNull
-    @TestApi
-    public native BufferingParams getBufferingParams();
-
-    /**
-     * Sets buffering management params.
-     * The object sets its internal BufferingParams to the input, except that the input is
-     * invalid or not supported.
-     * Call it only after {@code setDataSource} has been called.
-     * The input is a hint to MediaPlayer.
-     *
-     * @param params the buffering management params.
-     *
-     * @throws IllegalStateException if the internal player engine has not been
-     * initialized or has been released, or {@code setDataSource} has not been called.
-     * @throws IllegalArgumentException if params is invalid or not supported.
-     * @hide
-     */
-    @TestApi
-    public native void setBufferingParams(@NonNull BufferingParams params);
-
-    /**
      * Change playback speed of audio by resampling the audio.
      * <p>
      * Specifies resampling as audio mode for variable rate playback, i.e.,
diff --git a/media/java/android/media/MediaPlayer2.java b/media/java/android/media/MediaPlayer2.java
index d4b1c7f..b137ce2 100644
--- a/media/java/android/media/MediaPlayer2.java
+++ b/media/java/android/media/MediaPlayer2.java
@@ -544,32 +544,55 @@
     public native long getCurrentPosition();
 
     /**
-     * Gets the duration of the file.
+     * Gets the duration of the dsd.
      *
+     * @param dsd the descriptor of data source of which you want to get duration
      * @return the duration in milliseconds, if no duration is available
      *         (for example, if streaming live content), -1 is returned.
+     * @throws NullPointerException if dsd is null
      */
-    public native long getDuration();
+    public long getDuration(@NonNull DataSourceDesc dsd) {
+        if (dsd == null) {
+            throw new NullPointerException("non-null dsd is expected");
+        }
+        SourceInfo sourceInfo = getSourceInfo(dsd);
+        if (sourceInfo == null) {
+            return -1;
+        }
+
+        return native_getDuration(sourceInfo.mId);
+    }
+
+    private native long native_getDuration(long srcId);
 
     /**
-     * Gets the current buffered media source position received through progressive downloading.
+     * Gets the buffered media source position of given dsd.
      * For example a buffering update of 8000 milliseconds when 5000 milliseconds of the content
      * has already been played indicates that the next 3000 milliseconds of the
      * content to play has been buffered.
      *
+     * @param dsd the descriptor of data source of which you want to get buffered position
      * @return the current buffered media source position in milliseconds
+     * @throws NullPointerException if dsd is null
      */
-    public long getBufferedPosition() {
-        // Use cached buffered percent for now.
-        int bufferedPercentage;
-        synchronized (mSrcLock) {
-            if (mCurrentSourceInfo == null) {
-                bufferedPercentage = 0;
-            } else {
-                bufferedPercentage = mCurrentSourceInfo.mBufferedPercentage.get();
-            }
+    public long getBufferedPosition(@NonNull DataSourceDesc dsd) {
+        if (dsd == null) {
+            throw new NullPointerException("non-null dsd is expected");
         }
-        return getDuration() * bufferedPercentage / 100;
+        SourceInfo sourceInfo = getSourceInfo(dsd);
+        if (sourceInfo == null) {
+            return 0;
+        }
+
+        // Use cached buffered percent for now.
+        int bufferedPercentage = sourceInfo.mBufferedPercentage.get();
+
+        long duration = getDuration(dsd);
+        if (duration < 0) {
+            duration = 0;
+        }
+
+        return duration * bufferedPercentage / 100;
     }
 
     /**
@@ -1467,7 +1490,6 @@
 
     private native PersistableBundle native_getMetrics();
 
-
     /**
      * Gets the current buffering management params used by the source component.
      * Calling it only after {@code setDataSource} has been called.
@@ -1505,7 +1527,6 @@
 
     private native void native_setBufferingParams(@NonNull BufferingParams params);
 
-
     /**
      * Sets playback rate using {@link PlaybackParams}. The object sets its internal
      * PlaybackParams to the input. This allows the object to resume at previous speed
@@ -1969,19 +1990,31 @@
     /**
      * Returns a List of track information.
      *
+     * @param dsd the descriptor of data source of which you want to get track info
      * @return List of track info. The total number of tracks is the array length.
      * Must be called again if an external timed text source has been added after
      * addTimedTextSource method is called.
      * @throws IllegalStateException if it is called in an invalid state.
+     * @throws NullPointerException if dsd is null
      */
-    public @NonNull List<TrackInfo> getTrackInfo() {
-        TrackInfo[] trackInfo = getInbandTrackInfo();
+
+    public @NonNull List<TrackInfo> getTrackInfo(@NonNull DataSourceDesc dsd) {
+        if (dsd == null) {
+            throw new NullPointerException("non-null dsd is expected");
+        }
+        SourceInfo sourceInfo = getSourceInfo(dsd);
+        if (sourceInfo == null) {
+            return new ArrayList<TrackInfo>(0);
+        }
+
+        TrackInfo[] trackInfo = getInbandTrackInfo(sourceInfo);
         return (trackInfo != null ? Arrays.asList(trackInfo) : new ArrayList<TrackInfo>(0));
     }
 
-    private TrackInfo[] getInbandTrackInfo() throws IllegalStateException {
+    private TrackInfo[] getInbandTrackInfo(SourceInfo sourceInfo) throws IllegalStateException {
         PlayerMessage request = PlayerMessage.newBuilder()
                 .addValues(Value.newBuilder().setInt32Value(INVOKE_ID_GET_TRACK_INFO))
+                .addValues(Value.newBuilder().setInt64Value(sourceInfo.mId))
                 .build();
         PlayerMessage response = invoke(request);
         if (response == null) {
@@ -2001,9 +2034,10 @@
 
     /**
      * Returns the index of the audio, video, or subtitle track currently selected for playback,
-     * The return value is an index into the array returned by {@link #getTrackInfo()}, and can
-     * be used in calls to {@link #selectTrack(int)} or {@link #deselectTrack(int)}.
+     * The return value is an index into the array returned by {@link #getTrackInfo}, and can
+     * be used in calls to {@link #selectTrack} or {@link #deselectTrack}.
      *
+     * @param dsd the descriptor of data source of which you want to get selected track
      * @param trackType should be one of {@link TrackInfo#MEDIA_TRACK_TYPE_VIDEO},
      * {@link TrackInfo#MEDIA_TRACK_TYPE_AUDIO}, or
      * {@link TrackInfo#MEDIA_TRACK_TYPE_SUBTITLE}
@@ -2011,14 +2045,24 @@
      * a negative integer is returned when there is no selected track for {@code trackType} or
      * when {@code trackType} is not one of audio, video, or subtitle.
      * @throws IllegalStateException if called after {@link #close()}
+     * @throws NullPointerException if dsd is null
      *
-     * @see #getTrackInfo()
-     * @see #selectTrack(int)
-     * @see #deselectTrack(int)
+     * @see #getTrackInfo
+     * @see #selectTrack
+     * @see #deselectTrack
      */
-    public int getSelectedTrack(int trackType) {
+    public int getSelectedTrack(@NonNull DataSourceDesc dsd, int trackType) {
+        if (dsd == null) {
+            throw new NullPointerException("non-null dsd is expected");
+        }
+        SourceInfo sourceInfo = getSourceInfo(dsd);
+        if (sourceInfo == null) {
+            return -1;
+        }
+
         PlayerMessage request = PlayerMessage.newBuilder()
                 .addValues(Value.newBuilder().setInt32Value(INVOKE_ID_GET_SELECTED_TRACK))
+                .addValues(Value.newBuilder().setInt64Value(sourceInfo.mId))
                 .addValues(Value.newBuilder().setInt32Value(trackType))
                 .build();
         PlayerMessage response = invoke(request);
@@ -2049,19 +2093,20 @@
      * In addition, the support for selecting an audio track at runtime is pretty limited
      * in that an audio track can only be selected in the <em>Prepared</em> state.
      * </p>
+     * @param dsd the descriptor of data source of which you want to select track
      * @param index the index of the track to be selected. The valid range of the index
      * is 0..total number of track - 1. The total number of tracks as well as the type of
-     * each individual track can be found by calling {@link #getTrackInfo()} method.
+     * each individual track can be found by calling {@link #getTrackInfo} method.
      * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
      *
      * @see MediaPlayer2#getTrackInfo
      */
     // This is an asynchronous call.
-    public Object selectTrack(int index) {
+    public Object selectTrack(@NonNull DataSourceDesc dsd, int index) {
         return addTask(new Task(CALL_COMPLETED_SELECT_TRACK, false) {
             @Override
             void process() {
-                selectOrDeselectTrack(index, true /* select */);
+                selectOrDeselectTrack(dsd, index, true /* select */);
             }
         });
     }
@@ -2073,28 +2118,37 @@
      * deselected. If the timed text track identified by index has not been
      * selected before, it throws an exception.
      * </p>
+     * @param dsd the descriptor of data source of which you want to deselect track
      * @param index the index of the track to be deselected. The valid range of the index
      * is 0..total number of tracks - 1. The total number of tracks as well as the type of
-     * each individual track can be found by calling {@link #getTrackInfo()} method.
+     * each individual track can be found by calling {@link #getTrackInfo} method.
      * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
      *
      * @see MediaPlayer2#getTrackInfo
      */
     // This is an asynchronous call.
-    public Object deselectTrack(int index) {
+    public Object deselectTrack(@NonNull DataSourceDesc dsd, int index) {
         return addTask(new Task(CALL_COMPLETED_DESELECT_TRACK, false) {
             @Override
             void process() {
-                selectOrDeselectTrack(index, false /* select */);
+                selectOrDeselectTrack(dsd, index, false /* select */);
             }
         });
     }
 
-    private void selectOrDeselectTrack(int index, boolean select)
-            throws IllegalStateException {
+    private void selectOrDeselectTrack(@NonNull DataSourceDesc dsd, int index, boolean select) {
+        if (dsd == null) {
+            throw new IllegalArgumentException("non-null dsd is expected");
+        }
+        SourceInfo sourceInfo = getSourceInfo(dsd);
+        if (sourceInfo == null) {
+            return;
+        }
+
         PlayerMessage request = PlayerMessage.newBuilder()
                 .addValues(Value.newBuilder().setInt32Value(
                             select ? INVOKE_ID_SELECT_TRACK : INVOKE_ID_DESELECT_TRACK))
+                .addValues(Value.newBuilder().setInt64Value(sourceInfo.mId))
                 .addValues(Value.newBuilder().setInt32Value(index))
                 .build();
         invoke(request);
@@ -2568,7 +2622,7 @@
          * Currently only HTTP live streaming data URI's embedded with timed ID3 tags generates
          * {@link TimedMetaData}.
          *
-         * @see MediaPlayer2#selectTrack(int)
+         * @see MediaPlayer2#selectTrack
          * @see MediaPlayer2.OnTimedMetaDataAvailableListener
          * @see TimedMetaData
          *
diff --git a/media/java/android/media/MediaPlayerBase.java b/media/java/android/media/MediaPlayerBase.java
deleted file mode 100644
index a426552..0000000
--- a/media/java/android/media/MediaPlayerBase.java
+++ /dev/null
@@ -1,331 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.media;
-
-import android.annotation.IntDef;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.List;
-import java.util.concurrent.Executor;
-
-/**
- * @hide
- * Base class for all media players that want media session.
- */
-public abstract class MediaPlayerBase implements AutoCloseable {
-    /**
-     * @hide
-     */
-    @IntDef({
-        PLAYER_STATE_IDLE,
-        PLAYER_STATE_PAUSED,
-        PLAYER_STATE_PLAYING,
-        PLAYER_STATE_ERROR })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface PlayerState {}
-
-    /**
-     * @hide
-     */
-    @IntDef({
-        BUFFERING_STATE_UNKNOWN,
-        BUFFERING_STATE_BUFFERING_AND_PLAYABLE,
-        BUFFERING_STATE_BUFFERING_AND_STARVED,
-        BUFFERING_STATE_BUFFERING_COMPLETE })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface BuffState {}
-
-    /**
-     * State when the player is idle, and needs configuration to start playback.
-     */
-    public static final int PLAYER_STATE_IDLE = 0;
-
-    /**
-     * State when the player's playback is paused
-     */
-    public static final int PLAYER_STATE_PAUSED = 1;
-
-    /**
-     * State when the player's playback is ongoing
-     */
-    public static final int PLAYER_STATE_PLAYING = 2;
-
-    /**
-     * State when the player is in error state and cannot be recovered self.
-     */
-    public static final int PLAYER_STATE_ERROR = 3;
-
-    /**
-     * Buffering state is unknown.
-     */
-    public static final int BUFFERING_STATE_UNKNOWN = 0;
-
-    /**
-     * Buffering state indicating the player is buffering but enough has been buffered
-     * for this player to be able to play the content.
-     * See {@link #getBufferedPosition()} for how far is buffered already.
-     */
-    public static final int BUFFERING_STATE_BUFFERING_AND_PLAYABLE = 1;
-
-    /**
-     * Buffering state indicating the player is buffering, but the player is currently starved
-     * for data, and cannot play.
-     */
-    public static final int BUFFERING_STATE_BUFFERING_AND_STARVED = 2;
-
-    /**
-     * Buffering state indicating the player is done buffering, and the remainder of the content is
-     * available for playback.
-     */
-    public static final int BUFFERING_STATE_BUFFERING_COMPLETE = 3;
-
-    /**
-     * Starts or resumes playback.
-     */
-    public abstract void play();
-
-    /**
-     * Prepares the player for playback.
-     * See {@link PlayerEventCallback#onMediaPrepared(MediaPlayerBase, DataSourceDesc)} for being
-     * notified when the preparation phase completed. During this time, the player may allocate
-     * resources required to play, such as audio and video decoders.
-     */
-    public abstract void prepare();
-
-    /**
-     * Pauses playback.
-     */
-    public abstract void pause();
-
-    /**
-     * Resets the MediaPlayerBase to its uninitialized state.
-     */
-    public abstract void reset();
-
-    /**
-     *
-     */
-    public abstract void skipToNext();
-
-    /**
-     * Moves the playback head to the specified position
-     * @param pos the new playback position expressed in ms.
-     */
-    public abstract void seekTo(long pos);
-
-    public static final long UNKNOWN_TIME = -1;
-
-    /**
-     * Gets the current playback head position.
-     * @return the current playback position in ms, or {@link #UNKNOWN_TIME} if unknown.
-     */
-    public long getCurrentPosition() { return UNKNOWN_TIME; }
-
-    /**
-     * Returns the duration of the current data source, or {@link #UNKNOWN_TIME} if unknown.
-     * @return the duration in ms, or {@link #UNKNOWN_TIME}.
-     */
-    public long getDuration() { return UNKNOWN_TIME; }
-
-    /**
-     * Gets the buffered position of current playback, or {@link #UNKNOWN_TIME} if unknown.
-     * @return the buffered position in ms, or {@link #UNKNOWN_TIME}.
-     */
-    public long getBufferedPosition() { return UNKNOWN_TIME; }
-
-    /**
-     * Returns the current player state.
-     * See also {@link PlayerEventCallback#onPlayerStateChanged(MediaPlayerBase, int)} for
-     * notification of changes.
-     * @return the current player state
-     */
-    public abstract @PlayerState int getPlayerState();
-
-    /**
-     * Returns the current buffering state of the player.
-     * During buffering, see {@link #getBufferedPosition()} for the quantifying the amount already
-     * buffered.
-     * @return the buffering state.
-     */
-    public abstract @BuffState int getBufferingState();
-
-    /**
-     * Sets the {@link AudioAttributes} to be used during the playback of the media.
-     *
-     * @param attributes non-null <code>AudioAttributes</code>.
-     */
-    public abstract void setAudioAttributes(@NonNull AudioAttributes attributes);
-
-    /**
-     * Returns AudioAttributes that media player has.
-     */
-    public abstract @Nullable AudioAttributes getAudioAttributes();
-
-    /**
-     * Sets the data source to be played.
-     * @param dsd
-     */
-    public abstract void setDataSource(@NonNull DataSourceDesc dsd);
-
-    /**
-     * Sets the data source that will be played immediately after the current one is done playing.
-     * @param dsd
-     */
-    public abstract void setNextDataSource(@NonNull DataSourceDesc dsd);
-
-    /**
-     * Sets the list of data sources that will be sequentially played after the current one. Each
-     * data source is played immediately after the previous one is done playing.
-     * @param dsds
-     */
-    public abstract void setNextDataSources(@NonNull List<DataSourceDesc> dsds);
-
-    /**
-     * Returns the current data source.
-     * @return the current data source, or null if none is set, or none available to play.
-     */
-    public abstract @Nullable DataSourceDesc getCurrentDataSource();
-
-    /**
-     * Configures the player to loop on the current data source.
-     * @param loop true if the current data source is meant to loop.
-     */
-    public abstract void loopCurrent(boolean loop);
-
-    /**
-     * Sets the playback speed.
-     * A value of 1.0f is the default playback value.
-     * A negative value indicates reverse playback, check {@link #isReversePlaybackSupported()}
-     * before using negative values.<br>
-     * After changing the playback speed, it is recommended to query the actual speed supported
-     * by the player, see {@link #getPlaybackSpeed()}.
-     * @param speed
-     */
-    public abstract void setPlaybackSpeed(float speed);
-
-    /**
-     * Returns the actual playback speed to be used by the player when playing.
-     * Note that it may differ from the speed set in {@link #setPlaybackSpeed(float)}.
-     * @return the actual playback speed
-     */
-    public float getPlaybackSpeed() { return 1.0f; }
-
-    /**
-     * Indicates whether reverse playback is supported.
-     * Reverse playback is indicated by negative playback speeds, see
-     * {@link #setPlaybackSpeed(float)}.
-     * @return true if reverse playback is supported.
-     */
-    public boolean isReversePlaybackSupported() { return false; }
-
-    /**
-     * Sets the volume of the audio of the media to play, expressed as a linear multiplier
-     * on the audio samples.
-     * Note that this volume is specific to the player, and is separate from stream volume
-     * used across the platform.<br>
-     * A value of 0.0f indicates muting, a value of 1.0f is the nominal unattenuated and unamplified
-     * gain. See {@link #getMaxPlayerVolume()} for the volume range supported by this player.
-     * @param volume a value between 0.0f and {@link #getMaxPlayerVolume()}.
-     */
-    public abstract void setPlayerVolume(float volume);
-
-    /**
-     * Returns the current volume of this player to this player.
-     * Note that it does not take into account the associated stream volume.
-     * @return the player volume.
-     */
-    public abstract float getPlayerVolume();
-
-    /**
-     * @return the maximum volume that can be used in {@link #setPlayerVolume(float)}.
-     */
-    public float getMaxPlayerVolume() { return 1.0f; }
-
-    /**
-     * Adds a callback to be notified of events for this player.
-     * @param e the {@link Executor} to be used for the events.
-     * @param cb the callback to receive the events.
-     */
-    public abstract void registerPlayerEventCallback(@NonNull Executor e,
-            @NonNull PlayerEventCallback cb);
-
-    /**
-     * Removes a previously registered callback for player events
-     * @param cb the callback to remove
-     */
-    public abstract void unregisterPlayerEventCallback(@NonNull PlayerEventCallback cb);
-
-    /**
-     * A callback class to receive notifications for events on the media player.
-     * See {@link MediaPlayerBase#registerPlayerEventCallback(Executor, PlayerEventCallback)} to
-     * register this callback.
-     */
-    public static abstract class PlayerEventCallback {
-        /**
-         * Called when the player's current data source has changed.
-         *
-         * @param mpb the player whose data source changed.
-         * @param dsd the new current data source. null, if no more data sources available.
-         */
-        public void onCurrentDataSourceChanged(@NonNull MediaPlayerBase mpb,
-                @Nullable DataSourceDesc dsd) { }
-        /**
-         * Called when the player is <i>prepared</i>, i.e. it is ready to play the content
-         * referenced by the given data source.
-         * @param mpb the player that is prepared.
-         * @param dsd the data source that the player is prepared to play.
-         */
-        public void onMediaPrepared(@NonNull MediaPlayerBase mpb, @NonNull DataSourceDesc dsd) { }
-
-        /**
-         * Called to indicate that the state of the player has changed.
-         * See {@link MediaPlayerBase#getPlayerState()} for polling the player state.
-         * @param mpb the player whose state has changed.
-         * @param state the new state of the player.
-         */
-        public void onPlayerStateChanged(@NonNull MediaPlayerBase mpb, @PlayerState int state) { }
-
-        /**
-         * Called to report buffering events for a data source.
-         * @param mpb the player that is buffering
-         * @param dsd the data source for which buffering is happening.
-         * @param state the new buffering state.
-         */
-        public void onBufferingStateChanged(@NonNull MediaPlayerBase mpb,
-                @NonNull DataSourceDesc dsd, @BuffState int state) { }
-
-        /**
-         * Called to indicate that the playback speed has changed.
-         * @param mpb the player that has changed the playback speed.
-         * @param speed the new playback speed.
-         */
-        public void onPlaybackSpeedChanged(@NonNull MediaPlayerBase mpb, float speed) { }
-
-        /**
-         * Called to indicate that {@link #seekTo(long)} is completed.
-         *
-         * @param mpb the player that has completed seeking.
-         * @param position the previous seeking request.
-         * @see #seekTo(long)
-         */
-        public void onSeekCompleted(@NonNull MediaPlayerBase mpb, long position) { }
-    }
-
-}
diff --git a/media/jni/Android.bp b/media/jni/Android.bp
index e25e6a5..7481fff 100644
--- a/media/jni/Android.bp
+++ b/media/jni/Android.bp
@@ -97,9 +97,10 @@
     shared_libs: [
         "android.hardware.cas@1.0",  // for CasManager. VNDK???
         "android.hardware.cas.native@1.0",  // CasManager. VNDK???
+        "android.hidl.allocator@1.0",
+        "libhidlmemory",
         "libbinder",
         "libgui",  // for VideoFrameScheduler
-        "libhidlallocatorutils",
         "libhidlbase",  // VNDK???
         "libpowermanager",  // for JWakeLock. to be removed
 
diff --git a/media/jni/android_media_MediaPlayer.cpp b/media/jni/android_media_MediaPlayer.cpp
index 5dd01b0..76bbce7 100644
--- a/media/jni/android_media_MediaPlayer.cpp
+++ b/media/jni/android_media_MediaPlayer.cpp
@@ -39,7 +39,6 @@
 #include "utils/Errors.h"  // for status_t
 #include "utils/KeyedVector.h"
 #include "utils/String8.h"
-#include "android_media_BufferingParams.h"
 #include "android_media_MediaDataSource.h"
 #include "android_media_MediaMetricsJNI.h"
 #include "android_media_PlaybackParams.h"
@@ -94,7 +93,6 @@
 };
 static fields_t fields;
 
-static BufferingParams::fields_t gBufferingParamsFields;
 static PlaybackParams::fields_t gPlaybackParamsFields;
 static SyncParams::fields_t gSyncParamsFields;
 static VolumeShaperHelper::fields_t gVolumeShaperFields;
@@ -370,50 +368,6 @@
     setVideoSurface(env, thiz, jsurface, true /* mediaPlayerMustBeAlive */);
 }
 
-static jobject
-android_media_MediaPlayer_getBufferingParams(JNIEnv *env, jobject thiz)
-{
-    sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
-    if (mp == NULL) {
-        jniThrowException(env, "java/lang/IllegalStateException", NULL);
-        return NULL;
-    }
-
-    BufferingParams bp;
-    BufferingSettings &settings = bp.settings;
-    process_media_player_call(
-            env, thiz, mp->getBufferingSettings(&settings),
-            "java/lang/IllegalStateException", "unexpected error");
-    if (env->ExceptionCheck()) {
-        return nullptr;
-    }
-    ALOGV("getBufferingSettings:{%s}", settings.toString().string());
-
-    return bp.asJobject(env, gBufferingParamsFields);
-}
-
-static void
-android_media_MediaPlayer_setBufferingParams(JNIEnv *env, jobject thiz, jobject params)
-{
-    if (params == NULL) {
-        return;
-    }
-
-    sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
-    if (mp == NULL) {
-        jniThrowException(env, "java/lang/IllegalStateException", NULL);
-        return;
-    }
-
-    BufferingParams bp;
-    bp.fillFromJobject(env, gBufferingParamsFields, params);
-    ALOGV("setBufferingParams:{%s}", bp.settings.toString().string());
-
-    process_media_player_call(
-            env, thiz, mp->setBufferingSettings(bp.settings),
-            "java/lang/IllegalStateException", "unexpected error");
-}
-
 static void
 android_media_MediaPlayer_prepare(JNIEnv *env, jobject thiz)
 {
@@ -976,8 +930,6 @@
 
     env->DeleteLocalRef(clazz);
 
-    gBufferingParamsFields.init(env);
-
     // Modular DRM
     FIND_CLASS(clazz, "android/media/MediaDrm$MediaDrmStateException");
     if (clazz) {
@@ -1426,8 +1378,6 @@
     {"_setDataSource",      "(Ljava/io/FileDescriptor;JJ)V",    (void *)android_media_MediaPlayer_setDataSourceFD},
     {"_setDataSource",      "(Landroid/media/MediaDataSource;)V",(void *)android_media_MediaPlayer_setDataSourceCallback },
     {"_setVideoSurface",    "(Landroid/view/Surface;)V",        (void *)android_media_MediaPlayer_setVideoSurface},
-    {"getBufferingParams", "()Landroid/media/BufferingParams;", (void *)android_media_MediaPlayer_getBufferingParams},
-    {"setBufferingParams", "(Landroid/media/BufferingParams;)V", (void *)android_media_MediaPlayer_setBufferingParams},
     {"_prepare",            "()V",                              (void *)android_media_MediaPlayer_prepare},
     {"prepareAsync",        "()V",                              (void *)android_media_MediaPlayer_prepareAsync},
     {"_start",              "()V",                              (void *)android_media_MediaPlayer_start},
diff --git a/media/jni/android_media_MediaPlayer2.cpp b/media/jni/android_media_MediaPlayer2.cpp
index 8b6009e..7e6a8ab 100644
--- a/media/jni/android_media_MediaPlayer2.cpp
+++ b/media/jni/android_media_MediaPlayer2.cpp
@@ -820,7 +820,7 @@
 }
 
 static jlong
-android_media_MediaPlayer2_getDuration(JNIEnv *env, jobject thiz)
+android_media_MediaPlayer2_getDuration(JNIEnv *env, jobject thiz, jlong srcId)
 {
     sp<MediaPlayer2> mp = getMediaPlayer(env, thiz);
     if (mp == NULL ) {
@@ -828,7 +828,7 @@
         return 0;
     }
     int64_t msec;
-    process_media_player_call( env, thiz, mp->getDuration(&msec), NULL, NULL );
+    process_media_player_call( env, thiz, mp->getDuration(srcId, &msec), NULL, NULL );
     ALOGV("getDuration: %lld (msec)", (long long)msec);
     return (jlong) msec;
 }
@@ -1408,7 +1408,7 @@
     {"native_seekTo",       "(JI)V",                            (void *)android_media_MediaPlayer2_seekTo},
     {"native_pause",        "()V",                              (void *)android_media_MediaPlayer2_pause},
     {"getCurrentPosition",  "()J",                              (void *)android_media_MediaPlayer2_getCurrentPosition},
-    {"getDuration",         "()J",                              (void *)android_media_MediaPlayer2_getDuration},
+    {"native_getDuration",  "(J)J",                             (void *)android_media_MediaPlayer2_getDuration},
     {"native_release",      "()V",                              (void *)android_media_MediaPlayer2_release},
     {"native_reset",        "()V",                              (void *)android_media_MediaPlayer2_reset},
     {"native_setAudioAttributes", "(Landroid/media/AudioAttributes;)Z", (void *)android_media_MediaPlayer2_setAudioAttributes},
diff --git a/packages/CarSystemUI/res/layout/car_fullscreen_user_pod.xml b/packages/CarSystemUI/res/layout/car_fullscreen_user_pod.xml
index 1d67286..a2a628d 100644
--- a/packages/CarSystemUI/res/layout/car_fullscreen_user_pod.xml
+++ b/packages/CarSystemUI/res/layout/car_fullscreen_user_pod.xml
@@ -25,13 +25,15 @@
     android:orientation="vertical"
     android:gravity="center">
 
-    <ImageView android:id="@+id/user_avatar"
+    <ImageView
+        android:id="@+id/user_avatar"
         android:layout_width="@dimen/car_user_switcher_image_avatar_size"
         android:layout_height="@dimen/car_user_switcher_image_avatar_size"
-        android:background="@drawable/car_button_ripple_background_light"
+        android:background="?android:attr/selectableItemBackground"
         android:gravity="center"/>
 
-    <TextView android:id="@+id/user_name"
+    <TextView
+        android:id="@+id/user_name"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:layout_marginTop="@dimen/car_user_switcher_vertical_spacing_between_name_and_avatar"
diff --git a/packages/CarSystemUI/res/layout/car_fullscreen_user_switcher.xml b/packages/CarSystemUI/res/layout/car_fullscreen_user_switcher.xml
index 6cd70d6..e8c5134cd 100644
--- a/packages/CarSystemUI/res/layout/car_fullscreen_user_switcher.xml
+++ b/packages/CarSystemUI/res/layout/car_fullscreen_user_switcher.xml
@@ -19,12 +19,10 @@
         android:fitsSystemWindows="true"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
-        android:background="@color/car_user_switcher_background_color"
         android:visibility="gone">
 
     <LinearLayout
         android:id="@+id/container"
-        android:background="@color/car_user_switcher_background_color"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:orientation="vertical">
@@ -38,7 +36,7 @@
             android:layout_width="match_parent"
             android:layout_height="match_parent"
             android:layout_marginTop="@dimen/car_user_switcher_margin_top"
-            android:theme="@style/Theme.Car.Light.List"
+            android:theme="@style/PagedListTheme"
             app:verticallyCenterListContent="true"
             app:showPagedListViewDivider="false"
             app:gutter="both"
diff --git a/packages/CarSystemUI/res/layout/car_left_navigation_bar.xml b/packages/CarSystemUI/res/layout/car_left_navigation_bar.xml
index 141b28a..72ec8d8 100644
--- a/packages/CarSystemUI/res/layout/car_left_navigation_bar.xml
+++ b/packages/CarSystemUI/res/layout/car_left_navigation_bar.xml
@@ -68,7 +68,7 @@
         android:orientation="vertical">
 
         <com.android.keyguard.AlphaOptimizedImageButton
-            android:id="@+id/notifications"
+            android:id="@+id/note"
             android:layout_height="wrap_content"
             android:layout_width="match_parent"
             android:src="@drawable/car_ic_notification"
diff --git a/packages/CarSystemUI/res/layout/car_navigation_bar.xml b/packages/CarSystemUI/res/layout/car_navigation_bar.xml
index b67ce15..052566d 100644
--- a/packages/CarSystemUI/res/layout/car_navigation_bar.xml
+++ b/packages/CarSystemUI/res/layout/car_navigation_bar.xml
@@ -20,7 +20,7 @@
     xmlns:systemui="http://schemas.android.com/apk/res-auto"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    android:background="@android:color/black"
+    android:background="@drawable/system_bar_background"
     android:orientation="vertical">
     <LinearLayout
         android:id="@id/nav_buttons"
diff --git a/packages/CarSystemUI/res/layout/car_navigation_bar_unprovisioned.xml b/packages/CarSystemUI/res/layout/car_navigation_bar_unprovisioned.xml
index 46e60db..4fa877f 100644
--- a/packages/CarSystemUI/res/layout/car_navigation_bar_unprovisioned.xml
+++ b/packages/CarSystemUI/res/layout/car_navigation_bar_unprovisioned.xml
@@ -20,7 +20,7 @@
     xmlns:systemui="http://schemas.android.com/apk/res-auto"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    android:background="@android:color/black"
+    android:background="@drawable/system_bar_background"
     android:orientation="vertical">
 
     <LinearLayout
diff --git a/packages/CarSystemUI/res/layout/car_qs_footer.xml b/packages/CarSystemUI/res/layout/car_qs_footer.xml
index 6f19cfc..bf96c00 100644
--- a/packages/CarSystemUI/res/layout/car_qs_footer.xml
+++ b/packages/CarSystemUI/res/layout/car_qs_footer.xml
@@ -35,7 +35,7 @@
         android:layout_centerVertical="true"
         android:layout_width="@dimen/car_qs_footer_icon_width"
         android:layout_height="@dimen/car_qs_footer_icon_height"
-        android:background="@drawable/ripple_drawable"
+        android:background="?android:attr/selectableItemBackground"
         android:focusable="true">
 
         <ImageView
diff --git a/packages/CarSystemUI/res/layout/car_qs_panel.xml b/packages/CarSystemUI/res/layout/car_qs_panel.xml
index dfa48c3..d923e0f 100644
--- a/packages/CarSystemUI/res/layout/car_qs_panel.xml
+++ b/packages/CarSystemUI/res/layout/car_qs_panel.xml
@@ -21,8 +21,7 @@
     android:layout_height="wrap_content"
     android:background="@color/car_qs_background_primary"
     android:orientation="vertical"
-    android:elevation="4dp"
-    android:theme="@android:style/Theme">
+    android:elevation="4dp">
 
     <include layout="@layout/car_status_bar_header"/>
     <include layout="@layout/car_qs_footer"/>
@@ -39,7 +38,7 @@
             android:id="@+id/user_grid"
             android:layout_width="match_parent"
             android:layout_height="match_parent"
-            android:theme="@style/Theme.Car.Light.List"
+            android:theme="@style/PagedListTheme"
             app:showPagedListViewDivider="false"
             app:gutter="both"
             app:itemSpacing="@dimen/car_user_switcher_vertical_spacing_between_users"/>
diff --git a/packages/CarSystemUI/res/layout/car_right_navigation_bar.xml b/packages/CarSystemUI/res/layout/car_right_navigation_bar.xml
index 141b28a..72ec8d8 100644
--- a/packages/CarSystemUI/res/layout/car_right_navigation_bar.xml
+++ b/packages/CarSystemUI/res/layout/car_right_navigation_bar.xml
@@ -68,7 +68,7 @@
         android:orientation="vertical">
 
         <com.android.keyguard.AlphaOptimizedImageButton
-            android:id="@+id/notifications"
+            android:id="@+id/note"
             android:layout_height="wrap_content"
             android:layout_width="match_parent"
             android:src="@drawable/car_ic_notification"
diff --git a/packages/CarSystemUI/res/layout/car_top_navigation_bar.xml b/packages/CarSystemUI/res/layout/car_top_navigation_bar.xml
index 7b3333e..1dca10a 100644
--- a/packages/CarSystemUI/res/layout/car_top_navigation_bar.xml
+++ b/packages/CarSystemUI/res/layout/car_top_navigation_bar.xml
@@ -21,7 +21,7 @@
     android:id="@+id/car_top_bar"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    android:background="@android:color/black"
+    android:background="@drawable/system_bar_background"
     android:orientation="vertical">
 
     <RelativeLayout
diff --git a/packages/CarSystemUI/res/values/config.xml b/packages/CarSystemUI/res/values/config.xml
index 572737f..c527711 100644
--- a/packages/CarSystemUI/res/values/config.xml
+++ b/packages/CarSystemUI/res/values/config.xml
@@ -51,7 +51,6 @@
         <item>com.android.systemui.LatencyTester</item>
         <item>com.android.systemui.globalactions.GlobalActionsComponent</item>
         <item>com.android.systemui.ScreenDecorations</item>
-        <item>com.android.systemui.fingerprint.FingerprintDialogImpl</item>
         <item>com.android.systemui.SliceBroadcastRelayHandler</item>
         <item>com.android.systemui.notifications.NotificationsUI</item>
     </string-array>
diff --git a/packages/CarSystemUI/res/values/themes.xml b/packages/CarSystemUI/res/values/themes.xml
new file mode 100644
index 0000000..8a5961e
--- /dev/null
+++ b/packages/CarSystemUI/res/values/themes.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2018, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+-->
+
+<resources>
+    <!--This Theme contains attributes required for components from the car support lib -->
+    <style name="PagedListTheme" parent="Theme.CarSupportWrapper.NoActionBar">
+    </style>
+</resources>
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/DrivingStateHelper.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/DrivingStateHelper.java
index 730c3e3..a442426 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/DrivingStateHelper.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/DrivingStateHelper.java
@@ -52,6 +52,9 @@
      * is idling or moving, {@code false} otherwise.
      */
     public boolean isCurrentlyDriving() {
+        if (mDrivingStateManager == null) {
+            return false;
+        }
         try {
             CarDrivingStateEvent currentState = mDrivingStateManager.getCurrentCarDrivingState();
             if (currentState != null) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractWifiMacAddressPreferenceController.java b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractWifiMacAddressPreferenceController.java
index 9699294..7177821 100644
--- a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractWifiMacAddressPreferenceController.java
+++ b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractWifiMacAddressPreferenceController.java
@@ -19,9 +19,7 @@
 import android.annotation.SuppressLint;
 import android.content.Context;
 import android.net.ConnectivityManager;
-import android.net.wifi.WifiInfo;
 import android.net.wifi.WifiManager;
-import android.provider.Settings;
 import android.text.TextUtils;
 
 import androidx.annotation.VisibleForTesting;
@@ -83,15 +81,13 @@
     @SuppressLint("HardwareIds")
     @Override
     protected void updateConnectivity() {
-        WifiInfo wifiInfo = mWifiManager.getConnectionInfo();
-        final int macRandomizationMode = Settings.Global.getInt(mContext.getContentResolver(),
-                Settings.Global.WIFI_CONNECTED_MAC_RANDOMIZATION_ENABLED, OFF);
-        final String macAddress = wifiInfo == null ? null : wifiInfo.getMacAddress();
+        final String[] macAddresses = mWifiManager.getFactoryMacAddresses();
+        String macAddress = null;
+        if (macAddresses != null && macAddresses.length > 0) {
+            macAddress = macAddresses[0];
+        }
 
-        if (macRandomizationMode == ON && WifiInfo.DEFAULT_MAC_ADDRESS.equals(macAddress)) {
-            mWifiMacAddress.setSummary(R.string.wifi_status_mac_randomized);
-        } else if (TextUtils.isEmpty(macAddress)
-                || WifiInfo.DEFAULT_MAC_ADDRESS.equals(macAddress)) {
+        if (TextUtils.isEmpty(macAddress)) {
             mWifiMacAddress.setSummary(R.string.status_unavailable);
         } else {
             mWifiMacAddress.setSummary(macAddress);
diff --git a/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatterySaverUtils.java b/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatterySaverUtils.java
index f7b16f8..c8c05a0 100644
--- a/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatterySaverUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatterySaverUtils.java
@@ -22,6 +22,7 @@
 import android.os.PowerManager;
 import android.provider.Settings.Global;
 import android.provider.Settings.Secure;
+import android.text.TextUtils;
 import android.util.KeyValueListParser;
 import android.util.Log;
 import android.util.Slog;
@@ -176,4 +177,22 @@
             setAutoBatterySaverTriggerLevel(context, level);
         }
     }
+
+    /**
+     * Reverts battery saver schedule mode to none if we are in a bad state where routine mode
+     * is selected but no app is configured to actually provide the signal.
+     * @param context a valid context
+     */
+    public static void revertScheduleToNoneIfNeeded(Context context) {
+        ContentResolver resolver = context.getContentResolver();
+        final int currentMode = Global.getInt(resolver, Global.AUTOMATIC_POWER_SAVER_MODE,
+                PowerManager.POWER_SAVER_MODE_PERCENTAGE);
+        boolean providerConfigured = !TextUtils.isEmpty(context.getString(
+                com.android.internal.R.string.config_batterySaverScheduleProvider));
+        if (currentMode == PowerManager.POWER_SAVER_MODE_DYNAMIC && !providerConfigured) {
+            Global.putInt(resolver, Global.LOW_POWER_MODE_TRIGGER_LEVEL, 0);
+            Global.putInt(resolver, Global.AUTOMATIC_POWER_SAVER_MODE,
+                    PowerManager.POWER_SAVER_MODE_PERCENTAGE);
+        }
+    }
 }
diff --git a/packages/SettingsLib/src/com/android/settingslib/location/RecentLocationAccesses.java b/packages/SettingsLib/src/com/android/settingslib/location/RecentLocationAccesses.java
new file mode 100644
index 0000000..9af0670
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/location/RecentLocationAccesses.java
@@ -0,0 +1,189 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.location;
+
+import android.app.AppOpsManager;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.graphics.drawable.Drawable;
+import android.os.Process;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.text.format.DateUtils;
+import android.util.IconDrawableFactory;
+import android.util.Log;
+
+import androidx.annotation.VisibleForTesting;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+/**
+ * Retrieves the information of applications which accessed location recently.
+ */
+public class RecentLocationAccesses {
+    private static final String TAG = RecentLocationAccesses.class.getSimpleName();
+    @VisibleForTesting
+    static final String ANDROID_SYSTEM_PACKAGE_NAME = "android";
+
+    // Keep last 24 hours of location app information.
+    private static final long RECENT_TIME_INTERVAL_MILLIS = DateUtils.DAY_IN_MILLIS;
+
+    @VisibleForTesting
+    static final int[] LOCATION_OPS = new int[]{
+            AppOpsManager.OP_FINE_LOCATION,
+            AppOpsManager.OP_COARSE_LOCATION,
+    };
+
+    private final PackageManager mPackageManager;
+    private final Context mContext;
+    private final IconDrawableFactory mDrawableFactory;
+
+    public RecentLocationAccesses(Context context) {
+        mContext = context;
+        mPackageManager = context.getPackageManager();
+        mDrawableFactory = IconDrawableFactory.newInstance(context);
+    }
+
+    /**
+     * Fills a list of applications which queried location recently within specified time.
+     * Apps are sorted by recency. Apps with more recent location accesses are in the front.
+     */
+    public List<Access> getAppList() {
+        // Retrieve a location usage list from AppOps
+        AppOpsManager aoManager =
+                (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
+        List<AppOpsManager.PackageOps> appOps = aoManager.getPackagesForOps(LOCATION_OPS);
+
+        final int appOpsCount = appOps != null ? appOps.size() : 0;
+
+        // Process the AppOps list and generate a preference list.
+        ArrayList<Access> accesses = new ArrayList<>(appOpsCount);
+        final long now = System.currentTimeMillis();
+        final UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
+        final List<UserHandle> profiles = um.getUserProfiles();
+
+        for (int i = 0; i < appOpsCount; ++i) {
+            AppOpsManager.PackageOps ops = appOps.get(i);
+            // Don't show the Android System in the list - it's not actionable for the user.
+            // Also don't show apps belonging to background users except managed users.
+            String packageName = ops.getPackageName();
+            int uid = ops.getUid();
+            int userId = UserHandle.getUserId(uid);
+            boolean isAndroidOs =
+                    (uid == Process.SYSTEM_UID) && ANDROID_SYSTEM_PACKAGE_NAME.equals(packageName);
+            if (isAndroidOs || !profiles.contains(new UserHandle(userId))) {
+                continue;
+            }
+            Access access = getAccessFromOps(now, ops);
+            if (access != null) {
+                accesses.add(access);
+            }
+        }
+        return accesses;
+    }
+
+    public List<Access> getAppListSorted() {
+        List<Access> accesses = getAppList();
+        // Sort the list of Access by recency. Most recent accesses first.
+        Collections.sort(accesses, Collections.reverseOrder(new Comparator<Access>() {
+            @Override
+            public int compare(Access access1, Access access2) {
+                return Long.compare(access1.accessFinishTime, access2.accessFinishTime);
+            }
+        }));
+        return accesses;
+    }
+
+    /**
+     * Creates a Access entry for the given PackageOps.
+     *
+     * This method examines the time interval of the PackageOps first. If the PackageOps is older
+     * than the designated interval, this method ignores the PackageOps object and returns null.
+     * When the PackageOps is fresh enough, this method returns a Access object for the package
+     */
+    private Access getAccessFromOps(long now,
+            AppOpsManager.PackageOps ops) {
+        String packageName = ops.getPackageName();
+        List<AppOpsManager.OpEntry> entries = ops.getOps();
+        long locationAccessFinishTime = 0L;
+        // Earliest time for a location access to end and still be shown in list.
+        long recentLocationCutoffTime = now - RECENT_TIME_INTERVAL_MILLIS;
+        for (AppOpsManager.OpEntry entry : entries) {
+            locationAccessFinishTime = Math.max(entry.getLastAccessBackgroundTime(),
+                    entry.getLastAccessForegroundTime());
+        }
+        // Bail out if the entry is out of date.
+        if (locationAccessFinishTime < recentLocationCutoffTime) {
+            return null;
+        }
+
+        // The package is fresh enough, continue.
+        int uid = ops.getUid();
+        int userId = UserHandle.getUserId(uid);
+
+        Access access = null;
+        try {
+            ApplicationInfo appInfo = mPackageManager.getApplicationInfoAsUser(
+                    packageName, PackageManager.GET_META_DATA, userId);
+            if (appInfo == null) {
+                Log.w(TAG, "Null application info retrieved for package " + packageName
+                        + ", userId " + userId);
+                return null;
+            }
+
+            final UserHandle userHandle = new UserHandle(userId);
+            Drawable icon = mDrawableFactory.getBadgedIcon(appInfo, userId);
+            CharSequence appLabel = mPackageManager.getApplicationLabel(appInfo);
+            CharSequence badgedAppLabel = mPackageManager.getUserBadgedLabel(appLabel, userHandle);
+            if (appLabel.toString().contentEquals(badgedAppLabel)) {
+                // If badged label is not different from original then no need for it as
+                // a separate content description.
+                badgedAppLabel = null;
+            }
+            access = new Access(packageName, userHandle, icon, appLabel, badgedAppLabel,
+                    locationAccessFinishTime);
+        } catch (NameNotFoundException e) {
+            Log.w(TAG, "package name not found for " + packageName + ", userId " + userId);
+        }
+        return access;
+    }
+
+    public static class Access {
+        public final String packageName;
+        public final UserHandle userHandle;
+        public final Drawable icon;
+        public final CharSequence label;
+        public final CharSequence contentDescription;
+        public final long accessFinishTime;
+
+        private Access(String packageName, UserHandle userHandle, Drawable icon,
+                CharSequence label, CharSequence contentDescription,
+                long accessFinishTime) {
+            this.packageName = packageName;
+            this.userHandle = userHandle;
+            this.icon = icon;
+            this.label = label;
+            this.contentDescription = contentDescription;
+            this.accessFinishTime = accessFinishTime;
+        }
+    }
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/WifiMacAddressPreferenceControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/WifiMacAddressPreferenceControllerTest.java
index 74e5bf5..1f7f4bc 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/WifiMacAddressPreferenceControllerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/WifiMacAddressPreferenceControllerTest.java
@@ -27,7 +27,6 @@
 import android.net.ConnectivityManager;
 import android.net.wifi.WifiInfo;
 import android.net.wifi.WifiManager;
-import android.provider.Settings;
 
 import androidx.preference.Preference;
 import androidx.preference.PreferenceScreen;
@@ -93,105 +92,23 @@
     }
 
     @Test
-    public void updateConnectivity_nullWifiInfoWithMacRandomizationOff_setMacUnavailable() {
-        Settings.Global.putInt(mContext.getContentResolver(),
-                Settings.Global.WIFI_CONNECTED_MAC_RANDOMIZATION_ENABLED,
-                AbstractWifiMacAddressPreferenceController.OFF);
-        doReturn(null).when(mWifiManager).getConnectionInfo();
-
+    public void updateConnectivity_null_setMacUnavailable() {
+        doReturn(null).when(mWifiManager).getFactoryMacAddresses();
         mController.displayPreference(mScreen);
-
         assertThat(mPreference.getSummary())
                 .isEqualTo(mContext.getString(R.string.status_unavailable));
     }
 
     @Test
-    public void updateConnectivity_nullMacWithMacRandomizationOff_setMacUnavailable() {
-        Settings.Global.putInt(mContext.getContentResolver(),
-                Settings.Global.WIFI_CONNECTED_MAC_RANDOMIZATION_ENABLED,
-                AbstractWifiMacAddressPreferenceController.OFF);
-        doReturn(null).when(mWifiInfo).getMacAddress();
-
+    public void updateConnectivity_validMac_setValidMac() {
+        final String[] macAddresses = new String[]{TEST_MAC_ADDRESS};
+        doReturn(macAddresses).when(mWifiManager).getFactoryMacAddresses();
         mController.displayPreference(mScreen);
-
-        assertThat(mPreference.getSummary())
-                .isEqualTo(mContext.getString(R.string.status_unavailable));
-    }
-
-    @Test
-    public void updateConnectivity_defaultMacWithMacRandomizationOff_setMacUnavailable() {
-        Settings.Global.putInt(mContext.getContentResolver(),
-                Settings.Global.WIFI_CONNECTED_MAC_RANDOMIZATION_ENABLED,
-                AbstractWifiMacAddressPreferenceController.OFF);
-        doReturn(WifiInfo.DEFAULT_MAC_ADDRESS).when(mWifiInfo).getMacAddress();
-
-        mController.displayPreference(mScreen);
-
-        assertThat(mPreference.getSummary())
-                .isEqualTo(mContext.getString(R.string.status_unavailable));
-    }
-
-    @Test
-    public void updateConnectivity_validMacWithMacRandomizationOff_setValidMac() {
-        Settings.Global.putInt(mContext.getContentResolver(),
-                Settings.Global.WIFI_CONNECTED_MAC_RANDOMIZATION_ENABLED,
-                AbstractWifiMacAddressPreferenceController.OFF);
-        doReturn(TEST_MAC_ADDRESS).when(mWifiInfo).getMacAddress();
-
-        mController.displayPreference(mScreen);
-
         assertThat(mPreference.getSummary()).isEqualTo(TEST_MAC_ADDRESS);
-    }
 
-    @Test
-    public void updateConnectivity_nullWifiInfoWithMacRandomizationOn_setMacUnavailable() {
-        Settings.Global.putInt(mContext.getContentResolver(),
-                Settings.Global.WIFI_CONNECTED_MAC_RANDOMIZATION_ENABLED,
-                AbstractWifiMacAddressPreferenceController.ON);
-        doReturn(null).when(mWifiManager).getConnectionInfo();
 
-        mController.displayPreference(mScreen);
 
-        assertThat(mPreference.getSummary())
-                .isEqualTo(mContext.getString(R.string.status_unavailable));
-    }
 
-    @Test
-    public void updateConnectivity_nullMacWithMacRandomizationOn_setMacUnavailable() {
-        Settings.Global.putInt(mContext.getContentResolver(),
-                Settings.Global.WIFI_CONNECTED_MAC_RANDOMIZATION_ENABLED,
-                AbstractWifiMacAddressPreferenceController.ON);
-        doReturn(null).when(mWifiInfo).getMacAddress();
-
-        mController.displayPreference(mScreen);
-
-        assertThat(mPreference.getSummary())
-                .isEqualTo(mContext.getString(R.string.status_unavailable));
-    }
-
-    @Test
-    public void updateConnectivity_defaultMacWithMacRandomizationOn_setMacRandomized() {
-        Settings.Global.putInt(mContext.getContentResolver(),
-                Settings.Global.WIFI_CONNECTED_MAC_RANDOMIZATION_ENABLED,
-                AbstractWifiMacAddressPreferenceController.ON);
-        doReturn(WifiInfo.DEFAULT_MAC_ADDRESS).when(mWifiInfo).getMacAddress();
-
-        mController.displayPreference(mScreen);
-
-        assertThat(mPreference.getSummary())
-                .isEqualTo(mContext.getString(R.string.wifi_status_mac_randomized));
-    }
-
-    @Test
-    public void updateConnectivity_validMacWithMacRandomizationOn_setValidMac() {
-        Settings.Global.putInt(mContext.getContentResolver(),
-                Settings.Global.WIFI_CONNECTED_MAC_RANDOMIZATION_ENABLED,
-                AbstractWifiMacAddressPreferenceController.ON);
-        doReturn(TEST_MAC_ADDRESS).when(mWifiInfo).getMacAddress();
-
-        mController.displayPreference(mScreen);
-
-        assertThat(mPreference.getSummary()).isEqualTo(TEST_MAC_ADDRESS);
     }
 
     private static class ConcreteWifiMacAddressPreferenceController
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java
index 67b077e..7c30e48 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java
@@ -76,11 +76,14 @@
     private final Rect mLastDockedBounds = new Rect();
     private boolean mQsCustomizing;
 
+    private final Context mContext;
+
     public LightBarController(Context ctx) {
         mDarkModeColor = Color.valueOf(ctx.getColor(R.color.dark_mode_icon_color_single_tone));
         mStatusBarIconController = Dependency.get(DarkIconDispatcher.class);
         mBatteryController = Dependency.get(BatteryController.class);
         mBatteryController.addCallback(this);
+        mContext = ctx;
     }
 
     public void setNavigationBar(LightBarTransitionsController navigationBar) {
@@ -217,8 +220,9 @@
 
     private void updateNavigation() {
         if (mNavigationBarController != null) {
-            mNavigationBarController.setIconsDark(
-                    mNavigationLight, animateChange());
+            if (!NavBarTintController.isEnabled(mContext)) {
+                mNavigationBarController.setIconsDark(mNavigationLight, animateChange());
+            }
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java
index 57cc7d6..7876aa5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java
@@ -16,12 +16,16 @@
 
 package com.android.systemui.statusbar.phone;
 
+import static com.android.systemui.statusbar.phone.NavBarTintController.MIN_COLOR_ADAPT_TRANSITION_TIME;
+import static com.android.systemui.statusbar.phone.NavBarTintController.NAV_COLOR_TRANSITION_TIME_SETTING;
+
 import android.animation.ValueAnimator;
 import android.content.Context;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.SystemClock;
 import android.util.MathUtils;
+import android.provider.Settings;
 import android.util.TimeUtils;
 
 import com.android.systemui.Dependency;
@@ -42,13 +46,14 @@
 public class LightBarTransitionsController implements Dumpable, Callbacks,
         StatusBarStateController.StateListener {
 
-    public static final long DEFAULT_TINT_ANIMATION_DURATION = 120;
+    public static final int DEFAULT_TINT_ANIMATION_DURATION = 120;
     private static final String EXTRA_DARK_INTENSITY = "dark_intensity";
 
     private final Handler mHandler;
     private final DarkIntensityApplier mApplier;
     private final KeyguardMonitor mKeyguardMonitor;
     private final StatusBarStateController mStatusBarStateController;
+    private NavBarTintController mColorAdaptionController;
 
     private boolean mTransitionDeferring;
     private long mTransitionDeferringStartTime;
@@ -67,6 +72,8 @@
         }
     };
 
+    private final Context mContext;
+
     public LightBarTransitionsController(Context context, DarkIntensityApplier applier) {
         mApplier = applier;
         mHandler = new Handler();
@@ -76,6 +83,7 @@
                 .addCallback(this);
         mStatusBarStateController.addCallback(this);
         mDozeAmount = mStatusBarStateController.getDozeAmount();
+        mContext = context;
     }
 
     public void destroy(Context context) {
@@ -106,7 +114,7 @@
     public void appTransitionCancelled() {
         if (mTransitionPending && mTintChangePending) {
             mTintChangePending = false;
-            animateIconTint(mPendingDarkIntensity, 0 /* delay */, DEFAULT_TINT_ANIMATION_DURATION);
+            animateIconTint(mPendingDarkIntensity, 0 /* delay */, getTintAnimationDuration());
         }
         mTransitionPending = false;
     }
@@ -146,10 +154,19 @@
                     Math.max(0, mTransitionDeferringStartTime - SystemClock.uptimeMillis()),
                     mTransitionDeferringDuration);
         } else {
-            animateIconTint(dark ? 1.0f : 0.0f, 0 /* delay */, DEFAULT_TINT_ANIMATION_DURATION);
+            animateIconTint(dark ? 1.0f : 0.0f, 0 /* delay */, getTintAnimationDuration());
         }
     }
 
+    public long getTintAnimationDuration() {
+        if (NavBarTintController.isEnabled(mContext)) {
+            return Math.max(Settings.Global.getInt(mContext.getContentResolver(),
+                    NAV_COLOR_TRANSITION_TIME_SETTING, DEFAULT_TINT_ANIMATION_DURATION),
+                    MIN_COLOR_ADAPT_TRANSITION_TIME);
+        }
+        return DEFAULT_TINT_ANIMATION_DURATION;
+    }
+
     public float getCurrentDarkIntensity() {
         return mDarkIntensity;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavBarTintController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavBarTintController.java
new file mode 100644
index 0000000..9ecee18
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavBarTintController.java
@@ -0,0 +1,208 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.phone;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.Bitmap.Config;
+import android.graphics.Color;
+import android.graphics.Rect;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.provider.Settings;
+import android.util.DisplayMetrics;
+import android.view.SurfaceControl;
+
+public class NavBarTintController {
+    public static final String NAV_COLOR_TRANSITION_TIME_SETTING = "navbar_color_adapt_transition";
+    public static final int MIN_COLOR_ADAPT_TRANSITION_TIME = 400;
+
+    private final HandlerThread mColorAdaptHandlerThread = new HandlerThread("ColorExtractThread");
+    private Handler mColorAdaptionHandler;
+
+    // Poll time for each iteration to color sample
+    private static final int COLOR_ADAPTION_TIMEOUT = 300;
+
+    // Passing the threshold of this luminance value will make the button black otherwise white
+    private static final float LUMINANCE_THRESHOLD = 0.3f;
+
+    // The home button's icon is actually smaller than the button's size, the percentage will
+    // cut into the button's size to determine the icon size
+    private static final float PERCENTAGE_BUTTON_PADDING = 0.3f;
+
+    // The distance from the home button to color sample around
+    private static final int COLOR_SAMPLE_MARGIN = 20;
+
+    private boolean mRunning;
+
+    private final NavigationBarView mNavigationBarView;
+    private final LightBarTransitionsController mLightBarController;
+    private final Handler mMainHandler = new Handler(Looper.getMainLooper());
+
+    public NavBarTintController(NavigationBarView navigationBarView,
+            LightBarTransitionsController lightBarController) {
+        mNavigationBarView = navigationBarView;
+        mLightBarController = lightBarController;
+    }
+
+    public void start() {
+        if (!isEnabled(mNavigationBarView.getContext())) {
+            return;
+        }
+        if (mColorAdaptionHandler == null) {
+            mColorAdaptHandlerThread.start();
+            mColorAdaptionHandler = new Handler(mColorAdaptHandlerThread.getLooper());
+        }
+        mColorAdaptionHandler.removeCallbacksAndMessages(null);
+        mColorAdaptionHandler.post(this::updateTint);
+        mRunning = true;
+    }
+
+    public void end() {
+        if (mColorAdaptionHandler != null) {
+            mColorAdaptionHandler.removeCallbacksAndMessages(null);
+        }
+        mRunning = false;
+    }
+
+    public void stop() {
+        end();
+        if (mColorAdaptionHandler != null) {
+            mColorAdaptHandlerThread.quitSafely();
+        }
+    }
+
+    private void updateTint() {
+        int[] navPos = new int[2];
+        int[] butPos = new int[2];
+        if (mNavigationBarView.getHomeButton().getCurrentView() == null) {
+            return;
+        }
+
+        // Determine the area of the home icon in the larger home button
+        mNavigationBarView.getHomeButton().getCurrentView().getLocationInSurface(butPos);
+        final int navWidth = mNavigationBarView.getHomeButton().getCurrentView().getWidth();
+        final int navHeight = mNavigationBarView.getHomeButton().getCurrentView().getHeight();
+        final int xPadding = (int) (PERCENTAGE_BUTTON_PADDING * navWidth);
+        final int yPadding = (int) (PERCENTAGE_BUTTON_PADDING * navHeight);
+        final Rect homeButtonRect = new Rect(butPos[0] + xPadding, butPos[1] + yPadding,
+                navWidth + butPos[0]  - xPadding, navHeight + butPos[1] - yPadding);
+        if (mNavigationBarView.getCurrentView() == null || homeButtonRect.isEmpty()) {
+            scheduleColorAdaption();
+            return;
+        }
+        mNavigationBarView.getCurrentView().getLocationOnScreen(navPos);
+        homeButtonRect.offset(navPos[0], navPos[1]);
+
+        // Apply a margin area around the button region to sample the colors, crop from screenshot
+        final Rect cropRect = new Rect(homeButtonRect);
+        cropRect.inset(-COLOR_SAMPLE_MARGIN, -COLOR_SAMPLE_MARGIN);
+        if (cropRect.isEmpty()) {
+            scheduleColorAdaption();
+            return;
+        }
+
+        // Determine the size of the home area
+        Rect homeArea = new Rect(COLOR_SAMPLE_MARGIN, COLOR_SAMPLE_MARGIN,
+                homeButtonRect.width() + COLOR_SAMPLE_MARGIN,
+                homeButtonRect.height() + COLOR_SAMPLE_MARGIN);
+
+        // Get the screenshot around the home button icon to determine the color
+        DisplayMetrics mDisplayMetrics = new DisplayMetrics();
+        mNavigationBarView.getContext().getDisplay().getRealMetrics(mDisplayMetrics);
+        final Bitmap hardBitmap = SurfaceControl
+                .screenshot(new Rect(), mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels,
+                        mNavigationBarView.getContext().getDisplay().getRotation());
+        if (hardBitmap != null && cropRect.bottom <= hardBitmap.getHeight()) {
+            final Bitmap cropBitmap = Bitmap.createBitmap(hardBitmap, cropRect.left, cropRect.top,
+                    cropRect.width(), cropRect.height());
+            final Bitmap softBitmap = cropBitmap.copy(Config.ARGB_8888, false);
+
+            // Get the luminance value to determine if the home button should be black or white
+            final int[] pixels = new int[softBitmap.getByteCount() / 4];
+            softBitmap.getPixels(pixels, 0, softBitmap.getWidth(), 0, 0, softBitmap.getWidth(),
+                    softBitmap.getHeight());
+            float r = 0, g = 0, blue = 0;
+
+            int width = cropRect.width();
+            int total = 0;
+            for (int i = 0; i < pixels.length; i += 4) {
+                int x = i % width;
+                int y = i / width;
+                if (!homeArea.contains(x, y)) {
+                    r += Color.red(pixels[i]);
+                    g += Color.green(pixels[i]);
+                    blue += Color.blue(pixels[i]);
+                    total++;
+                }
+            }
+
+            r /= total;
+            g /= total;
+            blue /= total;
+
+            r = Math.max(Math.min(r / 255f, 1), 0);
+            g = Math.max(Math.min(g / 255f, 1), 0);
+            blue = Math.max(Math.min(blue / 255f, 1), 0);
+
+            if (r <= 0.03928) {
+                r /= 12.92;
+            } else {
+                r = (float) Math.pow((r + 0.055) / 1.055, 2.4);
+            }
+            if (g <= 0.03928) {
+                g /= 12.92;
+            } else {
+                g = (float) Math.pow((g + 0.055) / 1.055, 2.4);
+            }
+            if (blue <= 0.03928) {
+                blue /= 12.92;
+            } else {
+                blue = (float) Math.pow((blue + 0.055) / 1.055, 2.4);
+            }
+
+            if (r * 0.2126 + g * 0.7152 + blue * 0.0722 > LUMINANCE_THRESHOLD) {
+                // Black
+                mMainHandler.post(
+                        () -> mLightBarController
+                                .setIconsDark(true /* dark */, true /* animate */));
+            } else {
+                // White
+                mMainHandler.post(
+                        () -> mLightBarController
+                                .setIconsDark(false /* dark */, true /* animate */));
+            }
+            cropBitmap.recycle();
+            hardBitmap.recycle();
+        }
+        scheduleColorAdaption();
+    }
+
+    private void scheduleColorAdaption() {
+        mColorAdaptionHandler.removeCallbacksAndMessages(null);
+        if (!mRunning || !isEnabled(mNavigationBarView.getContext())) {
+            return;
+        }
+        mColorAdaptionHandler.postDelayed(this::updateTint, COLOR_ADAPTION_TIMEOUT);
+    }
+
+    public static boolean isEnabled(Context context) {
+        return Settings.Global.getInt(context.getContentResolver(),
+                NavigationPrototypeController.NAV_COLOR_ADAPT_ENABLE_SETTING, 0) == 1;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
index ae0a14529..55655d5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
@@ -851,6 +851,16 @@
             if (Intent.ACTION_SCREEN_OFF.equals(action)
                     || Intent.ACTION_SCREEN_ON.equals(action)) {
                 notifyNavigationBarScreenOn();
+
+                if (Intent.ACTION_SCREEN_ON.equals(action)) {
+                    // Enabled and screen is on, start it again if enabled
+                    if (NavBarTintController.isEnabled(getContext())) {
+                        mNavigationBarView.getColorAdaptionController().start();
+                    }
+                } else {
+                    // Screen off disable it
+                    mNavigationBarView.getColorAdaptionController().end();
+                }
             }
             if (Intent.ACTION_USER_SWITCHED.equals(action)) {
                 // The accessibility settings may be different for the new user
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
index 30e8409..6a7983a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -149,6 +149,7 @@
     private RecentsOnboarding mRecentsOnboarding;
     private NotificationPanelView mPanelView;
 
+    private NavBarTintController mColorAdaptionController;
     private NavigationPrototypeController mPrototypeController;
     private NavigationGestureAction[] mDefaultGestureMap;
     private QuickScrubAction mQuickScrubAction;
@@ -277,6 +278,15 @@
         public void onBackButtonVisibilityChanged(boolean visible) {
             getBackButton().setVisibility(visible ? VISIBLE : GONE);
         }
+
+        @Override
+        public void onColorAdaptChanged(boolean enabled) {
+            if (enabled) {
+                mColorAdaptionController.start();
+            } else {
+                mColorAdaptionController.end();
+            }
+        }
     };
 
     public NavigationBarView(Context context, AttributeSet attrs) {
@@ -334,6 +344,11 @@
         mPrototypeController = new NavigationPrototypeController(mHandler, mContext);
         mPrototypeController.register();
         mPrototypeController.setOnPrototypeChangedListener(mPrototypeListener);
+        mColorAdaptionController = new NavBarTintController(this, getLightTransitionsController());
+    }
+
+    public NavBarTintController getColorAdaptionController() {
+        return mColorAdaptionController;
     }
 
     public BarTransitions getBarTransitions() {
@@ -1097,6 +1112,7 @@
         Dependency.get(PluginManager.class).addPluginListener(this,
                 NavGesture.class, false /* Only one */);
         setUpSwipeUpOnboarding(isQuickStepSwipeUpEnabled());
+        mColorAdaptionController.start();
     }
 
     @Override
@@ -1107,6 +1123,7 @@
             mGestureHelper.destroy();
         }
         mPrototypeController.unregister();
+        mColorAdaptionController.stop();
         setUpSwipeUpOnboarding(false);
         for (int i = 0; i < mButtonDispatchers.size(); ++i) {
             mButtonDispatchers.valueAt(i).onDestroy();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationPrototypeController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationPrototypeController.java
index b11b6d4..40ac793 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationPrototypeController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationPrototypeController.java
@@ -37,6 +37,7 @@
 
     static final String NAVBAR_EXPERIMENTS_DISABLED = "navbarexperiments_disabled";
     private final String GESTURE_MATCH_SETTING = "quickstepcontroller_gesture_match_map";
+    public static final String NAV_COLOR_ADAPT_ENABLE_SETTING = "navbar_color_adapt_enable";
 
     @Retention(RetentionPolicy.SOURCE)
     @IntDef({ACTION_DEFAULT, ACTION_QUICKSTEP, ACTION_QUICKSCRUB, ACTION_BACK})
@@ -73,6 +74,7 @@
     public void register() {
         registerObserver(HIDE_BACK_BUTTON_SETTING);
         registerObserver(GESTURE_MATCH_SETTING);
+        registerObserver(NAV_COLOR_ADAPT_ENABLE_SETTING);
     }
 
     /**
@@ -96,6 +98,9 @@
                 } else if (path.endsWith(HIDE_BACK_BUTTON_SETTING)) {
                     mListener.onBackButtonVisibilityChanged(
                             !getGlobalBool(HIDE_BACK_BUTTON_SETTING));
+                } else if (path.endsWith(NAV_COLOR_ADAPT_ENABLE_SETTING)) {
+                    mListener.onColorAdaptChanged(
+                            NavBarTintController.isEnabled(mContext));
                 }
             } catch (SettingNotFoundException e) {
                 e.printStackTrace();
@@ -138,5 +143,6 @@
     public interface OnPrototypeChangedListener {
         void onGestureRemap(@GestureAction int[] actions);
         void onBackButtonVisibilityChanged(boolean visible);
+        void onColorAdaptChanged(boolean enabled);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 6caea1d..75e5cba 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -2230,6 +2230,11 @@
             mNavigationBar.getBarTransitions().setAutoDim(false);
         }
         mHandler.removeCallbacks(mAutoDim);
+
+        // Do not dim the navigation buttons if the its tint is controlled by the bar's background
+        if (NavBarTintController.isEnabled(mContext)) {
+            return;
+        }
         if (mState != StatusBarState.KEYGUARD && mState != StatusBarState.SHADE_LOCKED) {
             mHandler.postDelayed(mAutoDim, AUTOHIDE_TIMEOUT_MS);
         }
diff --git a/proto/src/metrics_constants/metrics_constants.proto b/proto/src/metrics_constants/metrics_constants.proto
index 66d64b1..529d78f 100644
--- a/proto/src/metrics_constants/metrics_constants.proto
+++ b/proto/src/metrics_constants/metrics_constants.proto
@@ -6649,6 +6649,21 @@
     // OS: Q
     QS_SENSOR_PRIVACY = 1598;
 
+    // Tagged data for SMART_REPLY_VISIBLE. Count of number of smart actions.
+    // OS: Q
+    NOTIFICATION_SMART_ACTION_COUNT = 1599;
+
+    // Tagged data for SMART_REPLY_VISIBLE and NOTIFICATION_ITEM_ACTION.
+    // Whether the notification has notification-assistant generated
+    // actions/replies.
+    // OS: Q
+    NOTIFICATION_SMART_SUGGESTION_ASSISTANT_GENERATED = 1600;
+
+    // Tagged data for NOTIFICATION_ITEM_ACTION. Whether the action is a smart
+    // action.
+    // OS: Q
+    NOTIFICATION_ACTION_IS_SMART = 1601;
+
     // ---- End Q Constants, all Q constants go above this line ----
 
     // Add new aosp constants above this line.
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index 2cbab49..a533640 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -16,10 +16,13 @@
 
 package com.android.server.backup;
 
+import static com.android.internal.util.Preconditions.checkNotNull;
+
 import android.Manifest;
 import android.annotation.Nullable;
 import android.annotation.UserIdInt;
 import android.app.ActivityManager;
+import android.app.backup.BackupManager;
 import android.app.backup.IBackupManagerMonitor;
 import android.app.backup.IBackupObserver;
 import android.app.backup.IFullBackupRestoreObserver;
@@ -41,6 +44,7 @@
 import android.os.Trace;
 import android.os.UserHandle;
 import android.util.Slog;
+import android.util.SparseArray;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.SystemConfig;
@@ -83,22 +87,27 @@
     }
 
     private final Context mContext;
-    private UserBackupManagerService mUserBackupManagerService;
+    private final Trampoline mTrampoline;
+    private final HandlerThread mBackupThread;
+
+    // Keeps track of all unlocked users registered with this service. Indexed by user id.
+    private final SparseArray<UserBackupManagerService> mServiceUsers = new SparseArray<>();
+
+    private Set<ComponentName> mTransportWhitelist;
 
     /** Instantiate a new instance of {@link BackupManagerService}. */
     public BackupManagerService(
             Context context, Trampoline trampoline, HandlerThread backupThread) {
-        // Set up our transport options and initialize the default transport
-        SystemConfig systemConfig = SystemConfig.getInstance();
-        Set<ComponentName> transportWhitelist = systemConfig.getBackupTransportWhitelist();
-        if (transportWhitelist == null) {
-            transportWhitelist = Collections.emptySet();
-        }
+        mContext = checkNotNull(context);
+        mTrampoline = checkNotNull(trampoline);
+        mBackupThread = checkNotNull(backupThread);
 
-        mContext = context;
-        mUserBackupManagerService =
-                UserBackupManagerService.createAndInitializeService(
-                        context, trampoline, backupThread, transportWhitelist);
+        // Set up our transport options.
+        SystemConfig systemConfig = SystemConfig.getInstance();
+        mTransportWhitelist = systemConfig.getBackupTransportWhitelist();
+        if (mTransportWhitelist == null) {
+            mTransportWhitelist = Collections.emptySet();
+        }
     }
 
     /**
@@ -115,12 +124,6 @@
         }
     }
 
-    // TODO(b/118520567): Remove when tests are modified to use per-user instance.
-    @VisibleForTesting
-    void setUserBackupManagerService(UserBackupManagerService userBackupManagerService) {
-        mUserBackupManagerService = userBackupManagerService;
-    }
-
     /**
      * Called through Trampoline from {@link Lifecycle#onUnlockUser(int)}. We run the heavy work on
      * a background thread to keep the unlock time down.
@@ -139,9 +142,42 @@
      * Starts the backup service for user {@code userId} by creating a new instance of {@link
      * UserBackupManagerService} and registering it with this service.
      */
-    // TODO(b/120212806): Add UserBackupManagerService initialization logic.
-    void startServiceForUser(int userId) {
-        // Intentionally empty.
+    @VisibleForTesting
+    protected void startServiceForUser(int userId) {
+        UserBackupManagerService userBackupManagerService =
+                UserBackupManagerService.createAndInitializeService(
+                        mContext, mTrampoline, mBackupThread, mTransportWhitelist);
+        startServiceForUser(userId, userBackupManagerService);
+    }
+
+    /**
+     * Starts the backup service for user {@code userId} by registering its instance of {@link
+     * UserBackupManagerService} with this service.
+     */
+    void startServiceForUser(int userId, UserBackupManagerService userBackupManagerService) {
+        mServiceUsers.put(userId, userBackupManagerService);
+    }
+
+    SparseArray<UserBackupManagerService> getServiceUsers() {
+        return mServiceUsers;
+    }
+
+    /**
+     * Returns the {@link UserBackupManagerService} instance for the specified user {@code userId}.
+     * If the user is not registered with the service (either the user is locked or not eligible for
+     * the backup service) then return {@code null}.
+     *
+     * @param userId The id of the user to retrieve its instance of {@link
+     *     UserBackupManagerService}.
+     * @param caller A {@link String} identifying the caller for logging purposes.
+     */
+    @Nullable
+    private UserBackupManagerService getServiceForUser(@UserIdInt int userId, String caller) {
+        UserBackupManagerService userBackupManagerService = mServiceUsers.get(userId);
+        if (userBackupManagerService == null) {
+            Slog.w(TAG, "Called " + caller + " for unknown user: " + userId);
+        }
+        return userBackupManagerService;
     }
 
     /*
@@ -149,7 +185,7 @@
      * They delegate to the appropriate per-user instance of UserBackupManagerService to perform the
      * action on the passed in user. Currently this is a straight redirection (see TODO).
      */
-    // TODO (b/118520567): Take in user id and call per-user instance of UserBackupManagerService.
+    // TODO (b/118520567): Stop hardcoding system user when we pass in user id as a parameter
 
     // ---------------------------------------------
     // BACKUP AGENT OPERATIONS
@@ -161,7 +197,12 @@
      * backup.
      */
     public void dataChanged(String packageName) {
-        mUserBackupManagerService.dataChanged(packageName);
+        UserBackupManagerService userBackupManagerService =
+                getServiceForUser(UserHandle.USER_SYSTEM, "dataChanged()");
+
+        if (userBackupManagerService != null) {
+            userBackupManagerService.dataChanged(packageName);
+        }
     }
 
     /**
@@ -169,7 +210,12 @@
      * {@link ActivityManager}.
      */
     public void agentConnected(String packageName, IBinder agentBinder) {
-        mUserBackupManagerService.agentConnected(packageName, agentBinder);
+        UserBackupManagerService userBackupManagerService =
+                getServiceForUser(UserHandle.USER_SYSTEM, "agentConnected()");
+
+        if (userBackupManagerService != null) {
+            userBackupManagerService.agentConnected(packageName, agentBinder);
+        }
     }
 
     /**
@@ -177,7 +223,12 @@
      * called from the {@link ActivityManager}.
      */
     public void agentDisconnected(String packageName) {
-        mUserBackupManagerService.agentDisconnected(packageName);
+        UserBackupManagerService userBackupManagerService =
+                getServiceForUser(UserHandle.USER_SYSTEM, "agentDisconnected()");
+
+        if (userBackupManagerService != null) {
+            userBackupManagerService.agentDisconnected(packageName);
+        }
     }
 
     /**
@@ -185,7 +236,12 @@
      * outstanding asynchronous backup/restore operation.
      */
     public void opComplete(int token, long result) {
-        mUserBackupManagerService.opComplete(token, result);
+        UserBackupManagerService userBackupManagerService =
+                getServiceForUser(UserHandle.USER_SYSTEM, "opComplete()");
+
+        if (userBackupManagerService != null) {
+            userBackupManagerService.opComplete(token, result);
+        }
     }
 
     // ---------------------------------------------
@@ -194,7 +250,12 @@
 
     /** Run an initialize operation for the given transports {@code transportNames}. */
     public void initializeTransports(String[] transportNames, IBackupObserver observer) {
-        mUserBackupManagerService.initializeTransports(transportNames, observer);
+        UserBackupManagerService userBackupManagerService =
+                getServiceForUser(UserHandle.USER_SYSTEM, "initializeTransports()");
+
+        if (userBackupManagerService != null) {
+            userBackupManagerService.initializeTransports(transportNames, observer);
+        }
     }
 
     /**
@@ -202,35 +263,70 @@
      * transportName}.
      */
     public void clearBackupData(String transportName, String packageName) {
-        mUserBackupManagerService.clearBackupData(transportName, packageName);
+        UserBackupManagerService userBackupManagerService =
+                getServiceForUser(UserHandle.USER_SYSTEM, "clearBackupData()");
+
+        if (userBackupManagerService != null) {
+            userBackupManagerService.clearBackupData(transportName, packageName);
+        }
     }
 
     /** Return the name of the currently active transport. */
+    @Nullable
     public String getCurrentTransport() {
-        return mUserBackupManagerService.getCurrentTransport();
+        UserBackupManagerService userBackupManagerService =
+                getServiceForUser(UserHandle.USER_SYSTEM, "getCurrentTransport()");
+
+        return userBackupManagerService == null
+                ? null
+                : userBackupManagerService.getCurrentTransport();
     }
 
     /**
      * Returns the {@link ComponentName} of the host service of the selected transport or {@code
      * null} if no transport selected or if the transport selected is not registered.
      */
+    @Nullable
     public ComponentName getCurrentTransportComponent() {
-        return mUserBackupManagerService.getCurrentTransportComponent();
+        UserBackupManagerService userBackupManagerService =
+                getServiceForUser(UserHandle.USER_SYSTEM, "getCurrentTransportComponent()");
+
+        return userBackupManagerService == null
+                ? null
+                : userBackupManagerService.getCurrentTransportComponent();
     }
 
     /** Report all known, available backup transports by name. */
+    @Nullable
     public String[] listAllTransports() {
-        return mUserBackupManagerService.listAllTransports();
+        UserBackupManagerService userBackupManagerService =
+                getServiceForUser(UserHandle.USER_SYSTEM, "listAllTransports()");
+
+        return userBackupManagerService == null
+                ? null
+                : userBackupManagerService.listAllTransports();
     }
 
     /** Report all known, available backup transports by {@link ComponentName}. */
+    @Nullable
     public ComponentName[] listAllTransportComponents() {
-        return mUserBackupManagerService.listAllTransportComponents();
+        UserBackupManagerService userBackupManagerService =
+                getServiceForUser(UserHandle.USER_SYSTEM, "listAllTransportComponents()");
+
+        return userBackupManagerService == null
+                ? null
+                : userBackupManagerService.listAllTransportComponents();
     }
 
     /** Report all system whitelisted transports. */
+    @Nullable
     public String[] getTransportWhitelist() {
-        return mUserBackupManagerService.getTransportWhitelist();
+        UserBackupManagerService userBackupManagerService =
+                getServiceForUser(UserHandle.USER_SYSTEM, "getTransportWhitelist()");
+
+        return userBackupManagerService == null
+                ? null
+                : userBackupManagerService.getTransportWhitelist();
     }
 
     /**
@@ -263,13 +359,18 @@
             String currentDestinationString,
             @Nullable Intent dataManagementIntent,
             String dataManagementLabel) {
-        mUserBackupManagerService.updateTransportAttributes(
-                transportComponent,
-                name,
-                configurationIntent,
-                currentDestinationString,
-                dataManagementIntent,
-                dataManagementLabel);
+        UserBackupManagerService userBackupManagerService =
+                getServiceForUser(UserHandle.USER_SYSTEM, "updateTransportAttributes()");
+
+        if (userBackupManagerService != null) {
+            userBackupManagerService.updateTransportAttributes(
+                    transportComponent,
+                    name,
+                    configurationIntent,
+                    currentDestinationString,
+                    dataManagementIntent,
+                    dataManagementLabel);
+        }
     }
 
     /**
@@ -281,7 +382,12 @@
     @Deprecated
     @Nullable
     public String selectBackupTransport(String transportName) {
-        return mUserBackupManagerService.selectBackupTransport(transportName);
+        UserBackupManagerService userBackupManagerService =
+                getServiceForUser(UserHandle.USER_SYSTEM, "selectBackupTransport()");
+
+        return userBackupManagerService == null
+                ? null
+                : userBackupManagerService.selectBackupTransport(transportName);
     }
 
     /**
@@ -290,7 +396,12 @@
      */
     public void selectBackupTransportAsync(
             ComponentName transportComponent, ISelectBackupTransportCallback listener) {
-        mUserBackupManagerService.selectBackupTransportAsync(transportComponent, listener);
+        UserBackupManagerService userBackupManagerService =
+                getServiceForUser(UserHandle.USER_SYSTEM, "selectBackupTransportAsync()");
+
+        if (userBackupManagerService != null) {
+            userBackupManagerService.selectBackupTransportAsync(transportComponent, listener);
+        }
     }
 
     /**
@@ -298,8 +409,14 @@
      * available transports, or if the transport does not supply any configuration UI, the method
      * returns {@code null}.
      */
+    @Nullable
     public Intent getConfigurationIntent(String transportName) {
-        return mUserBackupManagerService.getConfigurationIntent(transportName);
+        UserBackupManagerService userBackupManagerService =
+                getServiceForUser(UserHandle.USER_SYSTEM, "getConfigurationIntent()");
+
+        return userBackupManagerService == null
+                ? null
+                : userBackupManagerService.getConfigurationIntent(transportName);
     }
 
     /**
@@ -311,21 +428,39 @@
      * @param transportName The name of the registered transport.
      * @return The current destination string or null if the transport is not registered.
      */
+    @Nullable
     public String getDestinationString(String transportName) {
-        return mUserBackupManagerService.getDestinationString(transportName);
+        UserBackupManagerService userBackupManagerService =
+                getServiceForUser(UserHandle.USER_SYSTEM, "getDestinationString()");
+
+        return userBackupManagerService == null
+                ? null
+                : userBackupManagerService.getDestinationString(transportName);
     }
 
     /** Supply the manage-data intent for the given transport. */
+    @Nullable
     public Intent getDataManagementIntent(String transportName) {
-        return mUserBackupManagerService.getDataManagementIntent(transportName);
+        UserBackupManagerService userBackupManagerService =
+                getServiceForUser(UserHandle.USER_SYSTEM, "getDataManagementIntent()");
+
+        return userBackupManagerService == null
+                ? null
+                : userBackupManagerService.getDataManagementIntent(transportName);
     }
 
     /**
      * Supply the menu label for affordances that fire the manage-data intent for the given
      * transport.
      */
+    @Nullable
     public String getDataManagementLabel(String transportName) {
-        return mUserBackupManagerService.getDataManagementLabel(transportName);
+        UserBackupManagerService userBackupManagerService =
+                getServiceForUser(UserHandle.USER_SYSTEM, "getDataManagementLabel()");
+
+        return userBackupManagerService == null
+                ? null
+                : userBackupManagerService.getDataManagementLabel(transportName);
     }
 
     // ---------------------------------------------
@@ -335,17 +470,32 @@
     /** Enable/disable the backup service. This is user-configurable via backup settings. */
     public void setBackupEnabled(@UserIdInt int userId, boolean enable) {
         enforceCallingPermissionOnUserId(userId, "setBackupEnabled");
-        mUserBackupManagerService.setBackupEnabled(enable);
+        UserBackupManagerService userBackupManagerService =
+                getServiceForUser(userId, "setBackupEnabled()");
+
+        if (userBackupManagerService != null) {
+            userBackupManagerService.setBackupEnabled(enable);
+        }
     }
 
     /** Enable/disable automatic restore of app data at install time. */
     public void setAutoRestore(boolean autoRestore) {
-        mUserBackupManagerService.setAutoRestore(autoRestore);
+        UserBackupManagerService userBackupManagerService =
+                getServiceForUser(UserHandle.USER_SYSTEM, "setAutoRestore()");
+
+        if (userBackupManagerService != null) {
+            userBackupManagerService.setAutoRestore(autoRestore);
+        }
     }
 
     /** Mark the backup service as having been provisioned (device has gone through SUW). */
     public void setBackupProvisioned(boolean provisioned) {
-        mUserBackupManagerService.setBackupProvisioned(provisioned);
+        UserBackupManagerService userBackupManagerService =
+                getServiceForUser(UserHandle.USER_SYSTEM, "setBackupProvisioned()");
+
+        if (userBackupManagerService != null) {
+            userBackupManagerService.setBackupProvisioned(provisioned);
+        }
     }
 
     /**
@@ -353,7 +503,10 @@
      */
     public boolean isBackupEnabled(@UserIdInt int userId) {
         enforceCallingPermissionOnUserId(userId, "isBackupEnabled");
-        return mUserBackupManagerService.isBackupEnabled();
+        UserBackupManagerService userBackupManagerService =
+                getServiceForUser(userId, "isBackupEnabled()");
+
+        return userBackupManagerService != null && userBackupManagerService.isBackupEnabled();
     }
 
     // ---------------------------------------------
@@ -362,14 +515,24 @@
 
     /** Checks if the given package {@code packageName} is eligible for backup. */
     public boolean isAppEligibleForBackup(String packageName) {
-        return mUserBackupManagerService.isAppEligibleForBackup(packageName);
+        UserBackupManagerService userBackupManagerService =
+                getServiceForUser(UserHandle.USER_SYSTEM, "isAppEligibleForBackup()");
+
+        return userBackupManagerService != null
+                && userBackupManagerService.isAppEligibleForBackup(packageName);
     }
 
     /**
      * Returns from the inputted packages {@code packages}, the ones that are eligible for backup.
      */
+    @Nullable
     public String[] filterAppsEligibleForBackup(String[] packages) {
-        return mUserBackupManagerService.filterAppsEligibleForBackup(packages);
+        UserBackupManagerService userBackupManagerService =
+                getServiceForUser(UserHandle.USER_SYSTEM, "filterAppsEligibleForBackup()");
+
+        return userBackupManagerService == null
+                ? null
+                : userBackupManagerService.filterAppsEligibleForBackup(packages);
     }
 
     /**
@@ -378,7 +541,12 @@
      */
     public void backupNow(@UserIdInt int userId) {
         enforceCallingPermissionOnUserId(userId, "backupNow");
-        mUserBackupManagerService.backupNow();
+        UserBackupManagerService userBackupManagerService =
+                getServiceForUser(userId, "backupNow()");
+
+        if (userBackupManagerService != null) {
+            userBackupManagerService.backupNow();
+        }
     }
 
     /**
@@ -392,13 +560,23 @@
             IBackupManagerMonitor monitor,
             int flags) {
         enforceCallingPermissionOnUserId(userId, "requestBackup");
-        return mUserBackupManagerService.requestBackup(packages, observer, monitor, flags);
+        UserBackupManagerService userBackupManagerService =
+                getServiceForUser(userId, "requestBackup()");
+
+        return userBackupManagerService == null
+                ? BackupManager.ERROR_BACKUP_NOT_ALLOWED
+                : userBackupManagerService.requestBackup(packages, observer, monitor, flags);
     }
 
     /** Cancel all running backup operations. */
     public void cancelBackups(@UserIdInt int userId) {
         enforceCallingPermissionOnUserId(userId, "cancelBackups");
-        mUserBackupManagerService.cancelBackups();
+        UserBackupManagerService userBackupManagerService =
+                getServiceForUser(userId, "cancelBackups()");
+
+        if (userBackupManagerService != null) {
+            userBackupManagerService.cancelBackups();
+        }
     }
 
     /**
@@ -410,7 +588,11 @@
      *     return value to the callback {@link JobService#onStartJob(JobParameters)}.
      */
     public boolean beginFullBackup(FullBackupJob scheduledJob) {
-        return mUserBackupManagerService.beginFullBackup(scheduledJob);
+        UserBackupManagerService userBackupManagerService =
+                getServiceForUser(UserHandle.USER_SYSTEM, "beginFullBackup()");
+
+        return userBackupManagerService != null
+                && userBackupManagerService.beginFullBackup(scheduledJob);
     }
 
     /**
@@ -418,14 +600,24 @@
      * longer met for running the full backup job.
      */
     public void endFullBackup() {
-        mUserBackupManagerService.endFullBackup();
+        UserBackupManagerService userBackupManagerService =
+                getServiceForUser(UserHandle.USER_SYSTEM, "endFullBackup()");
+
+        if (userBackupManagerService != null) {
+            userBackupManagerService.endFullBackup();
+        }
     }
 
     /**
      * Run a full backup pass for the given packages {@code packageNames}. Used by 'adb shell bmgr'.
      */
     public void fullTransportBackup(String[] packageNames) {
-        mUserBackupManagerService.fullTransportBackup(packageNames);
+        UserBackupManagerService userBackupManagerService =
+                getServiceForUser(UserHandle.USER_SYSTEM, "fullTransportBackup()");
+
+        if (userBackupManagerService != null) {
+            userBackupManagerService.fullTransportBackup(packageNames);
+        }
     }
 
     // ---------------------------------------------
@@ -437,15 +629,26 @@
      * called from the {@link PackageManager}.
      */
     public void restoreAtInstall(String packageName, int token) {
-        mUserBackupManagerService.restoreAtInstall(packageName, token);
+        UserBackupManagerService userBackupManagerService =
+                getServiceForUser(UserHandle.USER_SYSTEM, "restoreAtInstall()");
+
+        if (userBackupManagerService != null) {
+            userBackupManagerService.restoreAtInstall(packageName, token);
+        }
     }
 
     /**
      * Begin a restore for the specified package {@code packageName} using the specified transport
      * {@code transportName}.
      */
+    @Nullable
     public IRestoreSession beginRestoreSession(String packageName, String transportName) {
-        return mUserBackupManagerService.beginRestoreSession(packageName, transportName);
+        UserBackupManagerService userBackupManagerService =
+                getServiceForUser(UserHandle.USER_SYSTEM, "beginRestoreSession()");
+
+        return userBackupManagerService == null
+                ? null
+                : userBackupManagerService.beginRestoreSession(packageName, transportName);
     }
 
     /**
@@ -453,7 +656,12 @@
      * the active set if possible, else the ancestral one. Returns zero if none available.
      */
     public long getAvailableRestoreToken(String packageName) {
-        return mUserBackupManagerService.getAvailableRestoreToken(packageName);
+        UserBackupManagerService userBackupManagerService =
+                getServiceForUser(UserHandle.USER_SYSTEM, "getAvailableRestoreToken()");
+
+        return userBackupManagerService == null
+                ? 0
+                : userBackupManagerService.getAvailableRestoreToken(packageName);
     }
 
     // ---------------------------------------------
@@ -462,12 +670,19 @@
 
     /** Sets the backup password used when running adb backup. */
     public boolean setBackupPassword(String currentPassword, String newPassword) {
-        return mUserBackupManagerService.setBackupPassword(currentPassword, newPassword);
+        UserBackupManagerService userBackupManagerService =
+                getServiceForUser(UserHandle.USER_SYSTEM, "setBackupPassword()");
+
+        return userBackupManagerService != null
+                && userBackupManagerService.setBackupPassword(currentPassword, newPassword);
     }
 
     /** Returns {@code true} if adb backup was run with a password, else returns {@code false}. */
     public boolean hasBackupPassword() {
-        return mUserBackupManagerService.hasBackupPassword();
+        UserBackupManagerService userBackupManagerService =
+                getServiceForUser(UserHandle.USER_SYSTEM, "hasBackupPassword()");
+
+        return userBackupManagerService != null && userBackupManagerService.hasBackupPassword();
     }
 
     /**
@@ -489,18 +704,22 @@
             boolean doKeyValue,
             String[] packageNames) {
         enforceCallingPermissionOnUserId(userId, "adbBackup");
+        UserBackupManagerService userBackupManagerService =
+                getServiceForUser(userId, "adbBackup()");
 
-        mUserBackupManagerService.adbBackup(
-                fd,
-                includeApks,
-                includeObbs,
-                includeShared,
-                doWidgets,
-                doAllApps,
-                includeSystem,
-                doCompress,
-                doKeyValue,
-                packageNames);
+        if (userBackupManagerService != null) {
+            userBackupManagerService.adbBackup(
+                    fd,
+                    includeApks,
+                    includeObbs,
+                    includeShared,
+                    doWidgets,
+                    doAllApps,
+                    includeSystem,
+                    doCompress,
+                    doKeyValue,
+                    packageNames);
+        }
     }
 
     /**
@@ -510,8 +729,12 @@
      */
     public void adbRestore(@UserIdInt int userId, ParcelFileDescriptor fd) {
         enforceCallingPermissionOnUserId(userId, "setBackupEnabled");
+        UserBackupManagerService userBackupManagerService =
+                getServiceForUser(userId, "adbRestore()");
 
-        mUserBackupManagerService.adbRestore(fd);
+        if (userBackupManagerService != null) {
+            userBackupManagerService.adbRestore(fd);
+        }
     }
 
     /**
@@ -524,8 +747,13 @@
             String currentPassword,
             String encryptionPassword,
             IFullBackupRestoreObserver observer) {
-        mUserBackupManagerService.acknowledgeAdbBackupOrRestore(
-                token, allow, currentPassword, encryptionPassword, observer);
+        UserBackupManagerService userBackupManagerService =
+                getServiceForUser(UserHandle.USER_SYSTEM, "acknowledgeAdbBackupOrRestore()");
+
+        if (userBackupManagerService != null) {
+            userBackupManagerService.acknowledgeAdbBackupOrRestore(
+                    token, allow, currentPassword, encryptionPassword, observer);
+        }
     }
 
     // ---------------------------------------------
@@ -534,7 +762,12 @@
 
     /** Prints service state for 'dumpsys backup'. */
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
-        mUserBackupManagerService.dump(fd, pw, args);
+        UserBackupManagerService userBackupManagerService =
+                getServiceForUser(UserHandle.USER_SYSTEM, "dump()");
+
+        if (userBackupManagerService != null) {
+            userBackupManagerService.dump(fd, pw, args);
+        }
     }
 
     private static boolean readBackupEnableState(int userId) {
@@ -592,7 +825,7 @@
             if (userId == UserHandle.USER_SYSTEM) {
                 sInstance.initializeServiceAndUnlockSystemUser();
             } else {
-                sInstance.startServiceForUser(userId);
+                sInstance.unlockUser(userId);
             }
         }
     }
diff --git a/services/backup/java/com/android/server/backup/Trampoline.java b/services/backup/java/com/android/server/backup/Trampoline.java
index 4acd5c4..59b72f9 100644
--- a/services/backup/java/com/android/server/backup/Trampoline.java
+++ b/services/backup/java/com/android/server/backup/Trampoline.java
@@ -116,7 +116,7 @@
         return SystemProperties.getBoolean(BACKUP_DISABLE_PROPERTY, false);
     }
 
-    protected boolean isMultiUserEnabled() {
+    private boolean isMultiUserEnabled() {
         return Settings.Global.getInt(
                 mContext.getContentResolver(),
                 Settings.Global.BACKUP_MULTI_USER_ENABLED,
@@ -145,6 +145,10 @@
         return new BackupManagerService(mContext, this, mHandlerThread);
     }
 
+    protected void postToHandler(Runnable runnable) {
+        mHandler.post(runnable);
+    }
+
     /**
      * Initialize {@link BackupManagerService} if the backup service is not disabled. Only the
      * system user can initialize the service.
@@ -174,14 +178,18 @@
      * to initialize {@link BackupManagerService} and set backup state for the system user.
      */
     void initializeServiceAndUnlockSystemUser() {
-        mHandler.post(
+        postToHandler(
                 () -> {
+                    // Initialize the backup service.
                     Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "backup init");
                     initializeService(UserHandle.USER_SYSTEM);
                     Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
 
+                    // Start the service for the system user.
                     BackupManagerService service = mService;
                     if (service != null) {
+                        Slog.i(TAG, "Starting service for system user");
+                        service.startServiceForUser(UserHandle.USER_SYSTEM);
                         Slog.i(TAG, "Unlocking system user");
                         service.unlockSystemUser();
                     }
@@ -195,20 +203,21 @@
      */
     // TODO(b/120212806): Consolidate service start for system and non-system users when system
     // user-only logic is removed.
-    void startServiceForUser(int userId) {
+    void unlockUser(int userId) {
         if (!isMultiUserEnabled()) {
             Slog.i(TAG, "Multi-user disabled, cannot start service for user: " + userId);
             return;
         }
 
-        mHandler.post(
-                () -> {
-                    BackupManagerService service = mService;
-                    if (service != null) {
-                        Slog.i(TAG, "Starting service for user: " + userId);
-                        service.startServiceForUser(userId);
-                    }
-                });
+        postToHandler(() -> startServiceForUser(userId));
+    }
+
+    private void startServiceForUser(int userId) {
+        BackupManagerService service = mService;
+        if (service != null) {
+            Slog.i(TAG, "Starting service for user: " + userId);
+            service.startServiceForUser(userId);
+        }
     }
 
     /**
@@ -242,6 +251,7 @@
             if (makeActive) {
                 mService = createBackupManagerService();
                 mSuppressFile.delete();
+                startServiceForUser(userId);
             } else {
                 mService = null;
                 try {
diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java
index 08034f7..0fa996e 100644
--- a/services/core/java/com/android/server/AlarmManagerService.java
+++ b/services/core/java/com/android/server/AlarmManagerService.java
@@ -1123,8 +1123,6 @@
         rescheduleKernelAlarmsLocked();
         updateNextAlarmClockLocked();
 
-        // And send a TIME_TICK right now, since it is important to get the UI updated.
-        mHandler.post(() ->  getContext().sendBroadcastAsUser(mTimeTickIntent, UserHandle.ALL));
     }
 
     static final class InFlight {
@@ -1298,7 +1296,7 @@
         mInjector.init();
 
         synchronized (mLock) {
-            mHandler = new AlarmHandler(Looper.myLooper());
+            mHandler = new AlarmHandler();
             mConstants = new Constants(mHandler);
 
             mNextWakeup = mNextNonWakeup = 0;
@@ -3050,6 +3048,9 @@
                         mNonInteractiveTime = dur;
                     }
                 }
+                // And send a TIME_TICK right now, since it is important to get the UI updated.
+                mHandler.post(() ->
+                        getContext().sendBroadcastAsUser(mTimeTickIntent, UserHandle.ALL));
             } else {
                 mNonInteractiveStartTime = nowELAPSED;
             }
@@ -3838,7 +3839,8 @@
         mWakeLock.setWorkSource(null);
     }
 
-    private class AlarmHandler extends Handler {
+    @VisibleForTesting
+    class AlarmHandler extends Handler {
         public static final int ALARM_EVENT = 1;
         public static final int SEND_NEXT_ALARM_CLOCK_CHANGED = 2;
         public static final int LISTENER_TIMEOUT = 3;
@@ -3847,8 +3849,8 @@
         public static final int APP_STANDBY_PAROLE_CHANGED = 6;
         public static final int REMOVE_FOR_STOPPED = 7;
 
-        AlarmHandler(Looper looper) {
-            super(looper);
+        AlarmHandler() {
+            super(Looper.myLooper());
         }
 
         public void postRemoveForStopped(int uid) {
@@ -3961,8 +3963,8 @@
 
             final WorkSource workSource = null; // Let system take blame for time tick events.
             setImpl(ELAPSED_REALTIME, mInjector.getElapsedRealtime() + tickEventDelay, 0,
-                    0, null, mTimeTickTrigger, null, AlarmManager.FLAG_STANDALONE, workSource,
-                    null, Process.myUid(), "android");
+                    0, null, mTimeTickTrigger, "TIME_TICK", AlarmManager.FLAG_STANDALONE,
+                    workSource, null, Process.myUid(), "android");
 
             // Finally, remember when we set the tick alarm
             synchronized (mLock) {
diff --git a/services/core/java/com/android/server/connectivity/NetdEventListenerService.java b/services/core/java/com/android/server/connectivity/NetdEventListenerService.java
index 422f556..e40949b 100644
--- a/services/core/java/com/android/server/connectivity/NetdEventListenerService.java
+++ b/services/core/java/com/android/server/connectivity/NetdEventListenerService.java
@@ -217,6 +217,19 @@
     @Override
     // Called concurrently by multiple binder threads.
     // This method must not block or perform long-running operations.
+    public synchronized void onNat64PrefixEvent(int netId,
+            boolean added, String prefixString, int prefixLength)
+            throws RemoteException {
+        for (INetdEventCallback callback : mNetdEventCallbackList) {
+            if (callback != null) {
+                callback.onNat64PrefixEvent(netId, added, prefixString, prefixLength);
+            }
+        }
+    }
+
+    @Override
+    // Called concurrently by multiple binder threads.
+    // This method must not block or perform long-running operations.
     public synchronized void onPrivateDnsValidationEvent(int netId,
             String ipAddress, String hostname, boolean validated)
             throws RemoteException {
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index e7c3c7b..d96b6cb 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -1951,11 +1951,6 @@
     }
 
     // Native callback.
-    private int getPointerDisplayId() {
-        return mWindowManagerCallbacks.getPointerDisplayId();
-    }
-
-    // Native callback.
     private String[] getKeyboardLayoutOverlay(InputDeviceIdentifier identifier) {
         if (!mSystemReady) {
             return null;
@@ -2022,8 +2017,6 @@
                 KeyEvent event, int policyFlags);
 
         public int getPointerLayer();
-
-        public int getPointerDisplayId();
     }
 
     /**
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 28a6ba4..67293b9 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -675,13 +675,6 @@
     private final int mHardKeyboardBehavior;
 
     /**
-     * Whether we temporarily allow IMEs implemented in instant apps to run for testing.
-     *
-     * <p>Note: This is quite dangerous.  Don't forget to reset after you finish testing.</p>
-     */
-    private boolean mBindInstantServiceAllowed = false;
-
-    /**
      * Internal state snapshot when {@link #MSG_START_INPUT} message is about to be posted to the
      * internal message queue. Any subsequent state change inside {@link InputMethodManagerService}
      * will not affect those tasks that are already posted.
@@ -1135,8 +1128,7 @@
                 final PackageManager pm = mContext.getPackageManager();
                 final List<ResolveInfo> services = pm.queryIntentServicesAsUser(
                         new Intent(InputMethod.SERVICE_INTERFACE).setPackage(packageName),
-                        getComponentMatchingFlags(PackageManager.MATCH_DISABLED_COMPONENTS),
-                        getChangingUserId());
+                        PackageManager.MATCH_DISABLED_COMPONENTS, getChangingUserId());
                 // No need to lock this because we access it only on getRegisteredHandler().
                 if (!services.isEmpty()) {
                     mImePackageAppeared = true;
@@ -1684,9 +1676,6 @@
             Slog.e(TAG, "--- bind failed: service = " + service + ", conn = " + conn);
             return false;
         }
-        if (mBindInstantServiceAllowed) {
-            flags |= Context.BIND_ALLOW_INSTANT;
-        }
         return mContext.bindServiceAsUser(service, conn, flags,
                 new UserHandle(mSettings.getCurrentUserId()));
     }
@@ -3631,16 +3620,6 @@
         return false;
     }
 
-    @PackageManager.ResolveInfoFlags
-    private int getComponentMatchingFlags(@PackageManager.ResolveInfoFlags int baseFlags) {
-        synchronized (mMethodMap) {
-            if (mBindInstantServiceAllowed) {
-                baseFlags |= PackageManager.MATCH_INSTANT;
-            }
-            return baseFlags;
-        }
-    }
-
     @GuardedBy("mMethodMap")
     void buildInputMethodListLocked(boolean resetDefaultEnabledIme) {
         if (DEBUG) {
@@ -3664,8 +3643,7 @@
         // services depending on the unlock state for the specified user.
         final List<ResolveInfo> services = pm.queryIntentServicesAsUser(
                 new Intent(InputMethod.SERVICE_INTERFACE),
-                getComponentMatchingFlags(PackageManager.GET_META_DATA
-                        | PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS),
+                PackageManager.GET_META_DATA | PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS,
                 mSettings.getCurrentUserId());
 
         final ArrayMap<String, List<InputMethodSubtype>> additionalSubtypeMap =
@@ -3707,8 +3685,7 @@
             // conservative, but it seems we cannot use it for now (Issue 35176630).
             final List<ResolveInfo> allInputMethodServices = pm.queryIntentServicesAsUser(
                     new Intent(InputMethod.SERVICE_INTERFACE),
-                    getComponentMatchingFlags(PackageManager.MATCH_DISABLED_COMPONENTS),
-                    mSettings.getCurrentUserId());
+                    PackageManager.MATCH_DISABLED_COMPONENTS, mSettings.getCurrentUserId());
             final int N = allInputMethodServices.size();
             for (int i = 0; i < N; ++i) {
                 final ServiceInfo si = allInputMethodServices.get(i).serviceInfo;
@@ -4606,8 +4583,7 @@
         synchronized (mMethodMap) {
             p.println("Current Input Method Manager state:");
             int N = mMethodList.size();
-            p.println("  Input Methods: mMethodMapUpdateCount=" + mMethodMapUpdateCount
-                    + " mBindInstantServiceAllowed=" + mBindInstantServiceAllowed);
+            p.println("  Input Methods: mMethodMapUpdateCount=" + mMethodMapUpdateCount);
             for (int i=0; i<N; i++) {
                 InputMethodInfo info = mMethodList.get(i);
                 p.println("  InputMethod #" + i + ":");
@@ -4719,9 +4695,6 @@
             if ("refresh_debug_properties".equals(cmd)) {
                 return refreshDebugProperties();
             }
-            if ("set-bind-instant-service-allowed".equals(cmd)) {
-                return setBindInstantServiceAllowed();
-            }
 
             // For existing "adb shell ime <command>".
             if ("ime".equals(cmd)) {
@@ -4752,12 +4725,6 @@
 
         @BinderThread
         @ShellCommandResult
-        private int setBindInstantServiceAllowed() {
-            return mService.handleSetBindInstantServiceAllowed(this);
-        }
-
-        @BinderThread
-        @ShellCommandResult
         private int refreshDebugProperties() {
             DebugFlags.FLAG_OPTIMIZE_START_INPUT.refresh();
             return ShellCommandResult.SUCCESS;
@@ -4774,9 +4741,6 @@
                 pw.println("    Synonym of dumpsys.");
                 pw.println("  ime <command> [options]");
                 pw.println("    Manipulate IMEs.  Run \"ime help\" for details.");
-                pw.println("  set-bind-instant-service-allowed true|false ");
-                pw.println("    Set whether binding to services provided by instant apps is "
-                        + "allowed.");
             }
         }
 
@@ -4825,53 +4789,6 @@
     // Shell command handlers:
 
     /**
-     * Handles {@code adb shell cmd input_method set-bind-instant-service-allowed}.
-     *
-     * @param shellCommand {@link ShellCommand} object that is handling this command.
-     * @return Exit code of the command.
-     */
-    @BinderThread
-    @RequiresPermission(android.Manifest.permission.MANAGE_BIND_INSTANT_SERVICE)
-    @ShellCommandResult
-    private int handleSetBindInstantServiceAllowed(@NonNull ShellCommand shellCommand) {
-        final String allowedString = shellCommand.getNextArgRequired();
-        if (allowedString == null) {
-            shellCommand.getErrPrintWriter().println("Error: no true/false specified");
-            return ShellCommandResult.FAILURE;
-        }
-        final boolean allowed = Boolean.parseBoolean(allowedString);
-        synchronized (mMethodMap) {
-            if (mContext.checkCallingOrSelfPermission(
-                    android.Manifest.permission.MANAGE_BIND_INSTANT_SERVICE)
-                    != PackageManager.PERMISSION_GRANTED) {
-                shellCommand.getErrPrintWriter().print(
-                        "Caller must have MANAGE_BIND_INSTANT_SERVICE permission");
-                return ShellCommandResult.FAILURE;
-            }
-
-            if (mBindInstantServiceAllowed == allowed) {
-                // Nothing to do.
-                return ShellCommandResult.SUCCESS;
-            }
-            mBindInstantServiceAllowed = allowed;
-
-            // Rebuild everything.
-            final long ident = Binder.clearCallingIdentity();
-            try {
-                // Reset the current IME
-                resetSelectedInputMethodAndSubtypeLocked(null);
-                // Also reset the settings of the current IME
-                mSettings.putSelectedInputMethod(null);
-                buildInputMethodListLocked(false /* resetDefaultEnabledIme */);
-                updateInputMethodsFromSettingsLocked(true /* enabledMayChange */);
-            } finally {
-                Binder.restoreCallingIdentity(ident);
-            }
-        }
-        return ShellCommandResult.SUCCESS;
-    }
-
-    /**
      * Handles {@code adb shell ime list}.
      * @param shellCommand {@link ShellCommand} object that is handling this command.
      * @return Exit code of the command.
diff --git a/services/core/java/com/android/server/pm/dex/PackageDexUsage.java b/services/core/java/com/android/server/pm/dex/PackageDexUsage.java
index 519a20d..33a9650 100644
--- a/services/core/java/com/android/server/pm/dex/PackageDexUsage.java
+++ b/services/core/java/com/android/server/pm/dex/PackageDexUsage.java
@@ -16,9 +16,9 @@
 
 package com.android.server.pm.dex;
 
+import android.os.Build;
 import android.util.AtomicFile;
 import android.util.Slog;
-import android.os.Build;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
@@ -26,26 +26,27 @@
 import com.android.server.pm.AbstractStatsBase;
 import com.android.server.pm.PackageManagerServiceUtils;
 
+import dalvik.system.VMRuntime;
+
+import libcore.io.IoUtils;
+
 import java.io.BufferedReader;
 import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
-import java.io.InputStreamReader;
 import java.io.IOException;
+import java.io.InputStreamReader;
 import java.io.OutputStreamWriter;
 import java.io.Reader;
 import java.io.StringWriter;
 import java.io.Writer;
 import java.util.Collections;
-import java.util.Iterator;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.Iterator;
 import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
 
-import dalvik.system.VMRuntime;
-import libcore.io.IoUtils;
-
 /**
  * Stat file which store usage information about dex files.
  */
@@ -86,6 +87,13 @@
     private static final String UNSUPPORTED_CLASS_LOADER_CONTEXT =
             "=UnsupportedClassLoaderContext=";
 
+    /**
+     * Limit on how many secondary DEX paths we store for a single owner, to avoid one app causing
+     * unbounded memory consumption.
+     */
+    @VisibleForTesting
+    /* package */ static final int MAX_SECONDARY_FILES_PER_OWNER = 100;
+
     // Map which structures the information we have on a package.
     // Maps package name to package data (which stores info about UsedByOtherApps and
     // secondary dex files.).
@@ -164,8 +172,12 @@
                     DexUseInfo existingData = packageUseInfo.mDexUseInfoMap.get(dexPath);
                     if (existingData == null) {
                         // It's the first time we see this dex file.
-                        packageUseInfo.mDexUseInfoMap.put(dexPath, newData);
-                        return true;
+                        if (packageUseInfo.mDexUseInfoMap.size() < MAX_SECONDARY_FILES_PER_OWNER) {
+                            packageUseInfo.mDexUseInfoMap.put(dexPath, newData);
+                            return true;
+                        } else {
+                            return updateLoadingPackages;
+                        }
                     } else {
                         if (ownerUserId != existingData.mOwnerUserId) {
                             // Oups, this should never happen, the DexManager who calls this should
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 64ff9cf..2157c99 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -60,6 +60,7 @@
 import android.hardware.display.DisplayManager;
 import android.os.Binder;
 import android.os.Bundle;
+import android.os.Debug;
 import android.os.Environment;
 import android.os.FileObserver;
 import android.os.FileUtils;
@@ -85,6 +86,7 @@
 import android.util.EventLog;
 import android.util.Slog;
 import android.util.SparseArray;
+import android.util.SparseBooleanArray;
 import android.util.Xml;
 import android.view.Display;
 import android.view.IWindowManager;
@@ -120,12 +122,13 @@
 import java.util.List;
 import java.util.Objects;
 import java.util.function.Consumer;
+import java.util.function.Predicate;
 
 public class WallpaperManagerService extends IWallpaperManager.Stub
         implements IWallpaperManagerService {
-    static final String TAG = "WallpaperManagerService";
-    static final boolean DEBUG = false;
-    static final boolean DEBUG_LIVE = DEBUG || true;
+    private static final String TAG = "WallpaperManagerService";
+    private static final boolean DEBUG = false;
+    private static final boolean DEBUG_LIVE = true;
 
     public static class Lifecycle extends SystemService {
         private IWallpaperManagerService mService;
@@ -163,14 +166,14 @@
         }
     }
 
-    final Object mLock = new Object();
+    private final Object mLock = new Object();
 
     /**
      * Minimum time between crashes of a wallpaper service for us to consider
      * restarting it vs. just reverting to the static wallpaper.
      */
-    static final long MIN_WALLPAPER_CRASH_TIME = 10000;
-    static final int MAX_WALLPAPER_COMPONENT_LOG_LENGTH = 128;
+    private static final long MIN_WALLPAPER_CRASH_TIME = 10000;
+    private static final int MAX_WALLPAPER_COMPONENT_LOG_LENGTH = 128;
     static final String WALLPAPER = "wallpaper_orig";
     static final String WALLPAPER_CROP = "wallpaper";
     static final String WALLPAPER_LOCK_ORIG = "wallpaper_lock_orig";
@@ -178,7 +181,7 @@
     static final String WALLPAPER_INFO = "wallpaper_info.xml";
 
     // All the various per-user state files we need to be aware of
-    static final String[] sPerUserFiles = new String[] {
+    private static final String[] sPerUserFiles = new String[] {
         WALLPAPER, WALLPAPER_CROP,
         WALLPAPER_LOCK_ORIG, WALLPAPER_LOCK_CROP,
         WALLPAPER_INFO
@@ -335,7 +338,7 @@
         }
     }
 
-    void notifyLockWallpaperChanged() {
+    private void notifyLockWallpaperChanged() {
         final IWallpaperManagerCallback cb = mKeyguardListener;
         if (cb != null) {
             try {
@@ -487,7 +490,7 @@
         boolean success = false;
 
         // Only generate crop for default display.
-        final WallpaperData.DisplayData wpData = getDisplayDataOrCreate(wallpaper, DEFAULT_DISPLAY);
+        final DisplayData wpData = getDisplayDataOrCreate(DEFAULT_DISPLAY);
         Rect cropHint = new Rect(wallpaper.cropHint);
 
         if (DEBUG) {
@@ -640,11 +643,11 @@
         }
     }
 
-    final Context mContext;
-    final IWindowManager mIWindowManager;
-    final IPackageManager mIPackageManager;
-    final MyPackageMonitor mMonitor;
-    final AppOpsManager mAppOpsManager;
+    private final Context mContext;
+    private final IWindowManager mIWindowManager;
+    private final IPackageManager mIPackageManager;
+    private final MyPackageMonitor mMonitor;
+    private final AppOpsManager mAppOpsManager;
 
     private final DisplayManager mDisplayManager;
     private final DisplayManager.DisplayListener mDisplayListener =
@@ -654,11 +657,23 @@
         public void onDisplayAdded(int displayId) {
             synchronized (mLock) {
                 if (mLastWallpaper != null) {
-                    final WallpaperConnection.DisplayConnector connector =
-                            mLastWallpaper.connection.getDisplayConnectorOrCreate(displayId);
-                    if (connector == null) return;
-
-                    connector.connectLocked(mLastWallpaper.connection, mLastWallpaper);
+                    if (supportsMultiDisplay(mLastWallpaper.connection)) {
+                        final WallpaperConnection.DisplayConnector connector =
+                                mLastWallpaper.connection.getDisplayConnectorOrCreate(displayId);
+                        if (connector == null) return;
+                        connector.connectLocked(mLastWallpaper.connection, mLastWallpaper);
+                        return;
+                    }
+                    // System wallpaper does not support multiple displays, attach this display to
+                    // the fallback wallpaper.
+                    if (mFallbackWallpaper != null) {
+                        final WallpaperConnection.DisplayConnector connector = mFallbackWallpaper
+                                .connection.getDisplayConnectorOrCreate(displayId);
+                        if (connector == null) return;
+                        connector.connectLocked(mFallbackWallpaper.connection, mFallbackWallpaper);
+                    } else {
+                        Slog.w(TAG, "No wallpaper can be added to the new display");
+                    }
                 }
             }
         }
@@ -667,12 +682,19 @@
         public void onDisplayRemoved(int displayId) {
             synchronized (mLock) {
                 if (mLastWallpaper != null) {
-                    final WallpaperConnection.DisplayConnector connector =
-                            mLastWallpaper.connection.getDisplayConnectorOrCreate(displayId);
+                    WallpaperData targetWallpaper = null;
+                    if (mLastWallpaper.connection.containsDisplay(displayId)) {
+                        targetWallpaper = mLastWallpaper;
+                    } else if (mFallbackWallpaper.connection.containsDisplay(displayId)) {
+                        targetWallpaper = mFallbackWallpaper;
+                    }
+                    if (targetWallpaper == null) return;
+                    WallpaperConnection.DisplayConnector connector =
+                            targetWallpaper.connection.getDisplayConnectorOrCreate(displayId);
                     if (connector == null) return;
                     connector.disconnectLocked();
-                    mLastWallpaper.connection.removeDisplayConnector(displayId);
-                    mLastWallpaper.removeDisplayData(displayId);
+                    targetWallpaper.connection.removeDisplayConnector(displayId);
+                    removeDisplayData(displayId);
                 }
             }
         }
@@ -686,35 +708,40 @@
      * Map of color listeners per user id.
      * The key will be the id of a user or UserHandle.USER_ALL - for wildcard listeners.
      */
-    final SparseArray<RemoteCallbackList<IWallpaperManagerCallback>> mColorsChangedListeners;
-    WallpaperData mLastWallpaper;
-    IWallpaperManagerCallback mKeyguardListener;
-    boolean mWaitingForUnlock;
-    boolean mShuttingDown;
+    private final SparseArray<RemoteCallbackList<IWallpaperManagerCallback>>
+            mColorsChangedListeners;
+    private WallpaperData mLastWallpaper;
+    private IWallpaperManagerCallback mKeyguardListener;
+    private boolean mWaitingForUnlock;
+    private boolean mShuttingDown;
 
     /**
      * ID of the current wallpaper, changed every time anything sets a wallpaper.
      * This is used for external detection of wallpaper update activity.
      */
-    int mWallpaperId;
+    private int mWallpaperId;
 
     /**
      * Name of the component used to display bitmap wallpapers from either the gallery or
      * built-in wallpapers.
      */
-    final ComponentName mImageWallpaper;
+    private final ComponentName mImageWallpaper;
 
     /**
      * Name of the default wallpaper component; might be different from mImageWallpaper
      */
-    final ComponentName mDefaultWallpaperComponent;
+    private final ComponentName mDefaultWallpaperComponent;
 
-    final SparseArray<WallpaperData> mWallpaperMap = new SparseArray<WallpaperData>();
-    final SparseArray<WallpaperData> mLockWallpaperMap = new SparseArray<WallpaperData>();
+    private final SparseArray<WallpaperData> mWallpaperMap = new SparseArray<WallpaperData>();
+    private final SparseArray<WallpaperData> mLockWallpaperMap = new SparseArray<WallpaperData>();
 
-    final SparseArray<Boolean> mUserRestorecon = new SparseArray<Boolean>();
-    int mCurrentUserId = UserHandle.USER_NULL;
-    boolean mInAmbientMode;
+    private SparseArray<DisplayData> mDisplayDatas = new SparseArray<>();
+
+    private WallpaperData mFallbackWallpaper;
+
+    private final SparseBooleanArray mUserRestorecon = new SparseBooleanArray();
+    private int mCurrentUserId = UserHandle.USER_NULL;
+    private boolean mInAmbientMode;
 
     static class WallpaperData {
 
@@ -780,18 +807,6 @@
         private RemoteCallbackList<IWallpaperManagerCallback> callbacks
                 = new RemoteCallbackList<IWallpaperManagerCallback>();
 
-        private static final class DisplayData {
-            int mWidth = -1;
-            int mHeight = -1;
-            final Rect mPadding = new Rect(0, 0, 0, 0);
-            final int mDisplayId;
-
-            DisplayData(int displayId) {
-                mDisplayId = displayId;
-            }
-        }
-        private SparseArray<DisplayData> mDisplayDatas = new SparseArray<>();
-
         /**
          * The crop hint supplied for displaying a subset of the source image
          */
@@ -812,24 +827,34 @@
         boolean sourceExists() {
             return wallpaperFile.exists();
         }
+    }
 
-        void removeDisplayData(int displayId) {
-            mDisplayDatas.remove(displayId);
+    private static final class DisplayData {
+        int mWidth = -1;
+        int mHeight = -1;
+        final Rect mPadding = new Rect(0, 0, 0, 0);
+        final int mDisplayId;
+
+        DisplayData(int displayId) {
+            mDisplayId = displayId;
         }
     }
 
-    private WallpaperData.DisplayData getDisplayDataOrCreate(WallpaperData data, int displayId) {
-        WallpaperData.DisplayData wpdData = data.mDisplayDatas.get(displayId);
+    private void removeDisplayData(int displayId) {
+        mDisplayDatas.remove(displayId);
+    }
+
+    private DisplayData getDisplayDataOrCreate(int displayId) {
+        DisplayData wpdData = mDisplayDatas.get(displayId);
         if (wpdData == null) {
-            wpdData = new WallpaperData.DisplayData(displayId);
+            wpdData = new DisplayData(displayId);
             ensureSaneWallpaperDisplaySize(wpdData, displayId);
-            data.mDisplayDatas.append(displayId, wpdData);
+            mDisplayDatas.append(displayId, wpdData);
         }
         return wpdData;
     }
 
-    private void ensureSaneWallpaperDisplaySize(WallpaperData.DisplayData wpdData,
-            int displayId) {
+    private void ensureSaneWallpaperDisplaySize(DisplayData wpdData, int displayId) {
         // We always want to have some reasonable width hint.
         final int baseSize = getMaximumSizeDimension(displayId);
         if (wpdData.mWidth < baseSize) {
@@ -842,12 +867,16 @@
 
     private int getMaximumSizeDimension(int displayId) {
         Display display = mDisplayManager.getDisplay(displayId);
+        if (display == null) {
+            Slog.w(TAG, "Invalid displayId=" + displayId + " " + Debug.getCallers(4));
+            display = mDisplayManager.getDisplay(DEFAULT_DISPLAY);
+        }
         return display.getMaximumSizeDimension();
     }
 
-    void forEachDisplayData(WallpaperData data, Consumer<WallpaperData.DisplayData> action) {
-        for (int i = data.mDisplayDatas.size() - 1; i >= 0; i--) {
-            final WallpaperData.DisplayData wpdData = data.mDisplayDatas.valueAt(i);
+    void forEachDisplayData(Consumer<DisplayData> action) {
+        for (int i = mDisplayDatas.size() - 1; i >= 0; i--) {
+            final DisplayData wpdData = mDisplayDatas.valueAt(i);
             action.accept(wpdData);
         }
     }
@@ -859,6 +888,36 @@
         return mWallpaperId;
     }
 
+    private boolean supportsMultiDisplay(WallpaperConnection connection) {
+        if (connection != null) {
+            return connection.mInfo == null // This is image wallpaper
+                    || connection.mInfo.supportsMultipleDisplays();
+        }
+        return false;
+    }
+
+    private void updateFallbackConnection() {
+        if (mLastWallpaper == null || mFallbackWallpaper == null) return;
+        final WallpaperConnection systemConnection = mLastWallpaper.connection;
+        final WallpaperConnection fallbackConnection = mFallbackWallpaper.connection;
+        if (supportsMultiDisplay(systemConnection)
+                && fallbackConnection.getConnectedEngineSize() != 0) {
+            fallbackConnection.forEachDisplayConnector(
+                    WallpaperConnection.DisplayConnector::disconnectLocked);
+            fallbackConnection.mDisplayConnector.clear();
+        } else {
+            fallbackConnection.appendConnectorWithCondition(display ->
+                    fallbackConnection.isUsableDisplay(display)
+                            && display.getDisplayId() != DEFAULT_DISPLAY
+                            && !fallbackConnection.containsDisplay(display.getDisplayId()));
+            fallbackConnection.forEachDisplayConnector(connector -> {
+                if (connector.mEngine == null) {
+                    connector.connectLocked(fallbackConnection, mFallbackWallpaper);
+                }
+            });
+        }
+    }
+
     class WallpaperConnection extends IWallpaperConnection.Stub
             implements ServiceConnection {
 
@@ -877,8 +936,7 @@
             }
 
             void ensureStatusHandled() {
-                final WallpaperData.DisplayData wpdData = getDisplayDataOrCreate(mWallpaper,
-                        mDisplayId);
+                final DisplayData wpdData = getDisplayDataOrCreate(mDisplayId);
                 if (mDimensionsChanged) {
                     try {
                         mEngine.setDesiredSize(wpdData.mWidth, wpdData.mHeight);
@@ -906,8 +964,7 @@
                     return;
                 }
 
-                final WallpaperData.DisplayData wpdData = getDisplayDataOrCreate(wallpaper,
-                        mDisplayId);
+                final DisplayData wpdData = getDisplayDataOrCreate(mDisplayId);
                 try {
                     connection.mService.attach(connection, mToken, TYPE_WALLPAPER, false,
                             wpdData.mWidth, wpdData.mHeight,
@@ -982,19 +1039,33 @@
         }
 
         private void initDisplayState() {
-            final Display[] displays = mDisplayManager.getDisplays();
-            for (Display display : displays) {
-                if (isUsableDisplay(display)) {
-                    final int displayId = display.getDisplayId();
-                    mDisplayConnector.append(displayId, new DisplayConnector(displayId));
+            // Do not initialize fallback wallpaper
+            if (!mWallpaper.equals(mFallbackWallpaper)) {
+                if (supportsMultiDisplay(this)) {
+                    // The system wallpaper is image wallpaper or it can supports multiple displays.
+                    appendConnectorWithCondition(this::isUsableDisplay);
+                } else {
+                    // The system wallpaper does not support multiple displays, so just attach it on
+                    // default display.
+                    mDisplayConnector.append(DEFAULT_DISPLAY,
+                            new DisplayConnector(DEFAULT_DISPLAY));
                 }
             }
         }
 
-        // TODO(b/115486823) Support the system decorations change at runtime.
+        private void appendConnectorWithCondition(Predicate<Display> tester) {
+            final Display[] displays = mDisplayManager.getDisplays();
+            for (Display display : displays) {
+                if (tester.test(display)) {
+                    final int displayId = display.getDisplayId();
+                    mDisplayConnector.append(displayId,
+                            new DisplayConnector(displayId));
+                }
+            }
+        }
+
         private boolean isUsableDisplay(Display display) {
             return display != null &&  display.hasAccess(mClientUid)
-                    // TODO(b/114338689) Use WindowManager.supportsSystemDecorations when ready
                     && (display.supportsSystemDecorations()
                             || display.getDisplayId() == DEFAULT_DISPLAY);
         }
@@ -1027,6 +1098,10 @@
             return connector;
         }
 
+        boolean containsDisplay(int displayId) {
+            return mDisplayConnector.get(displayId) != null;
+        }
+
         void removeDisplayConnector(int displayId) {
             final DisplayConnector connector = mDisplayConnector.get(displayId);
             if (connector != null) {
@@ -1044,7 +1119,9 @@
                     // when we have an engine, but I'm not sure about
                     // locking there and anyway we always need to be able to
                     // recover if there is something wrong.
-                    saveSettingsLocked(mWallpaper.userId);
+                    if (!mWallpaper.equals(mFallbackWallpaper)) {
+                        saveSettingsLocked(mWallpaper.userId);
+                    }
                     FgThread.getHandler().removeCallbacks(mResetRunnable);
                 }
             }
@@ -1533,8 +1610,8 @@
                 // This corrects for mislabeling bugs that might have arisen from move-to
                 // operations involving the wallpaper files.  This isn't timing-critical,
                 // so we do it in the background to avoid holding up the user unlock operation.
-                if (mUserRestorecon.get(userId) != Boolean.TRUE) {
-                    mUserRestorecon.put(userId, Boolean.TRUE);
+                if (!mUserRestorecon.get(userId)) {
+                    mUserRestorecon.put(userId, true);
                     Runnable relabeler = new Runnable() {
                         @Override
                         public void run() {
@@ -1562,7 +1639,7 @@
             for (String filename : sPerUserFiles) {
                 new File(wallpaperDir, filename).delete();
             }
-            mUserRestorecon.remove(userId);
+            mUserRestorecon.delete(userId);
         }
     }
 
@@ -1789,7 +1866,7 @@
                 throw new IllegalArgumentException("Cannot find display with id=" + displayId);
             }
 
-            final WallpaperData.DisplayData wpdData = getDisplayDataOrCreate(wallpaper, displayId);
+            final DisplayData wpdData = getDisplayDataOrCreate(displayId);
             if (width != wpdData.mWidth || height != wpdData.mHeight) {
                 wpdData.mWidth = width;
                 wpdData.mHeight = height;
@@ -1826,8 +1903,7 @@
             }
             WallpaperData wallpaper = mWallpaperMap.get(UserHandle.getCallingUserId());
             if (wallpaper != null) {
-                final WallpaperData.DisplayData wpdData = getDisplayDataOrCreate(wallpaper,
-                        displayId);
+                final DisplayData wpdData = getDisplayDataOrCreate(displayId);
                 return wpdData.mWidth;
             } else {
                 return 0;
@@ -1845,8 +1921,7 @@
             }
             WallpaperData wallpaper = mWallpaperMap.get(UserHandle.getCallingUserId());
             if (wallpaper != null) {
-                final WallpaperData.DisplayData wpdData = getDisplayDataOrCreate(wallpaper,
-                        displayId);
+                final DisplayData wpdData = getDisplayDataOrCreate(displayId);
                 return wpdData.mHeight;
             } else {
                 return 0;
@@ -1872,7 +1947,7 @@
                 throw new IllegalArgumentException("padding must be positive: " + padding);
             }
 
-            final WallpaperData.DisplayData wpdData = getDisplayDataOrCreate(wallpaper, displayId);
+            final DisplayData wpdData = getDisplayDataOrCreate(displayId);
             if (!padding.equals(wpdData.mPadding)) {
                 wpdData.mPadding.set(padding);
                 if (displayId == DEFAULT_DISPLAY) saveSettingsLocked(userId);
@@ -1940,8 +2015,7 @@
                 return null;
             }
             // Only for default display.
-            final WallpaperData.DisplayData wpdData = getDisplayDataOrCreate(wallpaper,
-                    DEFAULT_DISPLAY);
+            final DisplayData wpdData = getDisplayDataOrCreate(DEFAULT_DISPLAY);
             try {
                 if (outParams != null) {
                     outParams.putInt("width", wpdData.mWidth);
@@ -2173,14 +2247,8 @@
         // We know a-priori that there is no lock-only wallpaper currently
         WallpaperData lockWP = new WallpaperData(userId,
                 WALLPAPER_LOCK_ORIG, WALLPAPER_LOCK_CROP);
-        final WallpaperData.DisplayData lockWPDData = getDisplayDataOrCreate(lockWP,
-                DEFAULT_DISPLAY);
-        final WallpaperData.DisplayData sysWPDData = getDisplayDataOrCreate(sysWP,
-                DEFAULT_DISPLAY);
         lockWP.wallpaperId = sysWP.wallpaperId;
         lockWP.cropHint.set(sysWP.cropHint);
-        lockWPDData.mWidth = sysWPDData.mWidth;
-        lockWPDData.mHeight = sysWPDData.mHeight;
         lockWP.allowBackup = sysWP.allowBackup;
         lockWP.primaryColors = sysWP.primaryColors;
 
@@ -2320,7 +2388,7 @@
         return false;
     }
 
-    boolean bindWallpaperComponentLocked(ComponentName componentName, boolean force,
+    private boolean bindWallpaperComponentLocked(ComponentName componentName, boolean force,
             boolean fromUser, WallpaperData wallpaper, IRemoteCallback reply) {
         if (DEBUG_LIVE) {
             Slog.v(TAG, "bindWallpaperComponentLocked: componentName=" + componentName);
@@ -2443,15 +2511,17 @@
                 Slog.w(TAG, msg);
                 return false;
             }
-            if (wallpaper.userId == mCurrentUserId && mLastWallpaper != null) {
+            if (wallpaper.userId == mCurrentUserId && mLastWallpaper != null
+                    && !wallpaper.equals(mFallbackWallpaper)) {
                 detachWallpaperLocked(mLastWallpaper);
             }
             wallpaper.wallpaperComponent = componentName;
             wallpaper.connection = newConn;
             newConn.mReply = reply;
-            if (wallpaper.userId == mCurrentUserId) {
+            if (wallpaper.userId == mCurrentUserId && !wallpaper.equals(mFallbackWallpaper)) {
                 mLastWallpaper = wallpaper;
             }
+            updateFallbackConnection();
         } catch (RemoteException e) {
             String msg = "Remote exception for " + componentName + "\n" + e;
             if (fromUser) {
@@ -2463,7 +2533,7 @@
         return true;
     }
 
-    void detachWallpaperLocked(WallpaperData wallpaper) {
+    private void detachWallpaperLocked(WallpaperData wallpaper) {
         if (wallpaper.connection != null) {
             if (wallpaper.connection.mReply != null) {
                 try {
@@ -2473,7 +2543,8 @@
                 wallpaper.connection.mReply = null;
             }
             mContext.unbindService(wallpaper.connection);
-            wallpaper.connection.forEachDisplayConnector(connector -> connector.disconnectLocked());
+            wallpaper.connection.forEachDisplayConnector(
+                    WallpaperConnection.DisplayConnector::disconnectLocked);
             wallpaper.connection.mService = null;
             wallpaper.connection.mDisplayConnector.clear();
             wallpaper.connection = null;
@@ -2481,12 +2552,12 @@
         }
     }
 
-    void clearWallpaperComponentLocked(WallpaperData wallpaper) {
+    private void clearWallpaperComponentLocked(WallpaperData wallpaper) {
         wallpaper.wallpaperComponent = null;
         detachWallpaperLocked(wallpaper);
     }
 
-    void attachServiceLocked(WallpaperConnection conn, WallpaperData wallpaper) {
+    private void attachServiceLocked(WallpaperConnection conn, WallpaperData wallpaper) {
         conn.forEachDisplayConnector(connector-> connector.connectLocked(conn, wallpaper));
     }
 
@@ -2596,8 +2667,7 @@
         if (DEBUG) {
             Slog.v(TAG, "writeWallpaperAttributes id=" + wallpaper.wallpaperId);
         }
-        final WallpaperData.DisplayData wpdData = getDisplayDataOrCreate(wallpaper,
-                DEFAULT_DISPLAY);
+        final DisplayData wpdData = getDisplayDataOrCreate(DEFAULT_DISPLAY);
         out.startTag(null, tag);
         out.attribute(null, "id", Integer.toString(wallpaper.wallpaperId));
         out.attribute(null, "width", Integer.toString(wpdData.mWidth));
@@ -2755,10 +2825,10 @@
                     Slog.i(TAG, "No static wallpaper imagery; defaults will be shown");
                 }
             }
+            initializeFallbackWallpaper();
         }
         boolean success = false;
-        final WallpaperData.DisplayData wpdData = getDisplayDataOrCreate(wallpaper,
-                DEFAULT_DISPLAY);
+        final DisplayData wpdData = getDisplayDataOrCreate(DEFAULT_DISPLAY);
         try {
             stream = new FileInputStream(file);
             XmlPullParser parser = Xml.newPullParser();
@@ -2845,8 +2915,19 @@
         }
     }
 
+    private void initializeFallbackWallpaper() {
+        if (mFallbackWallpaper == null) {
+            if (DEBUG) Slog.d(TAG, "Initialize fallback wallpaper");
+            mFallbackWallpaper = new WallpaperData(
+                    UserHandle.USER_SYSTEM, WALLPAPER, WALLPAPER_CROP);
+            mFallbackWallpaper.allowBackup = false;
+            mFallbackWallpaper.wallpaperId = makeWallpaperIdLocked();
+            bindWallpaperComponentLocked(mImageWallpaper, true, false, mFallbackWallpaper, null);
+        }
+    }
+
     private void ensureSaneWallpaperData(WallpaperData wallpaper, int displayId) {
-        final WallpaperData.DisplayData size = getDisplayDataOrCreate(wallpaper, displayId);
+        final DisplayData size = getDisplayDataOrCreate(displayId);
 
         if (displayId == DEFAULT_DISPLAY) {
             // crop, if not previously specified
@@ -2869,7 +2950,7 @@
             wallpaper.wallpaperId = makeWallpaperIdLocked();
         }
 
-        final WallpaperData.DisplayData wpData = getDisplayDataOrCreate(wallpaper, DEFAULT_DISPLAY);
+        final DisplayData wpData = getDisplayDataOrCreate(DEFAULT_DISPLAY);
 
         if (!keepDimensionHints) {
             wpData.mWidth = Integer.parseInt(parser.getAttributeValue(null, "width"));
@@ -2963,7 +3044,7 @@
     }
 
     // Restore the named resource bitmap to both source + crop files
-    boolean restoreNamedResourceLocked(WallpaperData wallpaper) {
+    private boolean restoreNamedResourceLocked(WallpaperData wallpaper) {
         if (wallpaper.name.length() > 4 && "res:".equals(wallpaper.name.substring(0, 4))) {
             String resName = wallpaper.name.substring(4);
 
@@ -3048,8 +3129,9 @@
             for (int i = 0; i < mWallpaperMap.size(); i++) {
                 WallpaperData wallpaper = mWallpaperMap.valueAt(i);
                 pw.print(" User "); pw.print(wallpaper.userId);
-                    pw.print(": id="); pw.println(wallpaper.wallpaperId);
-                forEachDisplayData(wallpaper, wpSize -> {
+                pw.print(": id="); pw.println(wallpaper.wallpaperId);
+                pw.println(" Display state:");
+                forEachDisplayData(wpSize -> {
                     pw.print("  displayId=");
                     pw.println(wpSize.mDisplayId);
                     pw.print("  mWidth=");
@@ -3072,11 +3154,11 @@
                         pw.println(conn.mInfo.getComponent());
                     }
                     conn.forEachDisplayConnector(connector -> {
-                        pw.print("    mDisplayId=");
+                        pw.print("     mDisplayId=");
                         pw.println(connector.mDisplayId);
-                        pw.print("    mToken=");
+                        pw.print("     mToken=");
                         pw.println(connector.mToken);
-                        pw.print("    mEngine=");
+                        pw.print("     mEngine=");
                         pw.println(connector.mEngine);
                     });
                     pw.print("    mService=");
@@ -3090,18 +3172,38 @@
                 WallpaperData wallpaper = mLockWallpaperMap.valueAt(i);
                 pw.print(" User "); pw.print(wallpaper.userId);
                 pw.print(": id="); pw.println(wallpaper.wallpaperId);
-                forEachDisplayData(wallpaper, wpSize -> {
-                    pw.print("  displayId=");
-                    pw.println(wpSize.mDisplayId);
-                    pw.print("  mWidth="); pw.print(wpSize.mWidth);
-                    pw.print("  mHeight="); pw.println(wpSize.mHeight);
-                    pw.print("  mPadding="); pw.println(wpSize.mPadding);
-                });
                 pw.print("  mCropHint="); pw.println(wallpaper.cropHint);
                 pw.print("  mName=");  pw.println(wallpaper.name);
                 pw.print("  mAllowBackup="); pw.println(wallpaper.allowBackup);
             }
-
+            pw.println("Fallback wallpaper state:");
+            pw.print(" User "); pw.print(mFallbackWallpaper.userId);
+            pw.print(": id="); pw.println(mFallbackWallpaper.wallpaperId);
+            pw.print("  mCropHint="); pw.println(mFallbackWallpaper.cropHint);
+            pw.print("  mName=");  pw.println(mFallbackWallpaper.name);
+            pw.print("  mAllowBackup="); pw.println(mFallbackWallpaper.allowBackup);
+            if (mFallbackWallpaper.connection != null) {
+                WallpaperConnection conn = mFallbackWallpaper.connection;
+                pw.print("  Fallback Wallpaper connection ");
+                pw.print(conn);
+                pw.println(":");
+                if (conn.mInfo != null) {
+                    pw.print("    mInfo.component=");
+                    pw.println(conn.mInfo.getComponent());
+                }
+                conn.forEachDisplayConnector(connector -> {
+                    pw.print("     mDisplayId=");
+                    pw.println(connector.mDisplayId);
+                    pw.print("     mToken=");
+                    pw.println(connector.mToken);
+                    pw.print("     mEngine=");
+                    pw.println(connector.mEngine);
+                });
+                pw.print("    mService=");
+                pw.println(conn.mService);
+                pw.print("    mLastDiedTime=");
+                pw.println(mFallbackWallpaper.lastDiedTime - SystemClock.uptimeMillis());
+            }
         }
     }
 }
diff --git a/services/core/java/com/android/server/wm/InputManagerCallback.java b/services/core/java/com/android/server/wm/InputManagerCallback.java
index f9c9d33..639ed02 100644
--- a/services/core/java/com/android/server/wm/InputManagerCallback.java
+++ b/services/core/java/com/android/server/wm/InputManagerCallback.java
@@ -1,6 +1,5 @@
 package com.android.server.wm;
 
-import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
 
@@ -10,6 +9,7 @@
 import android.os.Debug;
 import android.os.IBinder;
 import android.util.Slog;
+import android.view.InputApplicationHandle;
 import android.view.KeyEvent;
 import android.view.WindowManager;
 
@@ -204,37 +204,6 @@
                 + WindowManagerService.TYPE_LAYER_OFFSET;
     }
 
-    /** Callback to get pointer display id. */
-    @Override
-    public int getPointerDisplayId() {
-        synchronized (mService.mGlobalLock) {
-            // If desktop mode is not enabled, show on the default display.
-            if (!mService.mForceDesktopModeOnExternalDisplays) {
-                return DEFAULT_DISPLAY;
-            }
-
-            // Look for the topmost freeform display.
-            int firstExternalDisplayId = DEFAULT_DISPLAY;
-            for (int i = mService.mRoot.mChildren.size() - 1; i >= 0; --i) {
-                final DisplayContent displayContent = mService.mRoot.mChildren.get(i);
-                // Heuristic solution here. Currently when "Freeform windows" developer option is
-                // enabled we automatically put secondary displays in freeform mode and emulating
-                // "desktop mode". It also makes sense to show the pointer on the same display.
-                if (displayContent.getWindowingMode() == WINDOWING_MODE_FREEFORM) {
-                    return displayContent.getDisplayId();
-                }
-
-                if (firstExternalDisplayId == DEFAULT_DISPLAY
-                        && displayContent.getDisplayId() != DEFAULT_DISPLAY) {
-                    firstExternalDisplayId = displayContent.getDisplayId();
-                }
-            }
-
-            // Look for the topmost non-default display
-            return firstExternalDisplayId;
-        }
-    }
-
     /** Waits until the built-in input devices have been configured. */
     public boolean waitForInputDevicesReady(long timeoutMillis) {
         synchronized (mInputDevicesReadyMonitor) {
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index bf83ca9..43d2dcf 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -107,7 +107,6 @@
     jmethodID getLongPressTimeout;
     jmethodID getPointerLayer;
     jmethodID getPointerIcon;
-    jmethodID getPointerDisplayId;
     jmethodID getKeyboardLayoutOverlay;
     jmethodID getDeviceAlias;
     jmethodID getTouchCalibrationForInputDevice;
@@ -175,6 +174,15 @@
     loadSystemIconAsSpriteWithPointerIcon(env, contextObj, style, &pointerIcon, outSpriteIcon);
 }
 
+static void updatePointerControllerFromViewport(
+        sp<PointerController> controller, const DisplayViewport* const viewport) {
+    if (controller != nullptr && viewport != nullptr) {
+        const int32_t width = viewport->logicalRight - viewport->logicalLeft;
+        const int32_t height = viewport->logicalBottom - viewport->logicalTop;
+        controller->setDisplayViewport(width, height, viewport->orientation);
+    }
+}
+
 enum {
     WM_ACTION_PASS_TO_USER = 1,
 };
@@ -234,7 +242,6 @@
             jfloatArray matrixArr);
     virtual TouchAffineTransformation getTouchAffineTransformation(
             const std::string& inputDeviceDescriptor, int32_t surfaceRotation);
-    virtual void updatePointerDisplay();
 
     /* --- InputDispatcherPolicyInterface implementation --- */
 
@@ -307,11 +314,10 @@
 
     std::atomic<bool> mInteractive;
 
-    void updateInactivityTimeoutLocked();
+    void updateInactivityTimeoutLocked(const sp<PointerController>& controller);
     void handleInterceptActions(jint wmActions, nsecs_t when, uint32_t& policyFlags);
     void ensureSpriteControllerLocked();
-    const DisplayViewport* findDisplayViewportLocked(int32_t displayId);
-    int32_t getPointerDisplayId();
+
     static bool checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName);
 
     static inline JNIEnv* jniEnv() {
@@ -385,10 +391,9 @@
     return false;
 }
 
-const DisplayViewport* NativeInputManager::findDisplayViewportLocked(int32_t displayId)
-        REQUIRES(mLock) {
-    for (const DisplayViewport& v : mLocked.viewports) {
-        if (v.displayId == displayId) {
+static const DisplayViewport* findInternalViewport(const std::vector<DisplayViewport>& viewports) {
+    for (const DisplayViewport& v : viewports) {
+        if (v.type == ViewportType::VIEWPORT_INTERNAL) {
             return &v;
         }
     }
@@ -415,10 +420,20 @@
         }
     }
 
-    { // acquire lock
+    const DisplayViewport* newInternalViewport = findInternalViewport(viewports);
+    {
         AutoMutex _l(mLock);
+        const DisplayViewport* oldInternalViewport = findInternalViewport(mLocked.viewports);
+        // Internal viewport has changed if there wasn't one earlier, and there is one now, or,
+        // if they are different.
+        const bool internalViewportChanged = (newInternalViewport != nullptr) &&
+                (oldInternalViewport == nullptr || (*oldInternalViewport != *newInternalViewport));
+        if (internalViewportChanged) {
+            sp<PointerController> controller = mLocked.pointerController.promote();
+            updatePointerControllerFromViewport(controller, newInternalViewport);
+        }
         mLocked.viewports = viewports;
-    } // release lock
+    }
 
     mInputManager->getReader()->requestRefreshConfiguration(
             InputReaderConfiguration::CHANGE_DISPLAY_INFO);
@@ -541,43 +556,15 @@
 
         controller = new PointerController(this, mLooper, mLocked.spriteController);
         mLocked.pointerController = controller;
-        updateInactivityTimeoutLocked();
-    }
 
+        const DisplayViewport* internalViewport = findInternalViewport(mLocked.viewports);
+        updatePointerControllerFromViewport(controller, internalViewport);
+
+        updateInactivityTimeoutLocked(controller);
+    }
     return controller;
 }
 
-int32_t NativeInputManager::getPointerDisplayId() {
-    JNIEnv* env = jniEnv();
-    jint pointerDisplayId = env->CallIntMethod(mServiceObj,
-            gServiceClassInfo.getPointerDisplayId);
-    if (checkAndClearExceptionFromCallback(env, "getPointerDisplayId")) {
-        pointerDisplayId = ADISPLAY_ID_DEFAULT;
-    }
-
-    return pointerDisplayId;
-}
-
-void NativeInputManager::updatePointerDisplay() {
-    ATRACE_CALL();
-
-    jint pointerDisplayId = getPointerDisplayId();
-
-    AutoMutex _l(mLock);
-    sp<PointerController> controller = mLocked.pointerController.promote();
-    if (controller != nullptr) {
-        const DisplayViewport* viewport = findDisplayViewportLocked(pointerDisplayId);
-        if (viewport == nullptr) {
-            ALOGW("Can't find pointer display viewport, fallback to default display.");
-            viewport = findDisplayViewportLocked(ADISPLAY_ID_DEFAULT);
-        }
-
-        if (viewport != nullptr) {
-            controller->setDisplayViewport(*viewport);
-        }
-    }
-}
-
 void NativeInputManager::ensureSpriteControllerLocked() REQUIRES(mLock) {
     if (mLocked.spriteController == nullptr) {
         JNIEnv* env = jniEnv();
@@ -834,16 +821,16 @@
 
     if (mLocked.systemUiVisibility != visibility) {
         mLocked.systemUiVisibility = visibility;
-        updateInactivityTimeoutLocked();
+
+        sp<PointerController> controller = mLocked.pointerController.promote();
+        if (controller != nullptr) {
+            updateInactivityTimeoutLocked(controller);
+        }
     }
 }
 
-void NativeInputManager::updateInactivityTimeoutLocked() REQUIRES(mLock) {
-    sp<PointerController> controller = mLocked.pointerController.promote();
-    if (controller == nullptr) {
-        return;
-    }
-
+void NativeInputManager::updateInactivityTimeoutLocked(const sp<PointerController>& controller)
+        REQUIRES(mLock) {
     bool lightsOut = mLocked.systemUiVisibility & ASYSTEM_UI_VISIBILITY_STATUS_BAR_HIDDEN;
     controller->setInactivityTimeout(lightsOut
             ? PointerController::INACTIVITY_TIMEOUT_SHORT
@@ -1837,9 +1824,6 @@
     GET_METHOD_ID(gServiceClassInfo.getPointerIcon, clazz,
             "getPointerIcon", "()Landroid/view/PointerIcon;");
 
-    GET_METHOD_ID(gServiceClassInfo.getPointerDisplayId, clazz,
-            "getPointerDisplayId", "()I");
-
     GET_METHOD_ID(gServiceClassInfo.getKeyboardLayoutOverlay, clazz,
             "getKeyboardLayoutOverlay",
             "(Landroid/hardware/input/InputDeviceIdentifier;)[Ljava/lang/String;");
diff --git a/services/net/java/android/net/dhcp/DhcpServer.java b/services/net/java/android/net/dhcp/DhcpServer.java
index cee6fa9..35d29e7 100644
--- a/services/net/java/android/net/dhcp/DhcpServer.java
+++ b/services/net/java/android/net/dhcp/DhcpServer.java
@@ -39,7 +39,6 @@
 import android.net.MacAddress;
 import android.net.NetworkUtils;
 import android.net.TrafficStats;
-import android.net.util.InterfaceParams;
 import android.net.util.SharedLog;
 import android.os.Handler;
 import android.os.Looper;
@@ -85,7 +84,7 @@
     @NonNull
     private final ServerHandler mHandler;
     @NonNull
-    private final InterfaceParams mIface;
+    private final String mIfName;
     @NonNull
     private final DhcpLeaseRepository mLeaseRepo;
     @NonNull
@@ -161,20 +160,20 @@
         }
     }
 
-    public DhcpServer(@NonNull Looper looper, @NonNull InterfaceParams iface,
+    public DhcpServer(@NonNull Looper looper, @NonNull String ifName,
             @NonNull DhcpServingParams params, @NonNull SharedLog log) {
-        this(looper, iface, params, log, null);
+        this(looper, ifName, params, log, null);
     }
 
     @VisibleForTesting
-    DhcpServer(@NonNull Looper looper, @NonNull InterfaceParams iface,
+    DhcpServer(@NonNull Looper looper, @NonNull String ifName,
             @NonNull DhcpServingParams params, @NonNull SharedLog log,
             @Nullable Dependencies deps) {
         if (deps == null) {
             deps = new DependenciesImpl();
         }
         mHandler = new ServerHandler(looper);
-        mIface = iface;
+        mIfName = ifName;
         mServingParams = params;
         mLog = log;
         mDeps = deps;
@@ -444,7 +443,7 @@
 
     private boolean addArpEntry(@NonNull MacAddress macAddr, @NonNull Inet4Address inetAddr) {
         try {
-            mDeps.addArpEntry(inetAddr, macAddr, mIface.name, mSocket);
+            mDeps.addArpEntry(inetAddr, macAddr, mIfName, mSocket);
             return true;
         } catch (IOException e) {
             mLog.e("Error adding client to ARP table", e);
@@ -526,7 +525,7 @@
                 // SO_BINDTODEVICE actually takes a string. This works because the first member
                 // of struct ifreq is a NULL-terminated interface name.
                 // TODO: add a setsockoptString()
-                Os.setsockoptIfreq(mSocket, SOL_SOCKET, SO_BINDTODEVICE, mIface.name);
+                Os.setsockoptIfreq(mSocket, SOL_SOCKET, SO_BINDTODEVICE, mIfName);
                 Os.setsockoptInt(mSocket, SOL_SOCKET, SO_BROADCAST, 1);
                 Os.bind(mSocket, Inet4Address.ANY, DHCP_SERVER);
                 NetworkUtils.protectFromVpn(mSocket);
diff --git a/services/net/java/android/net/ip/IpServer.java b/services/net/java/android/net/ip/IpServer.java
index 823c0a1..493350d 100644
--- a/services/net/java/android/net/ip/IpServer.java
+++ b/services/net/java/android/net/ip/IpServer.java
@@ -138,9 +138,9 @@
             return NetdService.getInstance();
         }
 
-        public DhcpServer makeDhcpServer(Looper looper, InterfaceParams iface,
+        public DhcpServer makeDhcpServer(Looper looper, String ifName,
                 DhcpServingParams params, SharedLog log) {
-            return new DhcpServer(looper, iface, params, log);
+            return new DhcpServer(looper, ifName, params, log);
         }
     }
 
@@ -256,12 +256,6 @@
         if (mUsingLegacyDhcp) {
             return true;
         }
-
-        final InterfaceParams ifaceParams = mDeps.getInterfaceParams(mIfaceName);
-        if (ifaceParams == null) {
-            Log.e(TAG, "Failed to find interface params for DHCPv4");
-            return false;
-        }
         final DhcpServingParams params;
         try {
             params = new DhcpServingParams.Builder()
@@ -277,7 +271,7 @@
             return false;
         }
 
-        mDhcpServer = mDeps.makeDhcpServer(getHandler().getLooper(), ifaceParams, params,
+        mDhcpServer = mDeps.makeDhcpServer(getHandler().getLooper(), mIfaceName, params,
                 mLog.forSubComponent("DHCP"));
         mDhcpServer.start();
         return true;
diff --git a/services/robotests/src/com/android/server/backup/BackupManagerServiceTest.java b/services/robotests/src/com/android/server/backup/BackupManagerServiceTest.java
index 58bce1c..83f66c5 100644
--- a/services/robotests/src/com/android/server/backup/BackupManagerServiceTest.java
+++ b/services/robotests/src/com/android/server/backup/BackupManagerServiceTest.java
@@ -16,11 +16,15 @@
 
 package com.android.server.backup;
 
+import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
+
+import static com.android.server.backup.testing.BackupManagerServiceTestUtils.startBackupThread;
 import static com.android.server.backup.testing.TransportData.backupTransport;
 
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
 import static org.robolectric.Shadows.shadowOf;
 import static org.testng.Assert.expectThrows;
@@ -37,8 +41,8 @@
 import android.os.ParcelFileDescriptor;
 import android.os.UserHandle;
 import android.platform.test.annotations.Presubmit;
+import android.util.SparseArray;
 
-import com.android.server.backup.testing.BackupManagerServiceTestUtils;
 import com.android.server.backup.testing.TransportData;
 import com.android.server.testing.shadows.ShadowBinder;
 
@@ -64,16 +68,14 @@
 public class BackupManagerServiceTest {
     private static final String TEST_PACKAGE = "package";
     private static final String TEST_TRANSPORT = "transport";
-
     private static final String[] ADB_TEST_PACKAGES = {TEST_PACKAGE};
 
-    private static final int NON_USER_SYSTEM = UserHandle.USER_SYSTEM + 1;
-
     private ShadowContextWrapper mShadowContext;
-    @Mock private UserBackupManagerService mUserBackupManagerService;
-    private BackupManagerService mBackupManagerService;
     private Context mContext;
-    @UserIdInt private int mUserId;
+    @UserIdInt private int mUserOneId;
+    @UserIdInt private int mUserTwoId;
+    @Mock private UserBackupManagerService mUserOneService;
+    @Mock private UserBackupManagerService mUserTwoService;
 
     /** Initialize {@link BackupManagerService}. */
     @Before
@@ -83,13 +85,11 @@
         Application application = RuntimeEnvironment.application;
         mContext = application;
         mShadowContext = shadowOf(application);
-        mUserId = NON_USER_SYSTEM;
-        mBackupManagerService =
-                new BackupManagerService(
-                        application,
-                        new Trampoline(application),
-                        BackupManagerServiceTestUtils.startBackupThread(null));
-        mBackupManagerService.setUserBackupManagerService(mUserBackupManagerService);
+
+        // TODO(b/120212806): Hardcoding system user for now since most methods in BMS don't yet
+        // take an user parameter (and instead hardcode the system user).
+        mUserOneId = UserHandle.USER_SYSTEM;
+        mUserTwoId = mUserOneId + 1;
     }
 
     /**
@@ -102,8 +102,8 @@
     }
 
     /**
-     * Test verifying that {@link BackupManagerService#MORE_DEBUG} is set to {@code false}.
-     * This is specifically to prevent overloading the logs in production.
+     * Test verifying that {@link BackupManagerService#MORE_DEBUG} is set to {@code false}. This is
+     * specifically to prevent overloading the logs in production.
      */
     @Test
     public void testMoreDebug_isFalse() throws Exception {
@@ -112,9 +112,73 @@
         assertThat(moreDebug).isFalse();
     }
 
-    // TODO(b/118520567): Change the following tests to use the per-user instance of
-    // UserBackupManagerService once it's implemented. Currently these tests only test the straight
-    // forward redirection.
+    /** Test that the constructor does not create {@link UserBackupManagerService} instances. */
+    @Test
+    public void testConstructor_doesNotRegisterUsers() throws Exception {
+        BackupManagerService backupManagerService = createService();
+
+        assertThat(backupManagerService.getServiceUsers().size()).isEqualTo(0);
+    }
+
+    /** Test that the constructor handles {@code null} parameters. */
+    @Test
+    public void testConstructor_withNullContext_throws() throws Exception {
+        expectThrows(
+                NullPointerException.class,
+                () ->
+                        new BackupManagerService(
+                                /* context */ null,
+                                new Trampoline(mContext),
+                                startBackupThread(null)));
+    }
+
+    /** Test that the constructor handles {@code null} parameters. */
+    @Test
+    public void testConstructor_withNullTrampoline_throws() throws Exception {
+        expectThrows(
+                NullPointerException.class,
+                () ->
+                        new BackupManagerService(
+                                mContext, /* trampoline */ null, startBackupThread(null)));
+    }
+
+    /** Test that the constructor handles {@code null} parameters. */
+    @Test
+    public void testConstructor_withNullBackupThread_throws() throws Exception {
+        expectThrows(
+                NullPointerException.class,
+                () ->
+                        new BackupManagerService(
+                                mContext, new Trampoline(mContext), /* backupThread */ null));
+    }
+
+    /** Test that the service registers users. */
+    @Test
+    public void testStartServiceForUser_registersUser() throws Exception {
+        BackupManagerService backupManagerService = createService();
+
+        backupManagerService.startServiceForUser(mUserOneId);
+
+        SparseArray<UserBackupManagerService> serviceUsers = backupManagerService.getServiceUsers();
+        assertThat(serviceUsers.size()).isEqualTo(1);
+        assertThat(serviceUsers.get(mUserOneId)).isNotNull();
+    }
+
+    /** Test that the service registers users. */
+    @Test
+    public void testStartServiceForUser_withServiceInstance_registersUser() throws Exception {
+        BackupManagerService backupManagerService = createService();
+
+        backupManagerService.startServiceForUser(mUserOneId, mUserOneService);
+
+        SparseArray<UserBackupManagerService> serviceUsers = backupManagerService.getServiceUsers();
+        assertThat(serviceUsers.size()).isEqualTo(1);
+        assertThat(serviceUsers.get(mUserOneId)).isEqualTo(mUserOneService);
+    }
+
+    // TODO(b/120212806): When BMS methods take in a user parameter, modify unknown user tests to
+    // check that that we don't call the method on another registered user. Currently these tests
+    // have no registered users since we hardcode the system user in BMS.
 
     // ---------------------------------------------
     // Backup agent tests
@@ -122,36 +186,88 @@
 
     /** Test that the backup service routes methods correctly to the user that requests it. */
     @Test
-    public void testDataChanged_callsDataChangedForUser() throws Exception {
-        mBackupManagerService.dataChanged(TEST_PACKAGE);
+    public void testDataChanged_onRegisteredUser_callsMethodForUser() throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
 
-        verify(mUserBackupManagerService).dataChanged(TEST_PACKAGE);
+        backupManagerService.dataChanged(TEST_PACKAGE);
+
+        verify(mUserOneService).dataChanged(TEST_PACKAGE);
+    }
+
+    /** Test that the backup service does not route methods for non-registered users. */
+    @Test
+    public void testDataChanged_onUnknownUser_doesNotPropagateCall() throws Exception {
+        BackupManagerService backupManagerService = createService();
+
+        backupManagerService.dataChanged(TEST_PACKAGE);
+
+        verify(mUserOneService, never()).dataChanged(TEST_PACKAGE);
     }
 
     /** Test that the backup service routes methods correctly to the user that requests it. */
     @Test
-    public void testAgentConnected_callsAgentConnectedForUser() throws Exception {
+    public void testAgentConnected_onRegisteredUser_callsMethodForUser() throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
         IBinder agentBinder = mock(IBinder.class);
 
-        mBackupManagerService.agentConnected(TEST_PACKAGE, agentBinder);
+        backupManagerService.agentConnected(TEST_PACKAGE, agentBinder);
 
-        verify(mUserBackupManagerService).agentConnected(TEST_PACKAGE, agentBinder);
+        verify(mUserOneService).agentConnected(TEST_PACKAGE, agentBinder);
+    }
+
+    /** Test that the backup service does not route methods for non-registered users. */
+    @Test
+    public void testAgentConnected_onUnknownUser_doesNotPropagateCall() throws Exception {
+        BackupManagerService backupManagerService = createService();
+        IBinder agentBinder = mock(IBinder.class);
+
+        backupManagerService.agentConnected(TEST_PACKAGE, agentBinder);
+
+        verify(mUserOneService, never()).agentConnected(TEST_PACKAGE, agentBinder);
     }
 
     /** Test that the backup service routes methods correctly to the user that requests it. */
     @Test
-    public void testAgentDisconnected_callsAgentDisconnectedForUser() throws Exception {
-        mBackupManagerService.agentDisconnected(TEST_PACKAGE);
+    public void testAgentDisconnected_onRegisteredUser_callsMethodForUser() throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
 
-        verify(mUserBackupManagerService).agentDisconnected(TEST_PACKAGE);
+        backupManagerService.agentDisconnected(TEST_PACKAGE);
+
+        verify(mUserOneService).agentDisconnected(TEST_PACKAGE);
+    }
+
+    /** Test that the backup service does not route methods for non-registered users. */
+    @Test
+    public void testAgentDisconnected_onUnknownUser_doesNotPropagateCall() throws Exception {
+        BackupManagerService backupManagerService = createService();
+
+        backupManagerService.agentDisconnected(TEST_PACKAGE);
+
+        verify(mUserOneService, never()).agentDisconnected(TEST_PACKAGE);
     }
 
     /** Test that the backup service routes methods correctly to the user that requests it. */
     @Test
-    public void testOpComplete_callsOpCompleteForUser() throws Exception {
-        mBackupManagerService.opComplete(/* token */ 0, /* result */ 0L);
+    public void testOpComplete_onRegisteredUser_callsMethodForUser() throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
 
-        verify(mUserBackupManagerService).opComplete(/* token */ 0, /* result */ 0L);
+        backupManagerService.opComplete(/* token */ 0, /* result */ 0L);
+
+        verify(mUserOneService).opComplete(/* token */ 0, /* result */ 0L);
+    }
+
+    /** Test that the backup service does not route methods for non-registered users. */
+    @Test
+    public void testOpComplete_onUnknownUser_doesNotPropagateCall() throws Exception {
+        BackupManagerService backupManagerService = createService();
+
+        backupManagerService.opComplete(/* token */ 0, /* result */ 0L);
+
+        verify(mUserOneService, never()).opComplete(/* token */ 0, /* result */ 0L);
     }
 
     // ---------------------------------------------
@@ -160,73 +276,168 @@
 
     /** Test that the backup service routes methods correctly to the user that requests it. */
     @Test
-    public void testInitializeTransports_callsInitializeTransportsForUser() throws Exception {
+    public void testInitializeTransports_onRegisteredUser_callsMethodForUser() throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
         String[] transports = {TEST_TRANSPORT};
 
-        mBackupManagerService.initializeTransports(transports, /* observer */ null);
+        backupManagerService.initializeTransports(transports, /* observer */ null);
 
-        verify(mUserBackupManagerService).initializeTransports(transports, /* observer */ null);
+        verify(mUserOneService).initializeTransports(transports, /* observer */ null);
+    }
+
+    /** Test that the backup service does not route methods for non-registered users. */
+    @Test
+    public void testInitializeTransports_onUnknownUser_doesNotPropagateCall() throws Exception {
+        BackupManagerService backupManagerService = createService();
+        String[] transports = {TEST_TRANSPORT};
+
+        backupManagerService.initializeTransports(transports, /* observer */ null);
+
+        verify(mUserOneService, never()).initializeTransports(transports, /* observer */ null);
     }
 
     /** Test that the backup service routes methods correctly to the user that requests it. */
     @Test
-    public void testClearBackupData_callsClearBackupDataForUser() throws Exception {
-        mBackupManagerService.clearBackupData(TEST_TRANSPORT, TEST_PACKAGE);
+    public void testClearBackupData_onRegisteredUser_callsMethodForUser() throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
 
-        verify(mUserBackupManagerService).clearBackupData(TEST_TRANSPORT, TEST_PACKAGE);
+        backupManagerService.clearBackupData(TEST_TRANSPORT, TEST_PACKAGE);
+
+        verify(mUserOneService).clearBackupData(TEST_TRANSPORT, TEST_PACKAGE);
+    }
+
+    /** Test that the backup service does not route methods for non-registered users. */
+    @Test
+    public void testClearBackupData_onUnknownUser_doesNotPropagateCall() throws Exception {
+        BackupManagerService backupManagerService = createService();
+
+        backupManagerService.clearBackupData(TEST_TRANSPORT, TEST_PACKAGE);
+
+        verify(mUserOneService, never()).clearBackupData(TEST_TRANSPORT, TEST_PACKAGE);
     }
 
     /** Test that the backup service routes methods correctly to the user that requests it. */
     @Test
-    public void testGetCurrentTransport_callsGetCurrentTransportForUser() throws Exception {
-        mBackupManagerService.getCurrentTransport();
+    public void testGetCurrentTransport_onRegisteredUser_callsMethodForUser() throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
 
-        verify(mUserBackupManagerService).getCurrentTransport();
+        backupManagerService.getCurrentTransport();
+
+        verify(mUserOneService).getCurrentTransport();
+    }
+
+    /** Test that the backup service does not route methods for non-registered users. */
+    @Test
+    public void testGetCurrentTransport_onUnknownUser_doesNotPropagateCall() throws Exception {
+        BackupManagerService backupManagerService = createService();
+
+        backupManagerService.getCurrentTransport();
+
+        verify(mUserOneService, never()).getCurrentTransport();
     }
 
     /** Test that the backup service routes methods correctly to the user that requests it. */
     @Test
-    public void testGetCurrentTransportComponent_callsGetCurrentTransportComponentForUser()
+    public void testGetCurrentTransportComponent_onRegisteredUser_callsMethodForUser()
             throws Exception {
-        mBackupManagerService.getCurrentTransportComponent();
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
 
-        verify(mUserBackupManagerService).getCurrentTransportComponent();
+        backupManagerService.getCurrentTransportComponent();
+
+        verify(mUserOneService).getCurrentTransportComponent();
     }
 
-    /** Test that the backup service routes methods correctly to the user that requests it. */
+    /** Test that the backup service does not route methods for non-registered users. */
     @Test
-    public void testListAllTransports_callsListAllTransportsForUser() throws Exception {
-        mBackupManagerService.listAllTransports();
-
-        verify(mUserBackupManagerService).listAllTransports();
-    }
-
-    /** Test that the backup service routes methods correctly to the user that requests it. */
-    @Test
-    public void testListAllTransportComponents_callsListAllTransportComponentsForUser()
+    public void testGetCurrentTransportComponent_onUnknownUser_doesNotPropagateCall()
             throws Exception {
-        mBackupManagerService.listAllTransportComponents();
+        BackupManagerService backupManagerService = createService();
 
-        verify(mUserBackupManagerService).listAllTransportComponents();
+        backupManagerService.getCurrentTransportComponent();
+
+        verify(mUserOneService, never()).getCurrentTransportComponent();
     }
 
     /** Test that the backup service routes methods correctly to the user that requests it. */
     @Test
-    public void testGetTransportWhitelist_callsGetTransportWhitelistForUser() throws Exception {
-        mBackupManagerService.getTransportWhitelist();
+    public void testListAllTransports_onRegisteredUser_callsMethodForUser() throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
 
-        verify(mUserBackupManagerService).getTransportWhitelist();
+        backupManagerService.listAllTransports();
+
+        verify(mUserOneService).listAllTransports();
+    }
+
+    /** Test that the backup service does not route methods for non-registered users. */
+    @Test
+    public void testListAllTransports_onUnknownUser_doesNotPropagateCall() throws Exception {
+        BackupManagerService backupManagerService = createService();
+
+        backupManagerService.listAllTransports();
+
+        verify(mUserOneService, never()).listAllTransports();
     }
 
     /** Test that the backup service routes methods correctly to the user that requests it. */
     @Test
-    public void testUpdateTransportAttributes_callsUpdateTransportAttributesForUser()
+    public void testListAllTransportComponents_onRegisteredUser_callsMethodForUser()
             throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+
+        backupManagerService.listAllTransportComponents();
+
+        verify(mUserOneService).listAllTransportComponents();
+    }
+
+    /** Test that the backup service does not route methods for non-registered users. */
+    @Test
+    public void testListAllTransportComponents_onUnknownUser_doesNotPropagateCall()
+            throws Exception {
+        BackupManagerService backupManagerService = createService();
+
+        backupManagerService.listAllTransportComponents();
+
+        verify(mUserOneService, never()).listAllTransportComponents();
+    }
+
+    /** Test that the backup service routes methods correctly to the user that requests it. */
+    @Test
+    public void testGetTransportWhitelist_onRegisteredUser_callsMethodForUser() throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+
+        backupManagerService.getTransportWhitelist();
+
+        verify(mUserOneService).getTransportWhitelist();
+    }
+
+    /** Test that the backup service does not route methods for non-registered users. */
+    @Test
+    public void testGetTransportWhitelist_onUnknownUser_doesNotPropagateCall() throws Exception {
+        BackupManagerService backupManagerService = createService();
+
+        backupManagerService.getTransportWhitelist();
+
+        verify(mUserOneService, never()).getTransportWhitelist();
+    }
+
+    /** Test that the backup service routes methods correctly to the user that requests it. */
+    @Test
+    public void testUpdateTransportAttributes_onRegisteredUser_callsMethodForUser()
+            throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
         TransportData transport = backupTransport();
         Intent configurationIntent = new Intent();
         Intent dataManagementIntent = new Intent();
 
-        mBackupManagerService.updateTransportAttributes(
+        backupManagerService.updateTransportAttributes(
                 transport.getTransportComponent(),
                 transport.transportName,
                 configurationIntent,
@@ -234,7 +445,34 @@
                 dataManagementIntent,
                 "dataManagementLabel");
 
-        verify(mUserBackupManagerService)
+        verify(mUserOneService)
+                .updateTransportAttributes(
+                        transport.getTransportComponent(),
+                        transport.transportName,
+                        configurationIntent,
+                        "currentDestinationString",
+                        dataManagementIntent,
+                        "dataManagementLabel");
+    }
+
+    /** Test that the backup service does not route methods for non-registered users. */
+    @Test
+    public void testUpdateTransportAttributes_onUnknownUser_doesNotPropagateCall()
+            throws Exception {
+        BackupManagerService backupManagerService = createService();
+        TransportData transport = backupTransport();
+        Intent configurationIntent = new Intent();
+        Intent dataManagementIntent = new Intent();
+
+        backupManagerService.updateTransportAttributes(
+                transport.getTransportComponent(),
+                transport.transportName,
+                configurationIntent,
+                "currentDestinationString",
+                dataManagementIntent,
+                "dataManagementLabel");
+
+        verify(mUserOneService, never())
                 .updateTransportAttributes(
                         transport.getTransportComponent(),
                         transport.transportName,
@@ -246,136 +484,292 @@
 
     /** Test that the backup service routes methods correctly to the user that requests it. */
     @Test
-    public void testSelectBackupTransport_callsSelectBackupTransportForUser() throws Exception {
-        mBackupManagerService.selectBackupTransport(TEST_TRANSPORT);
+    public void testSelectBackupTransport_onRegisteredUser_callsMethodForUser() throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
 
-        verify(mUserBackupManagerService).selectBackupTransport(TEST_TRANSPORT);
+        backupManagerService.selectBackupTransport(TEST_TRANSPORT);
+
+        verify(mUserOneService).selectBackupTransport(TEST_TRANSPORT);
+    }
+
+    /** Test that the backup service does not route methods for non-registered users. */
+    @Test
+    public void testSelectBackupTransport_onUnknownUser_doesNotPropagateCall() throws Exception {
+        BackupManagerService backupManagerService = createService();
+
+        backupManagerService.selectBackupTransport(TEST_TRANSPORT);
+
+        verify(mUserOneService, never()).selectBackupTransport(TEST_TRANSPORT);
     }
 
     /** Test that the backup service routes methods correctly to the user that requests it. */
     @Test
-    public void testSelectTransportAsync_callsSelectTransportAsyncForUser() throws Exception {
+    public void testSelectTransportAsync_onRegisteredUser_callsMethodForUser() throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
         TransportData transport = backupTransport();
         ISelectBackupTransportCallback callback = mock(ISelectBackupTransportCallback.class);
 
-        mBackupManagerService.selectBackupTransportAsync(
+        backupManagerService.selectBackupTransportAsync(
                 transport.getTransportComponent(), callback);
 
-        verify(mUserBackupManagerService)
+        verify(mUserOneService)
+                .selectBackupTransportAsync(transport.getTransportComponent(), callback);
+    }
+
+    /** Test that the backup service does not route methods for non-registered users. */
+    @Test
+    public void testSelectBackupTransportAsync_onUnknownUser_doesNotPropagateCall()
+            throws Exception {
+        BackupManagerService backupManagerService = createService();
+        TransportData transport = backupTransport();
+        ISelectBackupTransportCallback callback = mock(ISelectBackupTransportCallback.class);
+
+        backupManagerService.selectBackupTransportAsync(
+                transport.getTransportComponent(), callback);
+
+        verify(mUserOneService, never())
                 .selectBackupTransportAsync(transport.getTransportComponent(), callback);
     }
 
     /** Test that the backup service routes methods correctly to the user that requests it. */
     @Test
-    public void testGetConfigurationIntent_callsGetConfigurationIntentForUser() throws Exception {
-        mBackupManagerService.getConfigurationIntent(TEST_TRANSPORT);
+    public void testGetConfigurationIntent_onRegisteredUser_callsMethodForUser() throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
 
-        verify(mUserBackupManagerService).getConfigurationIntent(TEST_TRANSPORT);
+        backupManagerService.getConfigurationIntent(TEST_TRANSPORT);
+
+        verify(mUserOneService).getConfigurationIntent(TEST_TRANSPORT);
+    }
+
+    /** Test that the backup service does not route methods for non-registered users. */
+    @Test
+    public void testGetConfigurationIntent_onUnknownUser_doesNotPropagateCall() throws Exception {
+        BackupManagerService backupManagerService = createService();
+
+        backupManagerService.getConfigurationIntent(TEST_TRANSPORT);
+
+        verify(mUserOneService, never()).getConfigurationIntent(TEST_TRANSPORT);
     }
 
     /** Test that the backup service routes methods correctly to the user that requests it. */
     @Test
-    public void testGetDestinationString_callsGetDestinationStringForUser() throws Exception {
-        mBackupManagerService.getDestinationString(TEST_TRANSPORT);
+    public void testGetDestinationString_onRegisteredUser_callsMethodForUser() throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
 
-        verify(mUserBackupManagerService).getDestinationString(TEST_TRANSPORT);
+        backupManagerService.getDestinationString(TEST_TRANSPORT);
+
+        verify(mUserOneService).getDestinationString(TEST_TRANSPORT);
+    }
+
+    /** Test that the backup service does not route methods for non-registered users. */
+    @Test
+    public void testGetDestinationString_onUnknownUser_doesNotPropagateCall() throws Exception {
+        BackupManagerService backupManagerService = createService();
+
+        backupManagerService.getDestinationString(TEST_TRANSPORT);
+
+        verify(mUserOneService, never()).getDestinationString(TEST_TRANSPORT);
     }
 
     /** Test that the backup service routes methods correctly to the user that requests it. */
     @Test
-    public void testGetDataManagementIntent_callsGetDataManagementIntentForUser() throws Exception {
-        mBackupManagerService.getDataManagementIntent(TEST_TRANSPORT);
+    public void testGetDataManagementIntent_onRegisteredUser_callsMethodForUser() throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
 
-        verify(mUserBackupManagerService).getDataManagementIntent(TEST_TRANSPORT);
+        backupManagerService.getDataManagementIntent(TEST_TRANSPORT);
+
+        verify(mUserOneService).getDataManagementIntent(TEST_TRANSPORT);
+    }
+
+    /** Test that the backup service does not route methods for non-registered users. */
+    @Test
+    public void testGetDataManagementIntent_onUnknownUser_doesNotPropagateCall() throws Exception {
+        BackupManagerService backupManagerService = createService();
+
+        backupManagerService.getDataManagementIntent(TEST_TRANSPORT);
+
+        verify(mUserOneService, never()).getDataManagementIntent(TEST_TRANSPORT);
     }
 
     /** Test that the backup service routes methods correctly to the user that requests it. */
     @Test
-    public void testGetDataManagementLabel_callsGetDataManagementLabelForUser() throws Exception {
-        mBackupManagerService.getDataManagementLabel(TEST_TRANSPORT);
+    public void testGetDataManagementLabel_onRegisteredUser_callsMethodForUser() throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
 
-        verify(mUserBackupManagerService).getDataManagementLabel(TEST_TRANSPORT);
+        backupManagerService.getDataManagementLabel(TEST_TRANSPORT);
+
+        verify(mUserOneService).getDataManagementLabel(TEST_TRANSPORT);
+    }
+
+    /** Test that the backup service does not route methods for non-registered users. */
+    @Test
+    public void testGetDataManagementLabel_onUnknownUser_doesNotPropagateCall() throws Exception {
+        BackupManagerService backupManagerService = createService();
+
+        backupManagerService.getDataManagementLabel(TEST_TRANSPORT);
+
+        verify(mUserOneService, never()).getDataManagementLabel(TEST_TRANSPORT);
     }
 
     // ---------------------------------------------
     // Settings tests
     // ---------------------------------------------
+
     /**
-     * Test verifying that {@link BackupManagerService#setBackupEnabled(int, boolean)} throws a
-     * {@link SecurityException} if the caller does not have INTERACT_ACROSS_USERS_FULL permission.
+     * Test that the backup services throws a {@link SecurityException} if the caller does not have
+     * INTERACT_ACROSS_USERS_FULL permission and passes a different user id.
      */
     @Test
-    public void setBackupEnabled_withoutPermission_throwsSecurityException() {
-        mShadowContext.denyPermissions(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
+    public void testSetBackupEnabled_withoutPermission_throwsSecurityExceptionForNonCallingUser() {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
 
         expectThrows(
                 SecurityException.class,
-                () -> mBackupManagerService.setBackupEnabled(mUserId, true));
+                () -> backupManagerService.setBackupEnabled(mUserTwoId, true));
     }
 
     /**
-     * Test verifying that {@link BackupManagerService#setBackupEnabled(int, boolean)} does not
-     * require the caller to have INTERACT_ACROSS_USERS_FULL permission when the calling user id is
-     * the same as the target user id.
+     * Test that the backup service does not throw a {@link SecurityException} if the caller has
+     * INTERACT_ACROSS_USERS_FULL permission and passes a different user id.
      */
     @Test
-    public void setBackupEnabled_whenCallingUserIsTargetUser_doesntNeedPermission() {
-        ShadowBinder.setCallingUserHandle(UserHandle.of(mUserId));
-        mShadowContext.denyPermissions(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
+    public void testSetBackupEnabled_withPermission_propagatesForNonCallingUser() {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        backupManagerService.startServiceForUser(mUserTwoId, mUserTwoService);
+        setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ true);
 
-        mBackupManagerService.setBackupEnabled(mUserId, true);
+        backupManagerService.setBackupEnabled(mUserTwoId, true);
 
-        verify(mUserBackupManagerService).setBackupEnabled(true);
-    }
-
-
-    /** Test that the backup service routes methods correctly to the user that requests it. */
-    @Test
-    public void setBackupEnabled_callsSetBackupEnabledForUser() throws Exception {
-        mShadowContext.grantPermissions(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
-
-        mBackupManagerService.setBackupEnabled(mUserId, true);
-
-        verify(mUserBackupManagerService).setBackupEnabled(true);
+        verify(mUserTwoService).setBackupEnabled(true);
     }
 
     /** Test that the backup service routes methods correctly to the user that requests it. */
     @Test
-    public void setAutoRestore_callsSetAutoRestoreForUser() throws Exception {
-        mBackupManagerService.setAutoRestore(true);
+    public void testSetBackupEnabled_onRegisteredUser_callsMethodForUser() throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
 
-        verify(mUserBackupManagerService).setAutoRestore(true);
+        backupManagerService.setBackupEnabled(mUserOneId, true);
+
+        verify(mUserOneService).setBackupEnabled(true);
+    }
+
+    /** Test that the backup service does not route methods for non-registered users. */
+    @Test
+    public void testSetBackupEnabled_onUnknownUser_doesNotPropagateCall() throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
+
+        backupManagerService.setBackupEnabled(mUserTwoId, true);
+
+        verify(mUserOneService, never()).setBackupEnabled(true);
     }
 
     /** Test that the backup service routes methods correctly to the user that requests it. */
     @Test
-    public void testSetBackupProvisioned_callsSetBackupProvisionedForUser() throws Exception {
-        mBackupManagerService.setBackupProvisioned(true);
+    public void testSetAutoRestore_onRegisteredUser_callsMethodForUser() throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
 
-        verify(mUserBackupManagerService).setBackupProvisioned(true);
+        backupManagerService.setAutoRestore(true);
+
+        verify(mUserOneService).setAutoRestore(true);
+    }
+
+    /** Test that the backup service does not route methods for non-registered users. */
+    @Test
+    public void testSetAutoRestore_onUnknownUser_doesNotPropagateCall() throws Exception {
+        BackupManagerService backupManagerService = createService();
+
+        backupManagerService.setAutoRestore(true);
+
+        verify(mUserOneService, never()).setAutoRestore(true);
+    }
+
+    /** Test that the backup service routes methods correctly to the user that requests it. */
+    @Test
+    public void testSetBackupProvisioned_onRegisteredUser_callsMethodForUser() throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+
+        backupManagerService.setBackupProvisioned(true);
+
+        verify(mUserOneService).setBackupProvisioned(true);
+    }
+
+    /** Test that the backup service does not route methods for non-registered users. */
+    @Test
+    public void testSetBackupProvisioned_onUnknownUser_doesNotPropagateCall() throws Exception {
+        BackupManagerService backupManagerService = createService();
+
+        backupManagerService.setBackupProvisioned(true);
+
+        verify(mUserOneService, never()).setBackupProvisioned(true);
     }
 
     /**
-     * Test verifying that {@link BackupManagerService#isBackupEnabled(int)} throws a
-     * {@link SecurityException} if the caller does not have INTERACT_ACROSS_USERS_FULL permission.
+     * Test that the backup services throws a {@link SecurityException} if the caller does not have
+     * INTERACT_ACROSS_USERS_FULL permission and passes a different user id.
      */
     @Test
-    public void testIsBackupEnabled_withoutPermission_throwsSecurityException() {
-        mShadowContext.denyPermissions(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
+    public void testIsBackupEnabled_withoutPermission_throwsSecurityExceptionForNonCallingUser() {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
 
         expectThrows(
-                SecurityException.class,
-                () -> mBackupManagerService.isBackupEnabled(mUserId));
+                SecurityException.class, () -> backupManagerService.isBackupEnabled(mUserTwoId));
+    }
+
+    /**
+     * Test that the backup service does not throw a {@link SecurityException} if the caller has
+     * INTERACT_ACROSS_USERS_FULL permission and passes a different user id.
+     */
+    @Test
+    public void testIsBackupEnabled_withPermission_propagatesForNonCallingUser() {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        backupManagerService.startServiceForUser(mUserTwoId, mUserTwoService);
+        setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ true);
+
+        backupManagerService.isBackupEnabled(mUserTwoId);
+
+        verify(mUserTwoService).isBackupEnabled();
     }
 
     /** Test that the backup service routes methods correctly to the user that requests it. */
     @Test
-    public void testIsBackupEnabled_callsIsBackupEnabledForUser() throws Exception {
-        mShadowContext.grantPermissions(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
+    public void testIsBackupEnabled_onRegisteredUser_callsMethodForUser() throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
 
-        mBackupManagerService.isBackupEnabled(mUserId);
+        backupManagerService.isBackupEnabled(mUserOneId);
 
-        verify(mUserBackupManagerService).isBackupEnabled();
+        verify(mUserOneService).isBackupEnabled();
+    }
+
+    /** Test that the backup service does not route methods for non-registered users. */
+    @Test
+    public void testIsBackupEnabled_onUnknownUser_doesNotPropagateCall() throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
+
+        backupManagerService.isBackupEnabled(mUserTwoId);
+
+        verify(mUserOneService, never()).isBackupEnabled();
     }
 
     // ---------------------------------------------
@@ -384,128 +778,290 @@
 
     /** Test that the backup service routes methods correctly to the user that requests it. */
     @Test
-    public void testIsAppEligibleForBackup_callsIsAppEligibleForBackupForUser() throws Exception {
-        mBackupManagerService.isAppEligibleForBackup(TEST_PACKAGE);
+    public void testIsAppEligibleForBackup_onRegisteredUser_callsMethodForUser() throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
 
-        verify(mUserBackupManagerService).isAppEligibleForBackup(TEST_PACKAGE);
+        backupManagerService.isAppEligibleForBackup(TEST_PACKAGE);
+
+        verify(mUserOneService).isAppEligibleForBackup(TEST_PACKAGE);
+    }
+
+    /** Test that the backup service does not route methods for non-registered users. */
+    @Test
+    public void testIsAppEligibleForBackup_onUnknownUser_doesNotPropagateCall() throws Exception {
+        BackupManagerService backupManagerService = createService();
+
+        backupManagerService.isAppEligibleForBackup(TEST_PACKAGE);
+
+        verify(mUserOneService, never()).isAppEligibleForBackup(TEST_PACKAGE);
     }
 
     /** Test that the backup service routes methods correctly to the user that requests it. */
     @Test
-    public void testFilterAppsEligibleForBackup_callsFilterAppsEligibleForBackupForUser()
+    public void testFilterAppsEligibleForBackup_onRegisteredUser_callsMethodForUser()
             throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
         String[] packages = {TEST_PACKAGE};
 
-        mBackupManagerService.filterAppsEligibleForBackup(packages);
+        backupManagerService.filterAppsEligibleForBackup(packages);
 
-        verify(mUserBackupManagerService).filterAppsEligibleForBackup(packages);
+        verify(mUserOneService).filterAppsEligibleForBackup(packages);
+    }
+
+    /** Test that the backup service does not route methods for non-registered users. */
+    @Test
+    public void testFilterAppsEligibleForBackup_onUnknownUser_doesNotPropagateCall()
+            throws Exception {
+        BackupManagerService backupManagerService = createService();
+        String[] packages = {TEST_PACKAGE};
+
+        backupManagerService.filterAppsEligibleForBackup(packages);
+
+        verify(mUserOneService, never()).filterAppsEligibleForBackup(packages);
     }
 
     /**
-     * Test verifying that {@link BackupManagerService#backupNow(int)} throws a
-     * {@link SecurityException} if the caller does not have INTERACT_ACROSS_USERS_FULL permission.
+     * Test verifying that {@link BackupManagerService#backupNow(int)} throws a {@link
+     * SecurityException} if the caller does not have INTERACT_ACROSS_USERS_FULL permission.
      */
     @Test
-    public void testBackupNow_withoutPermission_throwsSecurityException() {
-        mShadowContext.denyPermissions(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
+    public void testBackupNow_withoutPermission_throwsSecurityExceptionForNonCallingUser() {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
 
-        expectThrows(
-                SecurityException.class,
-                () -> mBackupManagerService.backupNow(mUserId));
+        expectThrows(SecurityException.class, () -> backupManagerService.backupNow(mUserTwoId));
+    }
+
+    /**
+     * Test that the backup service does not throw a {@link SecurityException} if the caller has
+     * INTERACT_ACROSS_USERS_FULL permission and passes a different user id.
+     */
+    @Test
+    public void testBackupNow_withPermission_propagatesForNonCallingUser() {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        backupManagerService.startServiceForUser(mUserTwoId, mUserTwoService);
+        setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ true);
+
+        backupManagerService.backupNow(mUserTwoId);
+
+        verify(mUserTwoService).backupNow();
     }
 
     /** Test that the backup service routes methods correctly to the user that requests it. */
     @Test
-    public void testBackupNow_callsBackupNowForUser() throws Exception {
-        mShadowContext.grantPermissions(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
+    public void testBackupNow_onRegisteredUser_callsMethodForUser() throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
 
-        mBackupManagerService.backupNow(mUserId);
+        backupManagerService.backupNow(mUserOneId);
 
-        verify(mUserBackupManagerService).backupNow();
+        verify(mUserOneService).backupNow();
+    }
+
+    /** Test that the backup service does not route methods for non-registered users. */
+    @Test
+    public void testBackupNow_onUnknownUser_doesNotPropagateCall() throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
+
+        backupManagerService.backupNow(mUserTwoId);
+
+        verify(mUserOneService, never()).backupNow();
     }
 
     /**
-     * Test verifying that {@link BackupManagerService#requestBackup(int, String[], IBackupObserver,
-     * IBackupManagerMonitor, int)} throws a {@link SecurityException} if the caller does not have
-     * INTERACT_ACROSS_USERS_FULL permission.
+     * Test that the backup services throws a {@link SecurityException} if the caller does not have
+     * INTERACT_ACROSS_USERS_FULL permission and passes a different user id.
      */
     @Test
-    public void testRequestBackup_withoutPermission_throwsSecurityException() {
-        mShadowContext.denyPermissions(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
+    public void testRequestBackup_withoutPermission_throwsSecurityExceptionForNonCallingUser() {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
         String[] packages = {TEST_PACKAGE};
         IBackupObserver observer = mock(IBackupObserver.class);
         IBackupManagerMonitor monitor = mock(IBackupManagerMonitor.class);
 
         expectThrows(
                 SecurityException.class,
-                () -> mBackupManagerService.requestBackup(mUserId, packages, observer, monitor, 0));
-    }
-
-
-    /** Test that the backup service routes methods correctly to the user that requests it. */
-    @Test
-    public void testRequestBackup_callsRequestBackupForUser() throws Exception {
-        mShadowContext.grantPermissions(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
-        String[] packages = {TEST_PACKAGE};
-        IBackupObserver observer = mock(IBackupObserver.class);
-        IBackupManagerMonitor monitor = mock(IBackupManagerMonitor.class);
-
-        mBackupManagerService.requestBackup(mUserId, packages, observer, monitor,
-                /* flags */ 0);
-
-        verify(mUserBackupManagerService).requestBackup(packages, observer, monitor, /* flags */ 0);
+                () ->
+                        backupManagerService.requestBackup(
+                                mUserTwoId, packages, observer, monitor, 0));
     }
 
     /**
-     * Test verifying that {@link BackupManagerService#cancelBackups(int)} throws a
-     * {@link SecurityException} if the caller does not have INTERACT_ACROSS_USERS_FULL permission.
+     * Test that the backup service does not throw a {@link SecurityException} if the caller has
+     * INTERACT_ACROSS_USERS_FULL permission and passes a different user id.
      */
     @Test
-    public void testCancelBackups_withoutPermission_throwsSecurityException() {
-        mShadowContext.denyPermissions(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
+    public void testRequestBackup_withPermission_propagatesForNonCallingUser() {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        backupManagerService.startServiceForUser(mUserTwoId, mUserTwoService);
+        String[] packages = {TEST_PACKAGE};
+        IBackupObserver observer = mock(IBackupObserver.class);
+        IBackupManagerMonitor monitor = mock(IBackupManagerMonitor.class);
+        setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ true);
 
-        expectThrows(
-                SecurityException.class,
-                () -> mBackupManagerService.cancelBackups(mUserId));
-    }
+        backupManagerService.requestBackup(mUserTwoId, packages, observer, monitor, /* flags */ 0);
 
-
-    /** Test that the backup service routes methods correctly to the user that requests it. */
-    @Test
-    public void testCancelBackups_callsCancelBackupsForUser() throws Exception {
-        mShadowContext.grantPermissions(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
-
-        mBackupManagerService.cancelBackups(mUserId);
-
-        verify(mUserBackupManagerService).cancelBackups();
+        verify(mUserTwoService).requestBackup(packages, observer, monitor, /* flags */ 0);
     }
 
     /** Test that the backup service routes methods correctly to the user that requests it. */
     @Test
-    public void testBeginFullBackup_callsBeginFullBackupForUser() throws Exception {
+    public void testRequestBackup_onRegisteredUser_callsMethodForUser() throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        String[] packages = {TEST_PACKAGE};
+        IBackupObserver observer = mock(IBackupObserver.class);
+        IBackupManagerMonitor monitor = mock(IBackupManagerMonitor.class);
+        setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+
+        backupManagerService.requestBackup(mUserOneId, packages, observer, monitor, /* flags */ 0);
+
+        verify(mUserOneService).requestBackup(packages, observer, monitor, /* flags */ 0);
+    }
+
+    /** Test that the backup service routes methods correctly to the user that requests it. */
+    @Test
+    public void testRequestBackup_onUnknownUser_doesNotPropagateCall() throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        String[] packages = {TEST_PACKAGE};
+        IBackupObserver observer = mock(IBackupObserver.class);
+        IBackupManagerMonitor monitor = mock(IBackupManagerMonitor.class);
+        setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
+
+        backupManagerService.requestBackup(mUserTwoId, packages, observer, monitor, /* flags */ 0);
+
+        verify(mUserOneService, never()).requestBackup(packages, observer, monitor, /* flags */ 0);
+    }
+
+    /**
+     * Test verifying that {@link BackupManagerService#cancelBackups(int)} throws a {@link
+     * SecurityException} if the caller does not have INTERACT_ACROSS_USERS_FULL permission.
+     */
+    @Test
+    public void testCancelBackups_withoutPermission_throwsSecurityExceptionForNonCallingUser() {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+
+        expectThrows(SecurityException.class, () -> backupManagerService.cancelBackups(mUserTwoId));
+    }
+
+    /**
+     * Test that the backup service does not throw a {@link SecurityException} if the caller has
+     * INTERACT_ACROSS_USERS_FULL permission and passes a different user id.
+     */
+    @Test
+    public void testCancelBackups_withPermission_propagatesForNonCallingUser() {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        backupManagerService.startServiceForUser(mUserTwoId, mUserTwoService);
+        setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ true);
+
+        backupManagerService.cancelBackups(mUserTwoId);
+
+        verify(mUserTwoService).cancelBackups();
+    }
+
+    /** Test that the backup service routes methods correctly to the user that requests it. */
+    @Test
+    public void testCancelBackups_onRegisteredUser_callsMethodForUser() throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+
+        backupManagerService.cancelBackups(mUserOneId);
+
+        verify(mUserOneService).cancelBackups();
+    }
+
+    /** Test that the backup service does not route methods for non-registered users. */
+    @Test
+    public void testCancelBackups_onUnknownUser_doesNotPropagateCall() throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
+
+        backupManagerService.cancelBackups(mUserTwoId);
+
+        verify(mUserOneService, never()).cancelBackups();
+    }
+
+    /** Test that the backup service routes methods correctly to the user that requests it. */
+    @Test
+    public void testBeginFullBackup_onRegisteredUser_callsMethodForUser() throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
         FullBackupJob job = new FullBackupJob();
 
-        mBackupManagerService.beginFullBackup(job);
+        backupManagerService.beginFullBackup(job);
 
-        verify(mUserBackupManagerService).beginFullBackup(job);
+        verify(mUserOneService).beginFullBackup(job);
+    }
+
+    /** Test that the backup service does not route methods for non-registered users. */
+    @Test
+    public void testBeginFullBackup_onUnknownUser_doesNotPropagateCall() throws Exception {
+        BackupManagerService backupManagerService = createService();
+        FullBackupJob job = new FullBackupJob();
+
+        backupManagerService.beginFullBackup(job);
+
+        verify(mUserOneService, never()).beginFullBackup(job);
     }
 
     /** Test that the backup service routes methods correctly to the user that requests it. */
     @Test
-    public void testEndFullBackup_callsEndFullBackupForUser() throws Exception {
-        mBackupManagerService.endFullBackup();
+    public void testEndFullBackup_onRegisteredUser_callsMethodForUser() throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
 
-        verify(mUserBackupManagerService).endFullBackup();
+        backupManagerService.endFullBackup();
+
+        verify(mUserOneService).endFullBackup();
+    }
+
+    /** Test that the backup service does not route methods for non-registered users. */
+    @Test
+    public void testEndFullBackup_onUnknownUser_doesNotPropagateCall() throws Exception {
+        BackupManagerService backupManagerService = createService();
+
+        backupManagerService.endFullBackup();
+
+        verify(mUserOneService, never()).endFullBackup();
     }
 
     /** Test that the backup service routes methods correctly to the user that requests it. */
     @Test
-    public void testFullTransportBackup_callsFullTransportBackupForUser() throws Exception {
+    public void testFullTransportBackup_onRegisteredUser_callsMethodForUser() throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
         String[] packages = {TEST_PACKAGE};
 
-        mBackupManagerService.fullTransportBackup(packages);
+        backupManagerService.fullTransportBackup(packages);
 
-        verify(mUserBackupManagerService).fullTransportBackup(packages);
+        verify(mUserOneService).fullTransportBackup(packages);
+    }
+
+    /** Test that the backup service does not route methods for non-registered users. */
+    @Test
+    public void testFullTransportBackup_onUnknownUser_doesNotPropagateCall() throws Exception {
+        BackupManagerService backupManagerService = createService();
+        String[] packages = {TEST_PACKAGE};
+
+        backupManagerService.fullTransportBackup(packages);
+
+        verify(mUserOneService, never()).fullTransportBackup(packages);
     }
 
     // ---------------------------------------------
@@ -514,27 +1070,66 @@
 
     /** Test that the backup service routes methods correctly to the user that requests it. */
     @Test
-    public void testRestoreAtInstall_callsRestoreAtInstallForUser() throws Exception {
-        mBackupManagerService.restoreAtInstall(TEST_PACKAGE, /* token */ 0);
+    public void testRestoreAtInstall_onRegisteredUser_callsMethodForUser() throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
 
-        verify(mUserBackupManagerService).restoreAtInstall(TEST_PACKAGE, /* token */ 0);
+        backupManagerService.restoreAtInstall(TEST_PACKAGE, /* token */ 0);
+
+        verify(mUserOneService).restoreAtInstall(TEST_PACKAGE, /* token */ 0);
+    }
+
+    /** Test that the backup service does not route methods for non-registered users. */
+    @Test
+    public void testRestoreAtInstall_onUnknownUser_doesNotPropagateCall() throws Exception {
+        BackupManagerService backupManagerService = createService();
+
+        backupManagerService.restoreAtInstall(TEST_PACKAGE, /* token */ 0);
+
+        verify(mUserOneService, never()).restoreAtInstall(TEST_PACKAGE, /* token */ 0);
     }
 
     /** Test that the backup service routes methods correctly to the user that requests it. */
     @Test
-    public void testBeginRestoreSession_callsBeginRestoreSessionForUser() throws Exception {
-        mBackupManagerService.beginRestoreSession(TEST_PACKAGE, TEST_TRANSPORT);
+    public void testBeginRestoreSession_onRegisteredUser_callsMethodForUser() throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
 
-        verify(mUserBackupManagerService).beginRestoreSession(TEST_PACKAGE, TEST_TRANSPORT);
+        backupManagerService.beginRestoreSession(TEST_PACKAGE, TEST_TRANSPORT);
+
+        verify(mUserOneService).beginRestoreSession(TEST_PACKAGE, TEST_TRANSPORT);
+    }
+
+    /** Test that the backup service does not route methods for non-registered users. */
+    @Test
+    public void testBeginRestoreSession_onUnknownUser_doesNotPropagateCall() throws Exception {
+        BackupManagerService backupManagerService = createService();
+
+        backupManagerService.beginRestoreSession(TEST_PACKAGE, TEST_TRANSPORT);
+
+        verify(mUserOneService, never()).beginRestoreSession(TEST_PACKAGE, TEST_TRANSPORT);
     }
 
     /** Test that the backup service routes methods correctly to the user that requests it. */
     @Test
-    public void testGetAvailableRestoreToken_callsGetAvailableRestoreTokenForUser()
+    public void testGetAvailableRestoreToken_onRegisteredUser_callsMethodForUser()
             throws Exception {
-        mBackupManagerService.getAvailableRestoreToken(TEST_PACKAGE);
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
 
-        verify(mUserBackupManagerService).getAvailableRestoreToken(TEST_PACKAGE);
+        backupManagerService.getAvailableRestoreToken(TEST_PACKAGE);
+
+        verify(mUserOneService).getAvailableRestoreToken(TEST_PACKAGE);
+    }
+
+    /** Test that the backup service does not route methods for non-registered users. */
+    @Test
+    public void testGetAvailableRestoreToken_onUnknownUser_doesNotPropagateCall() throws Exception {
+        BackupManagerService backupManagerService = createService();
+
+        backupManagerService.getAvailableRestoreToken(TEST_PACKAGE);
+
+        verify(mUserOneService, never()).getAvailableRestoreToken(TEST_PACKAGE);
     }
 
     // ---------------------------------------------
@@ -543,33 +1138,61 @@
 
     /** Test that the backup service routes methods correctly to the user that requests it. */
     @Test
-    public void testSetBackupPassword_callsSetBackupPasswordForUser() throws Exception {
-        mBackupManagerService.setBackupPassword("currentPassword", "newPassword");
+    public void testSetBackupPassword_onRegisteredUser_callsMethodForUser() throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
 
-        verify(mUserBackupManagerService).setBackupPassword("currentPassword", "newPassword");
+        backupManagerService.setBackupPassword("currentPassword", "newPassword");
+
+        verify(mUserOneService).setBackupPassword("currentPassword", "newPassword");
+    }
+
+    /** Test that the backup service does not route methods for non-registered users. */
+    @Test
+    public void testSetBackupPassword_onUnknownUser_doesNotPropagateCall() throws Exception {
+        BackupManagerService backupManagerService = createService();
+
+        backupManagerService.setBackupPassword("currentPassword", "newPassword");
+
+        verify(mUserOneService, never()).setBackupPassword("currentPassword", "newPassword");
     }
 
     /** Test that the backup service routes methods correctly to the user that requests it. */
     @Test
-    public void testHasBackupPassword_callsHasBackupPasswordForUser() throws Exception {
-        mBackupManagerService.hasBackupPassword();
+    public void testHasBackupPassword_onRegisteredUser_callsMethodForUser() throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
 
-        verify(mUserBackupManagerService).hasBackupPassword();
+        backupManagerService.hasBackupPassword();
+
+        verify(mUserOneService).hasBackupPassword();
+    }
+
+    /** Test that the backup service does not route methods for non-registered users. */
+    @Test
+    public void testHasBackupPassword_onUnknownUser_doesNotPropagateCall() throws Exception {
+        BackupManagerService backupManagerService = createService();
+
+        backupManagerService.hasBackupPassword();
+
+        verify(mUserOneService, never()).hasBackupPassword();
     }
 
     /**
-     * Test verifying that {@link BackupManagerService#adbBackup(ParcelFileDescriptor, int, boolean,
-     * boolean, boolean, boolean, boolean, boolean, boolean, boolean, String[])} throws a
-     * {@link SecurityException} if the caller does not have INTERACT_ACROSS_USERS_FULL permission.
+     * Test that the backup services throws a {@link SecurityException} if the caller does not have
+     * INTERACT_ACROSS_USERS_FULL permission and passes a different user id.
      */
     @Test
-    public void testAdbBackup_withoutPermission_throwsSecurityException() {
-        mShadowContext.denyPermissions(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
+    public void testAdbBackup_withoutPermission_throwsSecurityExceptionForNonCallingUser() {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
 
-        expectThrows(SecurityException.class,
+        expectThrows(
+                SecurityException.class,
                 () ->
-                        mBackupManagerService.adbBackup(
-                                /* userId */ mUserId,
+                        backupManagerService.adbBackup(
+                                mUserTwoId,
                                 /* parcelFileDescriptor*/ null,
                                 /* includeApks */ true,
                                 /* includeObbs */ true,
@@ -580,24 +1203,22 @@
                                 /* doCompress */ true,
                                 /* doKeyValue */ true,
                                 null));
-
     }
 
     /**
-     * Test verifying that {@link BackupManagerService#adbBackup(ParcelFileDescriptor, int, boolean,
-     * boolean, boolean, boolean, boolean, boolean, boolean, boolean, String[])} does not require
-     * the caller to have INTERACT_ACROSS_USERS_FULL permission when the calling user id is the
-     * same as the target user id.
+     * Test that the backup service does not throw a {@link SecurityException} if the caller has
+     * INTERACT_ACROSS_USERS_FULL permission and passes a different user id.
      */
     @Test
-    public void testAdbBackup_whenCallingUserIsTargetUser_doesntNeedPermission() throws Exception {
-        ShadowBinder.setCallingUserHandle(UserHandle.of(mUserId));
-        mShadowContext.denyPermissions(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
-
+    public void testAdbBackup_withPermission_propagatesForNonCallingUser() throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        backupManagerService.startServiceForUser(mUserTwoId, mUserTwoService);
         ParcelFileDescriptor parcelFileDescriptor = getFileDescriptorForAdbTest();
+        setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ true);
 
-        mBackupManagerService.adbBackup(
-                /* userId */ mUserId,
+        backupManagerService.adbBackup(
+                mUserTwoId,
                 parcelFileDescriptor,
                 /* includeApks */ true,
                 /* includeObbs */ true,
@@ -609,7 +1230,7 @@
                 /* doKeyValue */ true,
                 ADB_TEST_PACKAGES);
 
-        verify(mUserBackupManagerService)
+        verify(mUserTwoService)
                 .adbBackup(
                         parcelFileDescriptor,
                         /* includeApks */ true,
@@ -625,13 +1246,14 @@
 
     /** Test that the backup service routes methods correctly to the user that requests it. */
     @Test
-    public void testAdbBackup_callsAdbBackupForUser() throws Exception {
-        mShadowContext.grantPermissions(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
-
+    public void testAdbBackup_onRegisteredUser_callsMethodForUser() throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
         ParcelFileDescriptor parcelFileDescriptor = getFileDescriptorForAdbTest();
+        setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
 
-        mBackupManagerService.adbBackup(
-                /* userId */ mUserId,
+        backupManagerService.adbBackup(
+                mUserOneId,
                 parcelFileDescriptor,
                 /* includeApks */ true,
                 /* includeObbs */ true,
@@ -643,7 +1265,42 @@
                 /* doKeyValue */ true,
                 ADB_TEST_PACKAGES);
 
-        verify(mUserBackupManagerService)
+        verify(mUserOneService)
+                .adbBackup(
+                        parcelFileDescriptor,
+                        /* includeApks */ true,
+                        /* includeObbs */ true,
+                        /* includeShared */ true,
+                        /* doWidgets */ true,
+                        /* doAllApps */ true,
+                        /* includeSystem */ true,
+                        /* doCompress */ true,
+                        /* doKeyValue */ true,
+                        ADB_TEST_PACKAGES);
+    }
+
+    /** Test that the backup service does not route methods for non-registered users. */
+    @Test
+    public void testAdbBackup_onUnknownUser_doesNotPropagateCall() throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        ParcelFileDescriptor parcelFileDescriptor = getFileDescriptorForAdbTest();
+        setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
+
+        backupManagerService.adbBackup(
+                mUserTwoId,
+                parcelFileDescriptor,
+                /* includeApks */ true,
+                /* includeObbs */ true,
+                /* includeShared */ true,
+                /* doWidgets */ true,
+                /* doAllApps */ true,
+                /* includeSystem */ true,
+                /* doCompress */ true,
+                /* doKeyValue */ true,
+                ADB_TEST_PACKAGES);
+
+        verify(mUserOneService, never())
                 .adbBackup(
                         parcelFileDescriptor,
                         /* includeApks */ true,
@@ -658,58 +1315,93 @@
     }
 
     /**
-     * Test verifying that {@link BackupManagerService#adbRestore(ParcelFileDescriptor, int)} throws
-     * a {@link SecurityException} if the caller does not have INTERACT_ACROSS_USERS_FULL
-     * permission.
+     * Test that the backup services throws a {@link SecurityException} if the caller does not have
+     * INTERACT_ACROSS_USERS_FULL permission and passes a different user id.
      */
     @Test
-    public void testAdbRestore_withoutPermission_throwsSecurityException() {
-        mShadowContext.denyPermissions(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
+    public void testAdbRestore_withoutPermission_throwsSecurityExceptionForNonCallingUser() {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
 
-        expectThrows(SecurityException.class,
-                () -> mBackupManagerService.adbRestore(mUserId, null));
-
+        expectThrows(
+                SecurityException.class, () -> backupManagerService.adbRestore(mUserTwoId, null));
     }
 
     /**
-     * Test verifying that {@link BackupManagerService#adbRestore(ParcelFileDescriptor, int)} does
-     * not require the caller to have INTERACT_ACROSS_USERS_FULL permission when the calling user id
-     * is the same as the target user id.
+     * Test that the backup service does not throw a {@link SecurityException} if the caller has
+     * INTERACT_ACROSS_USERS_FULL permission and passes a different user id.
      */
     @Test
-    public void testAdbRestore_whenCallingUserIsTargetUser_doesntNeedPermission() throws Exception {
-        ShadowBinder.setCallingUserHandle(UserHandle.of(mUserId));
-        mShadowContext.denyPermissions(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
-
+    public void testAdbRestore_withPermission_propagatesForNonCallingUser() throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        backupManagerService.startServiceForUser(mUserTwoId, mUserTwoService);
         ParcelFileDescriptor parcelFileDescriptor = getFileDescriptorForAdbTest();
+        setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ true);
 
-        mBackupManagerService.adbRestore(mUserId, parcelFileDescriptor);
+        backupManagerService.adbRestore(mUserTwoId, parcelFileDescriptor);
 
-        verify(mUserBackupManagerService).adbRestore(parcelFileDescriptor);
+        verify(mUserTwoService).adbRestore(parcelFileDescriptor);
     }
 
     /** Test that the backup service routes methods correctly to the user that requests it. */
     @Test
-    public void testAdbRestore_callsAdbRestoreForUser() throws Exception {
-        mShadowContext.grantPermissions(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
-
+    public void testAdbRestore_onRegisteredUser_callsMethodForUser() throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
         ParcelFileDescriptor parcelFileDescriptor = getFileDescriptorForAdbTest();
+        setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
 
-        mBackupManagerService.adbRestore(mUserId, parcelFileDescriptor);
+        backupManagerService.adbRestore(mUserOneId, parcelFileDescriptor);
 
-        verify(mUserBackupManagerService).adbRestore(parcelFileDescriptor);
+        verify(mUserOneService).adbRestore(parcelFileDescriptor);
+    }
+
+    /** Test that the backup service does not route methods for non-registered users. */
+    @Test
+    public void testAdbRestore_onUnknownUser_doesNotPropagateCall() throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        ParcelFileDescriptor parcelFileDescriptor = getFileDescriptorForAdbTest();
+        setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
+
+        backupManagerService.adbRestore(mUserTwoId, parcelFileDescriptor);
+
+        verify(mUserOneService, never()).adbRestore(parcelFileDescriptor);
     }
 
     /** Test that the backup service routes methods correctly to the user that requests it. */
     @Test
-    public void testAcknowledgeAdbBackupOrRestore_callsAcknowledgeAdbBackupOrRestoreForUser()
+    public void testAcknowledgeAdbBackupOrRestore_onRegisteredUser_callsMethodForUser()
             throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
         IFullBackupRestoreObserver observer = mock(IFullBackupRestoreObserver.class);
 
-        mBackupManagerService.acknowledgeAdbBackupOrRestore(
+        backupManagerService.acknowledgeAdbBackupOrRestore(
                 /* token */ 0, /* allow */ true, "currentPassword", "encryptionPassword", observer);
 
-        verify(mUserBackupManagerService)
+        verify(mUserOneService)
+                .acknowledgeAdbBackupOrRestore(
+                        /* token */ 0,
+                        /* allow */ true,
+                        "currentPassword",
+                        "encryptionPassword",
+                        observer);
+    }
+
+    /** Test that the backup service does not route methods for non-registered users. */
+    @Test
+    public void testAcknowledgeAdbBackupOrRestore_onUnknownUser_doesNotPropagateCall()
+            throws Exception {
+        BackupManagerService backupManagerService = createService();
+        IFullBackupRestoreObserver observer = mock(IFullBackupRestoreObserver.class);
+
+        backupManagerService.acknowledgeAdbBackupOrRestore(
+                /* token */ 0, /* allow */ true, "currentPassword", "encryptionPassword", observer);
+
+        verify(mUserOneService, never())
                 .acknowledgeAdbBackupOrRestore(
                         /* token */ 0,
                         /* allow */ true,
@@ -724,16 +1416,60 @@
 
     /** Test that the backup service routes methods correctly to the user that requests it. */
     @Test
-    public void testDump_callsDumpForUser() throws Exception {
+    public void testDump_onRegisteredUser_callsMethodForUser() throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
         File testFile = new File(mContext.getFilesDir(), "test");
         testFile.createNewFile();
         FileDescriptor fileDescriptor = new FileDescriptor();
         PrintWriter printWriter = new PrintWriter(testFile);
         String[] args = {"1", "2"};
 
-        mBackupManagerService.dump(fileDescriptor, printWriter, args);
+        backupManagerService.dump(fileDescriptor, printWriter, args);
 
-        verify(mUserBackupManagerService).dump(fileDescriptor, printWriter, args);
+        verify(mUserOneService).dump(fileDescriptor, printWriter, args);
+    }
+
+    /** Test that the backup service does not route methods for non-registered users. */
+    @Test
+    public void testDump_onUnknownUser_doesNotPropagateCall() throws Exception {
+        BackupManagerService backupManagerService = createService();
+        File testFile = new File(mContext.getFilesDir(), "test");
+        testFile.createNewFile();
+        FileDescriptor fileDescriptor = new FileDescriptor();
+        PrintWriter printWriter = new PrintWriter(testFile);
+        String[] args = {"1", "2"};
+
+        backupManagerService.dump(fileDescriptor, printWriter, args);
+
+        verify(mUserOneService, never()).dump(fileDescriptor, printWriter, args);
+    }
+
+    private BackupManagerService createService() {
+        return new BackupManagerService(
+                mContext, new Trampoline(mContext), startBackupThread(null));
+    }
+
+    private BackupManagerService createServiceAndRegisterUser(
+            int userId, UserBackupManagerService userBackupManagerService) {
+        BackupManagerService backupManagerService = createService();
+        backupManagerService.startServiceForUser(userId, userBackupManagerService);
+        return backupManagerService;
+    }
+
+    /**
+     * Sets the calling user to {@code userId} and grants the permission INTERACT_ACROSS_USERS_FULL
+     * to the caller if {@code shouldGrantPermission} is {@code true}, else it denies the
+     * permission.
+     */
+    private void setCallerAndGrantInteractUserPermission(
+            @UserIdInt int userId, boolean shouldGrantPermission) {
+        ShadowBinder.setCallingUserHandle(UserHandle.of(userId));
+        if (shouldGrantPermission) {
+            mShadowContext.grantPermissions(INTERACT_ACROSS_USERS_FULL);
+        } else {
+            mShadowContext.denyPermissions(INTERACT_ACROSS_USERS_FULL);
+        }
     }
 
     private ParcelFileDescriptor getFileDescriptorForAdbTest() throws Exception {
diff --git a/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java
index 3979a8e..148faad 100644
--- a/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java
@@ -25,7 +25,6 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
@@ -42,7 +41,6 @@
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.ArgumentMatchers.eq;
@@ -66,7 +64,6 @@
 import android.util.Log;
 import android.util.SparseArray;
 
-import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
@@ -96,6 +93,8 @@
     @Mock
     private ContentResolver mMockResolver;
     @Mock
+    private Context mMockContext;
+    @Mock
     private IActivityManager mIActivityManager;
     @Mock
     private UsageStatsManagerInternal mUsageStatsManagerInternal;
@@ -221,17 +220,16 @@
                 .thenReturn(STANDBY_BUCKET_ACTIVE);
         doReturn(Looper.getMainLooper()).when(Looper::myLooper);
 
-        final Context context = spy(InstrumentationRegistry.getTargetContext());
-        when(context.getContentResolver()).thenReturn(mMockResolver);
-        doNothing().when(mMockResolver).registerContentObserver(any(), anyBoolean(), any());
+        when(mMockContext.getContentResolver()).thenReturn(mMockResolver);
         doReturn("min_futurity=0").when(() ->
                 Settings.Global.getString(mMockResolver, Settings.Global.ALARM_MANAGER_CONSTANTS));
-        mInjector = new Injector(context);
-        mService = new AlarmManagerService(context, mInjector);
+        mInjector = new Injector(mMockContext);
+        mService = new AlarmManagerService(mMockContext, mInjector);
         spyOn(mService);
         doNothing().when(mService).publishBinderService(any(), any());
         mService.onStart();
         mService.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY);
+        spyOn(mService.mHandler);
 
         assertEquals(0, mService.mConstants.MIN_FUTURITY);
         assertEquals(mService.mSystemUiUid, SYSTEM_UI_UID);
@@ -273,7 +271,7 @@
 
         final ArgumentCaptor<PendingIntent.OnFinished> onFinishedCaptor =
                 ArgumentCaptor.forClass(PendingIntent.OnFinished.class);
-        verify(alarmPi).send(any(Context.class), eq(0), any(Intent.class),
+        verify(alarmPi).send(eq(mMockContext), eq(0), any(Intent.class),
                 onFinishedCaptor.capture(), any(Handler.class), isNull(), any());
         verify(mWakeLock).acquire();
         onFinishedCaptor.getValue().onSendFinished(alarmPi, null, 0, null, null);
@@ -423,11 +421,23 @@
         assertNotNull(restrictedAlarms.get(TEST_CALLING_UID));
 
         listenerArgumentCaptor.getValue().unblockAlarmsForUid(TEST_CALLING_UID);
-        verify(alarmPi).send(any(Context.class), eq(0), any(Intent.class), any(),
+        verify(alarmPi).send(eq(mMockContext), eq(0), any(Intent.class), any(),
                 any(Handler.class), isNull(), any());
         assertNull(restrictedAlarms.get(TEST_CALLING_UID));
     }
 
+    @Test
+    public void sendsTimeTickOnInteractive() {
+        final ArgumentCaptor<Runnable> runnableCaptor = ArgumentCaptor.forClass(Runnable.class);
+        // Stubbing so the handler doesn't actually run the runnable.
+        doReturn(true).when(mService.mHandler).post(runnableCaptor.capture());
+        // change interactive state: false -> true
+        mService.interactiveStateChangedLocked(false);
+        mService.interactiveStateChangedLocked(true);
+        runnableCaptor.getValue().run();
+        verify(mMockContext).sendBroadcastAsUser(mService.mTimeTickIntent, UserHandle.ALL);
+    }
+
     @After
     public void tearDown() {
         if (mMockingSession != null) {
diff --git a/services/tests/servicestests/src/com/android/server/backup/TrampolineTest.java b/services/tests/servicestests/src/com/android/server/backup/TrampolineTest.java
index d7a398e..ff31435 100644
--- a/services/tests/servicestests/src/com/android/server/backup/TrampolineTest.java
+++ b/services/tests/servicestests/src/com/android/server/backup/TrampolineTest.java
@@ -132,19 +132,21 @@
     }
 
     @Test
-    public void startServiceForUser_whenMultiUserSettingDisabled_isIgnored() {
+    public void unlockUser_whenMultiUserSettingDisabled_isIgnoredForNonSystemUser() {
         Settings.Global.putInt(mContentResolver, Settings.Global.BACKUP_MULTI_USER_ENABLED, 0);
+        mTrampoline.initializeService(UserHandle.USER_SYSTEM);
 
-        mTrampoline.startServiceForUser(10);
+        mTrampoline.unlockUser(10);
 
         verify(mBackupManagerServiceMock, never()).startServiceForUser(10);
     }
 
     @Test
-    public void startServiceForUser_whenMultiUserSettingEnabled_callsBackupManagerService() {
+    public void unlockUser_whenMultiUserSettingEnabled_callsBackupManagerServiceForNonSystemUser() {
         Settings.Global.putInt(mContentResolver, Settings.Global.BACKUP_MULTI_USER_ENABLED, 1);
+        mTrampoline.initializeService(UserHandle.USER_SYSTEM);
 
-        mTrampoline.startServiceForUser(10);
+        mTrampoline.unlockUser(10);
 
         verify(mBackupManagerServiceMock).startServiceForUser(10);
     }
@@ -989,6 +991,11 @@
             return sBackupManagerServiceMock;
         }
 
+        @Override
+        protected void postToHandler(Runnable runnable) {
+            runnable.run();
+        }
+
         int getCreateServiceCallsCount() {
             return mCreateServiceCallsCount;
         }
diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/PackageDexUsageTests.java b/services/tests/servicestests/src/com/android/server/pm/dex/PackageDexUsageTests.java
index 7755e94..5df4509 100644
--- a/services/tests/servicestests/src/com/android/server/pm/dex/PackageDexUsageTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/dex/PackageDexUsageTests.java
@@ -17,6 +17,7 @@
 package com.android.server.pm.dex;
 
 import static com.android.server.pm.dex.PackageDexUsage.DexUseInfo;
+import static com.android.server.pm.dex.PackageDexUsage.MAX_SECONDARY_FILES_PER_OWNER;
 import static com.android.server.pm.dex.PackageDexUsage.PackageUseInfo;
 
 import static org.junit.Assert.assertEquals;
@@ -31,24 +32,28 @@
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import dalvik.system.VMRuntime;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import dalvik.system.VMRuntime;
-
 import java.io.IOException;
 import java.io.StringReader;
 import java.io.StringWriter;
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
 @RunWith(AndroidJUnit4.class)
 @SmallTest
 public class PackageDexUsageTests {
+    private static final String ISA = VMRuntime.getInstructionSet(Build.SUPPORTED_ABIS[0]);
+
     private PackageDexUsage mPackageDexUsage;
 
     private TestData mFooBaseUser0;
@@ -71,25 +76,23 @@
         String fooCodeDir = "/data/app/com.google.foo/";
         String fooDataDir = "/data/user/0/com.google.foo/";
 
-        String isa = VMRuntime.getInstructionSet(Build.SUPPORTED_ABIS[0]);
-
         mFooBaseUser0 = new TestData(fooPackageName,
-                fooCodeDir + "base.apk", 0, isa, false, true, fooPackageName);
+                fooCodeDir + "base.apk", 0, ISA, false, true, fooPackageName);
 
         mFooSplit1User0 = new TestData(fooPackageName,
-                fooCodeDir + "split-1.apk", 0, isa, false, true, fooPackageName);
+                fooCodeDir + "split-1.apk", 0, ISA, false, true, fooPackageName);
 
         mFooSplit2UsedByOtherApps0 = new TestData(fooPackageName,
-                fooCodeDir + "split-2.apk", 0, isa, true, true, "used.by.other.com");
+                fooCodeDir + "split-2.apk", 0, ISA, true, true, "used.by.other.com");
 
         mFooSecondary1User0 = new TestData(fooPackageName,
-                fooDataDir + "sec-1.dex", 0, isa, false, false, fooPackageName);
+                fooDataDir + "sec-1.dex", 0, ISA, false, false, fooPackageName);
 
         mFooSecondary1User1 = new TestData(fooPackageName,
-                fooDataDir + "sec-1.dex", 1, isa, false, false, fooPackageName);
+                fooDataDir + "sec-1.dex", 1, ISA, false, false, fooPackageName);
 
         mFooSecondary2UsedByOtherApps0 = new TestData(fooPackageName,
-                fooDataDir + "sec-2.dex", 0, isa, true, false, "used.by.other.com");
+                fooDataDir + "sec-2.dex", 0, ISA, true, false, "used.by.other.com");
 
         mInvalidIsa = new TestData(fooPackageName,
                 fooCodeDir + "base.apk", 0, "INVALID_ISA", false, true, "INALID_USER");
@@ -100,11 +103,11 @@
         String barDataDir1 = "/data/user/1/com.google.bar/";
 
         mBarBaseUser0 = new TestData(barPackageName,
-                barCodeDir + "base.apk", 0, isa, false, true, barPackageName);
+                barCodeDir + "base.apk", 0, ISA, false, true, barPackageName);
         mBarSecondary1User0 = new TestData(barPackageName,
-                barDataDir + "sec-1.dex", 0, isa, false, false, barPackageName);
+                barDataDir + "sec-1.dex", 0, ISA, false, false, barPackageName);
         mBarSecondary2User1 = new TestData(barPackageName,
-                barDataDir1 + "sec-2.dex", 1, isa, false, false, barPackageName);
+                barDataDir1 + "sec-2.dex", 1, ISA, false, false, barPackageName);
     }
 
     @Test
@@ -183,6 +186,25 @@
     }
 
     @Test
+    public void testRecordTooManySecondaries() {
+        int tooManyFiles = MAX_SECONDARY_FILES_PER_OWNER + 1;
+        List<TestData> expectedSecondaries = new ArrayList<>();
+        for (int i = 1; i <= tooManyFiles; i++) {
+            String fooPackageName = "com.google.foo";
+            TestData testData = new TestData(fooPackageName,
+                    "/data/user/0/" + fooPackageName + "/sec-" + i + "1.dex", 0, ISA, false, false,
+                    fooPackageName);
+            if (i < tooManyFiles) {
+                assertTrue("Adding " + testData.mDexFile, record(testData));
+                expectedSecondaries.add(testData);
+            } else {
+                assertFalse("Adding " + testData.mDexFile, record(testData));
+            }
+            assertPackageDexUsage(mPackageDexUsage, null, null, expectedSecondaries);
+        }
+    }
+
+    @Test
     public void testMultiplePackages() {
         assertTrue(record(mFooBaseUser0));
         assertTrue(record(mFooSecondary1User0));
@@ -540,7 +562,14 @@
 
     private void assertPackageDexUsage(PackageDexUsage packageDexUsage, Set<String> users,
             TestData primary, TestData... secondaries) {
-        String packageName = primary == null ? secondaries[0].mPackageName : primary.mPackageName;
+        assertPackageDexUsage(packageDexUsage, users, primary, Arrays.asList(secondaries));
+    }
+
+    private void assertPackageDexUsage(PackageDexUsage packageDexUsage, Set<String> users,
+            TestData primary, List<TestData> secondaries) {
+        String packageName = primary == null
+                ? secondaries.get(0).mPackageName
+                : primary.mPackageName;
         boolean primaryUsedByOtherApps = primary != null && primary.mUsedByOtherApps;
         PackageUseInfo pInfo = packageDexUsage.getPackageUseInfo(packageName);
 
@@ -554,7 +583,7 @@
         }
 
         Map<String, DexUseInfo> dexUseInfoMap = pInfo.getDexUseInfoMap();
-        assertEquals(secondaries.length, dexUseInfoMap.size());
+        assertEquals(secondaries.size(), dexUseInfoMap.size());
 
         // Check dex use info
         for (TestData testData : secondaries) {
diff --git a/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java b/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java
index ec475bf..eddf8f9 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java
@@ -98,14 +98,14 @@
             stats.mLastTimeVisible = statsOut.beginTime + XmlUtils.readLongAttribute(
                     parser, LAST_TIME_VISIBLE_ATTR);
         } catch (IOException e) {
-            Log.e(TAG, "Failed to parse mLastTimeVisible", e);
+            Log.i(TAG, "Failed to parse mLastTimeVisible");
         }
 
         try {
             stats.mLastTimeForegroundServiceUsed = statsOut.beginTime + XmlUtils.readLongAttribute(
                     parser, LAST_TIME_SERVICE_USED_ATTR);
         } catch (IOException e) {
-            Log.e(TAG, "Failed to parse mLastTimeForegroundServiceUsed", e);
+            Log.i(TAG, "Failed to parse mLastTimeForegroundServiceUsed");
         }
 
         stats.mTotalTimeInForeground = XmlUtils.readLongAttribute(parser, TOTAL_TIME_ACTIVE_ATTR);
@@ -113,14 +113,14 @@
         try {
             stats.mTotalTimeVisible = XmlUtils.readLongAttribute(parser, TOTAL_TIME_VISIBLE_ATTR);
         } catch (IOException e) {
-            Log.e(TAG, "Failed to parse mTotalTimeVisible", e);
+            Log.i(TAG, "Failed to parse mTotalTimeVisible");
         }
 
         try {
             stats.mTotalTimeForegroundServiceUsed = XmlUtils.readLongAttribute(parser,
                     TOTAL_TIME_SERVICE_USED_ATTR);
         } catch (IOException e) {
-            Log.e(TAG, "Failed to parse mTotalTimeForegroundServiceUsed", e);
+            Log.i(TAG, "Failed to parse mTotalTimeForegroundServiceUsed");
         }
 
         stats.mLastEvent = XmlUtils.readIntAttribute(parser, LAST_EVENT_ATTR);
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
index 99ad1f4..bbf3d45 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
@@ -356,7 +356,12 @@
             }
 
             // No voice interactor, we'll just set up a simple recognizer.
-            curRecognizer = findAvailRecognizer(null, userHandle);
+            initSimpleRecognizer(curInteractorInfo, userHandle);
+        }
+
+        public void initSimpleRecognizer(VoiceInteractionServiceInfo curInteractorInfo,
+                int userHandle) {
+            ComponentName curRecognizer = findAvailRecognizer(null, userHandle);
             if (curRecognizer != null) {
                 if (curInteractorInfo == null) {
                     setCurInteractor(null, userHandle);
@@ -1236,34 +1241,46 @@
                 int userHandle = UserHandle.getUserId(uid);
                 ComponentName curInteractor = getCurInteractor(userHandle);
                 ComponentName curRecognizer = getCurRecognizer(userHandle);
-                boolean hit = false;
+                boolean hitInt = false;
+                boolean hitRec = false;
                 for (String pkg : packages) {
                     if (curInteractor != null && pkg.equals(curInteractor.getPackageName())) {
-                        hit = true;
+                        hitInt = true;
                         break;
                     } else if (curRecognizer != null
                             && pkg.equals(curRecognizer.getPackageName())) {
-                        hit = true;
+                        hitRec = true;
                         break;
                     }
                 }
-                if (hit && doit) {
-                    // The user is force stopping our current interactor/recognizer.
+                if (hitInt && doit) {
+                    // The user is force stopping our current interactor.
                     // Clear the current settings and restore default state.
                     synchronized (VoiceInteractionManagerServiceStub.this) {
+                        Slog.i(TAG, "Force stopping current voice interactor: "
+                                + getCurInteractor(userHandle));
                         unloadAllKeyphraseModels();
                         if (mImpl != null) {
                             mImpl.shutdownLocked();
                             setImplLocked(null);
                         }
+
                         setCurInteractor(null, userHandle);
                         setCurRecognizer(null, userHandle);
                         resetCurAssistant(userHandle);
                         initForUser(userHandle);
                         switchImplementationIfNeededLocked(true);
                     }
+                } else if (hitRec && doit) {
+                    // We are just force-stopping the current recognizer, which is not
+                    // also the current interactor.
+                    synchronized (VoiceInteractionManagerServiceStub.this) {
+                        Slog.i(TAG, "Force stopping current voice recognizer: "
+                                + getCurRecognizer(userHandle));
+                        initSimpleRecognizer(null, userHandle);
+                    }
                 }
-                return hit;
+                return hitInt || hitRec;
             }
 
             @Override
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index eaff50a..b61e99b 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -1198,7 +1198,8 @@
     }
 
     /**
-     * Request a refresh of the platform cache of profile information.
+     * Request a refresh of the platform cache of profile information for the eUICC which
+     * corresponds to the card ID returned by {@link TelephonyManager#getCardIdForDefaultEuicc()}.
      *
      * <p>Should be called by the EuiccService implementation whenever this information changes due
      * to an operation done outside the scope of a request initiated by the platform to the
@@ -1206,17 +1207,50 @@
      * were made through the EuiccService.
      *
      * <p>Requires the {@link android.Manifest.permission#WRITE_EMBEDDED_SUBSCRIPTIONS} permission.
+     *
+     * @see {@link TelephonyManager#getCardIdForDefaultEuicc()} for more information on the card ID.
+     *
      * @hide
      */
     @SystemApi
     public void requestEmbeddedSubscriptionInfoListRefresh() {
+        int cardId = TelephonyManager.from(mContext).getCardIdForDefaultEuicc();
         try {
             ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
             if (iSub != null) {
-                iSub.requestEmbeddedSubscriptionInfoListRefresh();
+                iSub.requestEmbeddedSubscriptionInfoListRefresh(cardId);
             }
         } catch (RemoteException ex) {
-            // ignore it
+            logd("requestEmbeddedSubscriptionInfoListFresh for card = " + cardId + " failed.");
+        }
+    }
+
+    /**
+     * Request a refresh of the platform cache of profile information for the eUICC with the given
+     * {@code cardId}.
+     *
+     * <p>Should be called by the EuiccService implementation whenever this information changes due
+     * to an operation done outside the scope of a request initiated by the platform to the
+     * EuiccService. There is no need to refresh for downloads, deletes, or other operations that
+     * were made through the EuiccService.
+     *
+     * <p>Requires the {@link android.Manifest.permission#WRITE_EMBEDDED_SUBSCRIPTIONS} permission.
+     *
+     * @param cardId the card ID of the eUICC.
+     *
+     * @see {@link TelephonyManager#getCardIdForDefaultEuicc()} for more information on the card ID.
+     *
+     * @hide
+     */
+    @SystemApi
+    public void requestEmbeddedSubscriptionInfoListRefresh(int cardId) {
+        try {
+            ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+            if (iSub != null) {
+                iSub.requestEmbeddedSubscriptionInfoListRefresh(cardId);
+            }
+        } catch (RemoteException ex) {
+            logd("requestEmbeddedSubscriptionInfoListFresh for card = " + cardId + " failed.");
         }
     }
 
diff --git a/telephony/java/com/android/internal/telephony/ISub.aidl b/telephony/java/com/android/internal/telephony/ISub.aidl
index 65eedb8..d169b7d 100755
--- a/telephony/java/com/android/internal/telephony/ISub.aidl
+++ b/telephony/java/com/android/internal/telephony/ISub.aidl
@@ -104,7 +104,7 @@
     /**
      * @see android.telephony.SubscriptionManager#requestEmbeddedSubscriptionInfoListRefresh
      */
-    oneway void requestEmbeddedSubscriptionInfoListRefresh();
+    oneway void requestEmbeddedSubscriptionInfoListRefresh(int cardId);
 
     /**
      * Add a new SubscriptionInfo to subinfo database if needed
diff --git a/tests/net/java/android/net/dhcp/DhcpServerTest.java b/tests/net/java/android/net/dhcp/DhcpServerTest.java
index df34c73..ab9bd84 100644
--- a/tests/net/java/android/net/dhcp/DhcpServerTest.java
+++ b/tests/net/java/android/net/dhcp/DhcpServerTest.java
@@ -25,7 +25,6 @@
 import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.assertFalse;
 import static junit.framework.Assert.assertNotNull;
-import static junit.framework.Assert.assertNull;
 import static junit.framework.Assert.assertTrue;
 
 import static org.mockito.ArgumentMatchers.any;
@@ -48,7 +47,6 @@
 import android.net.dhcp.DhcpLeaseRepository.OutOfAddressesException;
 import android.net.dhcp.DhcpServer.Clock;
 import android.net.dhcp.DhcpServer.Dependencies;
-import android.net.util.InterfaceParams;
 import android.net.util.SharedLog;
 import android.os.test.TestLooper;
 import android.support.test.filters.SmallTest;
@@ -74,9 +72,6 @@
 public class DhcpServerTest {
     private static final String PROP_DEXMAKER_SHARE_CLASSLOADER = "dexmaker.share_classloader";
     private static final String TEST_IFACE = "testiface";
-    private static final MacAddress TEST_IFACE_MAC = MacAddress.fromString("11:22:33:44:55:66");
-    private static final InterfaceParams TEST_IFACEPARAMS =
-            new InterfaceParams(TEST_IFACE, 1, TEST_IFACE_MAC);
 
     private static final Inet4Address TEST_SERVER_ADDR = parseAddr("192.168.0.2");
     private static final LinkAddress TEST_SERVER_LINKADDR = new LinkAddress(TEST_SERVER_ADDR, 20);
@@ -149,7 +144,7 @@
                 .build();
 
         mLooper = new TestLooper();
-        mServer = new DhcpServer(mLooper.getLooper(), TEST_IFACEPARAMS, servingParams,
+        mServer = new DhcpServer(mLooper.getLooper(), TEST_IFACE, servingParams,
                 new SharedLog(DhcpServerTest.class.getSimpleName()), mDeps);
 
         mServer.start();
diff --git a/tests/net/java/android/net/ip/IpServerTest.java b/tests/net/java/android/net/ip/IpServerTest.java
index cff0b54..2c675c6 100644
--- a/tests/net/java/android/net/ip/IpServerTest.java
+++ b/tests/net/java/android/net/ip/IpServerTest.java
@@ -404,7 +404,7 @@
 
     private void assertDhcpStarted(IpPrefix expectedPrefix) {
         verify(mDependencies, times(1)).makeDhcpServer(
-                eq(mLooper.getLooper()), eq(TEST_IFACE_PARAMS), any(), eq(mSharedLog));
+                eq(mLooper.getLooper()), eq(IFACE_NAME), any(), eq(mSharedLog));
         verify(mDhcpServer, times(1)).start();
         final DhcpServingParams params = mDhcpParamsCaptor.getValue();
         // Last address byte is random
diff --git a/tests/net/java/com/android/server/connectivity/TetheringTest.java b/tests/net/java/com/android/server/connectivity/TetheringTest.java
index bca9be7..e6b43d2 100644
--- a/tests/net/java/com/android/server/connectivity/TetheringTest.java
+++ b/tests/net/java/com/android/server/connectivity/TetheringTest.java
@@ -240,7 +240,7 @@
                 }
 
                 @Override
-                public DhcpServer makeDhcpServer(Looper looper, InterfaceParams iface,
+                public DhcpServer makeDhcpServer(Looper looper, String ifName,
                         DhcpServingParams params, SharedLog log) {
                     return mDhcpServer;
                 }
diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl
index a8e82b3..0362a1b 100644
--- a/wifi/java/android/net/wifi/IWifiManager.aidl
+++ b/wifi/java/android/net/wifi/IWifiManager.aidl
@@ -197,5 +197,7 @@
     int removeNetworkSuggestions(in List<WifiNetworkSuggestion> networkSuggestions, in String packageName);
 
     String[] getFactoryMacAddresses();
+
+    void setDeviceMobilityState(int state);
 }
 
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 8cb2063..a7c2ff0 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -4476,4 +4476,69 @@
             throw e.rethrowFromSystemServer();
         }
     }
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = {"DEVICE_MOBILITY_STATE_"}, value = {
+            DEVICE_MOBILITY_STATE_UNKNOWN,
+            DEVICE_MOBILITY_STATE_HIGH_MVMT,
+            DEVICE_MOBILITY_STATE_LOW_MVMT,
+            DEVICE_MOBILITY_STATE_STATIONARY})
+    public @interface DeviceMobilityState {}
+
+    /**
+     * Unknown device mobility state
+     *
+     * @see #setDeviceMobilityState(int)
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int DEVICE_MOBILITY_STATE_UNKNOWN = 0;
+
+    /**
+     * High movement device mobility state
+     *
+     * @see #setDeviceMobilityState(int)
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int DEVICE_MOBILITY_STATE_HIGH_MVMT = 1;
+
+    /**
+     * Low movement device mobility state
+     *
+     * @see #setDeviceMobilityState(int)
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int DEVICE_MOBILITY_STATE_LOW_MVMT = 2;
+
+    /**
+     * Stationary device mobility state
+     *
+     * @see #setDeviceMobilityState(int)
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int DEVICE_MOBILITY_STATE_STATIONARY = 3;
+
+    /**
+     * Updates the device mobility state. Wifi uses this information to adjust the interval between
+     * Wifi scans in order to balance power consumption with scan accuracy.
+     * @param state the updated device mobility state
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.WIFI_SET_DEVICE_MOBILITY_STATE)
+    public void setDeviceMobilityState(@DeviceMobilityState int state) {
+        try {
+            mService.setDeviceMobilityState(state);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
 }