Merge "Move car keyguard values to CarSystemUI"
diff --git a/Android.bp b/Android.bp
index 1b81306..dc9c4d4 100644
--- a/Android.bp
+++ b/Android.bp
@@ -357,6 +357,7 @@
         "core/java/android/view/autofill/IAutoFillManagerClient.aidl",
         "core/java/android/view/autofill/IAugmentedAutofillManagerClient.aidl",
         "core/java/android/view/autofill/IAutofillWindowPresenter.aidl",
+        "core/java/android/view/contentcapture/IContentCaptureDirectManager.aidl",
         "core/java/android/view/contentcapture/IContentCaptureManager.aidl",
         "core/java/android/view/IApplicationToken.aidl",
         "core/java/android/view/IAppTransitionAnimationSpecsFuture.aidl",
@@ -387,6 +388,7 @@
         "core/java/android/speech/tts/ITextToSpeechService.aidl",
         "core/java/com/android/internal/app/IAppOpsActiveCallback.aidl",
         "core/java/com/android/internal/app/IAppOpsCallback.aidl",
+        "core/java/com/android/internal/app/IAppOpsNotedCallback.aidl",
         "core/java/com/android/internal/app/IAppOpsService.aidl",
         "core/java/com/android/internal/app/IBatteryStats.aidl",
         "core/java/com/android/internal/app/ISoundTriggerService.aidl",
diff --git a/CleanSpec.mk b/CleanSpec.mk
index 478e4fe..d01e183 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -251,6 +251,7 @@
 $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/overlay/ExperimentNavigationBarSlim)
 $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/vendor/overlay/ExperimentNavigationBarSlim)
 $(call add-clean-step, rm -rf $(PRODUCT_OUT)/vendor/overlay/ExperimentNavigationBarSlim)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/priv-app/SystemUI)
 # ******************************************************************
 # NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST ABOVE THIS BANNER
 # ******************************************************************
diff --git a/apct-tests/perftests/multiuser/Android.mk b/apct-tests/perftests/multiuser/Android.mk
index 5ff4ebc..5852044 100644
--- a/apct-tests/perftests/multiuser/Android.mk
+++ b/apct-tests/perftests/multiuser/Android.mk
@@ -20,7 +20,7 @@
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
 LOCAL_STATIC_JAVA_LIBRARIES := \
-    android-support-test \
+    androidx.test.rules \
     apct-perftests-utils
 
 LOCAL_PACKAGE_NAME := MultiUserPerfTests
diff --git a/apct-tests/perftests/multiuser/AndroidManifest.xml b/apct-tests/perftests/multiuser/AndroidManifest.xml
index adb316f..e96771c 100644
--- a/apct-tests/perftests/multiuser/AndroidManifest.xml
+++ b/apct-tests/perftests/multiuser/AndroidManifest.xml
@@ -25,7 +25,7 @@
         <uses-library android:name="android.test.runner" />
     </application>
 
-    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+    <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
             android:targetPackage="com.android.perftests.multiuser"/>
 
 </manifest>
diff --git a/apct-tests/perftests/multiuser/src/android/multiuser/BenchmarkResultsReporter.java b/apct-tests/perftests/multiuser/src/android/multiuser/BenchmarkResultsReporter.java
index d3a3ce5..ba33e64 100644
--- a/apct-tests/perftests/multiuser/src/android/multiuser/BenchmarkResultsReporter.java
+++ b/apct-tests/perftests/multiuser/src/android/multiuser/BenchmarkResultsReporter.java
@@ -18,9 +18,10 @@
 import android.app.Activity;
 import android.app.Instrumentation;
 import android.os.Bundle;
-import android.support.test.InstrumentationRegistry;
 import android.util.Log;
 
+import androidx.test.InstrumentationRegistry;
+
 import org.junit.rules.TestRule;
 import org.junit.runner.Description;
 import org.junit.runners.model.Statement;
diff --git a/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java b/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java
index 855be08..2fdba0a 100644
--- a/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java
+++ b/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java
@@ -27,9 +27,10 @@
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.os.UserManager;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.LargeTest;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.After;
 import org.junit.Before;
@@ -50,7 +51,7 @@
  * adb install -r \
  *     ${ANDROID_PRODUCT_OUT}/data/app/MultiUserPerfTests/MultiUserPerfTests.apk &&
  * adb shell am instrument -e class android.multiuser.UserLifecycleTests \
- *     -w com.android.perftests.multiuser/android.support.test.runner.AndroidJUnitRunner
+ *     -w com.android.perftests.multiuser/androidx.test.runner.AndroidJUnitRunner
  *
  * or
  *
diff --git a/api/current.txt b/api/current.txt
index 4db12ea..2b34f4f 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -24988,8 +24988,9 @@
     field public static final int RATING_KEY_BY_USER = 268435457; // 0x10000001
   }
 
-  public class MediaMetadataRetriever {
+  public class MediaMetadataRetriever implements java.lang.AutoCloseable {
     ctor public MediaMetadataRetriever();
+    method public void close();
     method public java.lang.String extractMetadata(int);
     method public byte[] getEmbeddedPicture();
     method public android.graphics.Bitmap getFrameAtIndex(int, android.media.MediaMetadataRetriever.BitmapParams);
@@ -26076,7 +26077,12 @@
 
   public class ThumbnailUtils {
     ctor public ThumbnailUtils();
-    method public static android.graphics.Bitmap createVideoThumbnail(java.lang.String, int);
+    method public static deprecated android.graphics.Bitmap createAudioThumbnail(java.lang.String, int);
+    method public static android.graphics.Bitmap createAudioThumbnail(java.io.File, android.util.Size, android.os.CancellationSignal) throws java.io.IOException;
+    method public static deprecated android.graphics.Bitmap createImageThumbnail(java.lang.String, int);
+    method public static android.graphics.Bitmap createImageThumbnail(java.io.File, android.util.Size, android.os.CancellationSignal) throws java.io.IOException;
+    method public static deprecated android.graphics.Bitmap createVideoThumbnail(java.lang.String, int);
+    method public static android.graphics.Bitmap createVideoThumbnail(java.io.File, android.util.Size, android.os.CancellationSignal) throws java.io.IOException;
     method public static android.graphics.Bitmap extractThumbnail(android.graphics.Bitmap, int, int);
     method public static android.graphics.Bitmap extractThumbnail(android.graphics.Bitmap, int, int, int);
     field public static final int OPTIONS_RECYCLE_INPUT = 2; // 0x2
@@ -28319,6 +28325,11 @@
     field public int serverAddress;
   }
 
+  public class InetAddresses {
+    method public static boolean isNumericAddress(java.lang.String);
+    method public static java.net.InetAddress parseNumericAddress(java.lang.String);
+  }
+
   public final class IpPrefix implements android.os.Parcelable {
     method public boolean contains(java.net.InetAddress);
     method public int describeContents();
@@ -38337,6 +38348,11 @@
     field public static final java.lang.String VALUE = "value";
   }
 
+  public static final class Settings.Panel {
+    field public static final java.lang.String ACTION_INTERNET_CONNECTIVITY = "android.settings.panel.action.INTERNET_CONNECTIVITY";
+    field public static final java.lang.String ACTION_VOLUME = "android.settings.panel.action.VOLUME";
+  }
+
   public static final class Settings.Secure extends android.provider.Settings.NameValueTable {
     ctor public Settings.Secure();
     method public static float getFloat(android.content.ContentResolver, java.lang.String, float);
@@ -41225,10 +41241,12 @@
     method public android.graphics.drawable.Icon getIcon();
     method public java.lang.CharSequence getLabel();
     method public int getState();
+    method public java.lang.CharSequence getSubtitle();
     method public void setContentDescription(java.lang.CharSequence);
     method public void setIcon(android.graphics.drawable.Icon);
     method public void setLabel(java.lang.CharSequence);
     method public void setState(int);
+    method public void setSubtitle(java.lang.CharSequence);
     method public void updateTile();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.service.quicksettings.Tile> CREATOR;
@@ -41485,6 +41503,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 7d10b098..4278003 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -205,6 +205,10 @@
     field public static final int config_sendPackageName = 17891328; // 0x1110000
   }
 
+  public static final class R.color {
+    field public static final int system_notification_accent_color = 17170460; // 0x106001c
+  }
+
   public static final class R.dimen {
     field public static final int config_mediaMetadataBitmapMaxSize = 17104904; // 0x1050008
     field public static final int config_restrictedIconSize = 17104903; // 0x1050007
@@ -1467,6 +1471,8 @@
 
   public final class BrightnessConfiguration implements android.os.Parcelable {
     method public int describeContents();
+    method public android.hardware.display.BrightnessCorrection getCorrectionByCategory(int);
+    method public android.hardware.display.BrightnessCorrection getCorrectionByPackageName(java.lang.String);
     method public android.util.Pair<float[], float[]> getCurve();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.hardware.display.BrightnessConfiguration> CREATOR;
@@ -1474,10 +1480,22 @@
 
   public static class BrightnessConfiguration.Builder {
     ctor public BrightnessConfiguration.Builder(float[], float[]);
+    method public android.hardware.display.BrightnessConfiguration.Builder addCorrectionByCategory(int, android.hardware.display.BrightnessCorrection);
+    method public android.hardware.display.BrightnessConfiguration.Builder addCorrectionByPackageName(java.lang.String, android.hardware.display.BrightnessCorrection);
     method public android.hardware.display.BrightnessConfiguration build();
+    method public int getMaxCorrectionsByCategory();
+    method public int getMaxCorrectionsByPackageName();
     method public android.hardware.display.BrightnessConfiguration.Builder setDescription(java.lang.String);
   }
 
+  public final class BrightnessCorrection implements android.os.Parcelable {
+    method public float apply(float);
+    method public static android.hardware.display.BrightnessCorrection createScaleAndTranslateLog(float, float);
+    method public int describeContents();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.hardware.display.BrightnessCorrection> CREATOR;
+  }
+
   public final class DisplayManager {
     method public java.util.List<android.hardware.display.AmbientBrightnessDayStats> getAmbientBrightnessStats();
     method public android.hardware.display.BrightnessConfiguration getBrightnessConfiguration();
@@ -2516,7 +2534,37 @@
   }
 
   public class UsbManager {
+    method public java.util.List<android.hardware.usb.UsbPort> getPorts();
     method public void grantPermission(android.hardware.usb.UsbDevice, java.lang.String);
+    field public static final java.lang.String ACTION_USB_PORT_CHANGED = "android.hardware.usb.action.USB_PORT_CHANGED";
+  }
+
+  public final class UsbPort {
+    method public android.hardware.usb.UsbPortStatus getStatus();
+    method public void setRoles(int, int);
+  }
+
+  public final class UsbPortStatus implements android.os.Parcelable {
+    method public int describeContents();
+    method public int getCurrentDataRole();
+    method public int getCurrentMode();
+    method public int getCurrentPowerRole();
+    method public int getSupportedRoleCombinations();
+    method public boolean isConnected();
+    method public boolean isRoleCombinationSupported(int, int);
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.hardware.usb.UsbPortStatus> CREATOR;
+    field public static final int DATA_ROLE_DEVICE = 2; // 0x2
+    field public static final int DATA_ROLE_HOST = 1; // 0x1
+    field public static final int DATA_ROLE_NONE = 0; // 0x0
+    field public static final int MODE_AUDIO_ACCESSORY = 4; // 0x4
+    field public static final int MODE_DEBUG_ACCESSORY = 8; // 0x8
+    field public static final int MODE_DFP = 2; // 0x2
+    field public static final int MODE_NONE = 0; // 0x0
+    field public static final int MODE_UFP = 1; // 0x1
+    field public static final int POWER_ROLE_NONE = 0; // 0x0
+    field public static final int POWER_ROLE_SINK = 2; // 0x2
+    field public static final int POWER_ROLE_SOURCE = 1; // 0x1
   }
 
 }
@@ -5013,7 +5061,7 @@
     method public final java.util.Set<android.content.ComponentName> getContentCaptureDisabledActivities();
     method public final java.util.Set<java.lang.String> getContentCaptureDisabledPackages();
     method public void onActivitySnapshot(android.view.contentcapture.ContentCaptureSessionId, android.service.contentcapture.SnapshotData);
-    method public abstract void onContentCaptureEventsRequest(android.view.contentcapture.ContentCaptureSessionId, android.service.contentcapture.ContentCaptureEventsRequest);
+    method public void onContentCaptureEventsRequest(android.view.contentcapture.ContentCaptureSessionId, android.service.contentcapture.ContentCaptureEventsRequest);
     method public void onCreateContentCaptureSession(android.view.contentcapture.ContentCaptureContext, android.view.contentcapture.ContentCaptureSessionId);
     method public void onDestroyContentCaptureSession(android.view.contentcapture.ContentCaptureSessionId);
     method public final void setActivityContentCaptureEnabled(android.content.ComponentName, boolean);
@@ -5694,6 +5742,83 @@
     field public static final java.lang.String KEY_CARRIER_SETUP_APP_STRING = "carrier_setup_app_string";
   }
 
+  public class DisconnectCause {
+    field public static final int ALREADY_DIALING = 72; // 0x48
+    field public static final int ANSWERED_ELSEWHERE = 52; // 0x34
+    field public static final int BUSY = 4; // 0x4
+    field public static final int CALLING_DISABLED = 74; // 0x4a
+    field public static final int CALL_BARRED = 20; // 0x14
+    field public static final int CALL_PULLED = 51; // 0x33
+    field public static final int CANT_CALL_WHILE_RINGING = 73; // 0x49
+    field public static final int CDMA_ACCESS_BLOCKED = 35; // 0x23
+    field public static final int CDMA_ACCESS_FAILURE = 32; // 0x20
+    field public static final int CDMA_ALREADY_ACTIVATED = 49; // 0x31
+    field public static final int CDMA_DROP = 27; // 0x1b
+    field public static final int CDMA_INTERCEPT = 28; // 0x1c
+    field public static final int CDMA_LOCKED_UNTIL_POWER_CYCLE = 26; // 0x1a
+    field public static final int CDMA_NOT_EMERGENCY = 34; // 0x22
+    field public static final int CDMA_PREEMPTED = 33; // 0x21
+    field public static final int CDMA_REORDER = 29; // 0x1d
+    field public static final int CDMA_RETRY_ORDER = 31; // 0x1f
+    field public static final int CDMA_SO_REJECT = 30; // 0x1e
+    field public static final int CONGESTION = 5; // 0x5
+    field public static final int CS_RESTRICTED = 22; // 0x16
+    field public static final int CS_RESTRICTED_EMERGENCY = 24; // 0x18
+    field public static final int CS_RESTRICTED_NORMAL = 23; // 0x17
+    field public static final int DATA_DISABLED = 54; // 0x36
+    field public static final int DATA_LIMIT_REACHED = 55; // 0x37
+    field public static final int DIALED_CALL_FORWARDING_WHILE_ROAMING = 57; // 0x39
+    field public static final int DIALED_MMI = 39; // 0x27
+    field public static final int DIAL_LOW_BATTERY = 62; // 0x3e
+    field public static final int DIAL_MODIFIED_TO_DIAL = 48; // 0x30
+    field public static final int DIAL_MODIFIED_TO_DIAL_VIDEO = 66; // 0x42
+    field public static final int DIAL_MODIFIED_TO_SS = 47; // 0x2f
+    field public static final int DIAL_MODIFIED_TO_USSD = 46; // 0x2e
+    field public static final int DIAL_VIDEO_MODIFIED_TO_DIAL = 69; // 0x45
+    field public static final int DIAL_VIDEO_MODIFIED_TO_DIAL_VIDEO = 70; // 0x46
+    field public static final int DIAL_VIDEO_MODIFIED_TO_SS = 67; // 0x43
+    field public static final int DIAL_VIDEO_MODIFIED_TO_USSD = 68; // 0x44
+    field public static final int EMERGENCY_PERM_FAILURE = 64; // 0x40
+    field public static final int EMERGENCY_TEMP_FAILURE = 63; // 0x3f
+    field public static final int ERROR_UNSPECIFIED = 36; // 0x24
+    field public static final int FDN_BLOCKED = 21; // 0x15
+    field public static final int ICC_ERROR = 19; // 0x13
+    field public static final int IMEI_NOT_ACCEPTED = 58; // 0x3a
+    field public static final int IMS_ACCESS_BLOCKED = 60; // 0x3c
+    field public static final int IMS_MERGED_SUCCESSFULLY = 45; // 0x2d
+    field public static final int IMS_SIP_ALTERNATE_EMERGENCY_CALL = 71; // 0x47
+    field public static final int INCOMING_MISSED = 1; // 0x1
+    field public static final int INCOMING_REJECTED = 16; // 0x10
+    field public static final int INVALID_CREDENTIALS = 10; // 0xa
+    field public static final int INVALID_NUMBER = 7; // 0x7
+    field public static final int LIMIT_EXCEEDED = 15; // 0xf
+    field public static final int LOCAL = 3; // 0x3
+    field public static final int LOST_SIGNAL = 14; // 0xe
+    field public static final int LOW_BATTERY = 61; // 0x3d
+    field public static final int MAXIMUM_NUMBER_OF_CALLS_REACHED = 53; // 0x35
+    field public static final int MMI = 6; // 0x6
+    field public static final int NORMAL = 2; // 0x2
+    field public static final int NORMAL_UNSPECIFIED = 65; // 0x41
+    field public static final int NOT_DISCONNECTED = 0; // 0x0
+    field public static final int NOT_VALID = -1; // 0xffffffff
+    field public static final int NO_PHONE_NUMBER_SUPPLIED = 38; // 0x26
+    field public static final int NUMBER_UNREACHABLE = 8; // 0x8
+    field public static final int OTASP_PROVISIONING_IN_PROCESS = 76; // 0x4c
+    field public static final int OUTGOING_CANCELED = 44; // 0x2c
+    field public static final int OUTGOING_FAILURE = 43; // 0x2b
+    field public static final int OUT_OF_NETWORK = 11; // 0xb
+    field public static final int OUT_OF_SERVICE = 18; // 0x12
+    field public static final int POWER_OFF = 17; // 0x11
+    field public static final int SERVER_ERROR = 12; // 0xc
+    field public static final int SERVER_UNREACHABLE = 9; // 0x9
+    field public static final int TIMED_OUT = 13; // 0xd
+    field public static final int TOO_MANY_ONGOING_CALLS = 75; // 0x4b
+    field public static final int UNOBTAINABLE_NUMBER = 25; // 0x19
+    field public static final int VIDEO_CALL_NOT_ALLOWED_WHILE_TTY_ENABLED = 50; // 0x32
+    field public static final int VOICEMAIL_NUMBER_MISSING = 40; // 0x28
+    field public static final int WIFI_LOST = 59; // 0x3b
+  }
+
   public class MbmsDownloadSession implements java.lang.AutoCloseable {
     field public static final java.lang.String MBMS_DOWNLOAD_SERVICE_ACTION = "android.telephony.action.EmbmsDownload";
   }
@@ -5782,14 +5907,134 @@
   }
 
   public class PhoneStateListener {
+    method public void onCallDisconnectCauseChanged(int, int);
+    method public void onPreciseCallStateChanged(android.telephony.PreciseCallState);
     method public void onRadioPowerStateChanged(int);
     method public void onSrvccStateChanged(int);
     method public void onVoiceActivationStateChanged(int);
+    field public static final int LISTEN_CALL_DISCONNECT_CAUSES = 33554432; // 0x2000000
+    field public static final int LISTEN_PRECISE_CALL_STATE = 2048; // 0x800
     field public static final int LISTEN_RADIO_POWER_STATE_CHANGED = 8388608; // 0x800000
     field public static final int LISTEN_SRVCC_STATE_CHANGED = 16384; // 0x4000
     field public static final int LISTEN_VOICE_ACTIVATION_STATE = 131072; // 0x20000
   }
 
+  public final class PreciseCallState implements android.os.Parcelable {
+    method public int describeContents();
+    method public int getBackgroundCallState();
+    method public int getForegroundCallState();
+    method public int getRingingCallState();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.telephony.PreciseCallState> CREATOR;
+    field public static final int PRECISE_CALL_STATE_ACTIVE = 1; // 0x1
+    field public static final int PRECISE_CALL_STATE_ALERTING = 4; // 0x4
+    field public static final int PRECISE_CALL_STATE_DIALING = 3; // 0x3
+    field public static final int PRECISE_CALL_STATE_DISCONNECTED = 7; // 0x7
+    field public static final int PRECISE_CALL_STATE_DISCONNECTING = 8; // 0x8
+    field public static final int PRECISE_CALL_STATE_HOLDING = 2; // 0x2
+    field public static final int PRECISE_CALL_STATE_IDLE = 0; // 0x0
+    field public static final int PRECISE_CALL_STATE_INCOMING = 5; // 0x5
+    field public static final int PRECISE_CALL_STATE_NOT_VALID = -1; // 0xffffffff
+    field public static final int PRECISE_CALL_STATE_WAITING = 6; // 0x6
+  }
+
+  public class PreciseDisconnectCause {
+    field public static final int ACCESS_CLASS_BLOCKED = 260; // 0x104
+    field public static final int ACCESS_INFORMATION_DISCARDED = 43; // 0x2b
+    field public static final int ACM_LIMIT_EXCEEDED = 68; // 0x44
+    field public static final int BEARER_CAPABILITY_NOT_AUTHORIZED = 57; // 0x39
+    field public static final int BEARER_NOT_AVAIL = 58; // 0x3a
+    field public static final int BEARER_SERVICE_NOT_IMPLEMENTED = 65; // 0x41
+    field public static final int BUSY = 17; // 0x11
+    field public static final int CALL_BARRED = 240; // 0xf0
+    field public static final int CALL_REJECTED = 21; // 0x15
+    field public static final int CDMA_ACCESS_BLOCKED = 1009; // 0x3f1
+    field public static final int CDMA_ACCESS_FAILURE = 1006; // 0x3ee
+    field public static final int CDMA_DROP = 1001; // 0x3e9
+    field public static final int CDMA_INTERCEPT = 1002; // 0x3ea
+    field public static final int CDMA_LOCKED_UNTIL_POWER_CYCLE = 1000; // 0x3e8
+    field public static final int CDMA_NOT_EMERGENCY = 1008; // 0x3f0
+    field public static final int CDMA_PREEMPTED = 1007; // 0x3ef
+    field public static final int CDMA_REORDER = 1003; // 0x3eb
+    field public static final int CDMA_RETRY_ORDER = 1005; // 0x3ed
+    field public static final int CDMA_SO_REJECT = 1004; // 0x3ec
+    field public static final int CHANNEL_NOT_AVAIL = 44; // 0x2c
+    field public static final int CHANNEL_UNACCEPTABLE = 6; // 0x6
+    field public static final int CONDITIONAL_IE_ERROR = 100; // 0x64
+    field public static final int DESTINATION_OUT_OF_ORDER = 27; // 0x1b
+    field public static final int ERROR_UNSPECIFIED = 65535; // 0xffff
+    field public static final int FACILITY_REJECTED = 29; // 0x1d
+    field public static final int FDN_BLOCKED = 241; // 0xf1
+    field public static final int IMEI_NOT_ACCEPTED = 243; // 0xf3
+    field public static final int IMSI_UNKNOWN_IN_VLR = 242; // 0xf2
+    field public static final int INCOMING_CALLS_BARRED_WITHIN_CUG = 55; // 0x37
+    field public static final int INCOMPATIBLE_DESTINATION = 88; // 0x58
+    field public static final int INFORMATION_ELEMENT_NON_EXISTENT = 99; // 0x63
+    field public static final int INTERWORKING_UNSPECIFIED = 127; // 0x7f
+    field public static final int INVALID_MANDATORY_INFORMATION = 96; // 0x60
+    field public static final int INVALID_NUMBER_FORMAT = 28; // 0x1c
+    field public static final int INVALID_TRANSACTION_IDENTIFIER = 81; // 0x51
+    field public static final int MESSAGE_NOT_COMPATIBLE_WITH_PROTOCOL_STATE = 101; // 0x65
+    field public static final int MESSAGE_TYPE_NON_IMPLEMENTED = 97; // 0x61
+    field public static final int MESSAGE_TYPE_NOT_COMPATIBLE_WITH_PROTOCOL_STATE = 98; // 0x62
+    field public static final int NETWORK_DETACH = 261; // 0x105
+    field public static final int NETWORK_OUT_OF_ORDER = 38; // 0x26
+    field public static final int NETWORK_REJECT = 252; // 0xfc
+    field public static final int NETWORK_RESP_TIMEOUT = 251; // 0xfb
+    field public static final int NORMAL = 16; // 0x10
+    field public static final int NORMAL_UNSPECIFIED = 31; // 0x1f
+    field public static final int NOT_VALID = -1; // 0xffffffff
+    field public static final int NO_ANSWER_FROM_USER = 19; // 0x13
+    field public static final int NO_CIRCUIT_AVAIL = 34; // 0x22
+    field public static final int NO_DISCONNECT_CAUSE_AVAILABLE = 0; // 0x0
+    field public static final int NO_ROUTE_TO_DESTINATION = 3; // 0x3
+    field public static final int NO_USER_RESPONDING = 18; // 0x12
+    field public static final int NO_VALID_SIM = 249; // 0xf9
+    field public static final int NUMBER_CHANGED = 22; // 0x16
+    field public static final int OEM_CAUSE_1 = 61441; // 0xf001
+    field public static final int OEM_CAUSE_10 = 61450; // 0xf00a
+    field public static final int OEM_CAUSE_11 = 61451; // 0xf00b
+    field public static final int OEM_CAUSE_12 = 61452; // 0xf00c
+    field public static final int OEM_CAUSE_13 = 61453; // 0xf00d
+    field public static final int OEM_CAUSE_14 = 61454; // 0xf00e
+    field public static final int OEM_CAUSE_15 = 61455; // 0xf00f
+    field public static final int OEM_CAUSE_2 = 61442; // 0xf002
+    field public static final int OEM_CAUSE_3 = 61443; // 0xf003
+    field public static final int OEM_CAUSE_4 = 61444; // 0xf004
+    field public static final int OEM_CAUSE_5 = 61445; // 0xf005
+    field public static final int OEM_CAUSE_6 = 61446; // 0xf006
+    field public static final int OEM_CAUSE_7 = 61447; // 0xf007
+    field public static final int OEM_CAUSE_8 = 61448; // 0xf008
+    field public static final int OEM_CAUSE_9 = 61449; // 0xf009
+    field public static final int ONLY_DIGITAL_INFORMATION_BEARER_AVAILABLE = 70; // 0x46
+    field public static final int OPERATOR_DETERMINED_BARRING = 8; // 0x8
+    field public static final int OUT_OF_SRV = 248; // 0xf8
+    field public static final int PREEMPTION = 25; // 0x19
+    field public static final int PROTOCOL_ERROR_UNSPECIFIED = 111; // 0x6f
+    field public static final int QOS_NOT_AVAIL = 49; // 0x31
+    field public static final int RADIO_ACCESS_FAILURE = 253; // 0xfd
+    field public static final int RADIO_INTERNAL_ERROR = 250; // 0xfa
+    field public static final int RADIO_LINK_FAILURE = 254; // 0xfe
+    field public static final int RADIO_LINK_LOST = 255; // 0xff
+    field public static final int RADIO_OFF = 247; // 0xf7
+    field public static final int RADIO_RELEASE_ABNORMAL = 259; // 0x103
+    field public static final int RADIO_RELEASE_NORMAL = 258; // 0x102
+    field public static final int RADIO_SETUP_FAILURE = 257; // 0x101
+    field public static final int RADIO_UPLINK_FAILURE = 256; // 0x100
+    field public static final int RECOVERY_ON_TIMER_EXPIRED = 102; // 0x66
+    field public static final int REQUESTED_FACILITY_NOT_IMPLEMENTED = 69; // 0x45
+    field public static final int REQUESTED_FACILITY_NOT_SUBSCRIBED = 50; // 0x32
+    field public static final int RESOURCES_UNAVAILABLE_OR_UNSPECIFIED = 47; // 0x2f
+    field public static final int SEMANTICALLY_INCORRECT_MESSAGE = 95; // 0x5f
+    field public static final int SERVICE_OPTION_NOT_AVAILABLE = 63; // 0x3f
+    field public static final int SERVICE_OR_OPTION_NOT_IMPLEMENTED = 79; // 0x4f
+    field public static final int STATUS_ENQUIRY = 30; // 0x1e
+    field public static final int SWITCHING_CONGESTION = 42; // 0x2a
+    field public static final int TEMPORARY_FAILURE = 41; // 0x29
+    field public static final int UNOBTAINABLE_NUMBER = 1; // 0x1
+    field public static final int USER_NOT_MEMBER_OF_CUG = 87; // 0x57
+  }
+
   public class ServiceState implements android.os.Parcelable {
     method public android.telephony.NetworkRegistrationState getNetworkRegistrationState(int, int);
     method public java.util.List<android.telephony.NetworkRegistrationState> getNetworkRegistrationStates();
@@ -5827,6 +6072,7 @@
 
   public class SubscriptionInfo implements android.os.Parcelable {
     method public java.util.List<android.telephony.UiccAccessRule> getAccessRules();
+    method public int getCardId();
   }
 
   public class SubscriptionManager {
diff --git a/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
index e3748f1..3defdc5 100644
--- a/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
+++ b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
@@ -247,7 +247,7 @@
         }
 
         try {
-            mBmgr.dataChanged(pkg);
+            mBmgr.dataChangedForUser(userId, pkg);
         } catch (RemoteException e) {
             System.err.println(e.toString());
             System.err.println(BMGR_NOT_RUNNING_ERR);
@@ -264,7 +264,8 @@
         }
         if (allPkgs.size() > 0) {
             try {
-                mBmgr.fullTransportBackup(allPkgs.toArray(new String[allPkgs.size()]));
+                mBmgr.fullTransportBackupForUser(
+                        userId, allPkgs.toArray(new String[allPkgs.size()]));
             } catch (RemoteException e) {
                 System.err.println(e.toString());
                 System.err.println(BMGR_NOT_RUNNING_ERR);
@@ -393,7 +394,7 @@
                     installedPackages.stream().map(p -> p.packageName).toArray(String[]::new);
             String[] filteredPackages = {};
             try {
-                filteredPackages = mBmgr.filterAppsEligibleForBackup(packages);
+                filteredPackages = mBmgr.filterAppsEligibleForBackupForUser(userId, packages);
             } catch (RemoteException e) {
                 System.err.println(e.toString());
                 System.err.println(BMGR_NOT_RUNNING_ERR);
@@ -498,11 +499,11 @@
             }
 
             if ("-c".equals(which)) {
-                doTransportByComponent();
+                doTransportByComponent(userId);
                 return;
             }
 
-            String old = mBmgr.selectBackupTransport(which);
+            String old = mBmgr.selectBackupTransportForUser(userId, which);
             if (old == null) {
                 System.out.println("Unknown transport '" + which
                         + "' specified; no changes made.");
@@ -516,7 +517,7 @@
         }
     }
 
-    private void doTransportByComponent() {
+    private void doTransportByComponent(@UserIdInt int userId) {
         String which = nextArg();
         if (which == null) {
             showUsage();
@@ -526,7 +527,9 @@
         final CountDownLatch latch = new CountDownLatch(1);
 
         try {
-            mBmgr.selectBackupTransportAsync(ComponentName.unflattenFromString(which),
+            mBmgr.selectBackupTransportAsyncForUser(
+                    userId,
+                    ComponentName.unflattenFromString(which),
                     new ISelectBackupTransportCallback.Stub() {
                         @Override
                         public void onSuccess(String transportName) {
@@ -567,7 +570,7 @@
         }
 
         try {
-            mBmgr.clearBackupData(transport, pkg);
+            mBmgr.clearBackupDataForUser(userId, transport, pkg);
             System.out.println("Wiped backup data for " + pkg + " on " + transport);
         } catch (RemoteException e) {
             System.err.println(e.toString());
@@ -599,7 +602,8 @@
         InitObserver observer = new InitObserver();
         try {
             System.out.println("Initializing transports: " + transports);
-            mBmgr.initializeTransports(transports.toArray(new String[transports.size()]), observer);
+            mBmgr.initializeTransportsForUser(
+                    userId, transports.toArray(new String[transports.size()]), observer);
             observer.waitForCompletion(30*1000L);
             System.out.println("Initialization result: " + observer.result);
         } catch (RemoteException e) {
@@ -611,13 +615,13 @@
     private void doList(@UserIdInt int userId) {
         String arg = nextArg();     // sets, transports, packages set#
         if ("transports".equals(arg)) {
-            doListTransports();
+            doListTransports(userId);
             return;
         }
 
         // The rest of the 'list' options work with a restore session on the current transport
         try {
-            mRestore = mBmgr.beginRestoreSession(null, null);
+            mRestore = mBmgr.beginRestoreSessionForUser(userId, null, null);
             if (mRestore == null) {
                 System.err.println(BMGR_NOT_RUNNING_ERR);
                 return;
@@ -634,19 +638,19 @@
         }
     }
 
-    private void doListTransports() {
+    private void doListTransports(@UserIdInt int userId) {
         String arg = nextArg();
 
         try {
             if ("-c".equals(arg)) {
-                for (ComponentName transport : mBmgr.listAllTransportComponents()) {
+                for (ComponentName transport : mBmgr.listAllTransportComponentsForUser(userId)) {
                     System.out.println(transport.flattenToShortString());
                 }
                 return;
             }
 
-            String current = mBmgr.getCurrentTransport();
-            String[] transports = mBmgr.listAllTransports();
+            String current = mBmgr.getCurrentTransportForUser(userId);
+            String[] transports = mBmgr.listAllTransportsForUser(userId);
             if (transports == null || transports.length == 0) {
                 System.out.println("No transports available.");
                 return;
@@ -756,7 +760,7 @@
                     filter.add(arg);
                 }
 
-                doRestoreAll(token, filter);
+                doRestoreAll(userId, token, filter);
             } catch (NumberFormatException e) {
                 showUsage();
                 return;
@@ -769,12 +773,12 @@
         System.err.println("'restore <token> <package>'.");
     }
 
-    private void doRestoreAll(long token, HashSet<String> filter) {
+    private void doRestoreAll(@UserIdInt int userId, long token, HashSet<String> filter) {
         RestoreObserver observer = new RestoreObserver();
 
         try {
             boolean didRestore = false;
-            mRestore = mBmgr.beginRestoreSession(null, null);
+            mRestore = mBmgr.beginRestoreSessionForUser(userId, null, null);
             if (mRestore == null) {
                 System.err.println(BMGR_NOT_RUNNING_ERR);
                 return;
diff --git a/cmds/idmap2/idmap2/Lookup.cpp b/cmds/idmap2/idmap2/Lookup.cpp
index 020c443..8d0cee5 100644
--- a/cmds/idmap2/idmap2/Lookup.cpp
+++ b/cmds/idmap2/idmap2/Lookup.cpp
@@ -36,6 +36,7 @@
 
 #include "idmap2/CommandLineOptions.h"
 #include "idmap2/Idmap.h"
+#include "idmap2/Result.h"
 #include "idmap2/Xml.h"
 #include "idmap2/ZipFile.h"
 
@@ -53,39 +54,40 @@
 using android::idmap2::CommandLineOptions;
 using android::idmap2::IdmapHeader;
 using android::idmap2::ResourceId;
+using android::idmap2::Result;
 using android::idmap2::Xml;
 using android::idmap2::ZipFile;
 using android::util::Utf16ToUtf8;
 
 namespace {
-std::pair<bool, ResourceId> WARN_UNUSED ParseResReference(const AssetManager2& am,
-                                                          const std::string& res,
-                                                          const std::string& fallback_package) {
+
+Result<ResourceId> WARN_UNUSED ParseResReference(const AssetManager2& am, const std::string& res,
+                                                 const std::string& fallback_package) {
   // first, try to parse as a hex number
   char* endptr = nullptr;
   ResourceId resid;
   resid = strtol(res.c_str(), &endptr, 16);
   if (*endptr == '\0') {
-    return std::make_pair(true, resid);
+    return {resid};
   }
 
   // next, try to parse as a package:type/name string
   resid = am.GetResourceId(res, "", fallback_package);
   if (is_valid_resid(resid)) {
-    return std::make_pair(true, resid);
+    return {resid};
   }
 
   // end of the road: res could not be parsed
-  return std::make_pair(false, 0);
+  return {};
 }
 
-std::pair<bool, std::string> WARN_UNUSED GetValue(const AssetManager2& am, ResourceId resid) {
+Result<std::string> WARN_UNUSED GetValue(const AssetManager2& am, ResourceId resid) {
   Res_value value;
   ResTable_config config;
   uint32_t flags;
   ApkAssetsCookie cookie = am.GetResource(resid, false, 0, &value, &config, &flags);
   if (cookie == kInvalidCookie) {
-    return std::make_pair(false, "");
+    return {};
   }
 
   std::string out;
@@ -123,31 +125,31 @@
       out.append(StringPrintf("dataType=0x%02x data=0x%08x", value.dataType, value.data));
       break;
   }
-  return std::make_pair(true, out);
+  return {out};
 }
 
-std::pair<bool, std::string> GetTargetPackageNameFromManifest(const std::string& apk_path) {
+Result<std::string> GetTargetPackageNameFromManifest(const std::string& apk_path) {
   const auto zip = ZipFile::Open(apk_path);
   if (!zip) {
-    return std::make_pair(false, "");
+    return {};
   }
   const auto entry = zip->Uncompress("AndroidManifest.xml");
   if (!entry) {
-    return std::make_pair(false, "");
+    return {};
   }
   const auto xml = Xml::Create(entry->buf, entry->size);
   if (!xml) {
-    return std::make_pair(false, "");
+    return {};
   }
   const auto tag = xml->FindTag("overlay");
   if (!tag) {
-    return std::make_pair(false, "");
+    return {};
   }
   const auto iter = tag->find("targetPackage");
   if (iter == tag->end()) {
-    return std::make_pair(false, "");
+    return {};
   }
-  return std::make_pair(true, iter->second);
+  return {iter->second};
 }
 }  // namespace
 
@@ -195,14 +197,14 @@
       }
       apk_assets.push_back(std::move(target_apk));
 
-      bool lookup_ok;
-      std::tie(lookup_ok, target_package_name) =
+      const Result<std::string> package_name =
           GetTargetPackageNameFromManifest(idmap_header->GetOverlayPath().to_string());
-      if (!lookup_ok) {
+      if (!package_name) {
         out_error << "error: failed to parse android:targetPackage from overlay manifest"
                   << std::endl;
         return false;
       }
+      target_package_name = *package_name;
     } else if (target_path != idmap_header->GetTargetPath()) {
       out_error << "error: different target APKs (expected target APK " << target_path << " but "
                 << idmap_path << " has target APK " << idmap_header->GetTargetPath() << ")"
@@ -227,21 +229,18 @@
   am.SetApkAssets(raw_pointer_apk_assets);
   am.SetConfiguration(config);
 
-  ResourceId resid;
-  bool lookup_ok;
-  std::tie(lookup_ok, resid) = ParseResReference(am, resid_str, target_package_name);
-  if (!lookup_ok) {
+  const Result<ResourceId> resid = ParseResReference(am, resid_str, target_package_name);
+  if (!resid) {
     out_error << "error: failed to parse resource ID" << std::endl;
     return false;
   }
 
-  std::string value;
-  std::tie(lookup_ok, value) = GetValue(am, resid);
-  if (!lookup_ok) {
-    out_error << StringPrintf("error: resource 0x%08x not found", resid) << std::endl;
+  const Result<std::string> value = GetValue(am, *resid);
+  if (!value) {
+    out_error << StringPrintf("error: resource 0x%08x not found", *resid) << std::endl;
     return false;
   }
-  std::cout << value << std::endl;
+  std::cout << *value << std::endl;
 
   return true;
 }
diff --git a/cmds/idmap2/idmap2d/Idmap2Service.cpp b/cmds/idmap2/idmap2d/Idmap2Service.cpp
index cf72cb9..86b00f1 100644
--- a/cmds/idmap2/idmap2d/Idmap2Service.cpp
+++ b/cmds/idmap2/idmap2d/Idmap2Service.cpp
@@ -78,6 +78,18 @@
   }
 }
 
+Status Idmap2Service::verifyIdmap(const std::string& overlay_apk_path,
+                                  int32_t user_id ATTRIBUTE_UNUSED, bool* _aidl_return) {
+  assert(_aidl_return);
+  const std::string idmap_path = Idmap::CanonicalIdmapPathFor(kIdmapCacheDir, overlay_apk_path);
+  std::ifstream fin(idmap_path);
+  const std::unique_ptr<const IdmapHeader> header = IdmapHeader::FromBinaryStream(fin);
+  fin.close();
+  std::stringstream dev_null;
+  *_aidl_return = header && header->IsUpToDate(dev_null);
+  return ok();
+}
+
 Status Idmap2Service::createIdmap(const std::string& target_apk_path,
                                   const std::string& overlay_apk_path, int32_t user_id,
                                   std::unique_ptr<std::string>* _aidl_return) {
@@ -90,17 +102,6 @@
 
   _aidl_return->reset(nullptr);
 
-  const std::string idmap_path = Idmap::CanonicalIdmapPathFor(kIdmapCacheDir, overlay_apk_path);
-  std::ifstream fin(idmap_path);
-  const std::unique_ptr<const IdmapHeader> header = IdmapHeader::FromBinaryStream(fin);
-  fin.close();
-  // do not reuse error stream from IsUpToDate below, or error messages will be
-  // polluted with irrelevant data
-  std::stringstream dev_null;
-  if (header && header->IsUpToDate(dev_null)) {
-    return ok();
-  }
-
   const std::unique_ptr<const ApkAssets> target_apk = ApkAssets::Load(target_apk_path);
   if (!target_apk) {
     return error("failed to load apk " + target_apk_path);
@@ -119,6 +120,7 @@
   }
 
   umask(0133);  // u=rw,g=r,o=r
+  const std::string idmap_path = Idmap::CanonicalIdmapPathFor(kIdmapCacheDir, overlay_apk_path);
   std::ofstream fout(idmap_path);
   if (fout.fail()) {
     return error("failed to open idmap path " + idmap_path);
diff --git a/cmds/idmap2/idmap2d/Idmap2Service.h b/cmds/idmap2/idmap2d/Idmap2Service.h
index 2b32042..4e5abc9 100644
--- a/cmds/idmap2/idmap2d/Idmap2Service.h
+++ b/cmds/idmap2/idmap2d/Idmap2Service.h
@@ -39,6 +39,9 @@
   binder::Status removeIdmap(const std::string& overlay_apk_path, int32_t user_id,
                              bool* _aidl_return);
 
+  binder::Status verifyIdmap(const std::string& overlay_apk_path, int32_t user_id,
+                             bool* _aidl_return);
+
   binder::Status createIdmap(const std::string& target_apk_path,
                              const std::string& overlay_apk_path, int32_t user_id,
                              std::unique_ptr<std::string>* _aidl_return);
diff --git a/cmds/idmap2/idmap2d/aidl/android/os/IIdmap2.aidl b/cmds/idmap2/idmap2d/aidl/android/os/IIdmap2.aidl
index 5d19610..d475417 100644
--- a/cmds/idmap2/idmap2d/aidl/android/os/IIdmap2.aidl
+++ b/cmds/idmap2/idmap2d/aidl/android/os/IIdmap2.aidl
@@ -22,6 +22,7 @@
 interface IIdmap2 {
   @utf8InCpp String getIdmapPath(@utf8InCpp String overlayApkPath, int userId);
   boolean removeIdmap(@utf8InCpp String overlayApkPath, int userId);
+  boolean verifyIdmap(@utf8InCpp String overlayApkPath, int userId);
   @nullable @utf8InCpp String createIdmap(@utf8InCpp String targetApkPath,
                                           @utf8InCpp String overlayApkPath, int userId);
 }
diff --git a/cmds/idmap2/include/idmap2/ResourceUtils.h b/cmds/idmap2/include/idmap2/ResourceUtils.h
index 88a835b..d106f19 100644
--- a/cmds/idmap2/include/idmap2/ResourceUtils.h
+++ b/cmds/idmap2/include/idmap2/ResourceUtils.h
@@ -18,19 +18,18 @@
 #define IDMAP2_INCLUDE_IDMAP2_RESOURCEUTILS_H_
 
 #include <string>
-#include <utility>
 
 #include "android-base/macros.h"
 #include "androidfw/AssetManager2.h"
 
 #include "idmap2/Idmap.h"
+#include "idmap2/Result.h"
 
 namespace android {
 namespace idmap2 {
 namespace utils {
 
-std::pair<bool, std::string> WARN_UNUSED ResToTypeEntryName(const AssetManager2& am,
-                                                            ResourceId resid);
+Result<std::string> WARN_UNUSED ResToTypeEntryName(const AssetManager2& am, ResourceId resid);
 
 }  // namespace utils
 }  // namespace idmap2
diff --git a/cmds/idmap2/include/idmap2/Result.h b/cmds/idmap2/include/idmap2/Result.h
new file mode 100644
index 0000000..6189ea3
--- /dev/null
+++ b/cmds/idmap2/include/idmap2/Result.h
@@ -0,0 +1,31 @@
+/*
+ * 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.
+ */
+
+#ifndef IDMAP2_INCLUDE_IDMAP2_RESULT_H_
+#define IDMAP2_INCLUDE_IDMAP2_RESULT_H_
+
+#include <optional>
+
+namespace android::idmap2 {
+
+template <typename T>
+using Result = std::optional<T>;
+
+static constexpr std::nullopt_t kResultError = std::nullopt;
+
+}  // namespace android::idmap2
+
+#endif  // IDMAP2_INCLUDE_IDMAP2_RESULT_H_
diff --git a/cmds/idmap2/include/idmap2/ZipFile.h b/cmds/idmap2/include/idmap2/ZipFile.h
index 328bd36..9edbbe0 100644
--- a/cmds/idmap2/include/idmap2/ZipFile.h
+++ b/cmds/idmap2/include/idmap2/ZipFile.h
@@ -19,10 +19,10 @@
 
 #include <memory>
 #include <string>
-#include <utility>
 
 #include "android-base/macros.h"
 #include "ziparchive/zip_archive.h"
+#include "idmap2/Result.h"
 
 namespace android {
 namespace idmap2 {
@@ -43,7 +43,7 @@
   static std::unique_ptr<const ZipFile> Open(const std::string& path);
 
   std::unique_ptr<const MemoryChunk> Uncompress(const std::string& entryPath) const;
-  std::pair<bool, uint32_t> Crc(const std::string& entryPath) const;
+  Result<uint32_t> Crc(const std::string& entryPath) const;
 
   ~ZipFile();
 
diff --git a/cmds/idmap2/libidmap2/Idmap.cpp b/cmds/idmap2/libidmap2/Idmap.cpp
index 5a47e30..1ef3267 100644
--- a/cmds/idmap2/libidmap2/Idmap.cpp
+++ b/cmds/idmap2/libidmap2/Idmap.cpp
@@ -33,6 +33,7 @@
 
 #include "idmap2/Idmap.h"
 #include "idmap2/ResourceUtils.h"
+#include "idmap2/Result.h"
 #include "idmap2/ZipFile.h"
 
 namespace android {
@@ -143,18 +144,16 @@
     return false;
   }
 
-  bool status;
-  uint32_t target_crc;
-  std::tie(status, target_crc) = target_zip->Crc("resources.arsc");
-  if (!status) {
+  Result<uint32_t> target_crc = target_zip->Crc("resources.arsc");
+  if (!target_crc) {
     out_error << "error: failed to get target crc" << std::endl;
     return false;
   }
 
-  if (target_crc_ != target_crc) {
+  if (target_crc_ != *target_crc) {
     out_error << base::StringPrintf(
                      "error: bad target crc: idmap version 0x%08x, file system version 0x%08x",
-                     target_crc_, target_crc)
+                     target_crc_, *target_crc)
               << std::endl;
     return false;
   }
@@ -165,17 +164,16 @@
     return false;
   }
 
-  uint32_t overlay_crc;
-  std::tie(status, overlay_crc) = overlay_zip->Crc("resources.arsc");
-  if (!status) {
+  Result<uint32_t> overlay_crc = overlay_zip->Crc("resources.arsc");
+  if (!overlay_crc) {
     out_error << "error: failed to get overlay crc" << std::endl;
     return false;
   }
 
-  if (overlay_crc_ != overlay_crc) {
+  if (overlay_crc_ != *overlay_crc) {
     out_error << base::StringPrintf(
                      "error: bad overlay crc: idmap version 0x%08x, file system version 0x%08x",
-                     overlay_crc_, overlay_crc)
+                     overlay_crc_, *overlay_crc)
               << std::endl;
     return false;
   }
@@ -322,17 +320,20 @@
   std::unique_ptr<IdmapHeader> header(new IdmapHeader());
   header->magic_ = kIdmapMagic;
   header->version_ = kIdmapCurrentVersion;
-  bool crc_status;
-  std::tie(crc_status, header->target_crc_) = target_zip->Crc("resources.arsc");
-  if (!crc_status) {
+
+  Result<uint32_t> crc = target_zip->Crc("resources.arsc");
+  if (!crc) {
     out_error << "error: failed to get zip crc for target" << std::endl;
     return nullptr;
   }
-  std::tie(crc_status, header->overlay_crc_) = overlay_zip->Crc("resources.arsc");
-  if (!crc_status) {
+  header->target_crc_ = *crc;
+
+  crc = overlay_zip->Crc("resources.arsc");
+  if (!crc) {
     out_error << "error: failed to get zip crc for overlay" << std::endl;
     return nullptr;
   }
+  header->overlay_crc_ = *crc;
 
   if (target_apk_path.size() > sizeof(header->target_path_)) {
     out_error << "error: target apk path \"" << target_apk_path << "\" longer that maximum size "
@@ -358,15 +359,14 @@
   const auto end = overlay_pkg->end();
   for (auto iter = overlay_pkg->begin(); iter != end; ++iter) {
     const ResourceId overlay_resid = *iter;
-    bool lookup_ok;
-    std::string name;
-    std::tie(lookup_ok, name) = utils::ResToTypeEntryName(overlay_asset_manager, overlay_resid);
-    if (!lookup_ok) {
+    Result<std::string> name = utils::ResToTypeEntryName(overlay_asset_manager, overlay_resid);
+    if (!name) {
       continue;
     }
     // prepend "<package>:" to turn name into "<package>:<type>/<name>"
-    name = base::StringPrintf("%s:%s", target_pkg->GetPackageName().c_str(), name.c_str());
-    const ResourceId target_resid = NameToResid(target_asset_manager, name);
+    const std::string full_name =
+        base::StringPrintf("%s:%s", target_pkg->GetPackageName().c_str(), name->c_str());
+    const ResourceId target_resid = NameToResid(target_asset_manager, full_name);
     if (target_resid == 0) {
       continue;
     }
diff --git a/cmds/idmap2/libidmap2/PrettyPrintVisitor.cpp b/cmds/idmap2/libidmap2/PrettyPrintVisitor.cpp
index 492e6f0..fb3bc5b 100644
--- a/cmds/idmap2/libidmap2/PrettyPrintVisitor.cpp
+++ b/cmds/idmap2/libidmap2/PrettyPrintVisitor.cpp
@@ -15,7 +15,6 @@
  */
 
 #include <string>
-#include <utility>
 
 #include "android-base/macros.h"
 #include "android-base/stringprintf.h"
@@ -23,6 +22,7 @@
 
 #include "idmap2/PrettyPrintVisitor.h"
 #include "idmap2/ResourceUtils.h"
+#include "idmap2/Result.h"
 
 namespace android {
 namespace idmap2 {
@@ -63,11 +63,9 @@
 
     stream_ << base::StringPrintf("0x%08x -> 0x%08x", target_resid, overlay_resid);
     if (target_package_loaded) {
-      bool lookup_ok;
-      std::string name;
-      std::tie(lookup_ok, name) = utils::ResToTypeEntryName(target_am_, target_resid);
-      if (lookup_ok) {
-        stream_ << " " << name;
+      Result<std::string> name = utils::ResToTypeEntryName(target_am_, target_resid);
+      if (name) {
+        stream_ << " " << *name;
       }
     }
     stream_ << std::endl;
diff --git a/cmds/idmap2/libidmap2/RawPrintVisitor.cpp b/cmds/idmap2/libidmap2/RawPrintVisitor.cpp
index 57cfc8e..7c24445 100644
--- a/cmds/idmap2/libidmap2/RawPrintVisitor.cpp
+++ b/cmds/idmap2/libidmap2/RawPrintVisitor.cpp
@@ -16,7 +16,6 @@
 
 #include <cstdarg>
 #include <string>
-#include <utility>
 
 #include "android-base/macros.h"
 #include "android-base/stringprintf.h"
@@ -24,6 +23,7 @@
 
 #include "idmap2/RawPrintVisitor.h"
 #include "idmap2/ResourceUtils.h"
+#include "idmap2/Result.h"
 
 using android::ApkAssets;
 
@@ -75,14 +75,13 @@
       const ResourceId target_resid =
           RESID(last_seen_package_id_, te.GetTargetTypeId(), te.GetEntryOffset() + i);
       const ResourceId overlay_resid = RESID(last_seen_package_id_, te.GetOverlayTypeId(), entry);
-      bool lookup_ok = false;
-      std::string name;
+      Result<std::string> name;
       if (target_package_loaded) {
-        std::tie(lookup_ok, name) = utils::ResToTypeEntryName(target_am_, target_resid);
+        name = utils::ResToTypeEntryName(target_am_, target_resid);
       }
-      if (lookup_ok) {
+      if (name) {
         print(static_cast<uint32_t>(entry), "0x%08x -> 0x%08x %s", target_resid, overlay_resid,
-              name.c_str());
+              name->c_str());
       } else {
         print(static_cast<uint32_t>(entry), "0x%08x -> 0x%08x", target_resid, overlay_resid);
       }
diff --git a/cmds/idmap2/libidmap2/ResourceUtils.cpp b/cmds/idmap2/libidmap2/ResourceUtils.cpp
index e98f843..5c89783 100644
--- a/cmds/idmap2/libidmap2/ResourceUtils.cpp
+++ b/cmds/idmap2/libidmap2/ResourceUtils.cpp
@@ -15,12 +15,12 @@
  */
 
 #include <string>
-#include <utility>
 
 #include "androidfw/StringPiece.h"
 #include "androidfw/Util.h"
 
 #include "idmap2/ResourceUtils.h"
+#include "idmap2/Result.h"
 
 using android::StringPiece16;
 using android::util::Utf16ToUtf8;
@@ -29,11 +29,10 @@
 namespace idmap2 {
 namespace utils {
 
-std::pair<bool, std::string> WARN_UNUSED ResToTypeEntryName(const AssetManager2& am,
-                                                            ResourceId resid) {
+Result<std::string> WARN_UNUSED ResToTypeEntryName(const AssetManager2& am, ResourceId resid) {
   AssetManager2::ResourceName name;
   if (!am.GetResourceName(resid, &name)) {
-    return std::make_pair(false, "");
+    return {};
   }
   std::string out;
   if (name.type != nullptr) {
@@ -47,7 +46,7 @@
   } else {
     out += Utf16ToUtf8(StringPiece16(name.entry16, name.entry_len));
   }
-  return std::make_pair(true, out);
+  return {out};
 }
 
 }  // namespace utils
diff --git a/cmds/idmap2/libidmap2/ZipFile.cpp b/cmds/idmap2/libidmap2/ZipFile.cpp
index 3f2079a..9fb611d 100644
--- a/cmds/idmap2/libidmap2/ZipFile.cpp
+++ b/cmds/idmap2/libidmap2/ZipFile.cpp
@@ -16,8 +16,8 @@
 
 #include <memory>
 #include <string>
-#include <utility>
 
+#include "idmap2/Result.h"
 #include "idmap2/ZipFile.h"
 
 namespace android {
@@ -57,10 +57,10 @@
   return chunk;
 }
 
-std::pair<bool, uint32_t> ZipFile::Crc(const std::string& entryPath) const {
+Result<uint32_t> ZipFile::Crc(const std::string& entryPath) const {
   ::ZipEntry entry;
   int32_t status = ::FindEntry(handle_, ::ZipString(entryPath.c_str()), &entry);
-  return std::make_pair(status == 0, entry.crc32);
+  return status == 0 ? Result<uint32_t>(entry.crc32) : kResultError;
 }
 
 }  // namespace idmap2
diff --git a/cmds/idmap2/tests/ResourceUtilsTests.cpp b/cmds/idmap2/tests/ResourceUtilsTests.cpp
index 0547fa0..7f60d75 100644
--- a/cmds/idmap2/tests/ResourceUtilsTests.cpp
+++ b/cmds/idmap2/tests/ResourceUtilsTests.cpp
@@ -16,13 +16,13 @@
 
 #include <memory>
 #include <string>
-#include <utility>
 
 #include "gmock/gmock.h"
 #include "gtest/gtest.h"
 
 #include "androidfw/ApkAssets.h"
 #include "idmap2/ResourceUtils.h"
+#include "idmap2/Result.h"
 
 #include "TestHelpers.h"
 
@@ -52,17 +52,14 @@
 };
 
 TEST_F(ResourceUtilsTests, ResToTypeEntryName) {
-  bool lookup_ok;
-  std::string name;
-  std::tie(lookup_ok, name) = utils::ResToTypeEntryName(GetAssetManager(), 0x7f010000u);
-  ASSERT_TRUE(lookup_ok);
-  ASSERT_EQ(name, "integer/int1");
+  Result<std::string> name = utils::ResToTypeEntryName(GetAssetManager(), 0x7f010000u);
+  ASSERT_TRUE(name);
+  ASSERT_EQ(*name, "integer/int1");
 }
 
 TEST_F(ResourceUtilsTests, ResToTypeEntryNameNoSuchResourceId) {
-  bool lookup_ok;
-  std::tie(lookup_ok, std::ignore) = utils::ResToTypeEntryName(GetAssetManager(), 0x7f123456u);
-  ASSERT_FALSE(lookup_ok);
+  Result<std::string> name = utils::ResToTypeEntryName(GetAssetManager(), 0x7f123456u);
+  ASSERT_FALSE(name);
 }
 
 }  // namespace idmap2
diff --git a/cmds/idmap2/tests/ZipFileTests.cpp b/cmds/idmap2/tests/ZipFileTests.cpp
index a504d31..6e4a501 100644
--- a/cmds/idmap2/tests/ZipFileTests.cpp
+++ b/cmds/idmap2/tests/ZipFileTests.cpp
@@ -16,8 +16,8 @@
 
 #include <cstdio>  // fclose
 #include <string>
-#include <utility>
 
+#include "idmap2/Result.h"
 #include "idmap2/ZipFile.h"
 
 #include "gmock/gmock.h"
@@ -44,14 +44,12 @@
   auto zip = ZipFile::Open(GetTestDataPath() + "/target/target.apk");
   ASSERT_THAT(zip, NotNull());
 
-  bool status;
-  uint32_t crc;
-  std::tie(status, crc) = zip->Crc("AndroidManifest.xml");
-  ASSERT_TRUE(status);
-  ASSERT_EQ(crc, 0x762f3d24);
+  Result<uint32_t> crc = zip->Crc("AndroidManifest.xml");
+  ASSERT_TRUE(crc);
+  ASSERT_EQ(*crc, 0x762f3d24);
 
-  std::tie(status, std::ignore) = zip->Crc("does-not-exist");
-  ASSERT_FALSE(status);
+  Result<uint32_t> crc2 = zip->Crc("does-not-exist");
+  ASSERT_FALSE(crc2);
 }
 
 TEST(ZipFileTests, Uncompress) {
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 5899e0cf..0c05be1 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -171,6 +171,7 @@
         DocsUIUserActionReported docs_ui_user_action_reported = 112;
         WifiEnabledStateChanged wifi_enabled_state_changed = 113;
         WifiRunningStateChanged wifi_running_state_changed = 114;
+        AppCompacted app_compacted = 115;
     }
 
     // Pulled events will start at field 10000.
@@ -3649,3 +3650,65 @@
 message DocsUIInvalidScopedAccessRequestReported {
     optional android.stats.docsui.InvalidScopedAccess type = 1;
 }
+
+/**
+ * Logs when an app's memory is compacted.
+ *
+ * Logged from:
+ *   frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
+ */
+message AppCompacted {
+  // The pid of the process being compacted.
+  optional int32 pid = 1;
+
+  // The name of the process being compacted.
+  optional string process_name = 2;
+
+  // The type of compaction.
+  enum Action {
+    UNKNOWN = 0;
+    SOME = 1;
+    FULL = 2;
+  }
+  optional Action action = 3 [default = UNKNOWN];
+
+  // Total RSS in kilobytes consumed by the process prior to compaction.
+  optional int64 before_rss_total_kilobytes = 4;
+
+  // File RSS in kilobytes consumed by the process prior to compaction.
+  optional int64 before_rss_file_kilobytes = 5;
+
+  // Anonymous RSS in kilobytes consumed by the process prior to compaction.
+  optional int64 before_rss_anon_kilobytes = 6;
+
+  // Swap in kilobytes consumed by the process prior to compaction.
+  optional int64 before_swap_kilobytes = 7;
+
+  // Total RSS in kilobytes consumed by the process after compaction.
+  optional int64 after_rss_total_kilobytes = 8;
+
+  // File RSS in kilobytes consumed by the process after compaction.
+  optional int64 after_rss_file_kilobytes = 9;
+
+  // Anonymous RSS in kilobytes consumed by the process after compaction.
+  optional int64 after_rss_anon_kilobytes = 10;
+
+  // Swap in kilobytes consumed by the process after compaction.
+  optional int64 after_swap_kilobytes = 11;
+
+  // The time taken to perform compaction in milliseconds.
+  optional int64 time_to_compact_millis = 12;
+
+  // The last compaction action performed for this app.
+  optional Action last_action = 13;
+
+  // The last time that compaction was attempted on this process in seconds
+  // since boot.
+  optional int64 last_compact_timestamp = 14;
+
+  // The oom_adj at the time of compaction.
+  optional int32 oom_adj = 15;
+
+  // The process state at the time of compaction.
+  optional android.app.ProcessStateEnum process_state = 16 [default = PROCESS_STATE_UNKNOWN];
+}
diff --git a/cmds/statsd/src/metrics/CountMetricProducer.cpp b/cmds/statsd/src/metrics/CountMetricProducer.cpp
index 13579d2..7cc57c1 100644
--- a/cmds/statsd/src/metrics/CountMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/CountMetricProducer.cpp
@@ -154,7 +154,7 @@
         flushIfNeededLocked(dumpTimeNs);
     }
     protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_ID, (long long)mMetricId);
-    protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_IS_ACTIVE, isActiveLocked());
+    protoOutput->write(FIELD_TYPE_BOOL | FIELD_ID_IS_ACTIVE, isActiveLocked());
 
 
     if (mPastBuckets.empty()) {
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.cpp b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
index 0425671..69bafc3 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
@@ -463,7 +463,7 @@
         flushIfNeededLocked(dumpTimeNs);
     }
     protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_ID, (long long)mMetricId);
-    protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_IS_ACTIVE, isActiveLocked());
+    protoOutput->write(FIELD_TYPE_BOOL | FIELD_ID_IS_ACTIVE, isActiveLocked());
 
     if (mPastBuckets.empty()) {
         VLOG(" Duration metric, empty return");
diff --git a/cmds/statsd/src/metrics/EventMetricProducer.cpp b/cmds/statsd/src/metrics/EventMetricProducer.cpp
index ea125d0..c53c4ce 100644
--- a/cmds/statsd/src/metrics/EventMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/EventMetricProducer.cpp
@@ -110,7 +110,7 @@
                                              std::set<string> *str_set,
                                              ProtoOutputStream* protoOutput) {
     protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_ID, (long long)mMetricId);
-    protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_IS_ACTIVE, isActiveLocked());
+    protoOutput->write(FIELD_TYPE_BOOL | FIELD_ID_IS_ACTIVE, isActiveLocked());
     if (mProto->size() <= 0) {
         return;
     }
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
index 98a33f5..ec60244 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
@@ -194,7 +194,7 @@
     }
 
     protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_ID, (long long)mMetricId);
-    protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_IS_ACTIVE, isActiveLocked());
+    protoOutput->write(FIELD_TYPE_BOOL | FIELD_ID_IS_ACTIVE, isActiveLocked());
 
     if (mPastBuckets.empty()) {
         return;
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.cpp b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
index 7475b53..cf56e2d 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
@@ -192,7 +192,7 @@
         flushIfNeededLocked(dumpTimeNs);
     }
     protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_ID, (long long)mMetricId);
-    protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_IS_ACTIVE, isActiveLocked());
+    protoOutput->write(FIELD_TYPE_BOOL | FIELD_ID_IS_ACTIVE, isActiveLocked());
 
     if (mPastBuckets.empty() && mSkippedBuckets.empty()) {
         return;
diff --git a/cmds/statsd/tests/StatsLogProcessor_test.cpp b/cmds/statsd/tests/StatsLogProcessor_test.cpp
index 237f8b9..d52be44 100644
--- a/cmds/statsd/tests/StatsLogProcessor_test.cpp
+++ b/cmds/statsd/tests/StatsLogProcessor_test.cpp
@@ -279,7 +279,10 @@
     // Dump report again. There should be no data since we erased it.
     processor->onDumpReport(cfgKey, 5, true, true /* DO erase data. */, ADB_DUMP, &bytes);
     output.ParseFromArray(bytes.data(), bytes.size());
-    bool noData = (output.reports_size() == 0) || (output.reports(0).metrics_size() == 0);
+    // We don't care whether statsd has a report, as long as it has no count metrics in it.
+    bool noData = output.reports_size() == 0
+            || output.reports(0).metrics_size() == 0
+            || output.reports(0).metrics(0).count_metrics().data_size() == 0;
     EXPECT_TRUE(noData);
 }
 
diff --git a/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/Utils.java b/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/Utils.java
index 8464b8df..91d6490 100644
--- a/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/Utils.java
+++ b/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/Utils.java
@@ -62,7 +62,7 @@
         if (process.waitFor() == 0) {
             logger.fine("Adb command successful.");
         } else {
-            logger.severe("Abnormal adb shell cmd termination for: " + String.join(",", commands));
+            logger.severe("Abnormal adb shell termination for: " + String.join(",", commands));
             throw new RuntimeException("Error running adb command: " + err.toString());
         }
     }
@@ -118,6 +118,52 @@
         logger.addHandler(handler);
     }
 
+    /**
+     * Attempt to determine whether tool will work with this statsd, i.e. whether statsd is
+     * minCodename or higher.
+     * Algorithm: true if (sdk >= minSdk) || (sdk == minSdk-1 && codeName.startsWith(minCodeName))
+     * If all else fails, assume it will work (letting future commands deal with any errors).
+     */
+    public static boolean isAcceptableStatsd(Logger logger, int minSdk, String minCodename) {
+        BufferedReader in = null;
+        try {
+            File outFileSdk = File.createTempFile("shelltools_sdk", "tmp");
+            outFileSdk.deleteOnExit();
+            runCommand(outFileSdk, logger,
+                    "adb", "shell", "getprop", "ro.build.version.sdk");
+            in = new BufferedReader(new InputStreamReader(new FileInputStream(outFileSdk)));
+            // If NullPointerException/NumberFormatException/etc., just catch and return true.
+            int sdk = Integer.parseInt(in.readLine().trim());
+            if (sdk >= minSdk) {
+                return true;
+            } else if (sdk == minSdk - 1) { // Could be minSdk-1, or could be minSdk development.
+                in.close();
+                File outFileCode = File.createTempFile("shelltools_codename", "tmp");
+                outFileCode.deleteOnExit();
+                runCommand(outFileCode, logger,
+                        "adb", "shell", "getprop", "ro.build.version.codename");
+                in = new BufferedReader(new InputStreamReader(new FileInputStream(outFileCode)));
+                return in.readLine().startsWith(minCodename);
+            } else {
+                return false;
+            }
+        } catch (Exception e) {
+            logger.fine("Could not determine whether statsd version is compatibile "
+                    + "with tool: " + e.toString());
+        } finally {
+            try {
+                if (in != null) {
+                    in.close();
+                }
+            } catch (IOException e) {
+                logger.fine("Could not close temporary file: " + e.toString());
+            }
+        }
+        // Could not determine whether statsd is acceptable version.
+        // Just assume it is; if it isn't, we'll just get future errors via adb and deal with them.
+        return true;
+    }
+
     public static class LocalToolsFormatter extends Formatter {
         public String format(LogRecord record) {
             return record.getMessage() + "\n";
diff --git a/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/localdrive/LocalDrive.java b/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/localdrive/LocalDrive.java
index 67fb906..2eb4660 100644
--- a/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/localdrive/LocalDrive.java
+++ b/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/localdrive/LocalDrive.java
@@ -37,6 +37,9 @@
 public class LocalDrive {
     private static final boolean DEBUG = false;
 
+    public static final int MIN_SDK = 29;
+    public static final String MIN_CODENAME = "Q";
+
     public static final long DEFAULT_CONFIG_ID = 56789;
 
     public static final String BINARY_FLAG = "--binary";
@@ -46,7 +49,7 @@
     public static final String HELP_STRING =
         "Usage:\n\n" +
 
-        "statsd_local upload CONFIG_FILE [CONFIG_ID] [--binary]\n" +
+        "statsd_localdrive upload CONFIG_FILE [CONFIG_ID] [--binary]\n" +
         "  Uploads the given statsd config file (in binary or human-readable-text format).\n" +
         "  If a config with this id already exists, removes it first.\n" +
         "    CONFIG_FILE    Location of config file on host.\n" +
@@ -56,12 +59,12 @@
         // Similar to: adb shell cmd stats config update SHELL_UID CONFIG_ID
         "\n" +
 
-        "statsd_local update CONFIG_FILE [CONFIG_ID] [--binary]\n" +
+        "statsd_localdrive update CONFIG_FILE [CONFIG_ID] [--binary]\n" +
         "  Same as upload, but does not remove the old config first (if it already exists).\n" +
         // Similar to: adb shell cmd stats config update SHELL_UID CONFIG_ID
         "\n" +
 
-        "statsd_local get-data [CONFIG_ID] [--clear] [--binary] [--no-uid-map]\n" +
+        "statsd_localdrive get-data [CONFIG_ID] [--clear] [--binary] [--no-uid-map]\n" +
         "  Prints the output statslog data (in binary or human-readable-text format).\n" +
         "    CONFIG_ID      Long ID of the config. If absent, uses " + DEFAULT_CONFIG_ID + ".\n" +
         "    --binary       Output should be in binary, instead of default human-readable text.\n" +
@@ -72,13 +75,13 @@
         //                                                      --include_current_bucket --proto
         "\n" +
 
-        "statsd_local remove [CONFIG_ID]\n" +
+        "statsd_localdrive remove [CONFIG_ID]\n" +
         "  Removes the config.\n" +
         "    CONFIG_ID      Long ID of the config. If absent, uses " + DEFAULT_CONFIG_ID + ".\n" +
         // Equivalent to: adb shell cmd stats config remove SHELL_UID CONFIG_ID
         "\n" +
 
-        "statsd_local clear [CONFIG_ID]\n" +
+        "statsd_localdrive clear [CONFIG_ID]\n" +
         "  Clears the data associated with the config.\n" +
         "    CONFIG_ID      Long ID of the config. If absent, uses " + DEFAULT_CONFIG_ID + ".\n" +
         // Similar to: adb shell cmd stats dump-report SHELL_UID CONFIG_ID
@@ -92,6 +95,12 @@
     public static void main(String[] args) {
         Utils.setUpLogger(sLogger, DEBUG);
 
+        if (!Utils.isAcceptableStatsd(sLogger, MIN_SDK, MIN_CODENAME)) {
+            sLogger.severe("LocalDrive only works with statsd versions for Android "
+                    + MIN_CODENAME + " or higher.");
+            return;
+        }
+
         if (args.length > 0) {
             switch (args[0]) {
                 case "clear":
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 1b45d17..497e193c 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -6152,7 +6152,7 @@
         try {
             synchronized (getGetProviderLock(auth, userId)) {
                 holder = ActivityManager.getService().getContentProvider(
-                        getApplicationThread(), auth, userId, stable);
+                        getApplicationThread(), c.getOpPackageName(), auth, userId, stable);
             }
         } catch (RemoteException ex) {
             throw ex.rethrowFromSystemServer();
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 6905cb5..a278423 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -41,8 +41,10 @@
 import android.provider.Settings;
 import android.util.ArrayMap;
 
+import com.android.internal.annotations.GuardedBy;
 import com.android.internal.app.IAppOpsActiveCallback;
 import com.android.internal.app.IAppOpsCallback;
+import com.android.internal.app.IAppOpsNotedCallback;
 import com.android.internal.app.IAppOpsService;
 import com.android.internal.util.Preconditions;
 
@@ -86,10 +88,20 @@
      */
 
     final Context mContext;
+
     @UnsupportedAppUsage
     final IAppOpsService mService;
-    final ArrayMap<OnOpChangedListener, IAppOpsCallback> mModeWatchers = new ArrayMap<>();
-    final ArrayMap<OnOpActiveChangedListener, IAppOpsActiveCallback> mActiveWatchers =
+
+    @GuardedBy("mModeWatchers")
+    private final ArrayMap<OnOpChangedListener, IAppOpsCallback> mModeWatchers =
+            new ArrayMap<>();
+
+    @GuardedBy("mActiveWatchers")
+    private final ArrayMap<OnOpActiveChangedListener, IAppOpsActiveCallback> mActiveWatchers =
+            new ArrayMap<>();
+
+    @GuardedBy("mNotedWatchers")
+    private final ArrayMap<OnOpNotedListener, IAppOpsNotedCallback> mNotedWatchers =
             new ArrayMap<>();
 
     static IBinder sToken;
@@ -2471,6 +2483,23 @@
     }
 
     /**
+     * Callback for notification of an op being noted.
+     *
+     * @hide
+     */
+    public interface OnOpNotedListener {
+        /**
+         * Called when an op was noted.
+         *
+         * @param code The op code.
+         * @param uid The UID performing the operation.
+         * @param packageName The package performing the operation.
+         * @param result The result of the note.
+         */
+        void onOpNoted(String code, int uid, String packageName, int result);
+    }
+
+    /**
      * Callback for notification of changes to operation state.
      * This allows you to see the raw op codes instead of strings.
      * @hide
@@ -2819,7 +2848,7 @@
      */
     public void stopWatchingMode(OnOpChangedListener callback) {
         synchronized (mModeWatchers) {
-            IAppOpsCallback cb = mModeWatchers.get(callback);
+            IAppOpsCallback cb = mModeWatchers.remove(callback);
             if (cb != null) {
                 try {
                     mService.stopWatchingMode(cb);
@@ -2893,7 +2922,7 @@
     @TestApi
     public void stopWatchingActive(@NonNull OnOpActiveChangedListener callback) {
         synchronized (mActiveWatchers) {
-            final IAppOpsActiveCallback cb = mActiveWatchers.get(callback);
+            final IAppOpsActiveCallback cb = mActiveWatchers.remove(callback);
             if (cb != null) {
                 try {
                     mService.stopWatchingActive(cb);
@@ -2904,6 +2933,74 @@
         }
     }
 
+    /**
+     * Start watching for noted app ops. An app op may be immediate or long running.
+     * Immediate ops are noted while long running ones are started and stopped. This
+     * method allows registering a listener to be notified when an app op is noted. If
+     * an op is being noted by any package you will get a callback. To change the
+     * watched ops for a registered callback you need to unregister and register it again.
+     *
+     * <p> If you don't hold the {@link android.Manifest.permission#WATCH_APPOPS} permission
+     * you can watch changes only for your UID.
+     *
+     * @param ops The ops to watch.
+     * @param callback Where to report changes.
+     *
+     * @see #startWatchingActive(int[], OnOpActiveChangedListener)
+     * @see #stopWatchingNoted(OnOpNotedListener)
+     * @see #noteOp(String, int, String)
+     *
+     * @hide
+     */
+    @RequiresPermission(value=Manifest.permission.WATCH_APPOPS, conditional=true)
+    public void startWatchingNoted(@NonNull String[] ops, @NonNull OnOpNotedListener callback) {
+        IAppOpsNotedCallback cb;
+        synchronized (mNotedWatchers) {
+            cb = mNotedWatchers.get(callback);
+            if (cb != null) {
+                return;
+            }
+            cb = new IAppOpsNotedCallback.Stub() {
+                @Override
+                public void opNoted(int op, int uid, String packageName, int mode) {
+                    callback.onOpNoted(sOpToString[op], uid, packageName, mode);
+                }
+            };
+            mNotedWatchers.put(callback, cb);
+        }
+        try {
+            final int[] opCodes = new int[ops.length];
+            for (int i = 0; i < opCodes.length; i++) {
+                opCodes[i] = strOpToOp(ops[i]);
+            }
+            mService.startWatchingNoted(opCodes, cb);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Stop watching for noted app ops. An app op may be immediate or long running.
+     * Unregistering a non-registered callback has no effect.
+     *
+     * @see #startWatchingNoted(String[], OnOpNotedListener)
+     * @see #noteOp(String, int, String)
+     *
+     * @hide
+     */
+    public void stopWatchingNoted(@NonNull OnOpNotedListener callback) {
+        synchronized (mNotedWatchers) {
+            final IAppOpsNotedCallback cb = mNotedWatchers.get(callback);
+            if (cb != null) {
+                try {
+                    mService.stopWatchingNoted(cb);
+                } catch (RemoteException e) {
+                    throw e.rethrowFromSystemServer();
+                }
+            }
+        }
+    }
+
     private String buildSecurityExceptionMsg(int op, int uid, String packageName) {
         return packageName + " from uid " + uid + " not allowed to perform " + sOpNames[op];
     }
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index 88fb025..fb519b6 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -124,7 +124,7 @@
             int ignoreWindowingMode);
     void moveTaskToFront(int task, int flags, in Bundle options);
     int getTaskForActivity(in IBinder token, in boolean onlyRoot);
-    ContentProviderHolder getContentProvider(in IApplicationThread caller,
+    ContentProviderHolder getContentProvider(in IApplicationThread caller, in String callingPackage,
             in String name, int userId, boolean stable);
     void publishContentProviders(in IApplicationThread caller,
             in List<ContentProviderHolder> providers);
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index 306c366..606b00b 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -1838,12 +1838,17 @@
      * Recover a list of active notifications: ones that have been posted by the calling app that
      * have not yet been dismissed by the user or {@link #cancel(String, int)}ed by the app.
      *
-     * Each notification is embedded in a {@link StatusBarNotification} object, including the
+     * <p><Each notification is embedded in a {@link StatusBarNotification} object, including the
      * original <code>tag</code> and <code>id</code> supplied to
      * {@link #notify(String, int, Notification) notify()}
      * (via {@link StatusBarNotification#getTag() getTag()} and
      * {@link StatusBarNotification#getId() getId()}) as well as a copy of the original
      * {@link Notification} object (via {@link StatusBarNotification#getNotification()}).
+     * </p>
+     * <p>From {@link Build.VERSION_CODES#Q}, will also return notifications you've posted as an
+     * app's notification delegate via
+     * {@link NotificationManager#notifyAsPackage(String, String, int, Notification)}.
+     * </p>
      *
      * @return An array of {@link StatusBarNotification}.
      */
diff --git a/core/java/android/app/backup/BackupManager.java b/core/java/android/app/backup/BackupManager.java
index c6086f1..a6f6d06 100644
--- a/core/java/android/app/backup/BackupManager.java
+++ b/core/java/android/app/backup/BackupManager.java
@@ -335,7 +335,8 @@
         if (sService != null) {
             try {
                 // All packages, current transport
-                IRestoreSession binder = sService.beginRestoreSession(null, null);
+                IRestoreSession binder =
+                        sService.beginRestoreSessionForUser(mContext.getUserId(), null, null);
                 if (binder != null) {
                     session = new RestoreSession(mContext, binder);
                 }
@@ -465,7 +466,7 @@
         checkServiceBinder();
         if (sService != null) {
             try {
-                return sService.getCurrentTransportComponent();
+                return sService.getCurrentTransportComponentForUser(mContext.getUserId());
             } catch (RemoteException e) {
                 Log.e(TAG, "getCurrentTransportComponent() couldn't connect");
             }
@@ -530,7 +531,8 @@
         checkServiceBinder();
         if (sService != null) {
             try {
-                sService.updateTransportAttributes(
+                sService.updateTransportAttributesForUser(
+                        mContext.getUserId(),
                         transportComponent,
                         name,
                         configurationIntent,
@@ -590,7 +592,8 @@
             try {
                 SelectTransportListenerWrapper wrapper = listener == null ?
                         null : new SelectTransportListenerWrapper(mContext, listener);
-                sService.selectBackupTransportAsync(transport, wrapper);
+                sService.selectBackupTransportAsyncForUser(
+                        mContext.getUserId(), transport, wrapper);
             } catch (RemoteException e) {
                 Log.e(TAG, "selectBackupTransportAsync() couldn't connect");
             }
@@ -637,7 +640,7 @@
         checkServiceBinder();
         if (sService != null) {
             try {
-                return sService.getAvailableRestoreToken(packageName);
+                return sService.getAvailableRestoreTokenForUser(mContext.getUserId(), packageName);
             } catch (RemoteException e) {
                 Log.e(TAG, "getAvailableRestoreToken() couldn't connect");
             }
@@ -659,7 +662,7 @@
         checkServiceBinder();
         if (sService != null) {
             try {
-                return sService.isAppEligibleForBackup(packageName);
+                return sService.isAppEligibleForBackupForUser(mContext.getUserId(), packageName);
             } catch (RemoteException e) {
                 Log.e(TAG, "isAppEligibleForBackup(pkg) couldn't connect");
             }
@@ -760,7 +763,7 @@
     public Intent getConfigurationIntent(String transportName) {
         if (sService != null) {
             try {
-                return sService.getConfigurationIntent(transportName);
+                return sService.getConfigurationIntentForUser(mContext.getUserId(), transportName);
             } catch (RemoteException e) {
                 Log.e(TAG, "getConfigurationIntent() couldn't connect");
             }
@@ -781,7 +784,7 @@
     public String getDestinationString(String transportName) {
         if (sService != null) {
             try {
-                return sService.getDestinationString(transportName);
+                return sService.getDestinationStringForUser(mContext.getUserId(), transportName);
             } catch (RemoteException e) {
                 Log.e(TAG, "getDestinationString() couldn't connect");
             }
@@ -802,7 +805,7 @@
     public Intent getDataManagementIntent(String transportName) {
         if (sService != null) {
             try {
-                return sService.getDataManagementIntent(transportName);
+                return sService.getDataManagementIntentForUser(mContext.getUserId(), transportName);
             } catch (RemoteException e) {
                 Log.e(TAG, "getDataManagementIntent() couldn't connect");
             }
@@ -825,7 +828,7 @@
     public String getDataManagementLabel(String transportName) {
         if (sService != null) {
             try {
-                return sService.getDataManagementLabel(transportName);
+                return sService.getDataManagementLabelForUser(mContext.getUserId(), transportName);
             } catch (RemoteException e) {
                 Log.e(TAG, "getDataManagementLabel() couldn't connect");
             }
diff --git a/core/java/android/app/backup/IBackupManager.aidl b/core/java/android/app/backup/IBackupManager.aidl
index f1e6b06..19de19a 100644
--- a/core/java/android/app/backup/IBackupManager.aidl
+++ b/core/java/android/app/backup/IBackupManager.aidl
@@ -43,6 +43,15 @@
      * Any application can invoke this method for its own package, but
      * only callers who hold the android.permission.BACKUP permission
      * may invoke it for arbitrary packages.
+     * If {@code userId} is different from the calling user id, then the caller must hold the
+     * android.permission.INTERACT_ACROSS_USERS_FULL permission.
+     *
+     * @param userId User id for which the caller has made changes to its data.
+     */
+    void dataChangedForUser(int userId, String packageName);
+
+    /**
+     * {@link android.app.backup.IBackupManager.dataChangedForUser} for the calling user id.
      */
     void dataChanged(String packageName);
 
@@ -53,6 +62,15 @@
      * Any application can invoke this method for its own package, but
      * only callers who hold the android.permission.BACKUP permission
      * may invoke it for arbitrary packages.
+     * If {@code userId} is different from the calling user id, then the caller must hold the
+     * android.permission.INTERACT_ACROSS_USERS_FULL permission.
+     *
+     * @param userId User id for which backup data should be erased.
+     */
+    void clearBackupDataForUser(int userId, String transportName, String packageName);
+
+    /**
+     * {@link android.app.backup.IBackupManager.clearBackupDataForUser} for the calling user id.
      */
     void clearBackupData(String transportName, String packageName);
 
@@ -62,24 +80,59 @@
      * operations.
      *
      * <p>Callers must hold the android.permission.BACKUP permission to use this method.
+     * If {@code userId} is different from the calling user id, then the caller must hold the
+     * android.permission.INTERACT_ACROSS_USERS_FULL permission.
+     *
+     * @param userId User id for which the given transports should be initialized.
      */
-    void initializeTransports(in String[] transportNames, IBackupObserver observer);
+    void initializeTransportsForUser(int userId, in String[] transportNames,
+        IBackupObserver observer);
 
     /**
      * Notifies the Backup Manager Service that an agent has become available.  This
      * method is only invoked by the Activity Manager.
+     *
+     * If {@code userId} is different from the calling user id, then the caller must hold the
+     * android.permission.INTERACT_ACROSS_USERS_FULL permission.
+     *
+     * @param userId User id for which an agent has become available.
+     */
+    void agentConnectedForUser(int userId, String packageName, IBinder agent);
+
+    /**
+     * {@link android.app.backup.IBackupManager.agentConnected} for the calling user id.
      */
     void agentConnected(String packageName, IBinder agent);
 
     /**
      * Notify the Backup Manager Service that an agent has unexpectedly gone away.
      * This method is only invoked by the Activity Manager.
+     *
+     * If {@code userId} is different from the calling user id, then the caller must hold the
+     * android.permission.INTERACT_ACROSS_USERS_FULL permission.
+     *
+     * @param userId User id for which an agent has unexpectedly gone away.
+     */
+    void agentDisconnectedForUser(int userId, String packageName);
+
+    /**
+     * {@link android.app.backup.IBackupManager.agentDisconnected} for the calling user id.
      */
     void agentDisconnected(String packageName);
 
     /**
      * Notify the Backup Manager Service that an application being installed will
      * need a data-restore pass.  This method is only invoked by the Package Manager.
+     *
+     * If {@code userId} is different from the calling user id, then the caller must hold the
+     * android.permission.INTERACT_ACROSS_USERS_FULL permission.
+     *
+     * @param userId User id for which the application will need a data-restore pass.
+     */
+    void restoreAtInstallForUser(int userId, String packageName, int token);
+
+    /**
+     * {@link android.app.backup.IBackupManager.restoreAtInstallForUser} for the calling user id.
      */
     void restoreAtInstall(String packageName, int token);
 
@@ -112,10 +165,18 @@
      * is made generally available for launch.
      *
      * <p>Callers must hold  the android.permission.BACKUP permission to use this method.
+     * If {@code userId} is different from the calling user id, then the caller must hold the
+     * android.permission.INTERACT_ACROSS_USERS_FULL permission.
      *
+     * @param userId User id for which automatic restore should be enabled/disabled.
      * @param doAutoRestore When true, enables the automatic app-data restore facility.  When
      *   false, this facility will be disabled.
      */
+    void setAutoRestoreForUser(int userId, boolean doAutoRestore);
+
+    /**
+     * {@link android.app.backup.IBackupManager.setAutoRestoreForUser} for the calling user id.
+     */
     void setAutoRestore(boolean doAutoRestore);
 
     /**
@@ -220,9 +281,13 @@
      * Perform a full-dataset backup of the given applications via the currently active
      * transport.
      *
+     * If {@code userId} is different from the calling user id, then the caller must hold the
+     * android.permission.INTERACT_ACROSS_USERS_FULL permission.
+     *
+     * @param userId User id for which the full-dataset backup should be performed.
      * @param packageNames The package names of the apps whose data are to be backed up.
      */
-    void fullTransportBackup(in String[] packageNames);
+    void fullTransportBackupForUser(int userId, in String[] packageNames);
 
     /**
      * Restore device content from the data stream passed through the given socket.  The
@@ -250,6 +315,18 @@
      * backup dataset being used for restore.
      *
      * <p>Callers must hold the android.permission.BACKUP permission to use this method.
+     * If {@code userId} is different from the calling user id, then the caller must hold the
+     * android.permission.INTERACT_ACROSS_USERS_FULL permission.
+     *
+     * @param userId User id for which the requested backup/restore operation can proceed.
+     */
+    void acknowledgeFullBackupOrRestoreForUser(int userId, int token, boolean allow,
+            in String curPassword, in String encryptionPassword,
+            IFullBackupRestoreObserver observer);
+
+    /**
+     * {@link android.app.backup.IBackupManager.acknowledgeFullBackupOrRestoreForUser} for the
+     * calling user id.
      */
     void acknowledgeFullBackupOrRestore(int token, boolean allow,
             in String curPassword, in String encryptionPassword,
@@ -260,7 +337,10 @@
      * specified transport has not been bound at least once (for registration), this call will be
      * ignored. Only the host process of the transport can change its description, otherwise a
      * {@link SecurityException} will be thrown.
+     * If {@code userId} is different from the calling user id, then the caller must hold the
+     * android.permission.INTERACT_ACROSS_USERS_FULL permission.
      *
+     * @param userId User id for which the attributes of the transport should be updated.
      * @param transportComponent The identity of the transport being described.
      * @param name A {@link String} with the new name for the transport. This is NOT for
      *     identification. MUST NOT be {@code null}.
@@ -279,13 +359,23 @@
      * @throws SecurityException If the UID of the calling process differs from the package UID of
      *     {@code transportComponent} or if the caller does NOT have BACKUP permission.
      */
-    void updateTransportAttributes(in ComponentName transportComponent, in String name,
+    void updateTransportAttributesForUser(int userId, in ComponentName transportComponent,
+            in String name,
             in Intent configurationIntent, in String currentDestinationString,
             in Intent dataManagementIntent, in String dataManagementLabel);
 
     /**
      * Identify the currently selected transport.  Callers must hold the
      * android.permission.BACKUP permission to use this method.
+     * If {@code userId} is different from the calling user id, then the caller must hold the
+     * android.permission.INTERACT_ACROSS_USERS_FULL permission.
+     *
+     * @param userId User id for which the currently selected transport should be identified.
+     */
+    String getCurrentTransportForUser(int userId);
+
+    /**
+     * {@link android.app.backup.IBackupManager.getCurrentTransportForUser} for the calling user id.
      */
     String getCurrentTransport();
 
@@ -293,16 +383,35 @@
       * 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.  Callers must
       * hold the android.permission.BACKUP permission to use this method.
+      * If {@code userId} is different from the calling user id, then the caller must hold the
+      * android.permission.INTERACT_ACROSS_USERS_FULL permission.
+      *
+      * @param userId User id for which the currently selected transport should be identified.
       */
-    ComponentName getCurrentTransportComponent();
+    ComponentName getCurrentTransportComponentForUser(int userId);
 
     /**
      * Request a list of all available backup transports' names.  Callers must
      * hold the android.permission.BACKUP permission to use this method.
+     * If {@code userId} is different from the calling user id, then the caller must hold the
+     * android.permission.INTERACT_ACROSS_USERS_FULL permission.
+     *
+     * @param userId User id for which all available backup transports' names should be listed.
+     */
+    String[] listAllTransportsForUser(int userId);
+
+    /**
+     * {@link android.app.backup.IBackupManager.listAllTransportsForUser} for the calling user id.
      */
     String[] listAllTransports();
 
-    ComponentName[] listAllTransportComponents();
+    /**
+     * If {@code userId} is different from the calling user id, then the caller must hold the
+     * android.permission.INTERACT_ACROSS_USERS_FULL permission.
+     *
+     * @param userId User id for which all available backup transports should be listed.
+     */
+    ComponentName[] listAllTransportComponentsForUser(int userId);
 
     /**
      * Retrieve the list of whitelisted transport components.  Callers do </i>not</i> need
@@ -315,13 +424,22 @@
     /**
      * Specify the current backup transport.  Callers must hold the
      * android.permission.BACKUP permission to use this method.
+     * If {@code userId} is different from the calling user id, then the caller must hold the
+     * android.permission.INTERACT_ACROSS_USERS_FULL permission.
      *
+     * @param userId User id for which the transport should be selected.
      * @param transport The name of the transport to select.  This should be one
      * of {@link BackupManager.TRANSPORT_GOOGLE} or {@link BackupManager.TRANSPORT_ADB}.
      * @return The name of the previously selected transport.  If the given transport
      *   name is not one of the currently available transports, no change is made to
      *   the current transport setting and the method returns null.
      */
+    String selectBackupTransportForUser(int userId, String transport);
+
+    /**
+     * {@link android.app.backup.IBackupManager.selectBackupTransportForUser} for the calling user
+     * id.
+     */
     String selectBackupTransport(String transport);
 
     /**
@@ -330,43 +448,85 @@
      * which is in a separate process.
      *
      * <p>Callers must hold the android.permission.BACKUP permission to use this method.
+     * If {@code userId} is different from the calling user id, then the caller must hold the
+     * android.permission.INTERACT_ACROSS_USERS_FULL permission.
      *
+     * @param userId User id for which the transport should be selected.
      * @param transport ComponentName of the service hosting the transport. This is different from
      *                  the transport's name that is returned by {@link BackupTransport#name()}.
      * @param listener A listener object to get a callback on the transport being selected.
      */
-    void selectBackupTransportAsync(in ComponentName transport, ISelectBackupTransportCallback listener);
+    void selectBackupTransportAsyncForUser(int userId, in ComponentName transport,
+        ISelectBackupTransportCallback listener);
 
     /**
      * Get the configuration Intent, if any, from the given transport.  Callers must
      * hold the android.permission.BACKUP permission in order to use this method.
+     * If {@code userId} is different from the calling user id, then the caller must hold the
+     * android.permission.INTERACT_ACROSS_USERS_FULL permission.
      *
+     * @param userId User id for which the configuration Intent should be reported.
      * @param transport The name of the transport to query.
      * @return An Intent to use with Activity#startActivity() to bring up the configuration
      *   UI supplied by the transport.  If the transport has no configuration UI, it should
      *   return {@code null} here.
      */
+    Intent getConfigurationIntentForUser(int userId, String transport);
+
+    /**
+     * {@link android.app.backup.IBackupManager.getConfigurationIntentForUser} for the calling user
+     * id.
+     */
     Intent getConfigurationIntent(String transport);
 
     /**
      * Get the destination string supplied by the given transport.  Callers must
      * hold the android.permission.BACKUP permission in order to use this method.
+     * If {@code userId} is different from the calling user id, then the caller must hold the
+     * android.permission.INTERACT_ACROSS_USERS_FULL permission.
      *
+     * @param userId User id for which the transport destination string should be reported.
      * @param transport The name of the transport to query.
      * @return A string describing the current backup destination.  This string is used
      *   verbatim by the Settings UI as the summary text of the "configure..." item.
      */
+    String getDestinationStringForUser(int userId, String transport);
+
+    /**
+     * {@link android.app.backup.IBackupManager.getDestinationStringForUser} for the calling user
+     * id.
+     */
     String getDestinationString(String transport);
 
     /**
      * Get the manage-data UI intent, if any, from the given transport.  Callers must
      * hold the android.permission.BACKUP permission in order to use this method.
+     * If {@code userId} is different from the calling user id, then the caller must hold the
+     * android.permission.INTERACT_ACROSS_USERS_FULL permission.
+     *
+     * @param userId User id for which the manage-data UI intent should be reported.
+     */
+    Intent getDataManagementIntentForUser(int userId, String transport);
+
+    /**
+     * {@link android.app.backup.IBackupManager.getDataManagementIntentForUser} for the calling user
+     * id.
      */
     Intent getDataManagementIntent(String transport);
 
     /**
      * Get the manage-data menu label, if any, from the given transport.  Callers must
      * hold the android.permission.BACKUP permission in order to use this method.
+     * If {@code userId} is different from the calling user id, then the caller must hold the
+     * android.permission.INTERACT_ACROSS_USERS_FULL permission.
+     *
+     * @param userId User id for which the manage-data menu label should be reported.
+     */
+    String getDataManagementLabelForUser(int userId, String transport);
+
+    /**
+     * {@link android.app.backup.IBackupManager.getDataManagementLabelForUser} for the calling user
+     * id.
      */
     String getDataManagementLabel(String transport);
 
@@ -381,7 +541,10 @@
      * package.  In that case, the restore session returned is suitable for supporting
      * the BackupManager.requestRestore() functionality via RestoreSession.restorePackage()
      * without requiring the app to hold any special permission.
+     * If {@code userId} is different from the calling user id, then the caller must hold the
+     * android.permission.INTERACT_ACROSS_USERS_FULL permission.
      *
+     * @param userId User id for which a restore session should be begun.
      * @param packageName The name of the single package for which a restore will
      *        be requested.  May be null, in which case all packages in the restore
      *        set can be restored.
@@ -389,7 +552,7 @@
      *        May be null, in which case the current active transport is used.
      * @return An interface to the restore session, or null on error.
      */
-    IRestoreSession beginRestoreSession(String packageName, String transportID);
+    IRestoreSession beginRestoreSessionForUser(int userId, String packageName, String transportID);
 
     /**
      * Notify the backup manager that a BackupAgent has completed the operation
@@ -427,13 +590,16 @@
      * restored from if we were to install it right now.
      *
      * <p>Callers must hold the android.permission.BACKUP permission to use this method.
+     * If {@code userId} is different from the calling user id, then the caller must hold the
+     * android.permission.INTERACT_ACROSS_USERS_FULL permission.
      *
+     * @param userId User id for which this operation should be performed.
      * @param packageName The name of the package whose most-suitable dataset we
      *     wish to look up
      * @return The dataset token from which a restore should be attempted, or zero if
      *     no suitable data is available.
      */
-    long getAvailableRestoreToken(String packageName);
+    long getAvailableRestoreTokenForUser(int userId, String packageName);
 
     /**
      * Ask the framework whether this app is eligible for backup.
@@ -442,21 +608,27 @@
      * {@link #filterAppsEligibleForBackup(String[])} to save resources.
      *
      * <p>Callers must hold the android.permission.BACKUP permission to use this method.
+     * If {@code userId} is different from the calling user id, then the caller must hold the
+     * android.permission.INTERACT_ACROSS_USERS_FULL permission.
      *
+     * @param userId User id for which this operation should be performed.
      * @param packageName The name of the package.
      * @return Whether this app is eligible for backup.
      */
-    boolean isAppEligibleForBackup(String packageName);
+    boolean isAppEligibleForBackupForUser(int userId, String packageName);
 
     /**
      * Filter the packages that are eligible for backup and return the result.
      *
      * <p>Callers must hold the android.permission.BACKUP permission to use this method.
+     * If {@code userId} is different from the calling user id, then the caller must hold the
+     * android.permission.INTERACT_ACROSS_USERS_FULL permission.
      *
+     * @param userId User id for which the filter should be performed.
      * @param packages The list of packages to filter.
      * @return The packages eligible for backup.
      */
-    String[] filterAppsEligibleForBackup(in String[] packages);
+    String[] filterAppsEligibleForBackupForUser(int userId, in String[] packages);
 
     /**
      * Request an immediate backup, providing an observer to which results of the backup operation
diff --git a/core/java/android/bluetooth/BluetoothManager.java b/core/java/android/bluetooth/BluetoothManager.java
index e08d405..adedff3 100644
--- a/core/java/android/bluetooth/BluetoothManager.java
+++ b/core/java/android/bluetooth/BluetoothManager.java
@@ -67,7 +67,9 @@
         }
         // Legacy api - getDefaultAdapter does not take in the context
         mAdapter = BluetoothAdapter.getDefaultAdapter();
-        mAdapter.setContext(context);
+        if (mAdapter != null) {
+            mAdapter.setContext(context);
+        }
     }
 
     /**
diff --git a/core/java/android/hardware/display/BrightnessConfiguration.java b/core/java/android/hardware/display/BrightnessConfiguration.java
index 7e52ca3..be054297 100644
--- a/core/java/android/hardware/display/BrightnessConfiguration.java
+++ b/core/java/android/hardware/display/BrightnessConfiguration.java
@@ -19,26 +19,54 @@
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
+import android.content.pm.ApplicationInfo;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.util.Pair;
 
 import com.android.internal.util.Preconditions;
+import com.android.internal.util.XmlUtils;
 
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.IOException;
+import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
 import java.util.Objects;
 
 /** @hide */
 @SystemApi
 @TestApi
 public final class BrightnessConfiguration implements Parcelable {
+    private static final String TAG_BRIGHTNESS_CURVE = "brightness-curve";
+    private static final String TAG_BRIGHTNESS_POINT = "brightness-point";
+    private static final String TAG_BRIGHTNESS_CORRECTIONS = "brightness-corrections";
+    private static final String TAG_BRIGHTNESS_CORRECTION = "brightness-correction";
+    private static final String ATTR_LUX = "lux";
+    private static final String ATTR_NITS = "nits";
+    private static final String ATTR_DESCRIPTION = "description";
+    private static final String ATTR_PACKAGE_NAME = "package-name";
+    private static final String ATTR_CATEGORY = "category";
+
     private final float[] mLux;
     private final float[] mNits;
+    private final Map<String, BrightnessCorrection> mCorrectionsByPackageName;
+    private final Map<Integer, BrightnessCorrection> mCorrectionsByCategory;
     private final String mDescription;
 
-    private BrightnessConfiguration(float[] lux, float[] nits, String description) {
+    private BrightnessConfiguration(float[] lux, float[] nits,
+            Map<String, BrightnessCorrection> correctionsByPackageName,
+            Map<Integer, BrightnessCorrection> correctionsByCategory, String description) {
         mLux = lux;
         mNits = nits;
+        mCorrectionsByPackageName = correctionsByPackageName;
+        mCorrectionsByCategory = correctionsByCategory;
         mDescription = description;
     }
 
@@ -56,6 +84,38 @@
     }
 
     /**
+     * Returns a brightness correction by app, or null.
+     *
+     * @param packageName
+     *      The app's package name.
+     *
+     * @return The matching brightness correction, or null.
+     *
+     * @hide
+     */
+    @SystemApi
+    @Nullable
+    public BrightnessCorrection getCorrectionByPackageName(String packageName) {
+        return mCorrectionsByPackageName.get(packageName);
+    }
+
+    /**
+     * Returns a brightness correction by app category, or null.
+     *
+     * @param category
+     *      The app category.
+     *
+     * @return The matching brightness correction, or null.
+     *
+     * @hide
+     */
+    @SystemApi
+    @Nullable
+    public BrightnessCorrection getCorrectionByCategory(int category) {
+        return mCorrectionsByCategory.get(category);
+    }
+
+    /**
      * Returns description string.
      * @hide
      */
@@ -67,6 +127,20 @@
     public void writeToParcel(Parcel dest, int flags) {
         dest.writeFloatArray(mLux);
         dest.writeFloatArray(mNits);
+        dest.writeInt(mCorrectionsByPackageName.size());
+        for (Entry<String, BrightnessCorrection> entry : mCorrectionsByPackageName.entrySet()) {
+            final String packageName = entry.getKey();
+            final BrightnessCorrection correction = entry.getValue();
+            dest.writeString(packageName);
+            correction.writeToParcel(dest, flags);
+        }
+        dest.writeInt(mCorrectionsByCategory.size());
+        for (Entry<Integer, BrightnessCorrection> entry : mCorrectionsByCategory.entrySet()) {
+            final int category = entry.getKey();
+            final BrightnessCorrection correction = entry.getValue();
+            dest.writeInt(category);
+            correction.writeToParcel(dest, flags);
+        }
         dest.writeString(mDescription);
     }
 
@@ -85,7 +159,14 @@
             }
             sb.append("(").append(mLux[i]).append(", ").append(mNits[i]).append(")");
         }
-        sb.append("], '");
+        sb.append("], {");
+        for (Entry<String, BrightnessCorrection> entry : mCorrectionsByPackageName.entrySet()) {
+            sb.append("'" + entry.getKey() + "': " + entry.getValue() + ", ");
+        }
+        for (Entry<Integer, BrightnessCorrection> entry : mCorrectionsByCategory.entrySet()) {
+            sb.append(entry.getKey() + ": " + entry.getValue() + ", ");
+        }
+        sb.append("}, '");
         if (mDescription != null) {
             sb.append(mDescription);
         }
@@ -98,6 +179,8 @@
         int result = 1;
         result = result * 31 + Arrays.hashCode(mLux);
         result = result * 31 + Arrays.hashCode(mNits);
+        result = result * 31 + mCorrectionsByPackageName.hashCode();
+        result = result * 31 + mCorrectionsByCategory.hashCode();
         if (mDescription != null) {
             result = result * 31 + mDescription.hashCode();
         }
@@ -114,6 +197,8 @@
         }
         final BrightnessConfiguration other = (BrightnessConfiguration) o;
         return Arrays.equals(mLux, other.mLux) && Arrays.equals(mNits, other.mNits)
+                && mCorrectionsByPackageName.equals(other.mCorrectionsByPackageName)
+                && mCorrectionsByCategory.equals(other.mCorrectionsByCategory)
                 && Objects.equals(mDescription, other.mDescription);
     }
 
@@ -123,7 +208,25 @@
             float[] lux = in.createFloatArray();
             float[] nits = in.createFloatArray();
             Builder builder = new Builder(lux, nits);
-            builder.setDescription(in.readString());
+
+            int n = in.readInt();
+            for (int i = 0; i < n; i++) {
+                final String packageName = in.readString();
+                final BrightnessCorrection correction =
+                        BrightnessCorrection.CREATOR.createFromParcel(in);
+                builder.addCorrectionByPackageName(packageName, correction);
+            }
+
+            n = in.readInt();
+            for (int i = 0; i < n; i++) {
+                final int category = in.readInt();
+                final BrightnessCorrection correction =
+                        BrightnessCorrection.CREATOR.createFromParcel(in);
+                builder.addCorrectionByCategory(category, correction);
+            }
+
+            final String description = in.readString();
+            builder.setDescription(description);
             return builder.build();
         }
 
@@ -133,11 +236,146 @@
     };
 
     /**
+     * Writes the configuration to an XML serializer.
+     *
+     * @param serializer
+     *      The XML serializer.
+     *
+     * @hide
+     */
+    public void saveToXml(XmlSerializer serializer) throws IOException {
+        serializer.startTag(null, TAG_BRIGHTNESS_CURVE);
+        if (mDescription != null) {
+            serializer.attribute(null, ATTR_DESCRIPTION, mDescription);
+        }
+        for (int i = 0; i < mLux.length; i++) {
+            serializer.startTag(null, TAG_BRIGHTNESS_POINT);
+            serializer.attribute(null, ATTR_LUX, Float.toString(mLux[i]));
+            serializer.attribute(null, ATTR_NITS, Float.toString(mNits[i]));
+            serializer.endTag(null, TAG_BRIGHTNESS_POINT);
+        }
+        serializer.endTag(null, TAG_BRIGHTNESS_CURVE);
+        serializer.startTag(null, TAG_BRIGHTNESS_CORRECTIONS);
+        for (Map.Entry<String, BrightnessCorrection> entry :
+                mCorrectionsByPackageName.entrySet()) {
+            final String packageName = entry.getKey();
+            final BrightnessCorrection correction = entry.getValue();
+            serializer.startTag(null, TAG_BRIGHTNESS_CORRECTION);
+            serializer.attribute(null, ATTR_PACKAGE_NAME, packageName);
+            correction.saveToXml(serializer);
+            serializer.endTag(null, TAG_BRIGHTNESS_CORRECTION);
+        }
+        for (Map.Entry<Integer, BrightnessCorrection> entry : mCorrectionsByCategory.entrySet()) {
+            final int category = entry.getKey();
+            final BrightnessCorrection correction = entry.getValue();
+            serializer.startTag(null, TAG_BRIGHTNESS_CORRECTION);
+            serializer.attribute(null, ATTR_CATEGORY, Integer.toString(category));
+            correction.saveToXml(serializer);
+            serializer.endTag(null, TAG_BRIGHTNESS_CORRECTION);
+        }
+        serializer.endTag(null, TAG_BRIGHTNESS_CORRECTIONS);
+    }
+
+    /**
+     * Read a configuration from an XML parser.
+     *
+     * @param parser
+     *      The XML parser.
+     *
+     * @throws IOException
+     *      The parser failed to read the XML file.
+     * @throws XmlPullParserException
+     *      The parser failed to parse the XML file.
+     *
+     * @hide
+     */
+    public static BrightnessConfiguration loadFromXml(XmlPullParser parser)
+            throws IOException, XmlPullParserException {
+        String description = null;
+        List<Float> luxList = new ArrayList<>();
+        List<Float> nitsList = new ArrayList<>();
+        Map<String, BrightnessCorrection> correctionsByPackageName = new HashMap<>();
+        Map<Integer, BrightnessCorrection> correctionsByCategory = new HashMap<>();
+        final int configDepth = parser.getDepth();
+        while (XmlUtils.nextElementWithin(parser, configDepth)) {
+            if (TAG_BRIGHTNESS_CURVE.equals(parser.getName())) {
+                description = parser.getAttributeValue(null, ATTR_DESCRIPTION);
+                final int curveDepth = parser.getDepth();
+                while (XmlUtils.nextElementWithin(parser, curveDepth)) {
+                    if (!TAG_BRIGHTNESS_POINT.equals(parser.getName())) {
+                        continue;
+                    }
+                    final float lux = loadFloatFromXml(parser, ATTR_LUX);
+                    final float nits = loadFloatFromXml(parser, ATTR_NITS);
+                    luxList.add(lux);
+                    nitsList.add(nits);
+                }
+            }
+            if (TAG_BRIGHTNESS_CORRECTIONS.equals(parser.getName())) {
+                final int correctionsDepth = parser.getDepth();
+                while (XmlUtils.nextElementWithin(parser, correctionsDepth)) {
+                    if (!TAG_BRIGHTNESS_CORRECTION.equals(parser.getName())) {
+                        continue;
+                    }
+                    final String packageName = parser.getAttributeValue(null, ATTR_PACKAGE_NAME);
+                    final String categoryText = parser.getAttributeValue(null, ATTR_CATEGORY);
+                    BrightnessCorrection correction = BrightnessCorrection.loadFromXml(parser);
+                    if (packageName != null) {
+                        correctionsByPackageName.put(packageName, correction);
+                    } else if (categoryText != null) {
+                        try {
+                            final int category = Integer.parseInt(categoryText);
+                            correctionsByCategory.put(category, correction);
+                        } catch (NullPointerException | NumberFormatException e) {
+                            continue;
+                        }
+                    }
+                }
+            }
+        }
+        final int n = luxList.size();
+        float[] lux = new float[n];
+        float[] nits = new float[n];
+        for (int i = 0; i < n; i++) {
+            lux[i] = luxList.get(i);
+            nits[i] = nitsList.get(i);
+        }
+        final BrightnessConfiguration.Builder builder = new BrightnessConfiguration.Builder(lux,
+                nits);
+        builder.setDescription(description);
+        for (Map.Entry<String, BrightnessCorrection> entry : correctionsByPackageName.entrySet()) {
+            final String packageName = entry.getKey();
+            final BrightnessCorrection correction = entry.getValue();
+            builder.addCorrectionByPackageName(packageName, correction);
+        }
+        for (Map.Entry<Integer, BrightnessCorrection> entry : correctionsByCategory.entrySet()) {
+            final int category = entry.getKey();
+            final BrightnessCorrection correction = entry.getValue();
+            builder.addCorrectionByCategory(category, correction);
+        }
+        return builder.build();
+    }
+
+    private static float loadFloatFromXml(XmlPullParser parser, String attribute) {
+        final String string = parser.getAttributeValue(null, attribute);
+        try {
+            return Float.parseFloat(string);
+        } catch (NullPointerException | NumberFormatException e) {
+            return Float.NaN;
+        }
+    }
+
+    /**
      * A builder class for {@link BrightnessConfiguration}s.
      */
     public static class Builder {
+        private static final int MAX_CORRECTIONS_BY_PACKAGE_NAME = 20;
+        private static final int MAX_CORRECTIONS_BY_CATEGORY = 20;
+
         private float[] mCurveLux;
         private float[] mCurveNits;
+        private Map<String, BrightnessCorrection> mCorrectionsByPackageName;
+        private Map<Integer, BrightnessCorrection> mCorrectionsByCategory;
         private String mDescription;
 
         /**
@@ -169,6 +407,88 @@
             checkMonotonic(nits, false /*strictly increasing*/, "nits");
             mCurveLux = lux;
             mCurveNits = nits;
+            mCorrectionsByPackageName = new HashMap<>();
+            mCorrectionsByCategory = new HashMap<>();
+        }
+
+        /**
+         * Returns the maximum number of corrections by package name allowed.
+         *
+         * @return The maximum number of corrections by package name allowed.
+         *
+         * @hide
+         */
+        @SystemApi
+        public int getMaxCorrectionsByPackageName() {
+            return MAX_CORRECTIONS_BY_PACKAGE_NAME;
+        }
+
+        /**
+         * Returns the maximum number of corrections by category allowed.
+         *
+         * @return The maximum number of corrections by category allowed.
+         *
+         * @hide
+         */
+        @SystemApi
+        public int getMaxCorrectionsByCategory() {
+            return MAX_CORRECTIONS_BY_CATEGORY;
+        }
+
+        /**
+         * Add a brightness correction by app package name.
+         * This correction is applied whenever an app with this package name has the top activity
+         * of the focused stack.
+         *
+         * @param packageName
+         *      The app's package name.
+         * @param correction
+         *      The brightness correction.
+         *
+         * @return The builder.
+         *
+         * @throws IllegalArgumentExceptions
+         *      Maximum number of corrections by package name exceeded (see
+         *      {@link #getMaxCorrectionsByPackageName}).
+         *
+         * @hide
+         */
+        @SystemApi
+        public Builder addCorrectionByPackageName(String packageName,
+                BrightnessCorrection correction) {
+            if (mCorrectionsByPackageName.size() >= getMaxCorrectionsByPackageName()) {
+                throw new IllegalArgumentException("Too many corrections by package name");
+            }
+            mCorrectionsByPackageName.put(packageName, correction);
+            return this;
+        }
+
+        /**
+         * Add a brightness correction by app category.
+         * This correction is applied whenever an app with this category has the top activity of
+         * the focused stack, and only if a correction by package name has not been applied.
+         *
+         * @param category
+         *      The {@link android.content.pm.ApplicationInfo#category app category}.
+         * @param correction
+         *      The brightness correction.
+         *
+         * @return The builder.
+         *
+         * @throws IllegalArgumentException
+         *      Maximum number of corrections by category exceeded (see
+         *      {@link #getMaxCorrectionsByCategory}).
+         *
+         * @hide
+         */
+        @SystemApi
+        public Builder addCorrectionByCategory(@ApplicationInfo.Category int category,
+                BrightnessCorrection correction) {
+            if (mCorrectionsByCategory.size() >= getMaxCorrectionsByCategory()) {
+                throw new IllegalArgumentException("Too many corrections by category");
+            }
+            mCorrectionsByCategory.put(category, correction);
+            return this;
         }
 
         /**
@@ -191,7 +511,8 @@
             if (mCurveLux == null || mCurveNits == null) {
                 throw new IllegalStateException("A curve must be set!");
             }
-            return new BrightnessConfiguration(mCurveLux, mCurveNits, mDescription);
+            return new BrightnessConfiguration(mCurveLux, mCurveNits, mCorrectionsByPackageName,
+                    mCorrectionsByCategory, mDescription);
         }
 
         private static void checkMonotonic(float[] vals, boolean strictlyIncreasing, String name) {
diff --git a/core/java/android/hardware/usb/UsbPort.aidl b/core/java/android/hardware/display/BrightnessCorrection.aidl
similarity index 74%
copy from core/java/android/hardware/usb/UsbPort.aidl
copy to core/java/android/hardware/display/BrightnessCorrection.aidl
index b7a7920..3abe29c 100644
--- a/core/java/android/hardware/usb/UsbPort.aidl
+++ b/core/java/android/hardware/display/BrightnessCorrection.aidl
@@ -1,11 +1,11 @@
 /*
- * Copyright (C) 2015, The Android Open Source Project
+ * 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
+ *      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,
@@ -14,6 +14,6 @@
  * limitations under the License.
  */
 
-package android.hardware.usb;
+package android.hardware.display;
 
-parcelable UsbPort;
+parcelable BrightnessCorrection;
diff --git a/core/java/android/hardware/display/BrightnessCorrection.java b/core/java/android/hardware/display/BrightnessCorrection.java
new file mode 100644
index 0000000..c4e0e3b
--- /dev/null
+++ b/core/java/android/hardware/display/BrightnessCorrection.java
@@ -0,0 +1,245 @@
+/*
+ * 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 android.hardware.display;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.MathUtils;
+
+import com.android.internal.util.XmlUtils;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.IOException;
+
+/**
+ * BrightnessCorrection encapsulates a correction to the brightness, without comitting to the
+ * actual correction scheme.
+ * It is used by the BrightnessConfiguration, which maps context (e.g. the foreground app's package
+ * name and category) to corrections that need to be applied to the brightness within that context.
+ * Corrections are currently done by the app that has the top activity of the focused stack, either
+ * by its package name, or (if its package name is not mapped to any correction) by its category.
+ *
+ * @hide
+ */
+@SystemApi
+public final class BrightnessCorrection implements Parcelable {
+
+    private static final int SCALE_AND_TRANSLATE_LOG = 1;
+
+    private static final String TAG_SCALE_AND_TRANSLATE_LOG = "scale-and-translate-log";
+
+    private BrightnessCorrectionImplementation mImplementation;
+
+    // Parcelable classes must be final, and protected methods are not allowed in APIs, so we can't
+    // make this class abstract and use composition instead of inheritence.
+    private BrightnessCorrection(BrightnessCorrectionImplementation implementation) {
+        mImplementation = implementation;
+    }
+
+    /**
+     * Creates a BrightnessCorrection that given {@code brightness}, corrects it to be
+     * {@code exp(scale * log(brightness) + translate)}.
+     *
+     * @param scale
+     *      How much to scale the log brightness.
+     * @param translate
+     *      How much to translate the log brightness.
+     *
+     * @return A BrightnessCorrection that given {@code brightness}, corrects it to be
+     * {@code exp(scale * log(brightness) + translate)}.
+     *
+     * @throws IllegalArgumentException
+     *      - scale or translate are NaN.
+     */
+    @NonNull
+    public static BrightnessCorrection createScaleAndTranslateLog(float scale, float translate) {
+        BrightnessCorrectionImplementation implementation =
+                new ScaleAndTranslateLog(scale, translate);
+        return new BrightnessCorrection(implementation);
+    }
+
+    /**
+     * Applies the brightness correction to a given brightness.
+     *
+     * @param brightness
+     *      The brightness.
+     *
+     * @return The corrected brightness.
+     */
+    public float apply(float brightness) {
+        return mImplementation.apply(brightness);
+    }
+
+    /**
+     * Returns a string representation.
+     *
+     * @return A string representation.
+     */
+    public String toString() {
+        return mImplementation.toString();
+    }
+
+    public static final Creator<BrightnessCorrection> CREATOR =
+            new Creator<BrightnessCorrection>() {
+                public BrightnessCorrection createFromParcel(Parcel in) {
+                    final int type = in.readInt();
+                    switch (type) {
+                        case SCALE_AND_TRANSLATE_LOG:
+                            return ScaleAndTranslateLog.readFromParcel(in);
+                    }
+                    return null;
+                }
+
+                public BrightnessCorrection[] newArray(int size) {
+                    return new BrightnessCorrection[size];
+                }
+            };
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        mImplementation.writeToParcel(dest);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    /**
+     * Writes the correction to an XML serializer.
+     *
+     * @param serializer
+     *      The XML serializer.
+     *
+     * @hide
+     */
+    public void saveToXml(XmlSerializer serializer) throws IOException {
+        mImplementation.saveToXml(serializer);
+    }
+
+    /**
+     * Read a correction from an XML parser.
+     *
+     * @param parser
+     *      The XML parser.
+     *
+     * @throws IOException
+     *      The parser failed to read the XML file.
+     * @throws XmlPullParserException
+     *      The parser failed to parse the XML file.
+     *
+     * @hide
+     */
+    public static BrightnessCorrection loadFromXml(XmlPullParser parser) throws IOException,
+            XmlPullParserException {
+        final int depth = parser.getDepth();
+        while (XmlUtils.nextElementWithin(parser, depth)) {
+            if (TAG_SCALE_AND_TRANSLATE_LOG.equals(parser.getName())) {
+                return ScaleAndTranslateLog.loadFromXml(parser);
+            }
+        }
+        return null;
+    }
+
+    private static float loadFloatFromXml(XmlPullParser parser, String attribute) {
+        final String string = parser.getAttributeValue(null, attribute);
+        try {
+            return Float.parseFloat(string);
+        } catch (NullPointerException | NumberFormatException e) {
+            return Float.NaN;
+        }
+    }
+
+    private interface BrightnessCorrectionImplementation {
+        float apply(float brightness);
+        String toString();
+        void writeToParcel(Parcel dest);
+        void saveToXml(XmlSerializer serializer) throws IOException;
+        // Package-private static methods:
+        // static BrightnessCorrection readFromParcel(Parcel in);
+        // static BrightnessCorrection loadFromXml(XmlPullParser parser) throws IOException,
+        //      XmlPullParserException;
+    }
+
+    /**
+     * A BrightnessCorrection that given {@code brightness}, corrects it to be
+     * {@code exp(scale * log(brightness) + translate)}.
+     */
+    private static class ScaleAndTranslateLog implements BrightnessCorrectionImplementation {
+        private static final float MIN_SCALE = 0.5f;
+        private static final float MAX_SCALE = 2.0f;
+        private static final float MIN_TRANSLATE = -0.6f;
+        private static final float MAX_TRANSLATE = 0.7f;
+
+        private static final String ATTR_SCALE = "scale";
+        private static final String ATTR_TRANSLATE = "translate";
+
+        private final float mScale;
+        private final float mTranslate;
+
+        ScaleAndTranslateLog(float scale, float translate) {
+            if (Float.isNaN(scale) || Float.isNaN(translate)) {
+                throw new IllegalArgumentException("scale and translate must be numbers");
+            }
+            mScale = MathUtils.constrain(scale, MIN_SCALE, MAX_SCALE);
+            mTranslate = MathUtils.constrain(translate, MIN_TRANSLATE, MAX_TRANSLATE);
+        }
+
+        @Override
+        public float apply(float brightness) {
+            return MathUtils.exp(mScale * MathUtils.log(brightness) + mTranslate);
+        }
+
+        @Override
+        public String toString() {
+            return "ScaleAndTranslateLog(" + mScale + ", " + mTranslate + ")";
+        }
+
+        @Override
+        public void writeToParcel(Parcel dest) {
+            dest.writeInt(SCALE_AND_TRANSLATE_LOG);
+            dest.writeFloat(mScale);
+            dest.writeFloat(mTranslate);
+        }
+
+        @Override
+        public void saveToXml(XmlSerializer serializer) throws IOException {
+            serializer.startTag(null, TAG_SCALE_AND_TRANSLATE_LOG);
+            serializer.attribute(null, ATTR_SCALE, Float.toString(mScale));
+            serializer.attribute(null, ATTR_TRANSLATE, Float.toString(mTranslate));
+            serializer.endTag(null, TAG_SCALE_AND_TRANSLATE_LOG);
+        }
+
+        static BrightnessCorrection readFromParcel(Parcel in) {
+            float scale = in.readFloat();
+            float translate = in.readFloat();
+            return BrightnessCorrection.createScaleAndTranslateLog(scale, translate);
+        }
+
+        static BrightnessCorrection loadFromXml(XmlPullParser parser) throws IOException,
+                XmlPullParserException {
+            final float scale = loadFloatFromXml(parser, ATTR_SCALE);
+            final float translate = loadFloatFromXml(parser, ATTR_TRANSLATE);
+            return BrightnessCorrection.createScaleAndTranslateLog(scale, translate);
+        }
+    }
+}
diff --git a/core/java/android/hardware/usb/IUsbManager.aidl b/core/java/android/hardware/usb/IUsbManager.aidl
index f4e776c..edc3f94 100644
--- a/core/java/android/hardware/usb/IUsbManager.aidl
+++ b/core/java/android/hardware/usb/IUsbManager.aidl
@@ -20,7 +20,7 @@
 import android.content.ComponentName;
 import android.hardware.usb.UsbAccessory;
 import android.hardware.usb.UsbDevice;
-import android.hardware.usb.UsbPort;
+import android.hardware.usb.ParcelableUsbPort;
 import android.hardware.usb.UsbPortStatus;
 import android.os.Bundle;
 import android.os.ParcelFileDescriptor;
@@ -112,7 +112,7 @@
     ParcelFileDescriptor getControlFd(long function);
 
     /* Gets the list of USB ports. */
-    UsbPort[] getPorts();
+    List<ParcelableUsbPort> getPorts();
 
     /* Gets the status of the specified USB port. */
     UsbPortStatus getPortStatus(in String portId);
diff --git a/core/java/android/hardware/usb/UsbPort.aidl b/core/java/android/hardware/usb/ParcelableUsbPort.aidl
similarity index 87%
rename from core/java/android/hardware/usb/UsbPort.aidl
rename to core/java/android/hardware/usb/ParcelableUsbPort.aidl
index b7a7920..4431551 100644
--- a/core/java/android/hardware/usb/UsbPort.aidl
+++ b/core/java/android/hardware/usb/ParcelableUsbPort.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2015, The Android Open Source Project
+ * 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.
@@ -16,4 +16,4 @@
 
 package android.hardware.usb;
 
-parcelable UsbPort;
+parcelable ParcelableUsbPort;
diff --git a/core/java/android/hardware/usb/ParcelableUsbPort.java b/core/java/android/hardware/usb/ParcelableUsbPort.java
new file mode 100644
index 0000000..7f7ba96
--- /dev/null
+++ b/core/java/android/hardware/usb/ParcelableUsbPort.java
@@ -0,0 +1,87 @@
+/*
+ * 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 android.hardware.usb;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.annotations.Immutable;
+
+/**
+ * A parcelable wrapper to send UsbPorts over binders.
+ *
+ * @hide
+ */
+@Immutable
+public final class ParcelableUsbPort implements Parcelable {
+    private final @NonNull String mId;
+    private final int mSupportedModes;
+
+    private ParcelableUsbPort(@NonNull String id, int supportedModes) {
+        mId = id;
+        mSupportedModes = supportedModes;
+    }
+
+    /**
+     * Create the parcelable version of a {@link UsbPort}.
+     *
+     * @param port The port to create a parcealable version of
+     *
+     * @return The parcelable version of the port
+     */
+    public static @NonNull ParcelableUsbPort of(@NonNull UsbPort port) {
+        return new ParcelableUsbPort(port.getId(), port.getSupportedModes());
+    }
+
+    /**
+     * Create a {@link UsbPort} from this object.
+     *
+     * @param usbManager A link to the usbManager in the current context
+     *
+     * @return The UsbPort for this object
+     */
+    public @NonNull UsbPort getUsbPort(@NonNull UsbManager usbManager) {
+        return new UsbPort(usbManager, mId, mSupportedModes);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeString(mId);
+        dest.writeInt(mSupportedModes);
+    }
+
+    public static final Creator<ParcelableUsbPort> CREATOR =
+            new Creator<ParcelableUsbPort>() {
+                @Override
+                public ParcelableUsbPort createFromParcel(Parcel in) {
+                    String id = in.readString();
+                    int supportedModes = in.readInt();
+                    return new ParcelableUsbPort(id, supportedModes);
+                }
+
+                @Override
+                public ParcelableUsbPort[] newArray(int size) {
+                    return new ParcelableUsbPort[size];
+                }
+            };
+}
diff --git a/core/java/android/hardware/usb/UsbManager.java b/core/java/android/hardware/usb/UsbManager.java
index 4111941..6014478 100644
--- a/core/java/android/hardware/usb/UsbManager.java
+++ b/core/java/android/hardware/usb/UsbManager.java
@@ -18,6 +18,7 @@
 package android.hardware.usb;
 
 import android.Manifest;
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.RequiresFeature;
 import android.annotation.RequiresPermission;
@@ -39,9 +40,10 @@
 import android.os.RemoteException;
 import android.util.Log;
 
-import com.android.internal.util.Preconditions;
-
+import java.util.ArrayList;
+import java.util.Collections;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 import java.util.StringJoiner;
 
@@ -97,15 +99,11 @@
      * Broadcast Action: A broadcast for USB port changes.
      *
      * This intent is sent when a USB port is added, removed, or changes state.
-     * <ul>
-     * <li> {@link #EXTRA_PORT} containing the {@link android.hardware.usb.UsbPort}
-     * for the port.
-     * <li> {@link #EXTRA_PORT_STATUS} containing the {@link android.hardware.usb.UsbPortStatus}
-     * for the port, or null if the port has been removed
-     * </ul>
      *
      * @hide
      */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.MANAGE_USB)
     public static final String ACTION_USB_PORT_CHANGED =
             "android.hardware.usb.action.USB_PORT_CHANGED";
 
@@ -796,34 +794,44 @@
      * device class (which supports all types of ports despite its name).
      * </p>
      *
-     * @return The list of USB ports, or null if none.
+     * @return The list of USB ports
      *
      * @hide
      */
-    @UnsupportedAppUsage
-    public UsbPort[] getPorts() {
+    @SystemApi
+    @RequiresPermission(Manifest.permission.MANAGE_USB)
+    public @NonNull List<UsbPort> getPorts() {
         if (mService == null) {
-            return null;
+            return Collections.emptyList();
         }
+
+        List<ParcelableUsbPort> parcelablePorts;
         try {
-            return mService.getPorts();
+            parcelablePorts = mService.getPorts();
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
+
+        if (parcelablePorts == null) {
+            return Collections.emptyList();
+        } else {
+            int numPorts = parcelablePorts.size();
+
+            ArrayList<UsbPort> ports = new ArrayList<>(numPorts);
+            for (int i = 0; i < numPorts; i++) {
+                ports.add(parcelablePorts.get(i).getUsbPort(this));
+            }
+
+            return ports;
+        }
     }
 
     /**
-     * Gets the status of the specified USB port.
-     *
-     * @param port The port to query.
-     * @return The status of the specified USB port, or null if unknown.
+     * Should only be called by {@link UsbPort#getStatus}.
      *
      * @hide
      */
-    @UnsupportedAppUsage
-    public UsbPortStatus getPortStatus(UsbPort port) {
-        Preconditions.checkNotNull(port, "port must not be null");
-
+    UsbPortStatus getPortStatus(UsbPort port) {
         try {
             return mService.getPortStatus(port.getId());
         } catch (RemoteException e) {
@@ -832,29 +840,11 @@
     }
 
     /**
-     * Sets the desired role combination of the port.
-     * <p>
-     * The supported role combinations depend on what is connected to the port and may be
-     * determined by consulting
-     * {@link UsbPortStatus#isRoleCombinationSupported UsbPortStatus.isRoleCombinationSupported}.
-     * </p><p>
-     * Note: This function is asynchronous and may fail silently without applying
-     * the requested changes.  If this function does cause a status change to occur then
-     * a {@link #ACTION_USB_PORT_CHANGED} broadcast will be sent.
-     * </p>
-     *
-     * @param powerRole The desired power role: {@link UsbPort#POWER_ROLE_SOURCE}
-     * or {@link UsbPort#POWER_ROLE_SINK}, or 0 if no power role.
-     * @param dataRole The desired data role: {@link UsbPort#DATA_ROLE_HOST}
-     * or {@link UsbPort#DATA_ROLE_DEVICE}, or 0 if no data role.
+     * Should only be called by {@link UsbPort#setRoles}.
      *
      * @hide
      */
-    @UnsupportedAppUsage
-    public void setPortRoles(UsbPort port, int powerRole, int dataRole) {
-        Preconditions.checkNotNull(port, "port must not be null");
-        UsbPort.checkRoles(powerRole, dataRole);
-
+    void setPortRoles(UsbPort port, int powerRole, int dataRole) {
         Log.d(TAG, "setPortRoles Package:" + mContext.getPackageName());
         try {
             mService.setPortRoles(port.getId(), powerRole, dataRole);
diff --git a/core/java/android/hardware/usb/UsbPort.java b/core/java/android/hardware/usb/UsbPort.java
index afdb202..37154e4 100644
--- a/core/java/android/hardware/usb/UsbPort.java
+++ b/core/java/android/hardware/usb/UsbPort.java
@@ -16,104 +16,53 @@
 
 package android.hardware.usb;
 
+import static android.hardware.usb.UsbPortStatus.DATA_ROLE_DEVICE;
+import static android.hardware.usb.UsbPortStatus.DATA_ROLE_HOST;
+import static android.hardware.usb.UsbPortStatus.DATA_ROLE_NONE;
+import static android.hardware.usb.UsbPortStatus.MODE_AUDIO_ACCESSORY;
+import static android.hardware.usb.UsbPortStatus.MODE_DEBUG_ACCESSORY;
+import static android.hardware.usb.UsbPortStatus.MODE_DFP;
+import static android.hardware.usb.UsbPortStatus.MODE_DUAL;
+import static android.hardware.usb.UsbPortStatus.MODE_NONE;
+import static android.hardware.usb.UsbPortStatus.MODE_UFP;
+import static android.hardware.usb.UsbPortStatus.POWER_ROLE_NONE;
+import static android.hardware.usb.UsbPortStatus.POWER_ROLE_SINK;
+import static android.hardware.usb.UsbPortStatus.POWER_ROLE_SOURCE;
+
+import android.Manifest;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
 import android.hardware.usb.V1_0.Constants;
-import android.os.Parcel;
-import android.os.Parcelable;
 
 import com.android.internal.util.Preconditions;
 
 /**
  * Represents a physical USB port and describes its characteristics.
- * <p>
- * This object is immutable.
- * </p>
  *
  * @hide
  */
-public final class UsbPort implements Parcelable {
+@SystemApi
+public final class UsbPort {
     private final String mId;
     private final int mSupportedModes;
-
-    public static final int MODE_NONE = Constants.PortMode.NONE;
-    /**
-     * Mode bit: This USB port can act as a downstream facing port (host).
-     * <p>
-     * Implies that the port supports the {@link #POWER_ROLE_SOURCE} and {@link #DATA_ROLE_HOST}
-     * combination of roles (and possibly others as well).
-     * </p>
-     */
-    public static final int MODE_DFP = Constants.PortMode.DFP;
-
-    /**
-     * Mode bit: This USB port can act as an upstream facing port (device).
-     * <p>
-     * Implies that the port supports the {@link #POWER_ROLE_SINK} and {@link #DATA_ROLE_DEVICE}
-     * combination of roles (and possibly others as well).
-     * </p>
-     */
-    public static final int MODE_UFP = Constants.PortMode.UFP;
-
-    /**
-     * Mode bit: This USB port can act either as an downstream facing port (host) or as
-     * an upstream facing port (device).
-     * <p>
-     * Implies that the port supports the {@link #POWER_ROLE_SOURCE} and {@link #DATA_ROLE_HOST}
-     * combination of roles and the {@link #POWER_ROLE_SINK} and {@link #DATA_ROLE_DEVICE}
-     * combination of roles (and possibly others as well).
-     * </p>
-     */
-    public static final int MODE_DUAL = Constants.PortMode.DRP;
-
-    /**
-     * Mode bit: This USB port can support USB Type-C Audio accessory.
-     */
-    public static final int MODE_AUDIO_ACCESSORY =
-            android.hardware.usb.V1_1.Constants.PortMode_1_1.AUDIO_ACCESSORY;
-
-    /**
-     * Mode bit: This USB port can support USB Type-C debug accessory.
-     */
-    public static final int MODE_DEBUG_ACCESSORY =
-            android.hardware.usb.V1_1.Constants.PortMode_1_1.DEBUG_ACCESSORY;
-
-    /**
-     * Power role: This USB port does not have a power role.
-     */
-    public static final int POWER_ROLE_NONE = Constants.PortPowerRole.NONE;
-
-    /**
-     * Power role: This USB port can act as a source (provide power).
-     */
-    public static final int POWER_ROLE_SOURCE = Constants.PortPowerRole.SOURCE;
-
-    /**
-     * Power role: This USB port can act as a sink (receive power).
-     */
-    public static final int POWER_ROLE_SINK = Constants.PortPowerRole.SINK;
-
-    /**
-     * Power role: This USB port does not have a data role.
-     */
-    public static final int DATA_ROLE_NONE = Constants.PortDataRole.NONE;
-
-    /**
-     * Data role: This USB port can act as a host (access data services).
-     */
-    public static final int DATA_ROLE_HOST = Constants.PortDataRole.HOST;
-
-    /**
-     * Data role: This USB port can act as a device (offer data services).
-     */
-    public static final int DATA_ROLE_DEVICE = Constants.PortDataRole.DEVICE;
+    private final UsbManager mUsbManager;
 
     private static final int NUM_DATA_ROLES = Constants.PortDataRole.NUM_DATA_ROLES;
+
     /**
      * Points to the first power role in the IUsb HAL.
      */
     private static final int POWER_ROLE_OFFSET = Constants.PortPowerRole.NONE;
 
     /** @hide */
-    public UsbPort(String id, int supportedModes) {
+    public UsbPort(@NonNull UsbManager usbManager, @NonNull String id, int supportedModes) {
+        Preconditions.checkNotNull(id);
+        Preconditions.checkFlagsArgument(supportedModes,
+                MODE_DFP | MODE_UFP | MODE_AUDIO_ACCESSORY | MODE_DEBUG_ACCESSORY);
+
+        mUsbManager = usbManager;
         mId = id;
         mSupportedModes = supportedModes;
     }
@@ -122,6 +71,8 @@
      * Gets the unique id of the port.
      *
      * @return The unique id of the port; not intended for display.
+     *
+     * @hide
      */
     public String getId() {
         return mId;
@@ -133,23 +84,62 @@
      * The actual mode of the port may vary depending on what is plugged into it.
      * </p>
      *
-     * @return The supported modes: one of {@link #MODE_DFP}, {@link #MODE_UFP}, or
-     * {@link #MODE_DUAL}.
+     * @return The supported modes: one of {@link UsbPortStatus#MODE_DFP},
+     * {@link UsbPortStatus#MODE_UFP}, or {@link UsbPortStatus#MODE_DUAL}.
+     *
+     * @hide
      */
     public int getSupportedModes() {
         return mSupportedModes;
     }
 
     /**
+     * Gets the status of this USB port.
+     *
+     * @return The status of the this port, or {@code null} if port is unknown.
+     */
+    @RequiresPermission(Manifest.permission.MANAGE_USB)
+    public @Nullable UsbPortStatus getStatus() {
+        return mUsbManager.getPortStatus(this);
+    }
+
+    /**
+     * Sets the desired role combination of the port.
+     * <p>
+     * The supported role combinations depend on what is connected to the port and may be
+     * determined by consulting
+     * {@link UsbPortStatus#isRoleCombinationSupported UsbPortStatus.isRoleCombinationSupported}.
+     * </p><p>
+     * Note: This function is asynchronous and may fail silently without applying
+     * the requested changes.  If this function does cause a status change to occur then
+     * a {@link UsbManager#ACTION_USB_PORT_CHANGED} broadcast will be sent.
+     * </p>
+     *
+     * @param powerRole The desired power role: {@link UsbPortStatus#POWER_ROLE_SOURCE} or
+     *                  {@link UsbPortStatus#POWER_ROLE_SINK}, or
+     *                  {@link UsbPortStatus#POWER_ROLE_NONE} if no power role.
+     * @param dataRole The desired data role: {@link UsbPortStatus#DATA_ROLE_HOST} or
+     *                 {@link UsbPortStatus#DATA_ROLE_DEVICE}, or
+     *                 {@link UsbPortStatus#DATA_ROLE_NONE} if no data role.
+     */
+    @RequiresPermission(Manifest.permission.MANAGE_USB)
+    public void setRoles(@UsbPortStatus.UsbPowerRole int powerRole,
+            @UsbPortStatus.UsbDataRole int dataRole) {
+        UsbPort.checkRoles(powerRole, dataRole);
+
+        mUsbManager.setPortRoles(this, powerRole, dataRole);
+    }
+
+    /**
      * Combines one power and one data role together into a unique value with
      * exactly one bit set.  This can be used to efficiently determine whether
      * a combination of roles is supported by testing whether that bit is present
      * in a bit-field.
      *
-     * @param powerRole The desired power role: {@link UsbPort#POWER_ROLE_SOURCE}
-     *                  or {@link UsbPort#POWER_ROLE_SINK}, or 0 if no power role.
-     * @param dataRole  The desired data role: {@link UsbPort#DATA_ROLE_HOST}
-     *                  or {@link UsbPort#DATA_ROLE_DEVICE}, or 0 if no data role.
+     * @param powerRole The desired power role: {@link UsbPortStatus#POWER_ROLE_SOURCE}
+     *                  or {@link UsbPortStatus#POWER_ROLE_SINK}, or 0 if no power role.
+     * @param dataRole  The desired data role: {@link UsbPortStatus#DATA_ROLE_HOST}
+     *                  or {@link UsbPortStatus#DATA_ROLE_DEVICE}, or 0 if no data role.
      * @hide
      */
     public static int combineRolesAsBit(int powerRole, int dataRole) {
@@ -276,30 +266,4 @@
     public String toString() {
         return "UsbPort{id=" + mId + ", supportedModes=" + modeToString(mSupportedModes) + "}";
     }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        dest.writeString(mId);
-        dest.writeInt(mSupportedModes);
-    }
-
-    public static final Parcelable.Creator<UsbPort> CREATOR =
-            new Parcelable.Creator<UsbPort>() {
-                @Override
-                public UsbPort createFromParcel(Parcel in) {
-                    String id = in.readString();
-                    int supportedModes = in.readInt();
-                    return new UsbPort(id, supportedModes);
-                }
-
-                @Override
-                public UsbPort[] newArray(int size) {
-                    return new UsbPort[size];
-                }
-            };
 }
diff --git a/core/java/android/hardware/usb/UsbPortStatus.java b/core/java/android/hardware/usb/UsbPortStatus.java
index 2cd8209..d30201a 100644
--- a/core/java/android/hardware/usb/UsbPortStatus.java
+++ b/core/java/android/hardware/usb/UsbPortStatus.java
@@ -16,27 +16,134 @@
 
 package android.hardware.usb;
 
-import android.annotation.UnsupportedAppUsage;
+import android.annotation.IntDef;
+import android.annotation.SystemApi;
+import android.hardware.usb.V1_0.Constants;
 import android.os.Parcel;
 import android.os.Parcelable;
 
+import com.android.internal.annotations.Immutable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
 /**
  * Describes the status of a USB port.
- * <p>
- * This object is immutable.
- * </p>
  *
  * @hide
  */
+@Immutable
+@SystemApi
 public final class UsbPortStatus implements Parcelable {
     private final int mCurrentMode;
-    private final int mCurrentPowerRole;
-    private final int mCurrentDataRole;
+    private final @UsbPowerRole int mCurrentPowerRole;
+    private final @UsbDataRole int mCurrentDataRole;
     private final int mSupportedRoleCombinations;
 
+    /**
+     * Power role: This USB port does not have a power role.
+     */
+    public static final int POWER_ROLE_NONE = Constants.PortPowerRole.NONE;
+
+    /**
+     * Power role: This USB port can act as a source (provide power).
+     */
+    public static final int POWER_ROLE_SOURCE = Constants.PortPowerRole.SOURCE;
+
+    /**
+     * Power role: This USB port can act as a sink (receive power).
+     */
+    public static final int POWER_ROLE_SINK = Constants.PortPowerRole.SINK;
+
+    @IntDef(prefix = { "POWER_ROLE_" }, value = {
+            POWER_ROLE_NONE,
+            POWER_ROLE_SOURCE,
+            POWER_ROLE_SINK
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    @interface UsbPowerRole{}
+
+    /**
+     * Power role: This USB port does not have a data role.
+     */
+    public static final int DATA_ROLE_NONE = Constants.PortDataRole.NONE;
+
+    /**
+     * Data role: This USB port can act as a host (access data services).
+     */
+    public static final int DATA_ROLE_HOST = Constants.PortDataRole.HOST;
+
+    /**
+     * Data role: This USB port can act as a device (offer data services).
+     */
+    public static final int DATA_ROLE_DEVICE = Constants.PortDataRole.DEVICE;
+
+    @IntDef(prefix = { "DATA_ROLE_" }, value = {
+            DATA_ROLE_NONE,
+            DATA_ROLE_HOST,
+            DATA_ROLE_DEVICE
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    @interface UsbDataRole{}
+
+    /**
+     * There is currently nothing connected to this USB port.
+     */
+    public static final int MODE_NONE = Constants.PortMode.NONE;
+
+    /**
+     * This USB port can act as a downstream facing port (host).
+     *
+     * <p> Implies that the port supports the {@link #POWER_ROLE_SOURCE} and
+     * {@link #DATA_ROLE_HOST} combination of roles (and possibly others as well).
+     */
+    public static final int MODE_DFP = Constants.PortMode.DFP;
+
+    /**
+     * This USB port can act as an upstream facing port (device).
+     *
+     * <p> Implies that the port supports the {@link #POWER_ROLE_SINK} and
+     * {@link #DATA_ROLE_DEVICE} combination of roles (and possibly others as well).
+     */
+    public static final int MODE_UFP = Constants.PortMode.UFP;
+
+    /**
+     * This USB port can act either as an downstream facing port (host) or as
+     * an upstream facing port (device).
+     *
+     * <p> Implies that the port supports the {@link #POWER_ROLE_SOURCE} and
+     * {@link #DATA_ROLE_HOST} combination of roles and the {@link #POWER_ROLE_SINK} and
+     * {@link #DATA_ROLE_DEVICE} combination of roles (and possibly others as well).
+     *
+     * @hide
+     */
+    public static final int MODE_DUAL = Constants.PortMode.DRP;
+
+    /**
+     * This USB port can support USB Type-C Audio accessory.
+     */
+    public static final int MODE_AUDIO_ACCESSORY =
+            android.hardware.usb.V1_1.Constants.PortMode_1_1.AUDIO_ACCESSORY;
+
+    /**
+     * This USB port can support USB Type-C debug accessory.
+     */
+    public static final int MODE_DEBUG_ACCESSORY =
+            android.hardware.usb.V1_1.Constants.PortMode_1_1.DEBUG_ACCESSORY;
+
+    @IntDef(prefix = { "MODE_" }, flag = true, value = {
+            MODE_NONE,
+            MODE_DFP,
+            MODE_UFP,
+            MODE_AUDIO_ACCESSORY,
+            MODE_DEBUG_ACCESSORY,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    @interface UsbPortMode{}
+
     /** @hide */
-    public UsbPortStatus(int currentMode, int currentPowerRole, int currentDataRole,
-            int supportedRoleCombinations) {
+    public UsbPortStatus(int currentMode, @UsbPowerRole int currentPowerRole,
+            @UsbDataRole int currentDataRole, int supportedRoleCombinations) {
         mCurrentMode = currentMode;
         mCurrentPowerRole = currentPowerRole;
         mCurrentDataRole = currentDataRole;
@@ -46,9 +153,8 @@
     /**
      * Returns true if there is anything connected to the port.
      *
-     * @return True if there is anything connected to the port.
+     * @return {@code true} iff there is anything connected to the port.
      */
-    @UnsupportedAppUsage
     public boolean isConnected() {
         return mCurrentMode != 0;
     }
@@ -56,33 +162,31 @@
     /**
      * Gets the current mode of the port.
      *
-     * @return The current mode: {@link UsbPort#MODE_DFP}, {@link UsbPort#MODE_UFP},
-     * or 0 if nothing is connected.
+     * @return The current mode: {@link #MODE_DFP}, {@link #MODE_UFP},
+     * {@link #MODE_AUDIO_ACCESSORY}, {@link #MODE_DEBUG_ACCESSORY}, or {@link {@link #MODE_NONE} if
+     * nothing is connected.
      */
-    @UnsupportedAppUsage
-    public int getCurrentMode() {
+    public @UsbPortMode int getCurrentMode() {
         return mCurrentMode;
     }
 
     /**
      * Gets the current power role of the port.
      *
-     * @return The current power role: {@link UsbPort#POWER_ROLE_SOURCE},
-     * {@link UsbPort#POWER_ROLE_SINK}, or 0 if nothing is connected.
+     * @return The current power role: {@link #POWER_ROLE_SOURCE}, {@link #POWER_ROLE_SINK}, or
+     * {@link #POWER_ROLE_NONE} if nothing is connected.
      */
-    @UnsupportedAppUsage
-    public int getCurrentPowerRole() {
+    public @UsbPowerRole int getCurrentPowerRole() {
         return mCurrentPowerRole;
     }
 
     /**
      * Gets the current data role of the port.
      *
-     * @return The current data role: {@link UsbPort#DATA_ROLE_HOST},
-     * {@link UsbPort#DATA_ROLE_DEVICE}, or 0 if nothing is connected.
+     * @return The current data role: {@link #DATA_ROLE_HOST}, {@link #DATA_ROLE_DEVICE}, or
+     * {@link #DATA_ROLE_NONE} if nothing is connected.
      */
-    @UnsupportedAppUsage
-    public int getCurrentDataRole() {
+    public @UsbDataRole int getCurrentDataRole() {
         return mCurrentDataRole;
     }
 
@@ -90,19 +194,20 @@
      * Returns true if the specified power and data role combination is supported
      * given what is currently connected to the port.
      *
-     * @param powerRole The power role to check: {@link UsbPort#POWER_ROLE_SOURCE}
-     * or {@link UsbPort#POWER_ROLE_SINK}, or 0 if no power role.
-     * @param dataRole The data role to check: either {@link UsbPort#DATA_ROLE_HOST}
-     * or {@link UsbPort#DATA_ROLE_DEVICE}, or 0 if no data role.
+     * @param powerRole The power role to check: {@link #POWER_ROLE_SOURCE}  or
+     *                  {@link #POWER_ROLE_SINK}, or {@link #POWER_ROLE_NONE} if no power role.
+     * @param dataRole  The data role to check: either {@link #DATA_ROLE_HOST} or
+     *                  {@link #DATA_ROLE_DEVICE}, or {@link #DATA_ROLE_NONE} if no data role.
      */
-    @UnsupportedAppUsage
-    public boolean isRoleCombinationSupported(int powerRole, int dataRole) {
+    public boolean isRoleCombinationSupported(@UsbPowerRole int powerRole,
+            @UsbDataRole int dataRole) {
         return (mSupportedRoleCombinations &
                 UsbPort.combineRolesAsBit(powerRole, dataRole)) != 0;
     }
 
-    /** @hide */
-    @UnsupportedAppUsage
+    /**
+     * Get the supported role combinations.
+     */
     public int getSupportedRoleCombinations() {
         return mSupportedRoleCombinations;
     }
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/net/InetAddresses.java b/core/java/android/net/InetAddresses.java
new file mode 100644
index 0000000..8e6c69a
--- /dev/null
+++ b/core/java/android/net/InetAddresses.java
@@ -0,0 +1,63 @@
+/*
+ * 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 android.net;
+
+import libcore.net.InetAddressUtils;
+
+import java.net.InetAddress;
+
+/**
+ * Utility methods for {@link InetAddress} implementations.
+ */
+public class InetAddresses {
+
+    private InetAddresses() {}
+
+    /**
+     * Checks to see if the {@code address} is a numeric address (such as {@code "192.0.2.1"} or
+     * {@code "2001:db8::1:2"}).
+     *
+     * <p>A numeric address is either an IPv4 address containing exactly 4 decimal numbers or an
+     * IPv6 numeric address. IPv4 addresses that consist of either hexadecimal or octal digits or
+     * do not have exactly 4 numbers are not treated as numeric.
+     *
+     * <p>This method will never do a DNS lookup.
+     *
+     * @param address the address to parse.
+     * @return true if the supplied address is numeric, false otherwise.
+     */
+    public static boolean isNumericAddress(String address) {
+        return InetAddressUtils.isNumericAddress(address);
+    }
+
+    /**
+     * Returns an InetAddress corresponding to the given numeric address (such
+     * as {@code "192.168.0.1"} or {@code "2001:4860:800d::68"}).
+     *
+     * <p>See {@link #isNumericAddress(String)} (String)} for a definition as to what constitutes a
+     * numeric address.
+     *
+     * <p>This method will never do a DNS lookup.
+     *
+     * @param address the address to parse, must be numeric.
+     * @return an {@link InetAddress} instance corresponding to the address.
+     * @throws IllegalArgumentException if {@code address} is not a numeric address.
+     */
+    public static InetAddress parseNumericAddress(String address) {
+        return InetAddressUtils.parseNumericAddress(address);
+    }
+}
diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java
index 8a5f43d..c41a56c 100644
--- a/core/java/android/net/NetworkCapabilities.java
+++ b/core/java/android/net/NetworkCapabilities.java
@@ -1467,7 +1467,7 @@
             appendStringRepresentationOfBitMaskToStringBuilder(sb, mNetworkCapabilities,
                     NetworkCapabilities::capabilityNameOf, "&");
         }
-        if (0 != mNetworkCapabilities) {
+        if (0 != mUnwantedNetworkCapabilities) {
             sb.append(" Unwanted: ");
             appendStringRepresentationOfBitMaskToStringBuilder(sb, mUnwantedNetworkCapabilities,
                     NetworkCapabilities::capabilityNameOf, "&");
diff --git a/core/java/android/net/NetworkUtils.java b/core/java/android/net/NetworkUtils.java
index c431e40e..4eab49c 100644
--- a/core/java/android/net/NetworkUtils.java
+++ b/core/java/android/net/NetworkUtils.java
@@ -21,6 +21,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.UnsupportedAppUsage;
+import android.os.Build;
 import android.os.Parcel;
 import android.system.Os;
 import android.util.Log;
@@ -299,8 +300,10 @@
      * @param addrString
      * @return the InetAddress
      * @hide
+     * @deprecated Use {@link InetAddresses#parseNumericAddress(String)}, if possible.
      */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+    @Deprecated
     public static InetAddress numericToInetAddress(String addrString)
             throws IllegalArgumentException {
         return InetAddress.parseNumericAddress(addrString);
diff --git a/core/java/android/os/INetworkManagementService.aidl b/core/java/android/os/INetworkManagementService.aidl
index be8cf0e..fdd7488 100644
--- a/core/java/android/os/INetworkManagementService.aidl
+++ b/core/java/android/os/INetworkManagementService.aidl
@@ -390,7 +390,7 @@
     /**
      * Setup a new VPN.
      */
-    void createVirtualNetwork(int netId, boolean hasDNS, boolean secure);
+    void createVirtualNetwork(int netId, boolean secure);
 
     /**
      * Remove a network.
diff --git a/core/java/android/os/NativeHandle.java b/core/java/android/os/NativeHandle.java
index fbecc8e..f7ffc37 100644
--- a/core/java/android/os/NativeHandle.java
+++ b/core/java/android/os/NativeHandle.java
@@ -16,6 +16,8 @@
 
 package android.os;
 
+import static android.system.OsConstants.F_DUPFD_CLOEXEC;
+
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
 import android.system.ErrnoException;
@@ -108,7 +110,10 @@
         FileDescriptor[] fds = new FileDescriptor[mFds.length];
         try {
             for (int i = 0; i < mFds.length; i++) {
-                fds[i] = Os.dup(mFds[i]);
+                FileDescriptor newFd = new FileDescriptor();
+                int fdint = Os.fcntlInt(mFds[i], F_DUPFD_CLOEXEC, 0);
+                newFd.setInt$(fdint);
+                fds[i] = newFd;
             }
         } catch (ErrnoException e) {
             e.rethrowAsIOException();
diff --git a/core/java/android/os/ParcelFileDescriptor.java b/core/java/android/os/ParcelFileDescriptor.java
index 6de1ff4..63912ec 100644
--- a/core/java/android/os/ParcelFileDescriptor.java
+++ b/core/java/android/os/ParcelFileDescriptor.java
@@ -17,7 +17,11 @@
 package android.os;
 
 import static android.system.OsConstants.AF_UNIX;
+import static android.system.OsConstants.F_DUPFD;
+import static android.system.OsConstants.F_DUPFD_CLOEXEC;
+import static android.system.OsConstants.O_CLOEXEC;
 import static android.system.OsConstants.SEEK_SET;
+import static android.system.OsConstants.SOCK_CLOEXEC;
 import static android.system.OsConstants.SOCK_SEQPACKET;
 import static android.system.OsConstants.SOCK_STREAM;
 import static android.system.OsConstants.S_IROTH;
@@ -37,6 +41,7 @@
 import android.util.Log;
 
 import dalvik.system.CloseGuard;
+import dalvik.system.VMRuntime;
 
 import libcore.io.IoUtils;
 import libcore.io.Memory;
@@ -293,7 +298,7 @@
     }
 
     private static FileDescriptor openInternal(File file, int mode) throws FileNotFoundException {
-        final int flags = FileUtils.translateModePfdToPosix(mode);
+        final int flags = FileUtils.translateModePfdToPosix(mode) | ifAtLeastQ(O_CLOEXEC);
 
         int realMode = S_IRWXU | S_IRWXG;
         if ((mode & MODE_WORLD_READABLE) != 0) realMode |= S_IROTH;
@@ -315,7 +320,9 @@
      */
     public static ParcelFileDescriptor dup(FileDescriptor orig) throws IOException {
         try {
-            final FileDescriptor fd = Os.dup(orig);
+            final FileDescriptor fd = new FileDescriptor();
+            int intfd = Os.fcntlInt(orig, (isAtLeastQ() ? F_DUPFD_CLOEXEC : F_DUPFD), 0);
+            fd.setInt$(intfd);
             return new ParcelFileDescriptor(fd);
         } catch (ErrnoException e) {
             throw e.rethrowAsIOException();
@@ -351,7 +358,9 @@
         original.setInt$(fd);
 
         try {
-            final FileDescriptor dup = Os.dup(original);
+            final FileDescriptor dup = new FileDescriptor();
+            int intfd = Os.fcntlInt(original, (isAtLeastQ() ? F_DUPFD_CLOEXEC : F_DUPFD), 0);
+            dup.setInt$(intfd);
             return new ParcelFileDescriptor(dup);
         } catch (ErrnoException e) {
             throw e.rethrowAsIOException();
@@ -413,7 +422,7 @@
      */
     public static ParcelFileDescriptor[] createPipe() throws IOException {
         try {
-            final FileDescriptor[] fds = Os.pipe();
+            final FileDescriptor[] fds = Os.pipe2(ifAtLeastQ(O_CLOEXEC));
             return new ParcelFileDescriptor[] {
                     new ParcelFileDescriptor(fds[0]),
                     new ParcelFileDescriptor(fds[1]) };
@@ -435,7 +444,7 @@
     public static ParcelFileDescriptor[] createReliablePipe() throws IOException {
         try {
             final FileDescriptor[] comm = createCommSocketPair();
-            final FileDescriptor[] fds = Os.pipe();
+            final FileDescriptor[] fds = Os.pipe2(ifAtLeastQ(O_CLOEXEC));
             return new ParcelFileDescriptor[] {
                     new ParcelFileDescriptor(fds[0], comm[0]),
                     new ParcelFileDescriptor(fds[1], comm[1]) };
@@ -459,7 +468,7 @@
         try {
             final FileDescriptor fd0 = new FileDescriptor();
             final FileDescriptor fd1 = new FileDescriptor();
-            Os.socketpair(AF_UNIX, type, 0, fd0, fd1);
+            Os.socketpair(AF_UNIX, type | ifAtLeastQ(SOCK_CLOEXEC), 0, fd0, fd1);
             return new ParcelFileDescriptor[] {
                     new ParcelFileDescriptor(fd0),
                     new ParcelFileDescriptor(fd1) };
@@ -489,7 +498,7 @@
             final FileDescriptor[] comm = createCommSocketPair();
             final FileDescriptor fd0 = new FileDescriptor();
             final FileDescriptor fd1 = new FileDescriptor();
-            Os.socketpair(AF_UNIX, type, 0, fd0, fd1);
+            Os.socketpair(AF_UNIX, type | ifAtLeastQ(SOCK_CLOEXEC), 0, fd0, fd1);
             return new ParcelFileDescriptor[] {
                     new ParcelFileDescriptor(fd0, comm[0]),
                     new ParcelFileDescriptor(fd1, comm[1]) };
@@ -505,7 +514,7 @@
             // across multiple IO operations.
             final FileDescriptor comm1 = new FileDescriptor();
             final FileDescriptor comm2 = new FileDescriptor();
-            Os.socketpair(AF_UNIX, SOCK_SEQPACKET, 0, comm1, comm2);
+            Os.socketpair(AF_UNIX, SOCK_SEQPACKET | ifAtLeastQ(SOCK_CLOEXEC), 0, comm1, comm2);
             IoUtils.setBlocking(comm1, false);
             IoUtils.setBlocking(comm2, false);
             return new FileDescriptor[] { comm1, comm2 };
@@ -1111,4 +1120,12 @@
             return "{" + status + ": " + msg + "}";
         }
     }
+
+    private static boolean isAtLeastQ() {
+        return (VMRuntime.getRuntime().getTargetSdkVersion() >= Build.VERSION_CODES.Q);
+    }
+
+    private static int ifAtLeastQ(int value) {
+        return isAtLeastQ() ? value : 0;
+    }
 }
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index 8b1d3c6..9594a71 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -132,6 +132,8 @@
     public static final String PROP_VIRTUAL_DISK = "persist.sys.virtual_disk";
     /** {@hide} */
     public static final String PROP_ISOLATED_STORAGE = "persist.sys.isolated_storage";
+    /** {@hide} */
+    public static final String PROP_ISOLATED_STORAGE_SNAPSHOT = "sys.isolated_storage_snapshot";
 
     /** {@hide} */
     public static final String PROP_FORCE_AUDIO = "persist.fw.force_audio";
@@ -1540,7 +1542,9 @@
     /** {@hide} */
     @TestApi
     public static boolean hasIsolatedStorage() {
-        return SystemProperties.getBoolean(PROP_ISOLATED_STORAGE, false);
+        // Prefer to use snapshot for current boot when available
+        return SystemProperties.getBoolean(PROP_ISOLATED_STORAGE_SNAPSHOT,
+                SystemProperties.getBoolean(PROP_ISOLATED_STORAGE, false));
     }
 
     /**
diff --git a/core/java/android/provider/MediaStore.java b/core/java/android/provider/MediaStore.java
index f38f740..457453f 100644
--- a/core/java/android/provider/MediaStore.java
+++ b/core/java/android/provider/MediaStore.java
@@ -1002,6 +1002,7 @@
         public static final int MICRO_KIND = 3;
 
         public static final Point MINI_SIZE = new Point(512, 384);
+        public static final Point FULL_SCREEN_SIZE = new Point(1024, 786);
         public static final Point MICRO_SIZE = new Point(96, 96);
     }
 
@@ -1127,6 +1128,8 @@
             final Point size;
             if (kind == ThumbnailConstants.MICRO_KIND) {
                 size = ThumbnailConstants.MICRO_SIZE;
+            } else if (kind == ThumbnailConstants.FULL_SCREEN_KIND) {
+                size = ThumbnailConstants.FULL_SCREEN_SIZE;
             } else if (kind == ThumbnailConstants.MINI_KIND) {
                 size = ThumbnailConstants.MINI_SIZE;
             } else {
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 299db73..40f1946 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -14260,6 +14260,44 @@
         }
     }
 
+    /**
+     * <p>
+     *     A Settings panel is floating UI that contains a fixed subset of settings to address a
+     *     particular user problem. For example, the
+     *     {@link #ACTION_INTERNET_CONNECTIVITY Internet Panel} surfaces settings related to
+     *     connecting to the internet.
+     * <p>
+     *     Settings panels appear above the calling app to address the problem without
+     *     the user needing to open Settings and thus leave their current screen.
+     */
+    public static final class Panel {
+        private Panel() {
+        }
+
+        /**
+         * Activity Action: Show a settings dialog containing settings to enable internet
+         * connection.
+         * <p>
+         * Input: Nothing.
+         * <p>
+         * Output: Nothing.
+         */
+        @SdkConstant(SdkConstant.SdkConstantType.ACTIVITY_INTENT_ACTION)
+        public static final String ACTION_INTERNET_CONNECTIVITY =
+                "android.settings.panel.action.INTERNET_CONNECTIVITY";
+
+        /**
+         * Activity Action: Show a settings dialog containing all volume streams.
+         * <p>
+         * Input: Nothing.
+         * <p>
+         * Output: Nothing.
+         */
+        @SdkConstant(SdkConstant.SdkConstantType.ACTIVITY_INTENT_ACTION)
+        public static final String ACTION_VOLUME =
+                "android.settings.panel.action.VOLUME";
+    }
+
     private static final String[] PM_WRITE_SETTINGS = {
         android.Manifest.permission.WRITE_SETTINGS
     };
diff --git a/core/java/android/service/contentcapture/ContentCaptureEventsRequest.java b/core/java/android/service/contentcapture/ContentCaptureEventsRequest.java
index df58f52..028180d 100644
--- a/core/java/android/service/contentcapture/ContentCaptureEventsRequest.java
+++ b/core/java/android/service/contentcapture/ContentCaptureEventsRequest.java
@@ -17,6 +17,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
+import android.content.pm.ParceledListSlice;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.view.contentcapture.ContentCaptureEvent;
@@ -31,10 +32,10 @@
 @SystemApi
 public final class ContentCaptureEventsRequest implements Parcelable {
 
-    private final List<ContentCaptureEvent> mEvents;
+    private final ParceledListSlice<ContentCaptureEvent> mEvents;
 
     /** @hide */
-    public ContentCaptureEventsRequest(@NonNull List<ContentCaptureEvent> events) {
+    public ContentCaptureEventsRequest(@NonNull ParceledListSlice<ContentCaptureEvent> events) {
         mEvents = events;
     }
 
@@ -43,7 +44,7 @@
      */
     @NonNull
     public List<ContentCaptureEvent> getEvents() {
-        return mEvents;
+        return mEvents.getList();
     }
 
     @Override
@@ -53,7 +54,7 @@
 
     @Override
     public void writeToParcel(Parcel parcel, int flags) {
-        parcel.writeTypedList(mEvents, flags);
+        parcel.writeParcelable(mEvents, flags);
     }
 
     public static final Parcelable.Creator<ContentCaptureEventsRequest> CREATOR =
@@ -61,8 +62,7 @@
 
         @Override
         public ContentCaptureEventsRequest createFromParcel(Parcel parcel) {
-            return new ContentCaptureEventsRequest(parcel
-                    .createTypedArrayList(ContentCaptureEvent.CREATOR));
+            return new ContentCaptureEventsRequest(parcel.readParcelable(null));
         }
 
         @Override
diff --git a/core/java/android/service/contentcapture/ContentCaptureService.java b/core/java/android/service/contentcapture/ContentCaptureService.java
index 58848fc..953ccf1 100644
--- a/core/java/android/service/contentcapture/ContentCaptureService.java
+++ b/core/java/android/service/contentcapture/ContentCaptureService.java
@@ -24,15 +24,27 @@
 import android.app.Service;
 import android.content.ComponentName;
 import android.content.Intent;
+import android.content.pm.ParceledListSlice;
+import android.os.Binder;
+import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.RemoteException;
+import android.util.ArrayMap;
 import android.util.Log;
+import android.util.Slog;
 import android.view.contentcapture.ContentCaptureContext;
 import android.view.contentcapture.ContentCaptureEvent;
+import android.view.contentcapture.ContentCaptureManager;
+import android.view.contentcapture.ContentCaptureSession;
 import android.view.contentcapture.ContentCaptureSessionId;
+import android.view.contentcapture.IContentCaptureDirectManager;
 
+import com.android.internal.os.IResultReceiver;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
 import java.util.List;
 import java.util.Set;
 
@@ -63,29 +75,16 @@
 
     private Handler mHandler;
 
-    private final IContentCaptureService mInterface = new IContentCaptureService.Stub() {
+    /**
+     * Binder that receives calls from the system server.
+     */
+    private final IContentCaptureService mServerInterface = new IContentCaptureService.Stub() {
 
         @Override
-        public void onSessionLifecycle(ContentCaptureContext context, String sessionId)
-                throws RemoteException {
-            if (context != null) {
-                mHandler.sendMessage(
-                        obtainMessage(ContentCaptureService::handleOnCreateSession,
-                                ContentCaptureService.this, context, sessionId));
-            } else {
-                mHandler.sendMessage(
-                        obtainMessage(ContentCaptureService::handleOnDestroySession,
-                                ContentCaptureService.this, sessionId));
-            }
-        }
-
-        @Override
-        public void onContentCaptureEventsRequest(String sessionId,
-                ContentCaptureEventsRequest request) {
-            mHandler.sendMessage(
-                    obtainMessage(ContentCaptureService::handleOnContentCaptureEventsRequest,
-                            ContentCaptureService.this, sessionId, request));
-
+        public void onSessionStarted(ContentCaptureContext context, String sessionId, int uid,
+                IResultReceiver clientReceiver) {
+            mHandler.sendMessage(obtainMessage(ContentCaptureService::handleOnCreateSession,
+                    ContentCaptureService.this, context, sessionId, uid, clientReceiver));
         }
 
         @Override
@@ -94,8 +93,36 @@
                     obtainMessage(ContentCaptureService::handleOnActivitySnapshot,
                             ContentCaptureService.this, sessionId, snapshotData));
         }
+
+        @Override
+        public void onSessionFinished(String sessionId) {
+            mHandler.sendMessage(obtainMessage(ContentCaptureService::handleFinishSession,
+                    ContentCaptureService.this, sessionId));
+        }
     };
 
+    /**
+     * Binder that receives calls from the app.
+     */
+    private final IContentCaptureDirectManager mClientInterface =
+            new IContentCaptureDirectManager.Stub() {
+
+        @Override
+        public void sendEvents(String sessionId,
+                @SuppressWarnings("rawtypes") ParceledListSlice events) {
+            mHandler.sendMessage(obtainMessage(ContentCaptureService::handleSendEvents,
+                            ContentCaptureService.this, sessionId, Binder.getCallingUid(), events));
+        }
+    };
+
+    /**
+     * List of sessions per UID.
+     *
+     * <p>This map is populated when an session is started, which is called by the system server
+     * and can be trusted. Then subsequent calls made by the app are verified against this map.
+     */
+    private final ArrayMap<String, Integer> mSessionsByUid = new ArrayMap<>();
+
     @CallSuper
     @Override
     public void onCreate() {
@@ -107,7 +134,7 @@
     @Override
     public final IBinder onBind(Intent intent) {
         if (SERVICE_INTERFACE.equals(intent.getAction())) {
-            return mInterface.asBinder();
+            return mServerInterface.asBinder();
         }
         Log.w(TAG, "Tried to bind to wrong intent (should be " + SERVICE_INTERFACE + ": " + intent);
         return null;
@@ -196,8 +223,10 @@
      * @param sessionId the session's Id
      * @param request the events
      */
-    public abstract void onContentCaptureEventsRequest(@NonNull ContentCaptureSessionId sessionId,
-            @NonNull ContentCaptureEventsRequest request);
+    public void onContentCaptureEventsRequest(@NonNull ContentCaptureSessionId sessionId,
+            @NonNull ContentCaptureEventsRequest request) {
+        if (VERBOSE) Log.v(TAG, "onContentCaptureEventsRequest(id=" + sessionId + ")");
+    }
 
     /**
      * Notifies the service of {@link SnapshotData snapshot data} associated with a session.
@@ -212,10 +241,22 @@
      * Destroys the content capture session.
      *
      * @param sessionId the id of the session to destroy
-     */
+     * */
     public void onDestroyContentCaptureSession(@NonNull ContentCaptureSessionId sessionId) {
-        if (VERBOSE) {
-            Log.v(TAG, "onDestroyContentCaptureSession(id=" + sessionId + ")");
+        if (VERBOSE) Log.v(TAG, "onDestroyContentCaptureSession(id=" + sessionId + ")");
+    }
+
+    @Override
+    @CallSuper
+    protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        final int size = mSessionsByUid.size();
+        pw.print("Number sessions: "); pw.println(size);
+        if (size > 0) {
+            final String prefix = "  ";
+            for (int i = 0; i < size; i++) {
+                pw.print(prefix); pw.print(mSessionsByUid.keyAt(i));
+                pw.print(": uid="); pw.println(mSessionsByUid.valueAt(i));
+            }
         }
     }
 
@@ -223,13 +264,19 @@
     // so we don't need to create a temporary InteractionSessionId for each event.
 
     private void handleOnCreateSession(@NonNull ContentCaptureContext context,
-            @NonNull String sessionId) {
+            @NonNull String sessionId, int uid, IResultReceiver clientReceiver) {
+        mSessionsByUid.put(sessionId, uid);
         onCreateContentCaptureSession(context, new ContentCaptureSessionId(sessionId));
+        setClientState(clientReceiver, ContentCaptureSession.STATE_ACTIVE,
+                mClientInterface.asBinder());
     }
 
-    private void handleOnContentCaptureEventsRequest(@NonNull String sessionId,
-            @NonNull ContentCaptureEventsRequest request) {
-        onContentCaptureEventsRequest(new ContentCaptureSessionId(sessionId), request);
+    private void handleSendEvents(@NonNull String sessionId, int uid,
+            @NonNull ParceledListSlice<ContentCaptureEvent> events) {
+        if (handleIsRightCallerFor(sessionId, uid)) {
+            onContentCaptureEventsRequest(new ContentCaptureSessionId(sessionId),
+                    new ContentCaptureEventsRequest(events));
+        }
     }
 
     private void handleOnActivitySnapshot(@NonNull String sessionId,
@@ -237,7 +284,52 @@
         onActivitySnapshot(new ContentCaptureSessionId(sessionId), snapshotData);
     }
 
-    private void handleOnDestroySession(@NonNull String sessionId) {
+    private void handleFinishSession(@NonNull String sessionId) {
+        mSessionsByUid.remove(sessionId);
         onDestroyContentCaptureSession(new ContentCaptureSessionId(sessionId));
     }
+
+    /**
+     * Checks if the given {@code uid} owns the session.
+     */
+    private boolean handleIsRightCallerFor(@NonNull String sessionId, int uid) {
+        final Integer rightUid = mSessionsByUid.get(sessionId);
+        if (rightUid == null) {
+            if (VERBOSE) Log.v(TAG, "No session for " + sessionId);
+            // Just ignore, as the session could have finished
+            return false;
+        }
+        if (rightUid != uid) {
+            Log.e(TAG, "invalid call from UID " + uid + ": session " + sessionId + " belongs to "
+                    + rightUid);
+            //TODO(b/111276913): log metrics as this could be a malicious app forging a sessionId
+            return false;
+        }
+        return true;
+
+    }
+
+    /**
+     * Sends the state of the {@link ContentCaptureManager} in the cleint app.
+     *
+     * @param clientReceiver receiver in the client app.
+     * @param binder handle to the {@code IContentCaptureDirectManager} object that resides in the
+     * service.
+     * @hide
+     */
+    public static void setClientState(@NonNull IResultReceiver clientReceiver,
+            int sessionStatus, @Nullable IBinder binder) {
+        try {
+            final Bundle extras;
+            if (binder != null) {
+                extras = new Bundle();
+                extras.putBinder(ContentCaptureSession.EXTRA_BINDER, binder);
+            } else {
+                extras = null;
+            }
+            clientReceiver.send(sessionStatus, extras);
+        } catch (RemoteException e) {
+            Slog.w(TAG, "Error async reporting result to client: " + e);
+        }
+    }
 }
diff --git a/core/java/android/service/contentcapture/IContentCaptureService.aidl b/core/java/android/service/contentcapture/IContentCaptureService.aidl
index 8167be9..20e8e99 100644
--- a/core/java/android/service/contentcapture/IContentCaptureService.aidl
+++ b/core/java/android/service/contentcapture/IContentCaptureService.aidl
@@ -16,10 +16,11 @@
 
 package android.service.contentcapture;
 
-import android.service.contentcapture.ContentCaptureEventsRequest;
 import android.service.contentcapture.SnapshotData;
 import android.view.contentcapture.ContentCaptureContext;
 
+import com.android.internal.os.IResultReceiver;
+
 import java.util.List;
 
 /**
@@ -28,11 +29,8 @@
  * @hide
  */
 oneway interface IContentCaptureService {
-
-    // Called when session is created (context not null) or destroyed (context null)
-    void onSessionLifecycle(in ContentCaptureContext context, String sessionId);
-
-    void onContentCaptureEventsRequest(String sessionId, in ContentCaptureEventsRequest request);
-
+    void onSessionStarted(in ContentCaptureContext context, String sessionId, int uid,
+                          in IResultReceiver clientReceiver);
+    void onSessionFinished(String sessionId);
     void onActivitySnapshot(String sessionId, in SnapshotData snapshotData);
 }
diff --git a/core/java/android/service/quicksettings/Tile.java b/core/java/android/service/quicksettings/Tile.java
index 4b81a72..6b569cf 100644
--- a/core/java/android/service/quicksettings/Tile.java
+++ b/core/java/android/service/quicksettings/Tile.java
@@ -15,6 +15,7 @@
  */
 package android.service.quicksettings;
 
+import android.annotation.Nullable;
 import android.graphics.drawable.Icon;
 import android.os.IBinder;
 import android.os.Parcel;
@@ -62,6 +63,7 @@
     private IBinder mToken;
     private Icon mIcon;
     private CharSequence mLabel;
+    private CharSequence mSubtitle;
     private CharSequence mContentDescription;
     // Default to active until clients of the new API can update.
     private int mState = STATE_ACTIVE;
@@ -152,6 +154,22 @@
     }
 
     /**
+     * Gets the current subtitle for the tile.
+     */
+    @Nullable
+    public CharSequence getSubtitle() {
+        return mSubtitle;
+    }
+
+    /**
+     * Set the subtitle for the tile. Will be displayed as the secondary label.
+     * @param subtitle the subtitle to show.
+     */
+    public void setSubtitle(@Nullable CharSequence subtitle) {
+        this.mSubtitle = subtitle;
+    }
+
+    /**
      * Gets the current content description for the tile.
      */
     public CharSequence getContentDescription() {
@@ -195,6 +213,7 @@
         }
         dest.writeInt(mState);
         TextUtils.writeToParcel(mLabel, dest, flags);
+        TextUtils.writeToParcel(mSubtitle, dest, flags);
         TextUtils.writeToParcel(mContentDescription, dest, flags);
     }
 
@@ -206,6 +225,7 @@
         }
         mState = source.readInt();
         mLabel = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
+        mSubtitle = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
         mContentDescription = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
     }
 
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/view/NotificationHeaderView.java b/core/java/android/view/NotificationHeaderView.java
index ada7853..60eeeea 100644
--- a/core/java/android/view/NotificationHeaderView.java
+++ b/core/java/android/view/NotificationHeaderView.java
@@ -201,22 +201,16 @@
             int bottom = top + childHeight;
             int layoutLeft = left;
             int layoutRight = right;
-            if (child == mExpandButton && mShowExpandButtonAtEnd) {
-                layoutRight = end - mContentEndMargin;
-                end = layoutLeft = layoutRight - child.getMeasuredWidth();
-            }
-            if (child == mProfileBadge) {
-                int paddingEnd = getPaddingEnd();
-                if (mShowWorkBadgeAtEnd) {
-                    paddingEnd = mContentEndMargin;
+            if ((child == mExpandButton && mShowExpandButtonAtEnd)
+                    || child == mProfileBadge
+                    || child == mAppOps) {
+                if (end == getMeasuredWidth()) {
+                    layoutRight = end - mContentEndMargin;
+                } else {
+                    layoutRight = end - params.getMarginEnd();
                 }
-                layoutRight = end - paddingEnd;
-                end = layoutLeft = layoutRight - child.getMeasuredWidth();
-            }
-            if (child == mAppOps) {
-                int paddingEnd = mContentEndMargin;
-                layoutRight = end - paddingEnd;
-                end = layoutLeft = layoutRight - child.getMeasuredWidth();
+                layoutLeft = layoutRight - child.getMeasuredWidth();
+                end = layoutLeft - params.getMarginStart();
             }
             if (getLayoutDirection() == LAYOUT_DIRECTION_RTL) {
                 int ltrLeft = layoutLeft;
diff --git a/core/java/android/view/TouchDelegate.java b/core/java/android/view/TouchDelegate.java
index bef9f07..2ea95e9 100644
--- a/core/java/android/view/TouchDelegate.java
+++ b/core/java/android/view/TouchDelegate.java
@@ -157,6 +157,11 @@
      * Forward hover events to the delegate view if the event is within the bounds
      * specified in the constructor and touch exploration is enabled.
      *
+     * <p>This method is provided for accessibility purposes so touch exploration, which is
+     * commonly used by screen readers, can properly place accessibility focus on views that
+     * use touch delegates. Therefore, touch exploration must be enabled for hover events
+     * to be dispatched through the delegate.</p>
+     *
      * @param event The hover event to forward
      * @return True if the event was consumed by the delegate, false otherwise.
      *
diff --git a/core/java/android/view/contentcapture/ContentCaptureSession.java b/core/java/android/view/contentcapture/ContentCaptureSession.java
index 632955d..f411cf7 100644
--- a/core/java/android/view/contentcapture/ContentCaptureSession.java
+++ b/core/java/android/view/contentcapture/ContentCaptureSession.java
@@ -27,9 +27,11 @@
 import android.annotation.Nullable;
 import android.content.ComponentName;
 import android.content.Context;
+import android.content.pm.ParceledListSlice;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
+import android.os.IBinder.DeathRecipient;
 import android.os.RemoteException;
 import android.os.SystemClock;
 import android.util.Log;
@@ -45,6 +47,8 @@
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
 import java.util.UUID;
 import java.util.concurrent.atomic.AtomicBoolean;
 
@@ -100,6 +104,13 @@
     public static final int STATE_DISABLED = 3;
 
     /**
+     * Session is disabled because its id already existed on server.
+     *
+     * @hide
+     */
+    public static final int STATE_DISABLED_DUPLICATED_ID = 4;
+
+    /**
      * Handler message used to flush the buffer.
      */
     private static final int MSG_FLUSH = 1;
@@ -116,6 +127,13 @@
     // TODO(b/121044064): use settings
     private static final int FLUSHING_FREQUENCY_MS = 5_000;
 
+
+    /**
+     * Name of the {@link IResultReceiver} extra used to pass the binder interface to the service.
+     * @hide
+     */
+    public static final String EXTRA_BINDER = "binder";
+
     private final CloseGuard mCloseGuard = CloseGuard.get();
 
     @NonNull
@@ -127,8 +145,21 @@
     @NonNull
     private final Handler mHandler;
 
+    /**
+     * Interface to the system_server binder object - it's only used to start the session (and
+     * notify when the session is finished).
+     */
     @Nullable
-    private final IContentCaptureManager mService;
+    private final IContentCaptureManager mSystemServerInterface;
+
+    /**
+     * Direct interface to the service binder object - it's used to send the events, including the
+     * last ones (when the session is finished)
+     */
+    @Nullable
+    private IContentCaptureDirectManager mDirectServiceInterface;
+    @Nullable
+    private DeathRecipient mDirectServiceVulture;
 
     @Nullable
     private final String mId = UUID.randomUUID().toString();
@@ -165,11 +196,11 @@
 
     /** @hide */
     protected ContentCaptureSession(@NonNull Context context, @NonNull Handler handler,
-            @Nullable IContentCaptureManager service, @NonNull AtomicBoolean disabled,
+            @Nullable IContentCaptureManager systemServerInterface, @NonNull AtomicBoolean disabled,
             @Nullable ContentCaptureContext clientContext) {
         mContext = context;
         mHandler = handler;
-        mService = service;
+        mSystemServerInterface = systemServerInterface;
         mDisabled = disabled;
         mClientContext = clientContext;
         mCloseGuard.open("destroy");
@@ -227,6 +258,8 @@
             Log.v(TAG, "destroy(): state=" + getStateAsString(mState) + ", mId=" + mId);
         }
 
+        flush();
+
         mHandler.sendMessage(obtainMessage(ContentCaptureSession::handleDestroySession, this));
         mCloseGuard.close();
     }
@@ -267,11 +300,20 @@
         final int flags = 0; // TODO(b/111276913): get proper flags
 
         try {
-            mService.startSession(mContext.getUserId(), mApplicationToken, componentName,
-                    mId, mClientContext, flags, new IResultReceiver.Stub() {
+            mSystemServerInterface.startSession(mContext.getUserId(), mApplicationToken,
+                    componentName, mId, mClientContext, flags, new IResultReceiver.Stub() {
                         @Override
                         public void send(int resultCode, Bundle resultData) {
-                            handleSessionStarted(resultCode);
+                            IBinder binder = null;
+                            if (resultData != null) {
+                                binder = resultData.getBinder(EXTRA_BINDER);
+                                if (binder == null) {
+                                    Log.wtf(TAG, "No " + EXTRA_BINDER + " extra result");
+                                    handleResetState();
+                                    return;
+                                }
+                            }
+                            handleSessionStarted(resultCode, binder);
                         }
                     });
         } catch (RemoteException e) {
@@ -280,12 +322,38 @@
         }
     }
 
-    private void handleSessionStarted(int resultCode) {
+    /**
+     * Callback from {@code system_server} after call to
+     * {@link IContentCaptureManager#startSession(int, IBinder, ComponentName, String,
+     * ContentCaptureContext, int, IResultReceiver)}.
+     *
+     * @param resultCode session state
+     * @param binder handle to {@link IContentCaptureDirectManager}
+     */
+    private void handleSessionStarted(int resultCode, @Nullable IBinder binder) {
         mState = resultCode;
-        mDisabled.set(mState == STATE_DISABLED);
+        if (binder != null) {
+            mDirectServiceInterface = IContentCaptureDirectManager.Stub.asInterface(binder);
+            mDirectServiceVulture = () -> {
+                Log.w(TAG, "Destroying session " + mId + " because service died");
+                destroy();
+            };
+            try {
+                binder.linkToDeath(mDirectServiceVulture, 0);
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failed to link to death on " + binder + ": " + e);
+            }
+        }
+        if (resultCode == STATE_DISABLED || resultCode == STATE_DISABLED_DUPLICATED_ID) {
+            mDisabled.set(true);
+            handleResetSession(/* resetState= */ false);
+        } else {
+            mDisabled.set(false);
+        }
         if (VERBOSE) {
             Log.v(TAG, "handleSessionStarted() result: code=" + resultCode + ", id=" + mId
-                    + ", state=" + getStateAsString(mState) + ", disabled=" + mDisabled.get());
+                    + ", state=" + getStateAsString(mState) + ", disabled=" + mDisabled.get()
+                    + ", binder=" + binder);
         }
     }
 
@@ -307,7 +375,7 @@
         final boolean bufferEvent = numberEvents < MAX_BUFFER_SIZE;
 
         if (bufferEvent && !forceFlush) {
-            handleScheduleFlush();
+            handleScheduleFlush(/* checkExisting= */ true);
             return;
         }
 
@@ -331,8 +399,8 @@
         handleForceFlush();
     }
 
-    private void handleScheduleFlush() {
-        if (mHandler.hasMessages(MSG_FLUSH)) {
+    private void handleScheduleFlush(boolean checkExisting) {
+        if (checkExisting && mHandler.hasMessages(MSG_FLUSH)) {
             // "Renew" the flush message by removing the previous one
             mHandler.removeMessages(MSG_FLUSH);
         }
@@ -356,52 +424,80 @@
     private void handleForceFlush() {
         if (mEvents == null) return;
 
+        if (mDirectServiceInterface == null) {
+            Log.w(TAG, "handleForceFlush(): client not available yet");
+            if (!mHandler.hasMessages(MSG_FLUSH)) {
+                handleScheduleFlush(/* checkExisting= */ false);
+            }
+            return;
+        }
+
         final int numberEvents = mEvents.size();
         try {
             if (DEBUG) {
                 Log.d(TAG, "Flushing " + numberEvents + " event(s) for " + getActivityDebugName());
             }
             mHandler.removeMessages(MSG_FLUSH);
-            mService.sendEvents(mContext.getUserId(), mId, mEvents);
-            // TODO(b/111276913): decide whether we should clear or set it to null, as each has
-            // its own advantages: clearing will save extra allocations while the session is
-            // active, while setting to null would save memory if there's no more event coming.
-            mEvents.clear();
+
+            final ParceledListSlice<ContentCaptureEvent> events = handleClearEvents();
+            mDirectServiceInterface.sendEvents(mId, events);
         } catch (RemoteException e) {
             Log.w(TAG, "Error sending " + numberEvents + " for " + getActivityDebugName()
                     + ": " + e);
         }
     }
 
+    /**
+     * Resets the buffer and return a {@link ParceledListSlice} with the previous events.
+     */
+    @NonNull
+    private ParceledListSlice<ContentCaptureEvent> handleClearEvents() {
+        // NOTE: we must save a reference to the current mEvents and then set it to to null,
+        // otherwise clearing it would clear it in the receiving side if the service is also local.
+        final List<ContentCaptureEvent> events = mEvents == null
+                ? Collections.emptyList()
+                : mEvents;
+        mEvents = null;
+        return new ParceledListSlice<>(events);
+    }
+
     private void handleDestroySession() {
         //TODO(b/111276913): right now both the ContentEvents and lifecycle sessions are sent
         // to system_server, so it's ok to call both in sequence here. But once we split
         // them so the events are sent directly to the service, we need to make sure they're
         // sent in order.
-        try {
-            if (DEBUG) {
-                Log.d(TAG, "Destroying session (ctx=" + mContext + ", id=" + mId + ") with "
-                        + (mEvents == null ? 0 : mEvents.size()) + " event(s) for "
-                        + getActivityDebugName());
-            }
-
-            mService.finishSession(mContext.getUserId(), mId, mEvents);
-        } catch (RemoteException e) {
-            Log.e(TAG, "Error destroying session " + mId + " for " + getActivityDebugName()
-                    + ": " + e);
-        } finally {
-            handleResetState();
+        if (DEBUG) {
+            Log.d(TAG, "Destroying session (ctx=" + mContext + ", id=" + mId + ") with "
+                    + (mEvents == null ? 0 : mEvents.size()) + " event(s) for "
+                    + getActivityDebugName());
         }
+
+        try {
+            mSystemServerInterface.finishSession(mContext.getUserId(), mId);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error destroying system-service session " + mId + " for "
+                    + getActivityDebugName() + ": " + e);
+        }
+    }
+
+    private void handleResetState() {
+        handleResetSession(/* resetState= */ true);
     }
 
     // TODO(b/111276913): once we support multiple sessions, we might need to move some of these
     // clearings out.
-    private void handleResetState() {
-        mState = STATE_UNKNOWN;
+    private void handleResetSession(boolean resetState) {
+        if (resetState) {
+            mState = STATE_UNKNOWN;
+        }
         mContentCaptureSessionId = null;
         mApplicationToken = null;
         mComponentName = null;
         mEvents = null;
+        if (mDirectServiceInterface != null) {
+            mDirectServiceInterface.asBinder().unlinkToDeath(mDirectServiceVulture, 0);
+        }
+        mDirectServiceInterface = null;
         mHandler.removeMessages(MSG_FLUSH);
     }
 
@@ -492,15 +588,20 @@
     }
 
     private boolean isContentCaptureEnabled() {
-        return mService != null && !mDisabled.get();
+        return mSystemServerInterface != null && !mDisabled.get();
     }
 
     void dump(@NonNull String prefix, @NonNull PrintWriter pw) {
         pw.print(prefix); pw.print("id: "); pw.println(mId);
         pw.print(prefix); pw.print("mContext: "); pw.println(mContext);
         pw.print(prefix); pw.print("user: "); pw.println(mContext.getUserId());
-        if (mService != null) {
-            pw.print(prefix); pw.print("mService: "); pw.println(mService);
+        if (mSystemServerInterface != null) {
+            pw.print(prefix); pw.print("mSystemServerInterface: ");
+            pw.println(mSystemServerInterface);
+        }
+        if (mDirectServiceInterface != null) {
+            pw.print(prefix); pw.print("mDirectServiceInterface: ");
+            pw.println(mDirectServiceInterface);
         }
         if (mClientContext != null) {
             // NOTE: we don't dump clientContent because it could have PII
@@ -563,6 +664,8 @@
                 return "ACTIVE";
             case STATE_DISABLED:
                 return "DISABLED";
+            case STATE_DISABLED_DUPLICATED_ID:
+                return "DISABLED_DUPLICATED_ID";
             default:
                 return "INVALID:" + state;
         }
diff --git a/core/java/android/view/contentcapture/IContentCaptureDirectManager.aidl b/core/java/android/view/contentcapture/IContentCaptureDirectManager.aidl
new file mode 100644
index 0000000..145fc16
--- /dev/null
+++ b/core/java/android/view/contentcapture/IContentCaptureDirectManager.aidl
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.contentcapture;
+
+import android.content.pm.ParceledListSlice;
+import android.view.contentcapture.ContentCaptureEvent;
+
+/**
+  * Interface between an app (ContentCaptureManager / ContentCaptureSession) and the app providing
+  * the ContentCaptureService implementation.
+  *
+  * @hide
+  */
+oneway interface IContentCaptureDirectManager {
+    void sendEvents(in String sessionId, in ParceledListSlice events);
+}
diff --git a/core/java/android/view/contentcapture/IContentCaptureManager.aidl b/core/java/android/view/contentcapture/IContentCaptureManager.aidl
index 2002c5c..cbd3701 100644
--- a/core/java/android/view/contentcapture/IContentCaptureManager.aidl
+++ b/core/java/android/view/contentcapture/IContentCaptureManager.aidl
@@ -26,12 +26,14 @@
 import java.util.List;
 
 /**
- * {@hide}
- */
+  * Interface between an app (ContentCaptureManager / ContentCaptureSession) and the system-server
+  * implementation service (ContentCaptureManagerService).
+  *
+  * @hide
+  */
 oneway interface IContentCaptureManager {
     void startSession(int userId, IBinder activityToken, in ComponentName componentName,
                       String sessionId, in ContentCaptureContext clientContext, int flags,
                       in IResultReceiver result);
-    void finishSession(int userId, String sessionId, in List<ContentCaptureEvent> events);
-    void sendEvents(int userId, in String sessionId, in List<ContentCaptureEvent> events);
+    void finishSession(int userId, String sessionId);
 }
diff --git a/core/java/android/hardware/usb/UsbPort.aidl b/core/java/com/android/internal/app/IAppOpsNotedCallback.aidl
similarity index 63%
copy from core/java/android/hardware/usb/UsbPort.aidl
copy to core/java/com/android/internal/app/IAppOpsNotedCallback.aidl
index b7a7920..fa5c30a 100644
--- a/core/java/android/hardware/usb/UsbPort.aidl
+++ b/core/java/com/android/internal/app/IAppOpsNotedCallback.aidl
@@ -1,11 +1,11 @@
 /*
- * Copyright (C) 2015, The Android Open Source Project
+ * 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
+ *      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,
@@ -14,6 +14,9 @@
  * limitations under the License.
  */
 
-package android.hardware.usb;
+package com.android.internal.app;
 
-parcelable UsbPort;
+// Iterface to observe op note/checks of ops
+oneway interface IAppOpsNotedCallback {
+    void opNoted(int op, int uid, String packageName, int mode);
+}
diff --git a/core/java/com/android/internal/app/IAppOpsService.aidl b/core/java/com/android/internal/app/IAppOpsService.aidl
index 049103b..e571656 100644
--- a/core/java/com/android/internal/app/IAppOpsService.aidl
+++ b/core/java/com/android/internal/app/IAppOpsService.aidl
@@ -21,6 +21,7 @@
 import android.os.Bundle;
 import com.android.internal.app.IAppOpsCallback;
 import com.android.internal.app.IAppOpsActiveCallback;
+import com.android.internal.app.IAppOpsNotedCallback;
 
 interface IAppOpsService {
     // These first methods are also called by native code, so must
@@ -61,4 +62,7 @@
     boolean isOperationActive(int code, int uid, String packageName);
 
     void startWatchingModeWithFlags(int op, String packageName, int flags, IAppOpsCallback callback);
+
+    void startWatchingNoted(in int[] ops, IAppOpsNotedCallback callback);
+    void stopWatchingNoted(IAppOpsNotedCallback callback);
 }
diff --git a/core/java/com/android/internal/app/procstats/AssociationState.java b/core/java/com/android/internal/app/procstats/AssociationState.java
index 4da3391..2df5158 100644
--- a/core/java/com/android/internal/app/procstats/AssociationState.java
+++ b/core/java/com/android/internal/app/procstats/AssociationState.java
@@ -17,6 +17,7 @@
 package com.android.internal.app.procstats;
 
 
+import android.annotation.Nullable;
 import android.os.Parcel;
 import android.os.SystemClock;
 import android.os.UserHandle;
@@ -192,9 +193,16 @@
          */
         String mProcess;
 
-        SourceKey(int uid, String process) {
+        /**
+         * Optional package name, or null; consider this final.  Not final just to avoid a
+         * temporary object during lookup.
+         */
+        @Nullable String mPackage;
+
+        SourceKey(int uid, String process, String pkg) {
             mUid = uid;
             mProcess = process;
+            mPackage = pkg;
         }
 
         public boolean equals(Object o) {
@@ -202,12 +210,14 @@
                 return false;
             }
             SourceKey s = (SourceKey) o;
-            return s.mUid == mUid && Objects.equals(s.mProcess, mProcess);
+            return s.mUid == mUid && Objects.equals(s.mProcess, mProcess)
+                    && Objects.equals(s.mPackage, mPackage);
         }
 
         @Override
         public int hashCode() {
-            return Integer.hashCode(mUid) ^ (mProcess == null ? 0 : mProcess.hashCode());
+            return Integer.hashCode(mUid) ^ (mProcess == null ? 0 : mProcess.hashCode())
+                    ^ (mPackage == null ? 0 : (mPackage.hashCode() * 33));
         }
 
         @Override
@@ -217,6 +227,8 @@
             UserHandle.formatUid(sb, mUid);
             sb.append(' ');
             sb.append(mProcess);
+            sb.append(' ');
+            sb.append(mPackage);
             sb.append('}');
             return sb.toString();
         }
@@ -227,7 +239,7 @@
      */
     private final ArrayMap<SourceKey, SourceState> mSources = new ArrayMap<>();
 
-    private final SourceKey mTmpSourceKey = new SourceKey(0, null);
+    private final SourceKey mTmpSourceKey = new SourceKey(0, null, null);
 
     private ProcessState mProc;
 
@@ -266,12 +278,13 @@
         mProc = proc;
     }
 
-    public SourceState startSource(int uid, String processName) {
+    public SourceState startSource(int uid, String processName, String packageName) {
         mTmpSourceKey.mUid = uid;
         mTmpSourceKey.mProcess = processName;
+        mTmpSourceKey.mPackage = packageName;
         SourceState src = mSources.get(mTmpSourceKey);
         if (src == null) {
-            SourceKey key = new SourceKey(uid, processName);
+            SourceKey key = new SourceKey(uid, processName, packageName);
             src = new SourceState(key);
             mSources.put(key, src);
         }
@@ -379,6 +392,7 @@
             final SourceState src = mSources.valueAt(isrc);
             out.writeInt(key.mUid);
             stats.writeCommonString(out, key.mProcess);
+            stats.writeCommonString(out, key.mPackage);
             out.writeInt(src.mCount);
             out.writeLong(src.mDuration);
             out.writeInt(src.mActiveCount);
@@ -405,7 +419,8 @@
         for (int isrc = 0; isrc < NSRC; isrc++) {
             final int uid = in.readInt();
             final String procName = stats.readCommonString(in, parcelVersion);
-            final SourceKey key = new SourceKey(uid, procName);
+            final String pkgName = stats.readCommonString(in, parcelVersion);
+            final SourceKey key = new SourceKey(uid, procName, pkgName);
             final SourceState src = new SourceState(key);
             src.mCount = in.readInt();
             src.mDuration = in.readLong();
@@ -445,10 +460,11 @@
         }
     }
 
-    public boolean hasProcess(String procName) {
+    public boolean hasProcessOrPackage(String procName) {
         final int NSRC = mSources.size();
         for (int isrc = 0; isrc < NSRC; isrc++) {
-            if (mSources.keyAt(isrc).mProcess.equals(procName)) {
+            final SourceKey key = mSources.keyAt(isrc);
+            if (procName.equals(key.mProcess) || procName.equals(key.mPackage)) {
                 return true;
             }
         }
@@ -466,14 +482,20 @@
         for (int isrc = 0; isrc < NSRC; isrc++) {
             final SourceKey key = mSources.keyAt(isrc);
             final SourceState src = mSources.valueAt(isrc);
-            if (reqPackage != null && !reqPackage.equals(key.mProcess)) {
+            if (reqPackage != null && !reqPackage.equals(key.mProcess)
+                    && !reqPackage.equals(key.mPackage)) {
                 continue;
             }
             pw.print(prefixInner);
             pw.print("<- ");
             pw.print(key.mProcess);
-            pw.print(" / ");
+            pw.print("/");
             UserHandle.formatUid(pw, key.mUid);
+            if (key.mPackage != null) {
+                pw.print(" (");
+                pw.print(key.mPackage);
+                pw.print(")");
+            }
             pw.println(":");
             pw.print(prefixInner);
             pw.print("   Total count ");
@@ -683,6 +705,7 @@
             final SourceState src = mSources.valueAt(isrc);
             final long sourceToken = proto.start(PackageAssociationProcessStatsProto.SOURCES);
             proto.write(PackageAssociationSourceProcessStatsProto.PROCESS_NAME, key.mProcess);
+            proto.write(PackageAssociationSourceProcessStatsProto.PACKAGE_NAME, key.mPackage);
             proto.write(PackageAssociationSourceProcessStatsProto.PROCESS_UID, key.mUid);
             proto.write(PackageAssociationSourceProcessStatsProto.TOTAL_COUNT, src.mCount);
             long duration = src.mDuration;
diff --git a/core/java/com/android/internal/app/procstats/ProcessStats.java b/core/java/com/android/internal/app/procstats/ProcessStats.java
index 9ee583a..9b9b771 100644
--- a/core/java/com/android/internal/app/procstats/ProcessStats.java
+++ b/core/java/com/android/internal/app/procstats/ProcessStats.java
@@ -178,7 +178,7 @@
             {"proc", "pkg-proc", "pkg-svc", "pkg-asc", "pkg-all", "all"};
 
     // Current version of the parcel format.
-    private static final int PARCEL_VERSION = 34;
+    private static final int PARCEL_VERSION = 35;
     // In-memory Parcel magic number, used to detect attempts to unmarshall bad data
     private static final int MAGIC = 0x50535454;
 
@@ -1490,7 +1490,7 @@
                                 // package, so that if so we print those.
                                 for (int iasc = 0; iasc < NASCS; iasc++) {
                                     AssociationState asc = pkgState.mAssociations.valueAt(iasc);
-                                    if (asc.hasProcess(reqPackage)) {
+                                    if (asc.hasProcessOrPackage(reqPackage)) {
                                         onlyAssociations = true;
                                         break;
                                     }
@@ -1591,7 +1591,7 @@
                             for (int iasc = 0; iasc < NASCS; iasc++) {
                                 AssociationState asc = pkgState.mAssociations.valueAt(iasc);
                                 if (!pkgMatch && !reqPackage.equals(asc.getProcessName())) {
-                                    if (!onlyAssociations || !asc.hasProcess(reqPackage)) {
+                                    if (!onlyAssociations || !asc.hasProcessOrPackage(reqPackage)) {
                                         continue;
                                     }
                                 }
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index 8bdb000..c2c6ae6 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -536,9 +536,11 @@
     static ClassLoader createPathClassLoader(String classPath, int targetSdkVersion) {
         String libraryPath = System.getProperty("java.library.path");
 
+        // We use the boot class loader, that's what the runtime expects at AOT.
+        ClassLoader parent = ClassLoader.getSystemClassLoader().getParent();
+
         return ClassLoaderFactory.createClassLoader(classPath, libraryPath, libraryPath,
-                ClassLoader.getSystemClassLoader(), targetSdkVersion, true /* isNamespaceShared */,
-                null /* classLoaderName */);
+                parent, targetSdkVersion, true /* isNamespaceShared */, null /* classLoaderName */);
     }
 
     /**
diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
index 5118e5f..9087dd2 100644
--- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
@@ -67,7 +67,8 @@
             in NotificationVisibility[] noLongerVisibleKeys);
     void onNotificationExpansionChanged(in String key, in boolean userAction, in boolean expanded);
     void onNotificationDirectReplied(String key);
-    void onNotificationSmartRepliesAdded(in String key, in int replyCount);
+    void onNotificationSmartSuggestionsAdded(String key, int smartReplyCount, int smartActionCount,
+            boolean generatedByAsssistant);
     void onNotificationSmartReplySent(in String key, in int replyIndex, in CharSequence reply, boolean generatedByAssistant);
     void onNotificationSettingsViewed(String key);
     void setSystemUiVisibility(int displayId, int vis, int mask, String cause);
diff --git a/core/java/com/android/internal/usb/DumpUtils.java b/core/java/com/android/internal/usb/DumpUtils.java
index cac2265..240c2e7 100644
--- a/core/java/com/android/internal/usb/DumpUtils.java
+++ b/core/java/com/android/internal/usb/DumpUtils.java
@@ -16,12 +16,12 @@
 
 package com.android.internal.usb;
 
-import static android.hardware.usb.UsbPort.MODE_AUDIO_ACCESSORY;
-import static android.hardware.usb.UsbPort.MODE_DEBUG_ACCESSORY;
-import static android.hardware.usb.UsbPort.MODE_DFP;
-import static android.hardware.usb.UsbPort.MODE_DUAL;
-import static android.hardware.usb.UsbPort.MODE_NONE;
-import static android.hardware.usb.UsbPort.MODE_UFP;
+import static android.hardware.usb.UsbPortStatus.MODE_AUDIO_ACCESSORY;
+import static android.hardware.usb.UsbPortStatus.MODE_DEBUG_ACCESSORY;
+import static android.hardware.usb.UsbPortStatus.MODE_DFP;
+import static android.hardware.usb.UsbPortStatus.MODE_DUAL;
+import static android.hardware.usb.UsbPortStatus.MODE_NONE;
+import static android.hardware.usb.UsbPortStatus.MODE_UFP;
 
 import static com.android.internal.util.dump.DumpUtils.writeStringIfNotNull;
 
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.bp b/core/jni/Android.bp
index 43f8d00..21fa75e 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -274,6 +274,7 @@
         "libicuuc",
         "libmedia",
         "libmediametrics",
+        "libmeminfo",
         "libaudioclient",
         "libjpeg",
         "libusbhost",
diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp
index c32de0a..12ca78a 100755
--- a/core/jni/android/graphics/Bitmap.cpp
+++ b/core/jni/android/graphics/Bitmap.cpp
@@ -725,9 +725,10 @@
             return NULL;
         }
 
-        // Map the pixels in place and take ownership of the ashmem region.
-        nativeBitmap = sk_sp<Bitmap>(GraphicsJNI::mapAshmemBitmap(env, bitmap.get(),
-                dupFd, const_cast<void*>(blob.data()), size, !isMutable));
+        // Map the pixels in place and take ownership of the ashmem region. We must also respect the
+        // rowBytes value already set on the bitmap instead of attempting to compute our own.
+        nativeBitmap = Bitmap::createFrom(bitmap->info(), bitmap->rowBytes(), dupFd,
+                                          const_cast<void*>(blob.data()), size, !isMutable);
         if (!nativeBitmap) {
             close(dupFd);
             blob.release();
@@ -1097,21 +1098,20 @@
     SkBitmap src;
     hwuiBitmap.getSkBitmap(&src);
 
-    SkBitmap result;
-    HeapAllocator allocator;
-    if (!bitmapCopyTo(&result, hwuiBitmap.info().colorType(), src, &allocator)) {
+    if (src.pixelRef() == nullptr) {
         doThrowRE(env, "Could not copy a hardware bitmap.");
         return NULL;
     }
-    return createBitmap(env, allocator.getStorageObjAndReset(), getPremulBitmapCreateFlags(false));
+
+    sk_sp<Bitmap> bitmap = Bitmap::createFrom(src.info(), *src.pixelRef());
+    return createBitmap(env, bitmap.release(), getPremulBitmapCreateFlags(false));
 }
 
 static jobject Bitmap_createHardwareBitmap(JNIEnv* env, jobject, jobject graphicBuffer) {
     sp<GraphicBuffer> buffer(graphicBufferForJavaObject(env, graphicBuffer));
-    // Bitmap::createFrom currently assumes SRGB color space for RGBA images.
     // To support any color space, we need to pass an additional ColorSpace argument to
     // java Bitmap.createHardwareBitmap.
-    sk_sp<Bitmap> bitmap = Bitmap::createFrom(buffer);
+    sk_sp<Bitmap> bitmap = Bitmap::createFrom(buffer, SkColorSpace::MakeSRGB());
     if (!bitmap.get()) {
         ALOGW("failed to create hardware bitmap from graphic buffer");
         return NULL;
diff --git a/core/jni/android/graphics/Graphics.cpp b/core/jni/android/graphics/Graphics.cpp
index 26af15e..67d0c8a 100644
--- a/core/jni/android/graphics/Graphics.cpp
+++ b/core/jni/android/graphics/Graphics.cpp
@@ -424,36 +424,6 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
-android::Bitmap* GraphicsJNI::mapAshmemBitmap(JNIEnv* env, SkBitmap* bitmap,
-        int fd, void* addr, size_t size, bool readOnly) {
-    const SkImageInfo& info = bitmap->info();
-    if (info.colorType() == kUnknown_SkColorType) {
-        doThrowIAE(env, "unknown bitmap configuration");
-        return nullptr;
-    }
-
-    if (!addr) {
-        // Map existing ashmem region if not already mapped.
-        int flags = readOnly ? (PROT_READ) : (PROT_READ | PROT_WRITE);
-        size = ashmem_get_size_region(fd);
-        addr = mmap(NULL, size, flags, MAP_SHARED, fd, 0);
-        if (addr == MAP_FAILED) {
-            return nullptr;
-        }
-    }
-
-    // we must respect the rowBytes value already set on the bitmap instead of
-    // attempting to compute our own.
-    const size_t rowBytes = bitmap->rowBytes();
-
-    auto wrapper = new android::Bitmap(addr, fd, size, info, rowBytes);
-    wrapper->getSkBitmap(bitmap);
-    if (readOnly) {
-        bitmap->pixelRef()->setImmutable();
-    }
-    return wrapper;
-}
-
 SkColorSpaceTransferFn GraphicsJNI::getNativeTransferParameters(JNIEnv* env, jobject transferParams) {
     SkColorSpaceTransferFn p;
     p.fA = (float) env->GetDoubleField(transferParams, gTransferParams_aFieldID);
diff --git a/core/jni/android/graphics/GraphicsJNI.h b/core/jni/android/graphics/GraphicsJNI.h
index cee3c46..b0bd683 100644
--- a/core/jni/android/graphics/GraphicsJNI.h
+++ b/core/jni/android/graphics/GraphicsJNI.h
@@ -85,9 +85,6 @@
 
     static jobject createBitmapRegionDecoder(JNIEnv* env, SkBitmapRegionDecoder* bitmap);
 
-    static android::Bitmap* mapAshmemBitmap(JNIEnv* env, SkBitmap* bitmap,
-            int fd, void* addr, size_t size, bool readOnly);
-
     /**
      * Given a bitmap we natively allocate a memory block to store the contents
      * of that bitmap.  The memory is then attached to the bitmap via an
diff --git a/core/jni/android_os_Debug.cpp b/core/jni/android_os_Debug.cpp
index fa1da4b..888dab1 100644
--- a/core/jni/android_os_Debug.cpp
+++ b/core/jni/android_os_Debug.cpp
@@ -32,6 +32,7 @@
 #include <atomic>
 #include <iomanip>
 #include <string>
+#include <vector>
 
 #include <debuggerd/client.h>
 #include <log/log.h>
@@ -41,6 +42,7 @@
 #include <nativehelper/JNIHelp.h>
 #include <nativehelper/ScopedUtfChars.h>
 #include "jni.h"
+#include <meminfo/sysmeminfo.h>
 #include <memtrack/memtrack.h>
 #include <memunreachable/memunreachable.h>
 #include "android_os_Debug.h"
@@ -712,6 +714,8 @@
     return vmalloc_allocated_size;
 }
 
+// The 1:1 mapping of MEMINFO_* enums here must match with the constants from
+// Debug.java.
 enum {
     MEMINFO_TOTAL,
     MEMINFO_FREE,
@@ -731,138 +735,43 @@
     MEMINFO_COUNT
 };
 
-static long long get_zram_mem_used()
-{
-#define ZRAM_SYSFS "/sys/block/zram0/"
-    UniqueFile mm_stat_file = MakeUniqueFile(ZRAM_SYSFS "mm_stat", "re");
-    if (mm_stat_file) {
-        long long mem_used_total = 0;
-
-        int matched = fscanf(mm_stat_file.get(), "%*d %*d %lld %*d %*d %*d %*d", &mem_used_total);
-        if (matched != 1)
-            ALOGW("failed to parse " ZRAM_SYSFS "mm_stat");
-
-        return mem_used_total;
-    }
-
-    UniqueFile mem_used_total_file = MakeUniqueFile(ZRAM_SYSFS "mem_used_total", "re");
-    if (mem_used_total_file) {
-        long long mem_used_total = 0;
-
-        int matched = fscanf(mem_used_total_file.get(), "%lld", &mem_used_total);
-        if (matched != 1)
-            ALOGW("failed to parse " ZRAM_SYSFS "mem_used_total");
-
-        return mem_used_total;
-    }
-
-    return 0;
-}
-
 static void android_os_Debug_getMemInfo(JNIEnv *env, jobject clazz, jlongArray out)
 {
-    char buffer[4096];
-    size_t numFound = 0;
-
     if (out == NULL) {
         jniThrowNullPointerException(env, "out == null");
         return;
     }
 
-    int fd = open("/proc/meminfo", O_RDONLY | O_CLOEXEC);
-
-    if (fd < 0) {
-        ALOGW("Unable to open /proc/meminfo: %s\n", strerror(errno));
+    int outLen = env->GetArrayLength(out);
+    if (outLen < MEMINFO_COUNT) {
+        jniThrowRuntimeException(env, "outLen < MEMINFO_COUNT");
         return;
     }
 
-    int len = read(fd, buffer, sizeof(buffer)-1);
-    close(fd);
-
-    if (len < 0) {
-        ALOGW("Empty /proc/meminfo");
+    // Read system memory info including ZRAM. The values are stored in the vector
+    // in the same order as MEMINFO_* enum
+    std::vector<uint64_t> mem(MEMINFO_COUNT);
+    std::vector<std::string> tags(::android::meminfo::SysMemInfo::kDefaultSysMemInfoTags);
+    tags.insert(tags.begin() + MEMINFO_ZRAM_TOTAL, "Zram:");
+    ::android::meminfo::SysMemInfo smi;
+    if (!smi.ReadMemInfo(tags, &mem)) {
+        jniThrowRuntimeException(env, "SysMemInfo read failed");
         return;
     }
-    buffer[len] = 0;
 
-    static const char* const tags[] = {
-            "MemTotal:",
-            "MemFree:",
-            "Buffers:",
-            "Cached:",
-            "Shmem:",
-            "Slab:",
-            "SReclaimable:",
-            "SUnreclaim:",
-            "SwapTotal:",
-            "SwapFree:",
-            "ZRam:",
-            "Mapped:",
-            "VmallocUsed:",
-            "PageTables:",
-            "KernelStack:",
-            NULL
-    };
-    static const int tagsLen[] = {
-            9,
-            8,
-            8,
-            7,
-            6,
-            5,
-            13,
-            11,
-            10,
-            9,
-            5,
-            7,
-            12,
-            11,
-            12,
-            0
-    };
-    long mem[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
-
-    char* p = buffer;
-    while (*p && numFound < (sizeof(tagsLen) / sizeof(tagsLen[0]))) {
-        int i = 0;
-        while (tags[i]) {
-            if (strncmp(p, tags[i], tagsLen[i]) == 0) {
-                p += tagsLen[i];
-                while (*p == ' ') p++;
-                char* num = p;
-                while (*p >= '0' && *p <= '9') p++;
-                if (*p != 0) {
-                    *p = 0;
-                    p++;
-                }
-                mem[i] = atoll(num);
-                numFound++;
-                break;
-            }
-            i++;
-        }
-        while (*p && *p != '\n') {
-            p++;
-        }
-        if (*p) p++;
-    }
-
-    mem[MEMINFO_ZRAM_TOTAL] = get_zram_mem_used() / 1024;
-    // Recompute Vmalloc Used since the value in meminfo
-    // doesn't account for I/O remapping which doesn't use RAM.
-    mem[MEMINFO_VMALLOC_USED] = get_allocated_vmalloc_memory() / 1024;
-
-    int maxNum = env->GetArrayLength(out);
-    if (maxNum > MEMINFO_COUNT) {
-        maxNum = MEMINFO_COUNT;
-    }
     jlong* outArray = env->GetLongArrayElements(out, 0);
     if (outArray != NULL) {
-        for (int i=0; i<maxNum; i++) {
+        outLen = MEMINFO_COUNT;
+        for (int i = 0; i < outLen; i++) {
+            // TODO: move get_allocated_vmalloc_memory() to libmeminfo
+            if (i == MEMINFO_VMALLOC_USED) {
+                outArray[i] = get_allocated_vmalloc_memory() / 1024;
+                continue;
+            }
             outArray[i] = mem[i];
         }
     }
+
     env->ReleaseLongArrayElements(out, outArray, 0);
 }
 
diff --git a/core/jni/android_util_Process.cpp b/core/jni/android_util_Process.cpp
index 102a0b7..0c1a8aa 100644
--- a/core/jni/android_util_Process.cpp
+++ b/core/jni/android_util_Process.cpp
@@ -25,8 +25,12 @@
 #include <cutils/sched_policy.h>
 #include <utils/String8.h>
 #include <utils/Vector.h>
+#include <meminfo/sysmeminfo.h>
 #include <processgroup/processgroup.h>
 
+#include <string>
+#include <vector>
+
 #include "core_jni_helpers.h"
 
 #include "android_util_Binder.h"
@@ -39,9 +43,11 @@
 #include <inttypes.h>
 #include <pwd.h>
 #include <signal.h>
+#include <string.h>
 #include <sys/errno.h>
 #include <sys/resource.h>
 #include <sys/stat.h>
+#include <sys/sysinfo.h>
 #include <sys/types.h>
 #include <unistd.h>
 
@@ -603,66 +609,34 @@
     return *((const jint*)v1) - *((const jint*)v2);
 }
 
-static jlong getFreeMemoryImpl(const char* const sums[], const size_t sumsLen[], size_t num)
-{
-    int fd = open("/proc/meminfo", O_RDONLY | O_CLOEXEC);
-
-    if (fd < 0) {
-        ALOGW("Unable to open /proc/meminfo");
-        return -1;
-    }
-
-    char buffer[2048];
-    const int len = read(fd, buffer, sizeof(buffer)-1);
-    close(fd);
-
-    if (len < 0) {
-        ALOGW("Unable to read /proc/meminfo");
-        return -1;
-    }
-    buffer[len] = 0;
-
-    size_t numFound = 0;
-    jlong mem = 0;
-
-    char* p = buffer;
-    while (*p && numFound < num) {
-        int i = 0;
-        while (sums[i]) {
-            if (strncmp(p, sums[i], sumsLen[i]) == 0) {
-                p += sumsLen[i];
-                while (*p == ' ') p++;
-                char* num = p;
-                while (*p >= '0' && *p <= '9') p++;
-                if (*p != 0) {
-                    *p = 0;
-                    p++;
-                    if (*p == 0) p--;
-                }
-                mem += atoll(num) * 1024;
-                numFound++;
-                break;
-            }
-            i++;
-        }
-        p++;
-    }
-
-    return numFound > 0 ? mem : -1;
-}
-
 static jlong android_os_Process_getFreeMemory(JNIEnv* env, jobject clazz)
 {
-    static const char* const sums[] = { "MemFree:", "Cached:", NULL };
-    static const size_t sumsLen[] = { strlen("MemFree:"), strlen("Cached:"), 0 };
-    return getFreeMemoryImpl(sums, sumsLen, 2);
+    static const std::vector<std::string> memFreeTags = {
+        ::android::meminfo::SysMemInfo::kMemFree,
+        ::android::meminfo::SysMemInfo::kMemCached,
+    };
+    std::vector<uint64_t> mem(memFreeTags.size());
+    ::android::meminfo::SysMemInfo smi;
+
+    if (!smi.ReadMemInfo(memFreeTags, &mem)) {
+        jniThrowRuntimeException(env, "SysMemInfo read failed to get Free Memory");
+        return -1L;
+    }
+
+    jlong sum = 0;
+    std::for_each(mem.begin(), mem.end(), [&](uint64_t val) { sum += val; });
+    return sum * 1024;
 }
 
 static jlong android_os_Process_getTotalMemory(JNIEnv* env, jobject clazz)
 {
-    static const char* const sums[] = { "MemTotal:", NULL };
-    static const size_t sumsLen[] = { strlen("MemTotal:"), 0 };
-    return getFreeMemoryImpl(sums, sumsLen, 1);
+    struct sysinfo si;
+    if (sysinfo(&si) == -1) {
+        ALOGE("sysinfo failed: %s", strerror(errno));
+        return -1;
+    }
+
+    return si.totalram;
 }
 
 void android_os_Process_readProcLines(JNIEnv* env, jobject clazz, jstring fileStr,
diff --git a/core/jni/android_view_DisplayListCanvas.cpp b/core/jni/android_view_DisplayListCanvas.cpp
index e0fd1a6..40a133b 100644
--- a/core/jni/android_view_DisplayListCanvas.cpp
+++ b/core/jni/android_view_DisplayListCanvas.cpp
@@ -168,6 +168,11 @@
     canvas->drawCircle(xProp, yProp, radiusProp, paintProp);
 }
 
+static void android_view_DisplayListCanvas_drawWebViewFunctor(jlong canvasPtr, jint functor) {
+    Canvas* canvas = reinterpret_cast<Canvas*>(canvasPtr);
+    canvas->drawWebViewFunctor(functor);
+}
+
 // ----------------------------------------------------------------------------
 // JNI Glue
 // ----------------------------------------------------------------------------
@@ -192,6 +197,7 @@
     { "nDrawTextureLayer",        "(JJ)V",      (void*) android_view_DisplayListCanvas_drawTextureLayer },
     { "nDrawCircle",              "(JJJJJ)V",   (void*) android_view_DisplayListCanvas_drawCircleProps },
     { "nDrawRoundRect",           "(JJJJJJJJ)V",(void*) android_view_DisplayListCanvas_drawRoundRectProps },
+    { "nDrawWebViewFunctor",      "(JI)V",      (void*) android_view_DisplayListCanvas_drawWebViewFunctor },
 };
 
 int register_android_view_DisplayListCanvas(JNIEnv* env) {
diff --git a/core/jni/android_view_ThreadedRenderer.cpp b/core/jni/android_view_ThreadedRenderer.cpp
index 702741e..5a8ab3c 100644
--- a/core/jni/android_view_ThreadedRenderer.cpp
+++ b/core/jni/android_view_ThreadedRenderer.cpp
@@ -31,8 +31,6 @@
 #include <gui/BufferQueue.h>
 #include <gui/Surface.h>
 
-#include <EGL/egl.h>
-#include <EGL/eglext.h>
 #include <private/EGL/cache.h>
 
 #include <utils/Looper.h>
@@ -58,6 +56,7 @@
 #include <renderthread/RenderTask.h>
 #include <renderthread/RenderThread.h>
 #include <pipeline/skia/ShaderCache.h>
+#include <utils/Color.h>
 
 namespace android {
 
@@ -1011,10 +1010,9 @@
                 buffer->getWidth(), buffer->getHeight(), width, height);
         // Continue I guess?
     }
-    sk_sp<Bitmap> bitmap = Bitmap::createFrom(buffer);
-    // Bitmap::createFrom currently can only attach to a GraphicBuffer with PIXEL_FORMAT_RGBA_8888
-    // format and SRGB color space.
-    // To support any color space, we could extract it from BufferItem and pass it to Bitmap.
+
+    sk_sp<SkColorSpace> cs = uirenderer::DataSpaceToColorSpace(bufferItem.mDataSpace);
+    sk_sp<Bitmap> bitmap = Bitmap::createFrom(buffer, cs);
     return bitmap::createBitmap(env, bitmap.release(),
             android::bitmap::kBitmapCreateFlag_Premultiplied);
 }
diff --git a/core/proto/android/service/procstats.proto b/core/proto/android/service/procstats.proto
index ce19ce3..71ebcc1 100644
--- a/core/proto/android/service/procstats.proto
+++ b/core/proto/android/service/procstats.proto
@@ -186,7 +186,7 @@
     repeated PackageServiceOperationStatsProto operation_stats = 2;
 }
 
-// Next Tag: 7
+// Next Tag: 8
 message PackageAssociationSourceProcessStatsProto {
     option (android.msg_privacy).dest = DEST_AUTOMATIC;
 
@@ -194,6 +194,8 @@
     optional int32 process_uid = 1;
     // Process name.
     optional string process_name = 2;
+    // Package name.
+    optional string package_name = 7;
 
     // Total count of the times this association appeared.
     optional int32 total_count = 3;
diff --git a/core/res/res/layout/notification_template_header.xml b/core/res/res/layout/notification_template_header.xml
index 5ba1cf2..0f53549 100644
--- a/core/res/res/layout/notification_template_header.xml
+++ b/core/res/res/layout/notification_template_header.xml
@@ -111,6 +111,7 @@
         android:background="@null"
         android:layout_width="@dimen/notification_header_expand_icon_size"
         android:layout_height="@dimen/notification_header_expand_icon_size"
+        android:layout_marginStart="4dp"
         android:paddingTop="@dimen/notification_expand_button_padding_top"
         android:visibility="gone"
         android:contentDescription="@string/expand_button_content_description_collapsed"
diff --git a/core/res/res/values-night/themes_permission_controller.xml b/core/res/res/values-night/themes_permission_controller.xml
index 0ad2bdc..a071927 100644
--- a/core/res/res/values-night/themes_permission_controller.xml
+++ b/core/res/res/values-night/themes_permission_controller.xml
@@ -28,8 +28,5 @@
     <style name="Theme.DeviceDefault.PermissionGrant"
            parent="@style/Theme.DeviceDefault.Dialog">
         <item name="titleTextStyle">@style/PermissionGrantTitleMessage</item>
-        <item name="radioButtonStyle">@style/PermissionGrantRadioButton</item>
-        <item name="checkboxStyle">@style/PermissionGrantCheckbox</item>
-        <item name="buttonBarStyle">@style/PermissionGrantButtonBar</item>
     </style>
 </resources>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index dd0b1ee..3f1f9cd 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -3492,8 +3492,6 @@
 
     <!-- Name of a font family to use for headlines. If empty, falls back to platform default -->
     <string name="config_headlineFontFamily" translatable="false"></string>
-    <!-- Name of a font family to use for headlines. Defaults to sans-serif-light -->
-    <string name="config_headlineFontFamilyLight" translatable="false">sans-serif-light</string>
     <!-- Allows setting custom fontFeatureSettings on specific text. -->
     <string name="config_headlineFontFeatureSettings" translatable="false"></string>
 
@@ -3560,8 +3558,6 @@
     <string name="config_headlineFontFamilyMedium" translateable="false">@string/font_family_button_material</string>
     <!-- Name of a font family to use for body text. -->
     <string name="config_bodyFontFamily" translatable="false">sans-serif</string>
-    <!-- Name of a font family to use for light body text. -->
-    <string name="config_bodyFontFamilyLight" translatable="false">sans-serif-light</string>
     <!-- Name of a font family to use for medium body text. -->
     <string name="config_bodyFontFamilyMedium" translatable="false">sans-serif-medium</string>
 
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 6f75d90..799d9d8 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2978,6 +2978,11 @@
         <public name="config_mediaMetadataBitmapMaxSize" />
     </public-group>
 
+    <public-group type="color" first-id="0x0106001c">
+        <!-- @hide @SystemApi -->
+        <public name="system_notification_accent_color" />
+    </public-group>
+
   <!-- ===============================================================
        DO NOT ADD UN-GROUPED ITEMS HERE
 
diff --git a/core/res/res/values/styles_permission_controller.xml b/core/res/res/values/styles_permission_controller.xml
index e6e0de3..5a9d3e6 100644
--- a/core/res/res/values/styles_permission_controller.xml
+++ b/core/res/res/values/styles_permission_controller.xml
@@ -25,15 +25,16 @@
     </style>
 
     <style name="PermissionGrantTitleIcon">
-        <item name="layout_width">36dp</item>
-        <item name="layout_height">36dp</item>
+        <item name="layout_width">24dp</item>
+        <item name="layout_height">24dp</item>
+        <item name="layout_marginBottom">12dp</item>
         <item name="tint">?attr/colorAccent</item>
         <item name="scaleType">fitCenter</item>
     </style>
 
     <style name="PermissionGrantTitleMessage"
            parent="@style/TextAppearance.DeviceDefault">
-        <item name="paddingStart">22dp</item>
+        <item name="gravity">center</item>
         <item name="textSize">20sp</item>
         <item name="textColor">?attr/textColorPrimary</item>
     </style>
@@ -46,56 +47,22 @@
     </style>
 
     <style name="PermissionGrantDescription">
-        <item name="layout_marginTop">20dp</item>
         <item name="layout_marginStart">24dp</item>
-        <item name="layout_marginBottom">16dp</item>
         <item name="layout_marginEnd">24dp</item>
     </style>
 
     <style name="PermissionGrantContent">
-        <item name="layout_marginStart">16dp</item>
+        <item name="layout_marginStart">24dp</item>
         <item name="layout_marginEnd">24dp</item>
     </style>
 
-    <style name="PermissionGrantRadioGroup">
-        <item name="layout_marginStart">8dp</item>
-        <item name="layout_marginTop">-4dp</item>
-    </style>
-
-    <style name="PermissionGrantRadioButton"
-           parent="@style/Widget.DeviceDefault.CompoundButton.RadioButton">
-        <item name="paddingStart">16dp</item>
-        <item name="paddingTop">8dp</item>
-        <item name="paddingBottom">8dp</item>
-        <item name="textSize">16sp</item>
-    </style>
-
     <style name="PermissionGrantDetailMessage"
            parent="@style/TextAppearance.DeviceDefault">
-        <item name="layout_marginStart">8dp</item>
-        <item name="layout_marginBottom">4dp</item>
+        <item name="layout_marginTop">18dp</item>
         <item name="textColor">?attr/textColorPrimary</item>
         <item name="textSize">16sp</item>
     </style>
 
-    <style name="PermissionGrantDetailMessageSpace">
-        <item name="layout_height">8dp</item>
-    </style>
-
-    <style name="PermissionGrantCheckbox"
-           parent="@style/Widget.DeviceDefault.CompoundButton.CheckBox">
-        <item name="paddingStart">16dp</item>
-        <item name="textColor">?attr/textColorSecondary</item>
-        <item name="buttonTint">?attr/textColorSecondary</item>
-        <item name="textSize">16sp</item>
-    </style>
-
-    <style name="PermissionGrantButtonBar">
-        <item name="layout_marginStart">24dp</item>
-        <item name="layout_marginEnd">16dp</item>
-        <item name="layout_marginBottom">4dp</item>
-    </style>
-
     <!-- styles for the permission review screen. -->
     <style name="PermissionReviewDescription">
         <item name="layout_marginTop">20dp</item>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 87fdc1f..ed9f3b1 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1,4 +1,3 @@
-<?xml version="1.0" encoding="utf-8"?>
 <!--
 /* Copyright 2012, The Android Open Source Project
 **
@@ -3328,7 +3327,6 @@
   <java-symbol type="bool" name="config_displayBrightnessBucketsInDoze" />
   <java-symbol type="integer" name="config_storageManagerDaystoRetainDefault" />
   <java-symbol type="string" name="config_headlineFontFamily" />
-  <java-symbol type="string" name="config_headlineFontFamilyLight" />
   <java-symbol type="string" name="config_headlineFontFamilyMedium" />
 
   <java-symbol type="drawable" name="stat_sys_vitals" />
diff --git a/core/res/res/values/themes_permission_controller.xml b/core/res/res/values/themes_permission_controller.xml
index 369cee3..205c4eb 100644
--- a/core/res/res/values/themes_permission_controller.xml
+++ b/core/res/res/values/themes_permission_controller.xml
@@ -28,9 +28,6 @@
     <style name="Theme.DeviceDefault.PermissionGrant"
            parent="@style/Theme.DeviceDefault.Light.Dialog">
         <item name="titleTextStyle">@style/PermissionGrantTitleMessage</item>
-        <item name="radioButtonStyle">@style/PermissionGrantRadioButton</item>
-        <item name="checkboxStyle">@style/PermissionGrantCheckbox</item>
-        <item name="buttonBarStyle">@style/PermissionGrantButtonBar</item>
     </style>
 
     <!-- themes for the permission review dialog. -->
@@ -39,6 +36,5 @@
         <item name="windowActionBar">false</item>
         <item name="windowNoTitle">true</item>
         <item name="titleTextStyle">@style/PermissionReviewTitleMessage</item>
-        <item name="buttonBarStyle">@style/PermissionReviewButtonBar</item>
     </style>
 </resources>
diff --git a/core/tests/overlaytests/device/src/com/android/overlaytest/OverlayBaseTest.java b/core/tests/overlaytests/device/src/com/android/overlaytest/OverlayBaseTest.java
index 96ab977..5a86885 100644
--- a/core/tests/overlaytests/device/src/com/android/overlaytest/OverlayBaseTest.java
+++ b/core/tests/overlaytests/device/src/com/android/overlaytest/OverlayBaseTest.java
@@ -278,13 +278,12 @@
             }
         }
 
-        final String no = "Lorem ipsum dolor sit amet, consectetur adipisicing elit, " +
-            "sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. " +
-            "Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip " +
-            "ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit " +
-            "esse cillum dolore eu fugiat nulla pariatur. " +
-            "Excepteur sint occaecat cupidatat non proident, " +
-            "sunt in culpa qui officia deserunt mollit anim id est laborum.";
+        final String no = "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do "
+                + "eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim "
+                + "veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo "
+                + "consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse "
+                + "cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non "
+                + "proident, sunt in culpa qui officia deserunt mollit anim id est laborum.";
         final String so = "Lorem ipsum: single overlay.";
         final String mo = "Lorem ipsum: multiple overlays.";
 
diff --git a/core/tests/overlaytests/host/src/com/android/server/om/hosttest/InstallOverlayTests.java b/core/tests/overlaytests/host/src/com/android/server/om/hosttest/InstallOverlayTests.java
index 6d5276f..27986cc 100644
--- a/core/tests/overlaytests/host/src/com/android/server/om/hosttest/InstallOverlayTests.java
+++ b/core/tests/overlaytests/host/src/com/android/server/om/hosttest/InstallOverlayTests.java
@@ -1,17 +1,17 @@
 /*
  * 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
+ * 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
+ *      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.
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
  */
 package com.android.server.om.hosttest;
 
diff --git a/core/tests/overlaytests/host/test-apps/UpdateOverlay/Android.mk b/core/tests/overlaytests/host/test-apps/UpdateOverlay/Android.mk
index 8656781..c7b2dd1 100644
--- a/core/tests/overlaytests/host/test-apps/UpdateOverlay/Android.mk
+++ b/core/tests/overlaytests/host/test-apps/UpdateOverlay/Android.mk
@@ -20,7 +20,7 @@
 LOCAL_PACKAGE_NAME := OverlayHostTests_UpdateOverlay
 LOCAL_SDK_VERSION := current
 LOCAL_COMPATIBILITY_SUITE := general-tests
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
+LOCAL_STATIC_JAVA_LIBRARIES := androidx.test.rules
 LOCAL_USE_AAPT2 := true
 LOCAL_AAPT_FLAGS := --no-resource-removal
 include $(BUILD_PACKAGE)
diff --git a/core/tests/overlaytests/host/test-apps/UpdateOverlay/AndroidManifest.xml b/core/tests/overlaytests/host/test-apps/UpdateOverlay/AndroidManifest.xml
index 06077a7..8b5fe99 100644
--- a/core/tests/overlaytests/host/test-apps/UpdateOverlay/AndroidManifest.xml
+++ b/core/tests/overlaytests/host/test-apps/UpdateOverlay/AndroidManifest.xml
@@ -21,7 +21,7 @@
         <uses-library android:name="android.test.runner" />
     </application>
 
-    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+    <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
         android:targetPackage="com.android.server.om.hosttest.update_overlay_test"
         android:label="Update Overlay Test"/>
 </manifest>
diff --git a/core/tests/overlaytests/host/test-apps/UpdateOverlay/src/com/android/server/om/hosttest/update_overlay_test/UpdateOverlayTest.java b/core/tests/overlaytests/host/test-apps/UpdateOverlay/src/com/android/server/om/hosttest/update_overlay_test/UpdateOverlayTest.java
index a174d77..fef6320 100644
--- a/core/tests/overlaytests/host/test-apps/UpdateOverlay/src/com/android/server/om/hosttest/update_overlay_test/UpdateOverlayTest.java
+++ b/core/tests/overlaytests/host/test-apps/UpdateOverlay/src/com/android/server/om/hosttest/update_overlay_test/UpdateOverlayTest.java
@@ -1,17 +1,17 @@
 /*
  * 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
+ * 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
+ *      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.
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
  */
 package com.android.server.om.hosttest.update_overlay_test;
 
@@ -19,9 +19,10 @@
 
 import android.content.res.Configuration;
 import android.content.res.Resources;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Before;
 import org.junit.Test;
diff --git a/core/tests/packagemanagertests/Android.mk b/core/tests/packagemanagertests/Android.mk
index f95231f..8c00d14 100644
--- a/core/tests/packagemanagertests/Android.mk
+++ b/core/tests/packagemanagertests/Android.mk
@@ -9,7 +9,7 @@
     $(call all-java-files-under, src)
 
 LOCAL_STATIC_JAVA_LIBRARIES := \
-    android-support-test \
+    androidx.test.rules \
     frameworks-base-testutils \
     mockito-target-minus-junit4
 
diff --git a/core/tests/packagemanagertests/AndroidManifest.xml b/core/tests/packagemanagertests/AndroidManifest.xml
index 8f49008..ee4a775 100644
--- a/core/tests/packagemanagertests/AndroidManifest.xml
+++ b/core/tests/packagemanagertests/AndroidManifest.xml
@@ -24,7 +24,7 @@
     </application>
 
     <instrumentation
-            android:name="android.support.test.runner.AndroidJUnitRunner"
+            android:name="androidx.test.runner.AndroidJUnitRunner"
             android:targetPackage="com.android.frameworks.coretests.packagemanager"
             android:label="Frameworks PackageManager Core Tests" />
 
diff --git a/core/tests/packagemanagertests/src/android/content/pm/KernelPackageMappingTests.java b/core/tests/packagemanagertests/src/android/content/pm/KernelPackageMappingTests.java
index 4e0f2a8..c5c9700 100644
--- a/core/tests/packagemanagertests/src/android/content/pm/KernelPackageMappingTests.java
+++ b/core/tests/packagemanagertests/src/android/content/pm/KernelPackageMappingTests.java
@@ -22,10 +22,11 @@
 import android.os.FileUtils;
 import android.os.ServiceManager;
 import android.os.UserManager;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.runner.AndroidJUnit4;
 import android.util.Log;
 
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
 import org.junit.After;
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/privacytests/Android.mk b/core/tests/privacytests/Android.mk
index 3c1526b..7765977 100644
--- a/core/tests/privacytests/Android.mk
+++ b/core/tests/privacytests/Android.mk
@@ -8,7 +8,7 @@
 LOCAL_SRC_FILES := \
     $(call all-java-files-under, src)
 
-LOCAL_STATIC_JAVA_LIBRARIES := junit rappor-tests android-support-test truth-prebuilt
+LOCAL_STATIC_JAVA_LIBRARIES := junit rappor-tests androidx.test.rules truth-prebuilt
 
 LOCAL_JAVA_LIBRARIES := android.test.runner
 LOCAL_PACKAGE_NAME := FrameworksPrivacyLibraryTests
diff --git a/core/tests/privacytests/AndroidManifest.xml b/core/tests/privacytests/AndroidManifest.xml
index a0e5281..3c82614 100644
--- a/core/tests/privacytests/AndroidManifest.xml
+++ b/core/tests/privacytests/AndroidManifest.xml
@@ -22,7 +22,7 @@
     </application>
 
     <instrumentation
-            android:name="android.support.test.runner.AndroidJUnitRunner"
+            android:name="androidx.test.runner.AndroidJUnitRunner"
             android:targetPackage="com.android.frameworks.coretests.privacy"
             android:label="Frameworks Privacy Library Tests" />
 
diff --git a/core/tests/privacytests/src/android/privacy/LongitudinalReportingEncoderTest.java b/core/tests/privacytests/src/android/privacy/LongitudinalReportingEncoderTest.java
index c88a722..18928eb 100644
--- a/core/tests/privacytests/src/android/privacy/LongitudinalReportingEncoderTest.java
+++ b/core/tests/privacytests/src/android/privacy/LongitudinalReportingEncoderTest.java
@@ -17,21 +17,20 @@
 package android.privacy;
 
 import static com.google.common.truth.Truth.assertThat;
+
 import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
 import android.privacy.internal.longitudinalreporting.LongitudinalReportingConfig;
 import android.privacy.internal.longitudinalreporting.LongitudinalReportingEncoder;
 
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
 import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
 import java.nio.charset.StandardCharsets;
 import java.security.MessageDigest;
 
diff --git a/core/tests/privacytests/src/android/privacy/RapporEncoderTest.java b/core/tests/privacytests/src/android/privacy/RapporEncoderTest.java
index 71bd8f1..4a35350 100644
--- a/core/tests/privacytests/src/android/privacy/RapporEncoderTest.java
+++ b/core/tests/privacytests/src/android/privacy/RapporEncoderTest.java
@@ -22,8 +22,9 @@
 
 import android.privacy.internal.rappor.RapporConfig;
 import android.privacy.internal.rappor.RapporEncoder;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/utiltests/Android.mk b/core/tests/utiltests/Android.mk
index 5c60c81..343c07a 100644
--- a/core/tests/utiltests/Android.mk
+++ b/core/tests/utiltests/Android.mk
@@ -15,7 +15,7 @@
 LOCAL_JNI_SHARED_LIBRARIES := libmemoryintarraytest libcutils libc++
 
 LOCAL_STATIC_JAVA_LIBRARIES := \
-    android-support-test \
+    androidx.test.rules \
     frameworks-base-testutils \
     mockito-target-minus-junit4 \
 
diff --git a/core/tests/utiltests/AndroidManifest.xml b/core/tests/utiltests/AndroidManifest.xml
index 8db81ca..4ef4b1f 100644
--- a/core/tests/utiltests/AndroidManifest.xml
+++ b/core/tests/utiltests/AndroidManifest.xml
@@ -51,7 +51,7 @@
     </application>
 
     <instrumentation
-        android:name="android.support.test.runner.AndroidJUnitRunner"
+        android:name="androidx.test.runner.AndroidJUnitRunner"
         android:targetPackage="com.android.frameworks.utiltests"
         android:label="Frameworks Utility Tests" />
 
diff --git a/core/tests/utiltests/runtests.sh b/core/tests/utiltests/runtests.sh
index 853119f..3b46cbb 100755
--- a/core/tests/utiltests/runtests.sh
+++ b/core/tests/utiltests/runtests.sh
@@ -21,4 +21,4 @@
 
 adb install -r -g "$OUT/data/app/FrameworksUtilTests/FrameworksUtilTests.apk"
 
-adb shell am instrument -w "$@" 'com.android.frameworks.utiltests/android.support.test.runner.AndroidJUnitRunner'
+adb shell am instrument -w "$@" 'com.android.frameworks.utiltests/androidx.test.runner.AndroidJUnitRunner'
diff --git a/core/tests/utiltests/src/android/util/IntArrayTest.java b/core/tests/utiltests/src/android/util/IntArrayTest.java
index a6120a1..a76c640 100644
--- a/core/tests/utiltests/src/android/util/IntArrayTest.java
+++ b/core/tests/utiltests/src/android/util/IntArrayTest.java
@@ -19,8 +19,9 @@
 import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
 
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
diff --git a/core/tests/utiltests/src/android/util/LongArrayTest.java b/core/tests/utiltests/src/android/util/LongArrayTest.java
index a7afcbd..a9a168b 100644
--- a/core/tests/utiltests/src/android/util/LongArrayTest.java
+++ b/core/tests/utiltests/src/android/util/LongArrayTest.java
@@ -19,8 +19,9 @@
 import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
 
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
diff --git a/core/tests/utiltests/src/android/util/MemoryIntArrayTest.java b/core/tests/utiltests/src/android/util/MemoryIntArrayTest.java
index 24b33ef..2daefe7 100644
--- a/core/tests/utiltests/src/android/util/MemoryIntArrayTest.java
+++ b/core/tests/utiltests/src/android/util/MemoryIntArrayTest.java
@@ -24,8 +24,11 @@
 
 import android.os.Parcel;
 import android.os.ParcelFileDescriptor;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.runner.AndroidJUnit4;
+
 import libcore.io.IoUtils;
+
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
diff --git a/core/tests/utiltests/src/android/util/RemoteIntArray.java b/core/tests/utiltests/src/android/util/RemoteIntArray.java
index 11d0888..4a3a01e 100644
--- a/core/tests/utiltests/src/android/util/RemoteIntArray.java
+++ b/core/tests/utiltests/src/android/util/RemoteIntArray.java
@@ -24,7 +24,8 @@
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.SystemClock;
-import android.support.test.InstrumentationRegistry;
+
+import androidx.test.InstrumentationRegistry;
 
 import java.io.Closeable;
 import java.io.IOException;
diff --git a/core/tests/utiltests/src/com/android/internal/util/LockPatternUtilsTest.java b/core/tests/utiltests/src/com/android/internal/util/LockPatternUtilsTest.java
index b18ee17..03cf3eb 100644
--- a/core/tests/utiltests/src/com/android/internal/util/LockPatternUtilsTest.java
+++ b/core/tests/utiltests/src/com/android/internal/util/LockPatternUtilsTest.java
@@ -29,11 +29,12 @@
 import android.content.pm.UserInfo;
 import android.os.UserManager;
 import android.provider.Settings;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
-
 import android.test.mock.MockContentResolver;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
 import com.android.internal.util.test.FakeSettingsProvider;
 import com.android.internal.widget.ILockSettings;
 import com.android.internal.widget.LockPatternUtils;
diff --git a/data/etc/Android.bp b/data/etc/Android.bp
new file mode 100644
index 0000000..25dabad
--- /dev/null
+++ b/data/etc/Android.bp
@@ -0,0 +1,64 @@
+// 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.
+
+
+// Sysconfig files
+
+prebuilt_etc {
+    name: "framework-sysconfig.xml",
+    sub_dir: "sysconfig",
+    src: "framework-sysconfig.xml",
+}
+
+prebuilt_etc {
+    name: "hiddenapi-package-whitelist.xml",
+    sub_dir: "sysconfig",
+    src: "hiddenapi-package-whitelist.xml",
+}
+
+// Privapp permission whitelist files
+
+prebuilt_etc {
+    name: "platform.xml",
+    sub_dir: "permissions",
+    src: "platform.xml",
+}
+
+prebuilt_etc {
+    name: "privapp-permissions-platform.xml",
+    sub_dir: "permissions",
+    src: "privapp-permissions-platform.xml",
+}
+
+prebuilt_etc {
+    name: "privapp_whitelist_com.android.settings",
+    product_specific: true,
+    sub_dir: "permissions",
+    src: "com.android.settings.xml",
+    filename_from_src: true,
+}
+
+prebuilt_etc {
+    name: "privapp_whitelist_com.android.systemui",
+    product_specific: true,
+    sub_dir: "permissions",
+    src: "com.android.systemui.xml",
+    filename_from_src: true,
+}
+
+prebuilt_etc {
+    name: "com.android.timezone.updater.xml",
+    sub_dir: "permissions",
+    src: "com.android.timezone.updater.xml",
+}
diff --git a/data/etc/Android.mk b/data/etc/Android.mk
deleted file mode 100644
index 61ef426..0000000
--- a/data/etc/Android.mk
+++ /dev/null
@@ -1,76 +0,0 @@
-#
-# Copyright (C) 2008 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.
-#
-
-LOCAL_PATH := $(my-dir)
-
-########################
-include $(CLEAR_VARS)
-LOCAL_MODULE := framework-sysconfig.xml
-LOCAL_MODULE_CLASS := ETC
-LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/sysconfig
-LOCAL_SRC_FILES := $(LOCAL_MODULE)
-include $(BUILD_PREBUILT)
-
-########################
-include $(CLEAR_VARS)
-LOCAL_MODULE := platform.xml
-LOCAL_MODULE_CLASS := ETC
-LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/permissions
-LOCAL_SRC_FILES := $(LOCAL_MODULE)
-include $(BUILD_PREBUILT)
-
-########################
-include $(CLEAR_VARS)
-LOCAL_MODULE := privapp-permissions-platform.xml
-LOCAL_MODULE_CLASS := ETC
-LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/permissions
-LOCAL_SRC_FILES := $(LOCAL_MODULE)
-include $(BUILD_PREBUILT)
-
-########################
-include $(CLEAR_VARS)
-LOCAL_MODULE := hiddenapi-package-whitelist.xml
-LOCAL_MODULE_CLASS := ETC
-LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/sysconfig
-LOCAL_SRC_FILES := $(LOCAL_MODULE)
-include $(BUILD_PREBUILT)
-
-########################
-include $(CLEAR_VARS)
-LOCAL_MODULE := privapp_whitelist_com.android.settings
-LOCAL_MODULE_CLASS := ETC
-LOCAL_MODULE_RELATIVE_PATH := permissions
-LOCAL_MODULE_STEM := com.android.settings.xml
-LOCAL_SRC_FILES := com.android.settings.xml
-include $(BUILD_PREBUILT)
-
-########################
-include $(CLEAR_VARS)
-LOCAL_MODULE := privapp_whitelist_com.android.systemui
-LOCAL_MODULE_CLASS := ETC
-LOCAL_MODULE_RELATIVE_PATH := permissions
-LOCAL_MODULE_STEM := com.android.systemui.xml
-LOCAL_SRC_FILES := com.android.systemui.xml
-include $(BUILD_PREBUILT)
-
-
-########################
-include $(CLEAR_VARS)
-LOCAL_MODULE := com.android.timezone.updater.xml
-LOCAL_MODULE_CLASS := ETC
-LOCAL_MODULE_RELATIVE_PATH := permissions
-LOCAL_SRC_FILES := $(LOCAL_MODULE)
-include $(BUILD_PREBUILT)
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index af570b3..c216425 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -297,6 +297,8 @@
         <permission name="android.permission.POWER_SAVER" />
         <permission name="android.permission.READ_FRAME_BUFFER"/>
         <permission name="android.permission.READ_LOWPAN_CREDENTIAL"/>
+        <!-- Needed for test only -->
+        <permission name="android.permission.READ_PRECISE_PHONE_STATE" />
         <permission name="android.permission.READ_PRIVILEGED_PHONE_STATE"/>
         <permission name="android.permission.REAL_GET_TASKS"/>
         <permission name="android.permission.READ_PRIVILEGED_PHONE_STATE"/>
diff --git a/graphics/java/android/graphics/RecordingCanvas.java b/graphics/java/android/graphics/RecordingCanvas.java
index 67ad404..515532f 100644
--- a/graphics/java/android/graphics/RecordingCanvas.java
+++ b/graphics/java/android/graphics/RecordingCanvas.java
@@ -189,6 +189,14 @@
         nCallDrawGLFunction(mNativeCanvasWrapper, drawGLFunctor, releasedCallback);
     }
 
+    /**
+     * Calls the provided functor that was created via WebViewFunctor_create()
+     * @hide
+     */
+    public void drawWebViewFunctor(int functor) {
+        nDrawWebViewFunctor(mNativeCanvasWrapper, functor);
+    }
+
     ///////////////////////////////////////////////////////////////////////////
     // Display list
     ///////////////////////////////////////////////////////////////////////////
@@ -303,4 +311,6 @@
     @CriticalNative
     private static native void nDrawRoundRect(long renderer, long propLeft, long propTop,
             long propRight, long propBottom, long propRx, long propRy, long propPaint);
+    @CriticalNative
+    private static native void nDrawWebViewFunctor(long canvas, int functor);
 }
diff --git a/libs/androidfw/Android.bp b/libs/androidfw/Android.bp
index 98af3eb..eeaefc5 100644
--- a/libs/androidfw/Android.bp
+++ b/libs/androidfw/Android.bp
@@ -167,6 +167,7 @@
         },
     },
     data: ["tests/data/**/*.apk"],
+    test_suites: ["device-tests"],
 }
 
 cc_benchmark {
diff --git a/libs/androidfw/LoadedArsc.cpp b/libs/androidfw/LoadedArsc.cpp
index c20c720..5a26780 100644
--- a/libs/androidfw/LoadedArsc.cpp
+++ b/libs/androidfw/LoadedArsc.cpp
@@ -623,7 +623,7 @@
               }
 
               // Add the pairing of overlayable properties to resource ids to the package
-              OverlayableInfo overlayable_info;
+              OverlayableInfo overlayable_info{};
               overlayable_info.policy_flags = policy_header->policy_flags;
               loaded_package->overlayable_infos_.push_back(std::make_pair(overlayable_info, ids));
               break;
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index 6585bfc..7e69e3a 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -9,6 +9,8 @@
         "hwui_lto",
     ],
 
+    cpp_std: "experimental",
+
     cflags: [
         "-DEGL_EGLEXT_PROTOTYPES",
         "-DGL_GLEXT_PROTOTYPES",
@@ -226,6 +228,7 @@
         "RenderProperties.cpp",
         "SkiaCanvas.cpp",
         "TreeInfo.cpp",
+        "WebViewFunctorManager.cpp",
         "VectorDrawable.cpp",
         "protos/graphicsstats.proto",
     ],
@@ -330,6 +333,7 @@
         "tests/unit/TypefaceTests.cpp",
         "tests/unit/VectorDrawableTests.cpp",
         "tests/unit/VectorDrawableAtlasTests.cpp",
+        "tests/unit/WebViewFunctorManagerTests.cpp",
     ],
 }
 
diff --git a/libs/hwui/HardwareBitmapUploader.cpp b/libs/hwui/HardwareBitmapUploader.cpp
index a97c12c..b9860ad 100644
--- a/libs/hwui/HardwareBitmapUploader.cpp
+++ b/libs/hwui/HardwareBitmapUploader.cpp
@@ -253,7 +253,8 @@
         eglDestroySyncKHR(display, fence);
     }
 
-    return sk_sp<Bitmap>(new Bitmap(buffer.get(), bitmap.info(), Bitmap::computePalette(bitmap)));
+    return Bitmap::createFrom(buffer.get(), bitmap.refColorSpace(), bitmap.alphaType(),
+                              Bitmap::computePalette(bitmap));
 }
 
 }  // namespace android::uirenderer
diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp
index 00ce28a..1ff7ffe 100644
--- a/libs/hwui/RenderNode.cpp
+++ b/libs/hwui/RenderNode.cpp
@@ -288,7 +288,10 @@
     mDisplayList = mStagingDisplayList;
     mStagingDisplayList = nullptr;
     if (mDisplayList) {
-        mDisplayList->syncContents();
+        WebViewSyncData syncData {
+            .applyForceDark = info && !info->disableForceDark
+        };
+        mDisplayList->syncContents(syncData);
         handleForceDark(info);
     }
 }
diff --git a/libs/hwui/WebViewFunctorManager.cpp b/libs/hwui/WebViewFunctorManager.cpp
new file mode 100644
index 0000000..20e77b4
--- /dev/null
+++ b/libs/hwui/WebViewFunctorManager.cpp
@@ -0,0 +1,167 @@
+/*
+ * 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.
+ */
+
+#include "WebViewFunctorManager.h"
+
+#include <private/hwui/WebViewFunctor.h>
+#include "Properties.h"
+
+#include <log/log.h>
+#include <utils/Trace.h>
+#include <atomic>
+
+namespace android::uirenderer {
+
+RenderMode WebViewFunctor_queryPlatformRenderMode() {
+    auto pipelineType = Properties::getRenderPipelineType();
+    switch (pipelineType) {
+        case RenderPipelineType::SkiaGL:
+            return RenderMode::OpenGL_ES;
+        case RenderPipelineType::SkiaVulkan:
+            return RenderMode::Vulkan;
+        default:
+            LOG_ALWAYS_FATAL("Unknown render pipeline type: %d", (int)pipelineType);
+    }
+}
+
+int WebViewFunctor_create(const WebViewFunctorCallbacks& prototype, RenderMode functorMode) {
+    if (functorMode != RenderMode::OpenGL_ES && functorMode != RenderMode::Vulkan) {
+        ALOGW("Unknown rendermode %d", (int)functorMode);
+        return -1;
+    }
+    if (functorMode == RenderMode::Vulkan &&
+        WebViewFunctor_queryPlatformRenderMode() != RenderMode::Vulkan) {
+        ALOGW("Unable to map from GLES platform to a vulkan functor");
+        return -1;
+    }
+    return WebViewFunctorManager::instance().createFunctor(prototype, functorMode);
+}
+
+void WebViewFunctor_release(int functor) {
+    WebViewFunctorManager::instance().releaseFunctor(functor);
+}
+
+static std::atomic_int sNextId{1};
+
+WebViewFunctor::WebViewFunctor(const WebViewFunctorCallbacks& callbacks, RenderMode functorMode) {
+    mFunctor = sNextId++;
+    mCallbacks = callbacks;
+    mMode = functorMode;
+}
+
+WebViewFunctor::~WebViewFunctor() {
+    destroyContext();
+
+    ATRACE_NAME("WebViewFunctor::onDestroy");
+    mCallbacks.onDestroyed(mFunctor);
+}
+
+void WebViewFunctor::sync(const WebViewSyncData& syncData) const {
+    ATRACE_NAME("WebViewFunctor::sync");
+    mCallbacks.onSync(mFunctor, syncData);
+}
+
+void WebViewFunctor::drawGl(const DrawGlInfo& drawInfo) {
+    ATRACE_NAME("WebViewFunctor::drawGl");
+    if (!mHasContext) {
+        mHasContext = true;
+    }
+    mCallbacks.gles.draw(mFunctor, drawInfo);
+}
+
+void WebViewFunctor::destroyContext() {
+    if (mHasContext) {
+        mHasContext = false;
+        ATRACE_NAME("WebViewFunctor::onContextDestroyed");
+        mCallbacks.onContextDestroyed(mFunctor);
+    }
+}
+
+WebViewFunctorManager& WebViewFunctorManager::instance() {
+    static WebViewFunctorManager sInstance;
+    return sInstance;
+}
+
+int WebViewFunctorManager::createFunctor(const WebViewFunctorCallbacks& callbacks,
+                                         RenderMode functorMode) {
+    auto object = std::make_unique<WebViewFunctor>(callbacks, functorMode);
+    int id = object->id();
+    auto handle = object->createHandle();
+    {
+        std::lock_guard _lock{mLock};
+        mActiveFunctors.push_back(std::move(handle));
+        mFunctors.push_back(std::move(object));
+    }
+    return id;
+}
+
+void WebViewFunctorManager::releaseFunctor(int functor) {
+    sp<WebViewFunctor::Handle> toRelease;
+    {
+        std::lock_guard _lock{mLock};
+        for (auto iter = mActiveFunctors.begin(); iter != mActiveFunctors.end(); iter++) {
+            if ((*iter)->id() == functor) {
+                toRelease = std::move(*iter);
+                mActiveFunctors.erase(iter);
+                break;
+            }
+        }
+    }
+}
+
+void WebViewFunctorManager::onContextDestroyed() {
+    // WARNING: SKETCHY
+    // Because we know that we always remove from mFunctors on RenderThread, the same
+    // thread that always invokes onContextDestroyed, we know that the functor pointers
+    // will remain valid without the lock held.
+    // However, we won't block new functors from being added in the meantime.
+    mLock.lock();
+    const size_t size = mFunctors.size();
+    WebViewFunctor* toDestroyContext[size];
+    for (size_t i = 0; i < size; i++) {
+        toDestroyContext[i] = mFunctors[i].get();
+    }
+    mLock.unlock();
+    for (size_t i = 0; i < size; i++) {
+        toDestroyContext[i]->destroyContext();
+    }
+}
+
+void WebViewFunctorManager::destroyFunctor(int functor) {
+    std::unique_ptr<WebViewFunctor> toRelease;
+    {
+        std::lock_guard _lock{mLock};
+        for (auto iter = mFunctors.begin(); iter != mFunctors.end(); iter++) {
+            if ((*iter)->id() == functor) {
+                toRelease = std::move(*iter);
+                mFunctors.erase(iter);
+                break;
+            }
+        }
+    }
+}
+
+sp<WebViewFunctor::Handle> WebViewFunctorManager::handleFor(int functor) {
+    std::lock_guard _lock{mLock};
+    for (auto& iter : mActiveFunctors) {
+        if (iter->id() == functor) {
+            return iter;
+        }
+    }
+    return nullptr;
+}
+
+}  // namespace android::uirenderer
\ No newline at end of file
diff --git a/libs/hwui/WebViewFunctorManager.h b/libs/hwui/WebViewFunctorManager.h
new file mode 100644
index 0000000..2a621dd
--- /dev/null
+++ b/libs/hwui/WebViewFunctorManager.h
@@ -0,0 +1,92 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <private/hwui/WebViewFunctor.h>
+#include <renderthread/RenderProxy.h>
+
+#include <utils/LightRefBase.h>
+#include <mutex>
+#include <vector>
+
+namespace android::uirenderer {
+
+class WebViewFunctorManager;
+
+class WebViewFunctor {
+public:
+    WebViewFunctor(const WebViewFunctorCallbacks& callbacks, RenderMode functorMode);
+    ~WebViewFunctor();
+
+    class Handle : public LightRefBase<Handle> {
+    public:
+        ~Handle() { renderthread::RenderProxy::destroyFunctor(id()); }
+
+        int id() const { return mReference.id(); }
+
+        void sync(const WebViewSyncData& syncData) const { mReference.sync(syncData); }
+
+        void drawGl(const DrawGlInfo& drawInfo) const { mReference.drawGl(drawInfo); }
+
+    private:
+        friend class WebViewFunctor;
+
+        Handle(WebViewFunctor& ref) : mReference(ref) {}
+
+        WebViewFunctor& mReference;
+    };
+
+    int id() const { return mFunctor; }
+    void sync(const WebViewSyncData& syncData) const;
+    void drawGl(const DrawGlInfo& drawInfo);
+    void destroyContext();
+
+    sp<Handle> createHandle() {
+        LOG_ALWAYS_FATAL_IF(mCreatedHandle);
+        mCreatedHandle = true;
+        return sp<Handle>{new Handle(*this)};
+    }
+
+private:
+    WebViewFunctorCallbacks mCallbacks;
+    int mFunctor;
+    RenderMode mMode;
+    bool mHasContext = false;
+    bool mCreatedHandle = false;
+};
+
+class WebViewFunctorManager {
+public:
+    static WebViewFunctorManager& instance();
+
+    int createFunctor(const WebViewFunctorCallbacks& callbacks, RenderMode functorMode);
+    void releaseFunctor(int functor);
+    void onContextDestroyed();
+    void destroyFunctor(int functor);
+
+    sp<WebViewFunctor::Handle> handleFor(int functor);
+
+private:
+    WebViewFunctorManager() = default;
+    ~WebViewFunctorManager() = default;
+
+    std::mutex mLock;
+    std::vector<std::unique_ptr<WebViewFunctor>> mFunctors;
+    std::vector<sp<WebViewFunctor::Handle>> mActiveFunctors;
+};
+
+}  // namespace android::uirenderer
diff --git a/libs/hwui/hwui/Bitmap.cpp b/libs/hwui/hwui/Bitmap.cpp
index 6c77f9e..6e0258c 100644
--- a/libs/hwui/hwui/Bitmap.cpp
+++ b/libs/hwui/hwui/Bitmap.cpp
@@ -75,31 +75,6 @@
     return allocateBitmap(bitmap, &Bitmap::allocateAshmemBitmap);
 }
 
-static sk_sp<Bitmap> allocateHeapBitmap(size_t size, const SkImageInfo& info, size_t rowBytes) {
-    void* addr = calloc(size, 1);
-    if (!addr) {
-        return nullptr;
-    }
-    return sk_sp<Bitmap>(new Bitmap(addr, size, info, rowBytes));
-}
-
-sk_sp<Bitmap> Bitmap::allocateHardwareBitmap(SkBitmap& bitmap) {
-    return uirenderer::HardwareBitmapUploader::allocateHardwareBitmap(bitmap);
-}
-
-sk_sp<Bitmap> Bitmap::allocateHeapBitmap(SkBitmap* bitmap) {
-    return allocateBitmap(bitmap, &android::allocateHeapBitmap);
-}
-
-sk_sp<Bitmap> Bitmap::allocateHeapBitmap(const SkImageInfo& info) {
-    size_t size;
-    if (!computeAllocationSize(info.minRowBytes(), info.height(), &size)) {
-        LOG_ALWAYS_FATAL("trying to allocate too large bitmap");
-        return nullptr;
-    }
-    return android::allocateHeapBitmap(size, info, info.minRowBytes());
-}
-
 sk_sp<Bitmap> Bitmap::allocateAshmemBitmap(size_t size, const SkImageInfo& info, size_t rowBytes) {
     // Create new ashmem region with read/write priv
     int fd = ashmem_create_region("bitmap", size);
@@ -121,6 +96,31 @@
     return sk_sp<Bitmap>(new Bitmap(addr, fd, size, info, rowBytes));
 }
 
+sk_sp<Bitmap> Bitmap::allocateHardwareBitmap(const SkBitmap& bitmap) {
+    return uirenderer::HardwareBitmapUploader::allocateHardwareBitmap(bitmap);
+}
+
+sk_sp<Bitmap> Bitmap::allocateHeapBitmap(SkBitmap* bitmap) {
+    return allocateBitmap(bitmap, &Bitmap::allocateHeapBitmap);
+}
+
+sk_sp<Bitmap> Bitmap::allocateHeapBitmap(const SkImageInfo& info) {
+    size_t size;
+    if (!computeAllocationSize(info.minRowBytes(), info.height(), &size)) {
+        LOG_ALWAYS_FATAL("trying to allocate too large bitmap");
+        return nullptr;
+    }
+    return allocateHeapBitmap(size, info, info.minRowBytes());
+}
+
+sk_sp<Bitmap> Bitmap::allocateHeapBitmap(size_t size, const SkImageInfo& info, size_t rowBytes) {
+    void* addr = calloc(size, 1);
+    if (!addr) {
+        return nullptr;
+    }
+    return sk_sp<Bitmap>(new Bitmap(addr, size, info, rowBytes));
+}
+
 void FreePixelRef(void* addr, void* context) {
     auto pixelRef = (SkPixelRef*)context;
     pixelRef->unref();
@@ -132,17 +132,38 @@
                                     pixelRef.rowBytes()));
 }
 
-sk_sp<Bitmap> Bitmap::createFrom(sp<GraphicBuffer> graphicBuffer) {
-    return createFrom(graphicBuffer, SkColorSpace::MakeSRGB());
+
+sk_sp<Bitmap> Bitmap::createFrom(sp<GraphicBuffer> graphicBuffer, sk_sp<SkColorSpace> colorSpace,
+                                 SkAlphaType alphaType, BitmapPalette palette) {
+    // As we will be effectively texture-sampling the buffer (using either EGL or Vulkan), we can
+    // view the format as RGBA8888.
+    SkImageInfo info = SkImageInfo::Make(graphicBuffer->getWidth(), graphicBuffer->getHeight(),
+                                         kRGBA_8888_SkColorType, alphaType, colorSpace);
+    return sk_sp<Bitmap>(new Bitmap(graphicBuffer.get(), info, palette));
 }
 
-sk_sp<Bitmap> Bitmap::createFrom(sp<GraphicBuffer> graphicBuffer, sk_sp<SkColorSpace> colorSpace) {
-    // As we will be effectively texture-sampling the buffer (using either EGL or Vulkan), we can
-    // view the colorspace as RGBA8888.
-    SkImageInfo info = SkImageInfo::Make(graphicBuffer->getWidth(), graphicBuffer->getHeight(),
-                                         kRGBA_8888_SkColorType, kPremul_SkAlphaType,
-                                         colorSpace);
-    return sk_sp<Bitmap>(new Bitmap(graphicBuffer.get(), info));
+sk_sp<Bitmap> Bitmap::createFrom(const SkImageInfo& info, size_t rowBytes, int fd, void* addr,
+                                 size_t size, bool readOnly) {
+    if (info.colorType() == kUnknown_SkColorType) {
+        LOG_ALWAYS_FATAL("unknown bitmap configuration");
+        return nullptr;
+    }
+
+    if (!addr) {
+        // Map existing ashmem region if not already mapped.
+        int flags = readOnly ? (PROT_READ) : (PROT_READ | PROT_WRITE);
+        size = ashmem_get_size_region(fd);
+        addr = mmap(NULL, size, flags, MAP_SHARED, fd, 0);
+        if (addr == MAP_FAILED) {
+            return nullptr;
+        }
+    }
+
+    sk_sp<Bitmap> bitmap(new Bitmap(addr, fd, size, info, rowBytes));
+    if (readOnly) {
+        bitmap->setImmutable();
+    }
+    return bitmap;
 }
 
 void Bitmap::setColorSpace(sk_sp<SkColorSpace> colorSpace) {
diff --git a/libs/hwui/hwui/Bitmap.h b/libs/hwui/hwui/Bitmap.h
index d446377..2138040 100644
--- a/libs/hwui/hwui/Bitmap.h
+++ b/libs/hwui/hwui/Bitmap.h
@@ -54,28 +54,31 @@
 
 class ANDROID_API Bitmap : public SkPixelRef {
 public:
+    /* The allocate factories not only construct the Bitmap object but also allocate the
+     * backing store whose type is determined by the specific method that is called.
+     *
+     * The factories that accept SkBitmap* as a param will modify those params by
+     * installing the returned bitmap as their SkPixelRef.
+     *
+     * The factories that accept const SkBitmap& as a param will copy the contents of the
+     * provided bitmap into the newly allocated buffer.
+     */
+    static sk_sp<Bitmap> allocateAshmemBitmap(SkBitmap* bitmap);
+    static sk_sp<Bitmap> allocateHardwareBitmap(const SkBitmap& bitmap);
     static sk_sp<Bitmap> allocateHeapBitmap(SkBitmap* bitmap);
     static sk_sp<Bitmap> allocateHeapBitmap(const SkImageInfo& info);
 
-    static sk_sp<Bitmap> allocateHardwareBitmap(SkBitmap& bitmap);
-
-    static sk_sp<Bitmap> allocateAshmemBitmap(SkBitmap* bitmap);
-    static sk_sp<Bitmap> allocateAshmemBitmap(size_t allocSize, const SkImageInfo& info,
-                                              size_t rowBytes);
-
-    static sk_sp<Bitmap> createFrom(sp<GraphicBuffer> graphicBuffer);
+    /* The createFrom factories construct a new Bitmap object by wrapping the already allocated
+     * memory that is provided as an input param.
+     */
     static sk_sp<Bitmap> createFrom(sp<GraphicBuffer> graphicBuffer,
-                                    sk_sp<SkColorSpace> colorSpace);
-
+                                    sk_sp<SkColorSpace> colorSpace,
+                                    SkAlphaType alphaType = kPremul_SkAlphaType,
+                                    BitmapPalette palette = BitmapPalette::Unknown);
+    static sk_sp<Bitmap> createFrom(const SkImageInfo& info, size_t rowBytes, int fd, void* addr,
+                                    size_t size, bool readOnly);
     static sk_sp<Bitmap> createFrom(const SkImageInfo&, SkPixelRef&);
 
-    Bitmap(void* address, size_t allocSize, const SkImageInfo& info, size_t rowBytes);
-    Bitmap(void* address, void* context, FreeFunc freeFunc, const SkImageInfo& info,
-           size_t rowBytes);
-    Bitmap(void* address, int fd, size_t mappedSize, const SkImageInfo& info, size_t rowBytes);
-    Bitmap(GraphicBuffer* buffer, const SkImageInfo& info,
-           BitmapPalette palette = BitmapPalette::Unknown);
-
     int rowBytesAsPixels() const { return rowBytes() >> mInfo.shiftPerPixel(); }
 
     void reconfigure(const SkImageInfo& info, size_t rowBytes);
@@ -123,6 +126,15 @@
     }
 
 private:
+    static sk_sp<Bitmap> allocateAshmemBitmap(size_t size, const SkImageInfo& i, size_t rowBytes);
+    static sk_sp<Bitmap> allocateHeapBitmap(size_t size, const SkImageInfo& i, size_t rowBytes);
+
+    Bitmap(void* address, size_t allocSize, const SkImageInfo& info, size_t rowBytes);
+    Bitmap(void* address, void* context, FreeFunc freeFunc, const SkImageInfo& info,
+           size_t rowBytes);
+    Bitmap(void* address, int fd, size_t mappedSize, const SkImageInfo& info, size_t rowBytes);
+    Bitmap(GraphicBuffer* buffer, const SkImageInfo& info, BitmapPalette palette);
+
     virtual ~Bitmap();
     void* getStorage() const;
 
diff --git a/libs/hwui/hwui/Canvas.h b/libs/hwui/hwui/Canvas.h
index a5f21d8..71814c3 100644
--- a/libs/hwui/hwui/Canvas.h
+++ b/libs/hwui/hwui/Canvas.h
@@ -178,6 +178,9 @@
     virtual void drawRenderNode(uirenderer::RenderNode* renderNode) = 0;
     virtual void callDrawGLFunction(Functor* functor,
                                     uirenderer::GlFunctorLifecycleListener* listener) = 0;
+    virtual void drawWebViewFunctor(int /*functor*/) {
+        LOG_ALWAYS_FATAL("Not supported");
+    }
 
     // ----------------------------------------------------------------------------
     // Canvas state operations
diff --git a/libs/hwui/pipeline/skia/FunctorDrawable.h b/libs/hwui/pipeline/skia/FunctorDrawable.h
index af3a056..cf2f93b 100644
--- a/libs/hwui/pipeline/skia/FunctorDrawable.h
+++ b/libs/hwui/pipeline/skia/FunctorDrawable.h
@@ -21,7 +21,9 @@
 #include <SkCanvas.h>
 #include <SkDrawable.h>
 
+#include <WebViewFunctorManager.h>
 #include <utils/Functor.h>
+#include <variant>
 
 namespace android {
 namespace uirenderer {
@@ -35,17 +37,43 @@
 class FunctorDrawable : public SkDrawable {
 public:
     FunctorDrawable(Functor* functor, GlFunctorLifecycleListener* listener, SkCanvas* canvas)
-            : mFunctor(functor), mListener(listener), mBounds(canvas->getLocalClipBounds()) {}
+            : mBounds(canvas->getLocalClipBounds())
+            , mAnyFunctor(std::in_place_type<LegacyFunctor>, functor, listener) {}
+
+    FunctorDrawable(int functor, SkCanvas* canvas)
+            : mBounds(canvas->getLocalClipBounds())
+            , mAnyFunctor(std::in_place_type<NewFunctor>, functor) {}
+
     virtual ~FunctorDrawable() {}
 
-    virtual void syncFunctor() const = 0;
+    virtual void syncFunctor(const WebViewSyncData& data) const {
+        if (mAnyFunctor.index() == 0) {
+            std::get<0>(mAnyFunctor).handle->sync(data);
+        } else {
+            (*(std::get<1>(mAnyFunctor).functor))(DrawGlInfo::kModeSync, nullptr);
+        }
+    }
 
 protected:
     virtual SkRect onGetBounds() override { return mBounds; }
 
-    Functor* mFunctor;
-    sp<GlFunctorLifecycleListener> mListener;
     const SkRect mBounds;
+
+    struct LegacyFunctor {
+        explicit LegacyFunctor(Functor* functor, GlFunctorLifecycleListener* listener)
+                : functor(functor), listener(listener) {}
+        Functor* functor;
+        sp<GlFunctorLifecycleListener> listener;
+    };
+
+    struct NewFunctor {
+        explicit NewFunctor(int functor) {
+            handle = WebViewFunctorManager::instance().handleFor(functor);
+        }
+        sp<WebViewFunctor::Handle> handle;
+    };
+
+    std::variant<NewFunctor, LegacyFunctor> mAnyFunctor;
 };
 
 }  // namespace skiapipeline
diff --git a/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp b/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp
index 4a87e75..240efb4 100644
--- a/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp
+++ b/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp
@@ -17,29 +17,28 @@
 #include "GLFunctorDrawable.h"
 #include <GrContext.h>
 #include <private/hwui/DrawGlInfo.h>
+#include "FunctorDrawable.h"
 #include "GlFunctorLifecycleListener.h"
+#include "GrBackendSurface.h"
+#include "GrRenderTarget.h"
+#include "GrRenderTargetContext.h"
 #include "RenderNode.h"
 #include "SkAndroidFrameworkUtils.h"
 #include "SkClipStack.h"
 #include "SkRect.h"
-#include "GrBackendSurface.h"
-#include "GrRenderTarget.h"
-#include "GrRenderTargetContext.h"
 
 namespace android {
 namespace uirenderer {
 namespace skiapipeline {
 
 GLFunctorDrawable::~GLFunctorDrawable() {
-    if (mListener.get() != nullptr) {
-        mListener->onGlFunctorReleased(mFunctor);
+    if (auto lp = std::get_if<LegacyFunctor>(&mAnyFunctor)) {
+        if (lp->listener) {
+            lp->listener->onGlFunctorReleased(lp->functor);
+        }
     }
 }
 
-void GLFunctorDrawable::syncFunctor() const {
-    (*mFunctor)(DrawGlInfo::kModeSync, nullptr);
-}
-
 static void setScissor(int viewportHeight, const SkIRect& clip) {
     SkASSERT(!clip.isEmpty());
     // transform to Y-flipped GL space, and prevent negatives
@@ -49,14 +48,14 @@
 }
 
 static bool GetFboDetails(SkCanvas* canvas, GLuint* outFboID, SkISize* outFboSize) {
-    GrRenderTargetContext *renderTargetContext =
+    GrRenderTargetContext* renderTargetContext =
             canvas->internal_private_accessTopLayerRenderTargetContext();
     if (!renderTargetContext) {
         ALOGW("Unable to extract renderTarget info from canvas; aborting GLFunctor draw");
         return false;
     }
 
-    GrRenderTarget *renderTarget = renderTargetContext->accessRenderTarget();
+    GrRenderTarget* renderTarget = renderTargetContext->accessRenderTarget();
     if (!renderTarget) {
         ALOGW("Unable to extract renderTarget info from canvas; aborting GLFunctor draw");
         return false;
@@ -94,16 +93,16 @@
     sk_sp<SkSurface> tmpSurface;
     // we are in a state where there is an unclipped saveLayer
     if (fboID != 0 && !surfaceBounds.contains(clipBounds)) {
-
         // create an offscreen layer and clear it
-        SkImageInfo surfaceInfo = canvas->imageInfo().makeWH(clipBounds.width(), clipBounds.height());
-        tmpSurface = SkSurface::MakeRenderTarget(canvas->getGrContext(), SkBudgeted::kYes,
-                                                 surfaceInfo);
+        SkImageInfo surfaceInfo =
+                canvas->imageInfo().makeWH(clipBounds.width(), clipBounds.height());
+        tmpSurface =
+                SkSurface::MakeRenderTarget(canvas->getGrContext(), SkBudgeted::kYes, surfaceInfo);
         tmpSurface->getCanvas()->clear(SK_ColorTRANSPARENT);
 
         GrGLFramebufferInfo fboInfo;
         if (!tmpSurface->getBackendRenderTarget(SkSurface::kFlushWrite_BackendHandleAccess)
-                .getGLFramebufferInfo(&fboInfo)) {
+                     .getGLFramebufferInfo(&fboInfo)) {
             ALOGW("Unable to extract renderTarget info from offscreen canvas; aborting GLFunctor");
             return;
         }
@@ -144,7 +143,7 @@
     bool clearStencilAfterFunctor = false;
     if (CC_UNLIKELY(clipRegion.isComplex())) {
         // clear the stencil
-        //TODO: move stencil clear and canvas flush to SkAndroidFrameworkUtils::clipWithStencil
+        // TODO: move stencil clear and canvas flush to SkAndroidFrameworkUtils::clipWithStencil
         glDisable(GL_SCISSOR_TEST);
         glStencilMask(0x1);
         glClearStencil(0);
@@ -163,7 +162,7 @@
 
         // GL ops get inserted here if previous flush is missing, which could dirty the stencil
         bool stencilWritten = SkAndroidFrameworkUtils::clipWithStencil(tmpCanvas);
-        tmpCanvas->flush(); //need this flush for the single op that draws into the stencil
+        tmpCanvas->flush();  // need this flush for the single op that draws into the stencil
 
         // ensure that the framebuffer that the webview will render into is bound before after we
         // draw into the stencil
@@ -188,7 +187,11 @@
         setScissor(info.height, clipRegion.getBounds());
     }
 
-    (*mFunctor)(DrawGlInfo::kModeDraw, &info);
+    if (mAnyFunctor.index() == 0) {
+        std::get<0>(mAnyFunctor).handle->drawGl(info);
+    } else {
+        (*(std::get<1>(mAnyFunctor).functor))(DrawGlInfo::kModeDraw, &info);
+    }
 
     if (clearStencilAfterFunctor) {
         // clear stencil buffer as it may be used by Skia
diff --git a/libs/hwui/pipeline/skia/GLFunctorDrawable.h b/libs/hwui/pipeline/skia/GLFunctorDrawable.h
index 215979c..2ea4f67 100644
--- a/libs/hwui/pipeline/skia/GLFunctorDrawable.h
+++ b/libs/hwui/pipeline/skia/GLFunctorDrawable.h
@@ -31,11 +31,9 @@
  */
 class GLFunctorDrawable : public FunctorDrawable {
 public:
-    GLFunctorDrawable(Functor* functor, GlFunctorLifecycleListener* listener, SkCanvas* canvas)
-            : FunctorDrawable(functor, listener, canvas) {}
-    virtual ~GLFunctorDrawable();
+    using FunctorDrawable::FunctorDrawable;
 
-    void syncFunctor() const override;
+    virtual ~GLFunctorDrawable();
 
 protected:
     void onDraw(SkCanvas* canvas) override;
diff --git a/libs/hwui/pipeline/skia/LayerDrawable.cpp b/libs/hwui/pipeline/skia/LayerDrawable.cpp
index f08ac17..eed1942 100644
--- a/libs/hwui/pipeline/skia/LayerDrawable.cpp
+++ b/libs/hwui/pipeline/skia/LayerDrawable.cpp
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#include <utils/MathUtils.h>
 #include "LayerDrawable.h"
+#include <utils/MathUtils.h>
 
 #include "GrBackendSurface.h"
 #include "SkColorFilter.h"
@@ -44,10 +44,9 @@
     if (!matrix.isScaleTranslate()) return true;
 
     // We only care about meaningful scale here
-    bool noScale = MathUtils::isOne(matrix.getScaleX())
-            && MathUtils::isOne(matrix.getScaleY());
-    bool pixelAligned = SkScalarIsInt(matrix.getTranslateX())
-            && SkScalarIsInt(matrix.getTranslateY());
+    bool noScale = MathUtils::isOne(matrix.getScaleX()) && MathUtils::isOne(matrix.getScaleY());
+    bool pixelAligned =
+            SkScalarIsInt(matrix.getTranslateX()) && SkScalarIsInt(matrix.getTranslateY());
     return !(noScale && pixelAligned);
 }
 
@@ -120,11 +119,12 @@
             // Integer translation is defined as when src rect and dst rect align fractionally.
             // Skia TextureOp has the above logic build-in, but not NonAAFillRectOp. TextureOp works
             // only for SrcOver blending and without color filter (readback uses Src blending).
-            bool isIntegerTranslate = isBasicallyTranslate(totalMatrix)
-                    && SkScalarFraction(skiaDestRect.fLeft + totalMatrix[SkMatrix::kMTransX])
-                    == SkScalarFraction(skiaSrcRect.fLeft)
-                    && SkScalarFraction(skiaDestRect.fTop + totalMatrix[SkMatrix::kMTransY])
-                    == SkScalarFraction(skiaSrcRect.fTop);
+            bool isIntegerTranslate =
+                    isBasicallyTranslate(totalMatrix) &&
+                    SkScalarFraction(skiaDestRect.fLeft + totalMatrix[SkMatrix::kMTransX]) ==
+                            SkScalarFraction(skiaSrcRect.fLeft) &&
+                    SkScalarFraction(skiaDestRect.fTop + totalMatrix[SkMatrix::kMTransY]) ==
+                            SkScalarFraction(skiaSrcRect.fTop);
             if (layer->getForceFilter() || !isIntegerTranslate) {
                 paint.setFilterQuality(kLow_SkFilterQuality);
             }
diff --git a/libs/hwui/pipeline/skia/LayerDrawable.h b/libs/hwui/pipeline/skia/LayerDrawable.h
index 95dc6d0..7cd515a 100644
--- a/libs/hwui/pipeline/skia/LayerDrawable.h
+++ b/libs/hwui/pipeline/skia/LayerDrawable.h
@@ -32,8 +32,8 @@
 public:
     explicit LayerDrawable(DeferredLayerUpdater* layerUpdater) : mLayerUpdater(layerUpdater) {}
 
-    static bool DrawLayer(GrContext* context, SkCanvas* canvas, Layer* layer,
-                          const SkRect* srcRect, const SkRect* dstRect, bool useLayerTransform);
+    static bool DrawLayer(GrContext* context, SkCanvas* canvas, Layer* layer, const SkRect* srcRect,
+                          const SkRect* dstRect, bool useLayerTransform);
 
 protected:
     virtual SkRect onGetBounds() override {
diff --git a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
index 4494cb0..df1537e 100644
--- a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
+++ b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
@@ -127,6 +127,7 @@
             mNode.markDrawEnd(mCanvas);
         }
     }
+
 private:
     SkCanvas& mCanvas;
     RenderNode& mNode;
@@ -140,7 +141,7 @@
     // ensures that we paint the layer even if it is not currently visible in the
     // event that the properties change and it becomes visible.
     if ((mProjectedDisplayList == nullptr && !renderNode->isRenderable()) ||
-            (renderNode->nothingToDraw() && mComposeLayer)) {
+        (renderNode->nothingToDraw() && mComposeLayer)) {
         return;
     }
 
@@ -234,8 +235,8 @@
             // we need to restrict the portion of the surface drawn to the size of the renderNode.
             SkASSERT(renderNode->getLayerSurface()->width() >= bounds.width());
             SkASSERT(renderNode->getLayerSurface()->height() >= bounds.height());
-            canvas->drawImageRect(renderNode->getLayerSurface()->makeImageSnapshot().get(),
-                    bounds, bounds, &paint);
+            canvas->drawImageRect(renderNode->getLayerSurface()->makeImageSnapshot().get(), bounds,
+                                  bounds, &paint);
 
             if (!renderNode->getSkiaLayer()->hasRenderedSinceRepaint) {
                 renderNode->getSkiaLayer()->hasRenderedSinceRepaint = true;
diff --git a/libs/hwui/pipeline/skia/ShaderCache.cpp b/libs/hwui/pipeline/skia/ShaderCache.cpp
index 073b481..562a3b2 100644
--- a/libs/hwui/pipeline/skia/ShaderCache.cpp
+++ b/libs/hwui/pipeline/skia/ShaderCache.cpp
@@ -15,11 +15,11 @@
  */
 
 #include "ShaderCache.h"
-#include <algorithm>
 #include <log/log.h>
-#include <thread>
-#include <array>
 #include <openssl/sha.h>
+#include <algorithm>
+#include <array>
+#include <thread>
 #include "FileBlobCache.h"
 #include "Properties.h"
 #include "utils/TraceUtils.h"
@@ -44,8 +44,7 @@
 }
 
 bool ShaderCache::validateCache(const void* identity, ssize_t size) {
-    if (nullptr == identity && size == 0)
-        return true;
+    if (nullptr == identity && size == 0) return true;
 
     if (nullptr == identity || size < 0) {
         if (CC_UNLIKELY(Properties::debugLevel & kDebugCaches)) {
@@ -66,8 +65,7 @@
     auto key = sIDKey;
     auto loaded = mBlobCache->get(&key, sizeof(key), hash.data(), hash.size());
 
-    if (loaded && std::equal(hash.begin(), hash.end(), mIDHash.begin()))
-        return true;
+    if (loaded && std::equal(hash.begin(), hash.end(), mIDHash.begin())) return true;
 
     if (CC_UNLIKELY(Properties::debugLevel & kDebugCaches)) {
         ALOGW("ShaderCache::validateCache cache validation fails");
@@ -119,7 +117,7 @@
     int maxTries = 3;
     while (valueSize > mObservedBlobValueSize && maxTries > 0) {
         mObservedBlobValueSize = std::min(valueSize, maxValueSize);
-        void *newValueBuffer = realloc(valueBuffer, mObservedBlobValueSize);
+        void* newValueBuffer = realloc(valueBuffer, mObservedBlobValueSize);
         if (!newValueBuffer) {
             free(valueBuffer);
             return nullptr;
@@ -133,7 +131,7 @@
         return nullptr;
     }
     if (valueSize > mObservedBlobValueSize) {
-        ALOGE("ShaderCache::load value size is too big %d", (int) valueSize);
+        ALOGE("ShaderCache::load value size is too big %d", (int)valueSize);
         free(valueBuffer);
         return nullptr;
     }
diff --git a/libs/hwui/pipeline/skia/ShaderCache.h b/libs/hwui/pipeline/skia/ShaderCache.h
index 82804cf..d41aadb 100644
--- a/libs/hwui/pipeline/skia/ShaderCache.h
+++ b/libs/hwui/pipeline/skia/ShaderCache.h
@@ -16,12 +16,12 @@
 
 #pragma once
 
+#include <GrContextOptions.h>
 #include <cutils/compiler.h>
 #include <memory>
 #include <mutex>
 #include <string>
 #include <vector>
-#include <GrContextOptions.h>
 
 namespace android {
 
@@ -52,7 +52,7 @@
      * the initialized state the load and store methods will return without
      * performing any cache operations.
      */
-    virtual void initShaderDiskCache(const void *identity, ssize_t size);
+    virtual void initShaderDiskCache(const void* identity, ssize_t size);
 
     virtual void initShaderDiskCache() { initShaderDiskCache(nullptr, 0); }
 
@@ -153,7 +153,7 @@
     /**
      *  "mObservedBlobValueSize" is the maximum value size observed by the cache reading function.
      */
-    size_t mObservedBlobValueSize = 20*1024;
+    size_t mObservedBlobValueSize = 20 * 1024;
 
     /**
      *  The time in seconds to wait before saving newly inserted cache entries.
@@ -176,7 +176,7 @@
      */
     static constexpr uint8_t sIDKey = 0;
 
-    friend class ShaderCacheTestUtils; //used for unit testing
+    friend class ShaderCacheTestUtils;  // used for unit testing
 };
 
 } /* namespace skiapipeline */
diff --git a/libs/hwui/pipeline/skia/SkiaDisplayList.cpp b/libs/hwui/pipeline/skia/SkiaDisplayList.cpp
index ac6f6a3..230065c 100644
--- a/libs/hwui/pipeline/skia/SkiaDisplayList.cpp
+++ b/libs/hwui/pipeline/skia/SkiaDisplayList.cpp
@@ -27,9 +27,9 @@
 namespace uirenderer {
 namespace skiapipeline {
 
-void SkiaDisplayList::syncContents() {
+void SkiaDisplayList::syncContents(const WebViewSyncData& data) {
     for (auto& functor : mChildFunctors) {
-        functor->syncFunctor();
+        functor->syncFunctor(data);
     }
     for (auto& animatedImage : mAnimatedImages) {
         animatedImage->syncProperties();
diff --git a/libs/hwui/pipeline/skia/SkiaDisplayList.h b/libs/hwui/pipeline/skia/SkiaDisplayList.h
index 45f3a4c..3219ad1 100644
--- a/libs/hwui/pipeline/skia/SkiaDisplayList.h
+++ b/libs/hwui/pipeline/skia/SkiaDisplayList.h
@@ -16,11 +16,11 @@
 
 #pragma once
 
-#include "hwui/AnimatedImageDrawable.h"
 #include "FunctorDrawable.h"
 #include "RecordingCanvas.h"
 #include "RenderNodeDrawable.h"
 #include "TreeInfo.h"
+#include "hwui/AnimatedImageDrawable.h"
 #include "utils/LinearAllocator.h"
 
 #include <deque>
@@ -109,7 +109,7 @@
      * NOTE: This function can be folded into RenderNode when we no longer need
      *       to subclass from DisplayList
      */
-    void syncContents();
+    void syncContents(const WebViewSyncData& data);
 
     /**
      * ONLY to be called by RenderNode::prepareTree in order to prepare this
diff --git a/libs/hwui/pipeline/skia/SkiaMemoryTracer.cpp b/libs/hwui/pipeline/skia/SkiaMemoryTracer.cpp
index ea578cb..e48ecf4 100644
--- a/libs/hwui/pipeline/skia/SkiaMemoryTracer.cpp
+++ b/libs/hwui/pipeline/skia/SkiaMemoryTracer.cpp
@@ -21,16 +21,16 @@
 namespace skiapipeline {
 
 SkiaMemoryTracer::SkiaMemoryTracer(std::vector<ResourcePair> resourceMap, bool itemizeType)
-            : mResourceMap(resourceMap)
-            , mItemizeType(itemizeType)
-            , mTotalSize("bytes", 0)
-            , mPurgeableSize("bytes", 0) {}
+        : mResourceMap(resourceMap)
+        , mItemizeType(itemizeType)
+        , mTotalSize("bytes", 0)
+        , mPurgeableSize("bytes", 0) {}
 
 SkiaMemoryTracer::SkiaMemoryTracer(const char* categoryKey, bool itemizeType)
-            : mCategoryKey(categoryKey)
-            , mItemizeType(itemizeType)
-            , mTotalSize("bytes", 0)
-            , mPurgeableSize("bytes", 0) {}
+        : mCategoryKey(categoryKey)
+        , mItemizeType(itemizeType)
+        , mTotalSize("bytes", 0)
+        , mPurgeableSize("bytes", 0) {}
 
 const char* SkiaMemoryTracer::mapName(const char* resourceName) {
     for (auto& resource : mResourceMap) {
@@ -42,7 +42,7 @@
 }
 
 void SkiaMemoryTracer::processElement() {
-    if(!mCurrentElement.empty()) {
+    if (!mCurrentElement.empty()) {
         // Only count elements that contain "size", other values just provide metadata.
         auto sizeResult = mCurrentValues.find("size");
         if (sizeResult != mCurrentValues.end()) {
@@ -136,8 +136,8 @@
             for (const auto& typedValue : namedItem.second) {
                 TraceValue traceValue = convertUnits(typedValue.second);
                 const char* entry = (traceValue.count > 1) ? "entries" : "entry";
-                log.appendFormat("    %s: %.2f %s (%d %s)\n", typedValue.first,
-                                 traceValue.value, traceValue.units, traceValue.count, entry);
+                log.appendFormat("    %s: %.2f %s (%d %s)\n", typedValue.first, traceValue.value,
+                                 traceValue.units, traceValue.count, entry);
             }
         } else {
             auto result = namedItem.second.find("size");
diff --git a/libs/hwui/pipeline/skia/SkiaMemoryTracer.h b/libs/hwui/pipeline/skia/SkiaMemoryTracer.h
index abf1f4b..e9a7981 100644
--- a/libs/hwui/pipeline/skia/SkiaMemoryTracer.h
+++ b/libs/hwui/pipeline/skia/SkiaMemoryTracer.h
@@ -50,8 +50,8 @@
     }
 
     bool shouldDumpWrappedObjects() const override { return true; }
-    void setMemoryBacking(const char*, const char*, const char*) override { }
-    void setDiscardableMemoryBacking(const char*, const SkDiscardableMemory&) override { }
+    void setMemoryBacking(const char*, const char*, const char*) override {}
+    void setDiscardableMemoryBacking(const char*, const SkDiscardableMemory&) override {}
 
 private:
     struct TraceValue {
diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.cpp b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
index 7f62ab5..2e7850d 100644
--- a/libs/hwui/pipeline/skia/SkiaPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
@@ -184,15 +184,15 @@
         } else {
             String8 cachesOutput;
             mRenderThread.cacheManager().dumpMemoryUsage(cachesOutput,
-                    &mRenderThread.renderState());
+                                                         &mRenderThread.renderState());
             ALOGE("%s", cachesOutput.string());
             if (errorHandler) {
                 std::ostringstream err;
                 err << "Unable to create layer for " << node->getName();
                 const int maxTextureSize = DeviceInfo::get()->maxTextureSize();
                 err << ", size " << info.width() << "x" << info.height() << " max size "
-                    << maxTextureSize << " color type " << (int)info.colorType()
-                    << " has context " << (int)(mRenderThread.getGrContext() != nullptr);
+                    << maxTextureSize << " color type " << (int)info.colorType() << " has context "
+                    << (int)(mRenderThread.getGrContext() != nullptr);
                 errorHandler->onError(err.str());
             }
         }
@@ -301,8 +301,7 @@
                 mSavePictureProcessor->savePicture(data, mCapturedFile);
             } else {
                 mSavePictureProcessor->savePicture(
-                        data,
-                        mCapturedFile + "_" + std::to_string(mCaptureSequence));
+                        data, mCapturedFile + "_" + std::to_string(mCaptureSequence));
             }
             mCaptureSequence--;
         }
diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.h b/libs/hwui/pipeline/skia/SkiaPipeline.h
index af58f63..f2906de 100644
--- a/libs/hwui/pipeline/skia/SkiaPipeline.h
+++ b/libs/hwui/pipeline/skia/SkiaPipeline.h
@@ -97,8 +97,7 @@
         return mLightCenter;
     }
 
-    static void updateLighting(const LightGeometry& lightGeometry,
-                               const LightInfo& lightInfo) {
+    static void updateLighting(const LightGeometry& lightGeometry, const LightInfo& lightInfo) {
         mLightRadius = lightGeometry.radius;
         mAmbientShadowAlpha = lightInfo.ambientShadowAlpha;
         mSpotShadowAlpha = lightInfo.spotShadowAlpha;
diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
index b56c3ef..6eefed9 100644
--- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
+++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
@@ -24,8 +24,8 @@
 #include "RenderNode.h"
 #include "pipeline/skia/AnimatedDrawables.h"
 #include "pipeline/skia/GLFunctorDrawable.h"
-#include "pipeline/skia/VkInteropFunctorDrawable.h"
 #include "pipeline/skia/VkFunctorDrawable.h"
+#include "pipeline/skia/VkInteropFunctorDrawable.h"
 
 namespace android {
 namespace uirenderer {
@@ -95,8 +95,8 @@
         drawDrawable(drawable);
     }
     if (enableReorder) {
-        mCurrentBarrier = mDisplayList->allocateDrawable<StartReorderBarrierDrawable>(
-                mDisplayList.get());
+        mCurrentBarrier =
+                mDisplayList->allocateDrawable<StartReorderBarrierDrawable>(mDisplayList.get());
         drawDrawable(mCurrentBarrier);
     }
 }
@@ -127,11 +127,25 @@
     if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaVulkan) {
         // TODO(cblume) use VkFunctorDrawable instead of VkInteropFunctorDrawable here when the
         // interop is disabled/moved.
-        functorDrawable = mDisplayList->allocateDrawable<VkInteropFunctorDrawable>(functor,
-                listener, asSkCanvas());
+        functorDrawable = mDisplayList->allocateDrawable<VkInteropFunctorDrawable>(
+                functor, listener, asSkCanvas());
     } else {
-        functorDrawable = mDisplayList->allocateDrawable<GLFunctorDrawable>(functor, listener,
-                asSkCanvas());
+        functorDrawable =
+                mDisplayList->allocateDrawable<GLFunctorDrawable>(functor, listener, asSkCanvas());
+    }
+    mDisplayList->mChildFunctors.push_back(functorDrawable);
+    drawDrawable(functorDrawable);
+}
+
+void SkiaRecordingCanvas::drawWebViewFunctor(int functor) {
+    FunctorDrawable* functorDrawable;
+    if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaVulkan) {
+        // TODO(cblume) use VkFunctorDrawable instead of VkInteropFunctorDrawable here when the
+        // interop is disabled.
+        functorDrawable =
+                mDisplayList->allocateDrawable<VkInteropFunctorDrawable>(functor, asSkCanvas());
+    } else {
+        functorDrawable = mDisplayList->allocateDrawable<GLFunctorDrawable>(functor, asSkCanvas());
     }
     mDisplayList->mChildFunctors.push_back(functorDrawable);
     drawDrawable(functorDrawable);
@@ -167,7 +181,7 @@
         if (colorSpaceFilter) {
             if (tmpPaint.getColorFilter()) {
                 tmpPaint.setColorFilter(SkColorFilter::MakeComposeFilter(
-                       tmpPaint.refColorFilter(), std::move(colorSpaceFilter)));
+                        tmpPaint.refColorFilter(), std::move(colorSpaceFilter)));
             } else {
                 tmpPaint.setColorFilter(std::move(colorSpaceFilter));
             }
@@ -248,8 +262,7 @@
         filteredPaint.writeable().setFilterQuality(kLow_SkFilterQuality);
     }
     sk_sp<SkImage> image = bitmap.makeImage();
-    mRecorder.drawImageLattice(image, lattice, dst,
-                               filterPaint(std::move(filteredPaint)),
+    mRecorder.drawImageLattice(image, lattice, dst, filterPaint(std::move(filteredPaint)),
                                bitmap.palette());
     if (!bitmap.isImmutable() && image.get() && !image->unique() && !dst.isEmpty()) {
         mDisplayList->mMutableImages.push_back(image.get());
diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h
index d6107a9..afeccea 100644
--- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h
+++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h
@@ -74,6 +74,7 @@
     virtual void drawRenderNode(uirenderer::RenderNode* renderNode) override;
     virtual void callDrawGLFunction(Functor* functor,
                                     uirenderer::GlFunctorLifecycleListener* listener) override;
+    void drawWebViewFunctor(int functor) override;
 
 private:
     RecordingCanvas mRecorder;
diff --git a/libs/hwui/pipeline/skia/SkiaUtils.h b/libs/hwui/pipeline/skia/SkiaUtils.h
index 8344469..fa7f1fe 100644
--- a/libs/hwui/pipeline/skia/SkiaUtils.h
+++ b/libs/hwui/pipeline/skia/SkiaUtils.h
@@ -22,7 +22,7 @@
 
 static inline SkRect SkRectMakeLargest() {
     const SkScalar v = SK_ScalarMax;
-    return { -v, -v, v, v };
+    return {-v, -v, v, v};
 };
 
 } /* namespace android */
diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
index 65ae0dd..1d3a244 100644
--- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
@@ -20,9 +20,9 @@
 #include "Readback.h"
 #include "SkiaPipeline.h"
 #include "SkiaProfileRenderer.h"
+#include "VkInteropFunctorDrawable.h"
 #include "renderstate/RenderState.h"
 #include "renderthread/Frame.h"
-#include "VkInteropFunctorDrawable.h"
 
 #include <SkSurface.h>
 #include <SkTypes.h>
@@ -158,7 +158,7 @@
         ALOGW("SkiaVulkanPipeline::allocateHardwareBitmap() failed in GraphicBuffer.create()");
         return nullptr;
     }
-    return sk_sp<Bitmap>(new Bitmap(buffer.get(), skBitmap.info()));
+    return Bitmap::createFrom(buffer, skBitmap.refColorSpace());
 }
 
 } /* namespace skiapipeline */
diff --git a/libs/hwui/pipeline/skia/VkFunctorDrawable.cpp b/libs/hwui/pipeline/skia/VkFunctorDrawable.cpp
index 71ad5e1..156f74a 100644
--- a/libs/hwui/pipeline/skia/VkFunctorDrawable.cpp
+++ b/libs/hwui/pipeline/skia/VkFunctorDrawable.cpp
@@ -17,23 +17,21 @@
 #include "VkFunctorDrawable.h"
 #include <private/hwui/DrawVkInfo.h>
 
-#include "thread/ThreadBase.h"
-#include "utils/TimeUtils.h"
 #include <GrBackendDrawableInfo.h>
-#include <thread>
+#include <SkImage.h>
 #include <utils/Color.h>
 #include <utils/Trace.h>
 #include <utils/TraceUtils.h>
-#include <SkImage.h>
 #include <vk/GrVkTypes.h>
+#include <thread>
+#include "thread/ThreadBase.h"
+#include "utils/TimeUtils.h"
 
 namespace android {
 namespace uirenderer {
 namespace skiapipeline {
 
-VkFunctorDrawHandler::VkFunctorDrawHandler(Functor *functor)
-    : INHERITED()
-    , mFunctor(functor) {}
+VkFunctorDrawHandler::VkFunctorDrawHandler(Functor* functor) : INHERITED(), mFunctor(functor) {}
 
 VkFunctorDrawHandler::~VkFunctorDrawHandler() {
     // TODO(cblume) Fill in the DrawVkInfo parameters.
@@ -55,14 +53,12 @@
     (*mFunctor)(DrawVkInfo::kModeComposite, &draw_vk_info);
 }
 
-VkFunctorDrawable::VkFunctorDrawable(Functor* functor, GlFunctorLifecycleListener* listener,
-                                     SkCanvas* canvas)
-    : FunctorDrawable(functor, listener, canvas) {}
-
-VkFunctorDrawable::~VkFunctorDrawable() = default;
-
-void VkFunctorDrawable::syncFunctor() const {
-    (*mFunctor)(DrawVkInfo::kModeSync, nullptr);
+VkFunctorDrawable::~VkFunctorDrawable() {
+    if (auto lp = std::get_if<LegacyFunctor>(&mAnyFunctor)) {
+        if (lp->listener) {
+            lp->listener->onGlFunctorReleased(lp->functor);
+        }
+    }
 }
 
 void VkFunctorDrawable::onDraw(SkCanvas* /*canvas*/) {
@@ -71,12 +67,17 @@
 }
 
 std::unique_ptr<FunctorDrawable::GpuDrawHandler> VkFunctorDrawable::onSnapGpuDrawHandler(
-    GrBackendApi backendApi, const SkMatrix& matrix) {
+        GrBackendApi backendApi, const SkMatrix& matrix) {
     if (backendApi != GrBackendApi::kVulkan) {
         return nullptr;
     }
-    std::unique_ptr<VkFunctorDrawHandler> draw(new VkFunctorDrawHandler(mFunctor));
-    return std::move(draw);
+    std::unique_ptr<VkFunctorDrawHandler> draw;
+    if (mAnyFunctor.index() == 0) {
+        LOG_ALWAYS_FATAL("Not implemented");
+        return nullptr;
+    } else {
+        return std::make_unique<VkFunctorDrawHandler>(std::get<1>(mAnyFunctor).functor);
+    }
 }
 
 }  // namespace skiapipeline
diff --git a/libs/hwui/pipeline/skia/VkFunctorDrawable.h b/libs/hwui/pipeline/skia/VkFunctorDrawable.h
index 5cd1314..d6fefc1 100644
--- a/libs/hwui/pipeline/skia/VkFunctorDrawable.h
+++ b/libs/hwui/pipeline/skia/VkFunctorDrawable.h
@@ -18,9 +18,9 @@
 
 #include "FunctorDrawable.h"
 
-#include <utils/RefBase.h>
-#include <ui/GraphicBuffer.h>
 #include <SkImageInfo.h>
+#include <ui/GraphicBuffer.h>
+#include <utils/RefBase.h>
 
 namespace android {
 namespace uirenderer {
@@ -36,6 +36,7 @@
     ~VkFunctorDrawHandler() override;
 
     void draw(const GrBackendDrawableInfo& info) override;
+
 private:
     typedef GpuDrawHandler INHERITED;
 
@@ -48,17 +49,15 @@
  */
 class VkFunctorDrawable : public FunctorDrawable {
 public:
-    VkFunctorDrawable(Functor* functor, GlFunctorLifecycleListener* listener,
-            SkCanvas* canvas);
-    ~VkFunctorDrawable() override;
+    using FunctorDrawable::FunctorDrawable;
 
-    void syncFunctor() const override;
+    ~VkFunctorDrawable() override;
 
 protected:
     // SkDrawable functions:
     void onDraw(SkCanvas* canvas) override;
-    std::unique_ptr<FunctorDrawable::GpuDrawHandler> onSnapGpuDrawHandler(GrBackendApi backendApi,
-            const SkMatrix& matrix) override;
+    std::unique_ptr<FunctorDrawable::GpuDrawHandler> onSnapGpuDrawHandler(
+            GrBackendApi backendApi, const SkMatrix& matrix) override;
 };
 
 }  // namespace skiapipeline
diff --git a/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.cpp b/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.cpp
index 8228550..a5faae7 100644
--- a/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.cpp
+++ b/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.cpp
@@ -17,13 +17,13 @@
 #include "VkInteropFunctorDrawable.h"
 #include <private/hwui/DrawGlInfo.h>
 
-#include "renderthread/EglManager.h"
-#include "thread/ThreadBase.h"
-#include "utils/TimeUtils.h"
-#include <thread>
 #include <utils/Color.h>
 #include <utils/Trace.h>
 #include <utils/TraceUtils.h>
+#include <thread>
+#include "renderthread/EglManager.h"
+#include "thread/ThreadBase.h"
+#include "utils/TimeUtils.h"
 
 #include <EGL/eglext.h>
 #include <GLES2/gl2.h>
@@ -44,6 +44,7 @@
 class ScopedDrawRequest {
 public:
     ScopedDrawRequest() { beginDraw(); }
+
 private:
     void beginDraw() {
         std::lock_guard _lock{sLock};
@@ -57,9 +58,7 @@
         }
 
         if (!sEglManager.hasEglContext()) {
-            sGLDrawThread->queue().runSync([]() {
-                sEglManager.initialize();
-            });
+            sGLDrawThread->queue().runSync([]() { sEglManager.initialize(); });
         }
     }
 };
@@ -93,14 +92,14 @@
     if (!mFrameBuffer.get() || mFBInfo != surfaceInfo) {
         // Buffer will be used as an OpenGL ES render target.
         mFrameBuffer = new GraphicBuffer(
-                 //TODO: try to reduce the size of the buffer: possibly by using clip bounds.
-                 static_cast<uint32_t>(surfaceInfo.width()),
-                 static_cast<uint32_t>(surfaceInfo.height()),
-                 ColorTypeToPixelFormat(surfaceInfo.colorType()),
-                 GraphicBuffer::USAGE_HW_TEXTURE | GraphicBuffer::USAGE_SW_WRITE_NEVER |
-                         GraphicBuffer::USAGE_SW_READ_NEVER | GraphicBuffer::USAGE_HW_RENDER,
-                 std::string("VkInteropFunctorDrawable::onDraw pid [") + std::to_string(getpid()) +
-                         "]");
+                // TODO: try to reduce the size of the buffer: possibly by using clip bounds.
+                static_cast<uint32_t>(surfaceInfo.width()),
+                static_cast<uint32_t>(surfaceInfo.height()),
+                ColorTypeToPixelFormat(surfaceInfo.colorType()),
+                GraphicBuffer::USAGE_HW_TEXTURE | GraphicBuffer::USAGE_SW_WRITE_NEVER |
+                        GraphicBuffer::USAGE_SW_READ_NEVER | GraphicBuffer::USAGE_HW_RENDER,
+                std::string("VkInteropFunctorDrawable::onDraw pid [") + std::to_string(getpid()) +
+                        "]");
         status_t error = mFrameBuffer->initCheck();
         if (error < 0) {
             ALOGW("VkInteropFunctorDrawable::onDraw() failed in GraphicBuffer.create()");
@@ -110,16 +109,15 @@
         mFBInfo = surfaceInfo;
     }
 
-    //TODO: Synchronization is needed on mFrameBuffer to guarantee that the previous Vulkan
-    //TODO: draw command has completed.
-    //TODO: A simple but inefficient way is to flush and issue a QueueWaitIdle call. See
-    //TODO: GrVkGpu::destroyResources() for example.
+    // TODO: Synchronization is needed on mFrameBuffer to guarantee that the previous Vulkan
+    // TODO: draw command has completed.
+    // TODO: A simple but inefficient way is to flush and issue a QueueWaitIdle call. See
+    // TODO: GrVkGpu::destroyResources() for example.
     bool success = sGLDrawThread->queue().runSync([&]() -> bool {
         ATRACE_FORMAT("WebViewDraw_%dx%d", mFBInfo.width(), mFBInfo.height());
         EGLDisplay display = sEglManager.eglDisplay();
-        LOG_ALWAYS_FATAL_IF(display == EGL_NO_DISPLAY,
-                "Failed to get EGL_DEFAULT_DISPLAY! err=%s",
-                uirenderer::renderthread::EglManager::eglErrorString());
+        LOG_ALWAYS_FATAL_IF(display == EGL_NO_DISPLAY, "Failed to get EGL_DEFAULT_DISPLAY! err=%s",
+                            uirenderer::renderthread::EglManager::eglErrorString());
         // We use an EGLImage to access the content of the GraphicBuffer
         // The EGL image is later bound to a 2D texture
         EGLClientBuffer clientBuffer = (EGLClientBuffer)mFrameBuffer->getNativeBuffer();
@@ -154,10 +152,10 @@
         AutoGLFramebuffer glFb;
         // Bind texture to the frame buffer.
         glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
-                             glTexture.mTexture, 0);
+                               glTexture.mTexture, 0);
         if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
             ALOGE("Failed framebuffer check for created target buffer: %s",
-                    GLUtils::getGLFramebufferError());
+                  GLUtils::getGLFramebufferError());
             return false;
         }
 
@@ -166,19 +164,22 @@
         glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
         glClear(GL_COLOR_BUFFER_BIT);
 
-        (*mFunctor)(DrawGlInfo::kModeDraw, &info);
+        if (mAnyFunctor.index() == 0) {
+            std::get<0>(mAnyFunctor).handle->drawGl(info);
+        } else {
+            (*(std::get<1>(mAnyFunctor).functor))(DrawGlInfo::kModeDraw, &info);
+        }
 
         EGLSyncKHR glDrawFinishedFence =
                 eglCreateSyncKHR(eglGetCurrentDisplay(), EGL_SYNC_FENCE_KHR, NULL);
         LOG_ALWAYS_FATAL_IF(glDrawFinishedFence == EGL_NO_SYNC_KHR,
-                "Could not create sync fence %#x", eglGetError());
+                            "Could not create sync fence %#x", eglGetError());
         glFlush();
         // TODO: export EGLSyncKHR in file descr
         // TODO: import file desc in Vulkan Semaphore
         // TODO: instead block the GPU: probably by using external Vulkan semaphore.
         // Block the CPU until the glFlush finish.
-        EGLint waitStatus = eglClientWaitSyncKHR(display, glDrawFinishedFence, 0,
-                FENCE_TIMEOUT);
+        EGLint waitStatus = eglClientWaitSyncKHR(display, glDrawFinishedFence, 0, FENCE_TIMEOUT);
         LOG_ALWAYS_FATAL_IF(waitStatus != EGL_CONDITION_SATISFIED_KHR,
                             "Failed to wait for the fence %#x", eglGetError());
         eglDestroySyncKHR(display, glDrawFinishedFence);
@@ -197,26 +198,25 @@
     canvas->resetMatrix();
 
     auto functorImage = SkImage::MakeFromAHardwareBuffer(
-        reinterpret_cast<AHardwareBuffer*>(mFrameBuffer.get()), kPremul_SkAlphaType,
-        nullptr, kBottomLeft_GrSurfaceOrigin);
+            reinterpret_cast<AHardwareBuffer*>(mFrameBuffer.get()), kPremul_SkAlphaType, nullptr,
+            kBottomLeft_GrSurfaceOrigin);
     canvas->drawImage(functorImage, 0, 0, &paint);
     canvas->restore();
 }
 
 VkInteropFunctorDrawable::~VkInteropFunctorDrawable() {
-    if (mListener.get() != nullptr) {
-        ScopedDrawRequest _drawRequest{};
-        sGLDrawThread->queue().runSync([&]() {
-             mListener->onGlFunctorReleased(mFunctor);
-        });
+    if (auto lp = std::get_if<LegacyFunctor>(&mAnyFunctor)) {
+        if (lp->listener) {
+            ScopedDrawRequest _drawRequest{};
+            sGLDrawThread->queue().runSync(
+                    [&]() { lp->listener->onGlFunctorReleased(lp->functor); });
+        }
     }
 }
 
-void VkInteropFunctorDrawable::syncFunctor() const {
+void VkInteropFunctorDrawable::syncFunctor(const WebViewSyncData& data) const {
     ScopedDrawRequest _drawRequest{};
-    sGLDrawThread->queue().runSync([&]() {
-         (*mFunctor)(DrawGlInfo::kModeSync, nullptr);
-    });
+    sGLDrawThread->queue().runSync([&]() { FunctorDrawable::syncFunctor(data); });
 }
 
 }  // namespace skiapipeline
diff --git a/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.h b/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.h
index 8fe52c5..c47ee11 100644
--- a/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.h
+++ b/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.h
@@ -18,8 +18,8 @@
 
 #include "FunctorDrawable.h"
 
-#include <utils/RefBase.h>
 #include <ui/GraphicBuffer.h>
+#include <utils/RefBase.h>
 
 namespace android {
 namespace uirenderer {
@@ -32,20 +32,18 @@
  */
 class VkInteropFunctorDrawable : public FunctorDrawable {
 public:
-    VkInteropFunctorDrawable(Functor* functor, GlFunctorLifecycleListener* listener,
-            SkCanvas* canvas)
-            : FunctorDrawable(functor, listener, canvas) {}
+    using FunctorDrawable::FunctorDrawable;
+
     virtual ~VkInteropFunctorDrawable();
 
-    void syncFunctor() const override;
-
     static void vkInvokeFunctor(Functor* functor);
 
+    void syncFunctor(const WebViewSyncData& data) const override;
+
 protected:
     virtual void onDraw(SkCanvas* canvas) override;
 
 private:
-
     // Variables below describe/store temporary offscreen buffer used for Vulkan pipeline.
     sp<GraphicBuffer> mFrameBuffer;
     SkImageInfo mFBInfo;
diff --git a/libs/hwui/private/hwui/WebViewFunctor.h b/libs/hwui/private/hwui/WebViewFunctor.h
new file mode 100644
index 0000000..e5346aa
--- /dev/null
+++ b/libs/hwui/private/hwui/WebViewFunctor.h
@@ -0,0 +1,82 @@
+/*
+ * 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.
+ */
+
+#ifndef FRAMEWORKS_BASE_WEBVIEWFUNCTOR_H
+#define FRAMEWORKS_BASE_WEBVIEWFUNCTOR_H
+
+#include <private/hwui/DrawGlInfo.h>
+
+namespace android::uirenderer {
+
+enum class RenderMode {
+    OpenGL_ES,
+    Vulkan,
+};
+
+// Static for the lifetime of the process
+RenderMode WebViewFunctor_queryPlatformRenderMode();
+
+struct WebViewSyncData {
+    bool applyForceDark;
+};
+
+struct WebViewFunctorCallbacks {
+    // kModeSync, called on RenderThread
+    void (*onSync)(int functor, const WebViewSyncData& syncData);
+
+    // Called when either the context is destroyed _or_ when the functor's last reference goes
+    // away. Will always be called with an active context and always on renderthread.
+    void (*onContextDestroyed)(int functor);
+
+    // Called when the last reference to the handle goes away and the handle is considered
+    // irrevocably destroyed. Will always be proceeded by a call to onContextDestroyed if
+    // this functor had ever been drawn.
+    void (*onDestroyed)(int functor);
+
+    union {
+        struct {
+            // Called on RenderThread. initialize is guaranteed to happen before this call
+            void (*draw)(int functor, const DrawGlInfo& params);
+        } gles;
+        // TODO: VK support. The current DrawVkInfo is monolithic and needs to be split up for
+        // what params are valid on what callbacks
+        struct {
+            // Called either the first time the functor is used or the first time it's used after
+            // a call to onContextDestroyed.
+            // void (*initialize)(int functor, const InitParams& params);
+            // void (*frameStart)(int functor, /* todo: what params are actually needed for this to
+            // be useful? Is this useful? */)
+            // void (*draw)(int functor, const CompositeParams& params /* todo: rename - composite
+            // almost always means something else, and we aren't compositing */);
+            // void (*frameEnd)(int functor, const PostCompositeParams& params /* todo: same as
+            // CompositeParams - rename */);
+        } vk;
+    };
+};
+
+// Creates a new WebViewFunctor from the given prototype. The prototype is copied after
+// this function returns. Caller retains full ownership of it.
+// Returns -1 if the creation fails (such as an unsupported functorMode + platform mode combination)
+int WebViewFunctor_create(const WebViewFunctorCallbacks& prototype, RenderMode functorMode);
+
+// May be called on any thread to signal that the functor should be destroyed.
+// The functor will receive an onDestroyed when the last usage of it is released,
+// and it should be considered alive & active until that point.
+void WebViewFunctor_release(int functor);
+
+}  // namespace android::uirenderer
+
+#endif  // FRAMEWORKS_BASE_WEBVIEWFUNCTOR_H
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 4e4262c..8e57a3a 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -576,8 +576,7 @@
     ATRACE_CALL();
     if (level >= TRIM_MEMORY_COMPLETE) {
         thread.cacheManager().trimMemory(CacheManager::TrimMemoryMode::Complete);
-        thread.destroyGlContext();
-        thread.vulkanManager().destroy();
+        thread.destroyRenderingContext();
     } else if (level >= TRIM_MEMORY_UI_HIDDEN) {
         thread.cacheManager().trimMemory(CacheManager::TrimMemoryMode::UiHidden);
     }
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index 085812a0..aa6af23 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -30,6 +30,7 @@
 #include "renderthread/RenderThread.h"
 #include "utils/Macros.h"
 #include "utils/TimeUtils.h"
+#include "WebViewFunctorManager.h"
 
 #include <ui/GraphicBuffer.h>
 
@@ -143,6 +144,14 @@
     }
 }
 
+void RenderProxy::destroyFunctor(int functor) {
+    ATRACE_CALL();
+    RenderThread& thread = RenderThread::getInstance();
+    thread.queue().post([=]() {
+        WebViewFunctorManager::instance().destroyFunctor(functor);
+    });
+}
+
 DeferredLayerUpdater* RenderProxy::createTextureLayer() {
     return mRenderThread.queue().runSync([this]() -> auto {
         return mContext->createTextureLayer();
diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h
index d9b789f..9dc9181 100644
--- a/libs/hwui/renderthread/RenderProxy.h
+++ b/libs/hwui/renderthread/RenderProxy.h
@@ -82,6 +82,7 @@
     ANDROID_API void destroy();
 
     ANDROID_API static void invokeFunctor(Functor* functor, bool waitForCompletion);
+    static void destroyFunctor(int functor);
 
     ANDROID_API DeferredLayerUpdater* createTextureLayer();
     ANDROID_API void buildLayer(RenderNode* node);
diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp
index 207673c1..c06fadd 100644
--- a/libs/hwui/renderthread/RenderThread.cpp
+++ b/libs/hwui/renderthread/RenderThread.cpp
@@ -132,6 +132,7 @@
         , mFrameCallbackTaskPending(false)
         , mRenderState(nullptr)
         , mEglManager(nullptr)
+        , mFunctorManager(WebViewFunctorManager::instance())
         , mVkManager(nullptr) {
     Properties::load();
     start("RenderThread");
@@ -197,11 +198,13 @@
     setGrContext(grContext);
 }
 
-void RenderThread::destroyGlContext() {
+void RenderThread::destroyRenderingContext() {
+    mFunctorManager.onContextDestroyed();
     if (mEglManager->hasEglContext()) {
         setGrContext(nullptr);
         mEglManager->destroy();
     }
+    vulkanManager().destroy();
 }
 
 void RenderThread::dumpGraphicsMemory(int fd) {
diff --git a/libs/hwui/renderthread/RenderThread.h b/libs/hwui/renderthread/RenderThread.h
index 2384f95..12666b3 100644
--- a/libs/hwui/renderthread/RenderThread.h
+++ b/libs/hwui/renderthread/RenderThread.h
@@ -23,6 +23,7 @@
 #include "CacheManager.h"
 #include "TimeLord.h"
 #include "thread/ThreadBase.h"
+#include "WebViewFunctorManager.h"
 
 #include <GrContext.h>
 #include <SkBitmap.h>
@@ -104,7 +105,7 @@
     void dumpGraphicsMemory(int fd);
 
     void requireGlContext();
-    void destroyGlContext();
+    void destroyRenderingContext();
 
     /**
      * isCurrent provides a way to query, if the caller is running on
@@ -151,6 +152,7 @@
     TimeLord mTimeLord;
     RenderState* mRenderState;
     EglManager* mEglManager;
+    WebViewFunctorManager& mFunctorManager;
 
     ProfileDataContainer mGlobalProfileData;
     Readback* mReadback = nullptr;
diff --git a/libs/hwui/surfacetexture/ImageConsumer.cpp b/libs/hwui/surfacetexture/ImageConsumer.cpp
index 15aec9f..4a2f57e 100644
--- a/libs/hwui/surfacetexture/ImageConsumer.cpp
+++ b/libs/hwui/surfacetexture/ImageConsumer.cpp
@@ -70,7 +70,8 @@
             int slot = st.mCurrentTexture;
             if (slot != BufferItem::INVALID_BUFFER_SLOT) {
                 *queueEmpty = true;
-                mImageSlots[slot].createIfNeeded(st.mSlots[slot].mGraphicBuffer, item.mDataSpace);
+                mImageSlots[slot].createIfNeeded(st.mSlots[slot].mGraphicBuffer,
+                        st.mCurrentDataSpace);
                 return mImageSlots[slot].mImage;
             }
         }
diff --git a/libs/hwui/tests/common/TestUtils.cpp b/libs/hwui/tests/common/TestUtils.cpp
index f812022..7aa9b82 100644
--- a/libs/hwui/tests/common/TestUtils.cpp
+++ b/libs/hwui/tests/common/TestUtils.cpp
@@ -32,6 +32,8 @@
 namespace android {
 namespace uirenderer {
 
+std::unordered_map<int, TestUtils::CallCounts> TestUtils::sMockFunctorCounts{};
+
 SkColor TestUtils::interpolateColor(float fraction, SkColor start, SkColor end) {
     int startA = (start >> 24) & 0xff;
     int startR = (start >> 16) & 0xff;
@@ -82,12 +84,10 @@
     uint32_t length = strlen(text);
     SkPaint glyphPaint(paint);
     glyphPaint.setTextEncoding(kGlyphID_SkTextEncoding);
-    canvas->drawText(
-            utf16.get(), length,  // text buffer
-            0, length,  // draw range
-            0, length,  // context range
-            x, y, minikin::Bidi::LTR,
-            glyphPaint, nullptr, nullptr /* measured text */);
+    canvas->drawText(utf16.get(), length,  // text buffer
+                     0, length,            // draw range
+                     0, length,            // context range
+                     x, y, minikin::Bidi::LTR, glyphPaint, nullptr, nullptr /* measured text */);
 }
 
 void TestUtils::drawUtf8ToCanvas(Canvas* canvas, const char* text, const SkPaint& paint,
@@ -96,7 +96,7 @@
     SkPaint glyphPaint(paint);
     glyphPaint.setTextEncoding(kGlyphID_SkTextEncoding);
     canvas->drawTextOnPath(utf16.get(), strlen(text), minikin::Bidi::LTR, path, 0, 0, glyphPaint,
-            nullptr);
+                           nullptr);
 }
 
 void TestUtils::TestTask::run() {
@@ -110,11 +110,7 @@
 
     rtCallback(renderThread);
 
-    if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaVulkan) {
-        renderThread.vulkanManager().destroy();
-    } else {
-        renderThread.destroyGlContext();
-    }
+    renderThread.destroyRenderingContext();
 }
 
 std::unique_ptr<uint16_t[]> TestUtils::asciiToUtf16(const char* str) {
diff --git a/libs/hwui/tests/common/TestUtils.h b/libs/hwui/tests/common/TestUtils.h
index c5db861d..5ff8993 100644
--- a/libs/hwui/tests/common/TestUtils.h
+++ b/libs/hwui/tests/common/TestUtils.h
@@ -27,6 +27,7 @@
 #include <renderstate/RenderState.h>
 #include <renderthread/RenderThread.h>
 
+#include <gtest/gtest.h>
 #include <memory>
 
 namespace android {
@@ -201,8 +202,7 @@
 
     static void recordNode(RenderNode& node, std::function<void(Canvas&)> contentCallback) {
         std::unique_ptr<Canvas> canvas(Canvas::create_recording_canvas(
-                node.stagingProperties().getWidth(), node.stagingProperties().getHeight(),
-                &node));
+                node.stagingProperties().getWidth(), node.stagingProperties().getHeight(), &node));
         contentCallback(*canvas.get());
         node.setStagingDisplayList(canvas->finishRecording());
     }
@@ -267,7 +267,14 @@
         renderthread::RenderThread::getInstance().queue().runSync([&]() { task.run(); });
     }
 
+    static void runOnRenderThreadUnmanaged(RtCallback rtCallback) {
+        auto& rt = renderthread::RenderThread::getInstance();
+        rt.queue().runSync([&]() { rtCallback(rt); });
+    }
+
+
     static bool isRenderThreadRunning() { return renderthread::RenderThread::hasInstance(); }
+    static pid_t getRenderThreadTid() { return renderthread::RenderThread::getInstance().getTid(); }
 
     static SkColor interpolateColor(float fraction, SkColor start, SkColor end);
 
@@ -296,7 +303,52 @@
     static SkRect getClipBounds(const SkCanvas* canvas);
     static SkRect getLocalClipBounds(const SkCanvas* canvas);
 
+    struct CallCounts {
+        int sync = 0;
+        int contextDestroyed = 0;
+        int destroyed = 0;
+        int glesDraw = 0;
+    };
+
+    static void expectOnRenderThread() { EXPECT_EQ(gettid(), TestUtils::getRenderThreadTid()); }
+
+    static WebViewFunctorCallbacks createMockFunctor(RenderMode mode) {
+        auto callbacks = WebViewFunctorCallbacks{
+                .onSync =
+                        [](int functor, const WebViewSyncData& data) {
+                            expectOnRenderThread();
+                            sMockFunctorCounts[functor].sync++;
+                        },
+                .onContextDestroyed =
+                        [](int functor) {
+                            expectOnRenderThread();
+                            sMockFunctorCounts[functor].contextDestroyed++;
+                        },
+                .onDestroyed =
+                        [](int functor) {
+                            expectOnRenderThread();
+                            sMockFunctorCounts[functor].destroyed++;
+                        },
+        };
+        switch (mode) {
+            case RenderMode::OpenGL_ES:
+                callbacks.gles.draw = [](int functor, const DrawGlInfo& params) {
+                    expectOnRenderThread();
+                    sMockFunctorCounts[functor].glesDraw++;
+                };
+                break;
+            default:
+                ADD_FAILURE();
+                return WebViewFunctorCallbacks{};
+        }
+        return callbacks;
+    }
+
+    static CallCounts& countsForFunctor(int functor) { return sMockFunctorCounts[functor]; }
+
 private:
+    static std::unordered_map<int, CallCounts> sMockFunctorCounts;
+
     static void syncHierarchyPropertiesAndDisplayListImpl(RenderNode* node) {
         MarkAndSweepRemoved observer(nullptr);
         node->syncProperties();
@@ -306,9 +358,9 @@
         }
         auto displayList = node->getDisplayList();
         if (displayList) {
-            for (auto&& childDr : static_cast<skiapipeline::SkiaDisplayList*>(
-                                          const_cast<DisplayList*>(displayList))
-                                          ->mChildNodes) {
+            for (auto&& childDr :
+                 static_cast<skiapipeline::SkiaDisplayList*>(const_cast<DisplayList*>(displayList))
+                         ->mChildNodes) {
                 syncHierarchyPropertiesAndDisplayListImpl(childDr.getRenderNode());
             }
         }
diff --git a/libs/hwui/tests/common/scenes/HwBitmapInCompositeShader.cpp b/libs/hwui/tests/common/scenes/HwBitmapInCompositeShader.cpp
index 448408d..ec81f62 100644
--- a/libs/hwui/tests/common/scenes/HwBitmapInCompositeShader.cpp
+++ b/libs/hwui/tests/common/scenes/HwBitmapInCompositeShader.cpp
@@ -50,7 +50,7 @@
             pixels[4000 + 4 * i + 3] = 255;
         }
         buffer->unlock();
-        sk_sp<Bitmap> hardwareBitmap(Bitmap::createFrom(buffer));
+        sk_sp<Bitmap> hardwareBitmap(Bitmap::createFrom(buffer, SkColorSpace::MakeSRGB()));
         sk_sp<SkShader> hardwareShader(createBitmapShader(*hardwareBitmap));
 
         SkPoint center;
diff --git a/libs/hwui/tests/unit/DeferredLayerUpdaterTests.cpp b/libs/hwui/tests/unit/DeferredLayerUpdaterTests.cpp
index a686979..f4c3e13 100644
--- a/libs/hwui/tests/unit/DeferredLayerUpdaterTests.cpp
+++ b/libs/hwui/tests/unit/DeferredLayerUpdaterTests.cpp
@@ -19,9 +19,9 @@
 
 #include "tests/common/TestUtils.h"
 
-#include <gtest/gtest.h>
 #include <SkBitmap.h>
 #include <SkImage.h>
+#include <gtest/gtest.h>
 
 using namespace android;
 using namespace android::uirenderer;
diff --git a/libs/hwui/tests/unit/GpuMemoryTrackerTests.cpp b/libs/hwui/tests/unit/GpuMemoryTrackerTests.cpp
index 08b9679..dac888c 100644
--- a/libs/hwui/tests/unit/GpuMemoryTrackerTests.cpp
+++ b/libs/hwui/tests/unit/GpuMemoryTrackerTests.cpp
@@ -39,7 +39,7 @@
 // current thread can spoof being a GPU thread
 static void destroyEglContext() {
     if (TestUtils::isRenderThreadRunning()) {
-        TestUtils::runOnRenderThread([](RenderThread& thread) { thread.destroyGlContext(); });
+        TestUtils::runOnRenderThread([](RenderThread& thread) { thread.destroyRenderingContext(); });
     }
 }
 
diff --git a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
index 0331581..c813cd9 100644
--- a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
+++ b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
@@ -355,9 +355,7 @@
     class ProjectionTestCanvas : public SkCanvas {
     public:
         ProjectionTestCanvas(int width, int height) : SkCanvas(width, height) {}
-        void onDrawRect(const SkRect& rect, const SkPaint& paint) override {
-            mDrawCounter++;
-        }
+        void onDrawRect(const SkRect& rect, const SkPaint& paint) override { mDrawCounter++; }
 
         int getDrawCounter() { return mDrawCounter; }
 
@@ -370,7 +368,7 @@
             [](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
                 properties.setProjectionReceiver(true);
             },
-            "B"); // a receiver with an empty display list
+            "B");  // a receiver with an empty display list
 
     auto projectingRipple = TestUtils::createSkiaNode(
             0, 0, 100, 100,
@@ -389,14 +387,14 @@
                 canvas.drawRenderNode(projectingRipple.get());
             },
             "C");
-    auto parent = TestUtils::createSkiaNode(
-            0, 0, 100, 100,
-            [&receiverBackground, &child](RenderProperties& properties,
-                                          SkiaRecordingCanvas& canvas) {
-                canvas.drawRenderNode(receiverBackground.get());
-                canvas.drawRenderNode(child.get());
-            },
-            "A");
+    auto parent =
+            TestUtils::createSkiaNode(0, 0, 100, 100,
+                                      [&receiverBackground, &child](RenderProperties& properties,
+                                                                    SkiaRecordingCanvas& canvas) {
+                                          canvas.drawRenderNode(receiverBackground.get());
+                                          canvas.drawRenderNode(child.get());
+                                      },
+                                      "A");
     ContextFactory contextFactory;
     std::unique_ptr<CanvasContext> canvasContext(
             CanvasContext::create(renderThread, false, parent.get(), &contextFactory));
@@ -1058,7 +1056,7 @@
     public:
         FrameTestCanvas() : TestCanvasBase(CANVAS_WIDTH, CANVAS_HEIGHT) {}
         void onDrawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
-                const SkPaint* paint, SrcRectConstraint constraint) override {
+                             const SkPaint* paint, SrcRectConstraint constraint) override {
             mDrawCounter++;
             EXPECT_EQ(kLow_SkFilterQuality, paint->getFilterQuality());
         }
@@ -1076,7 +1074,7 @@
     FrameTestCanvas canvas;
     RenderNodeDrawable drawable(layerNode.get(), &canvas, true);
     canvas.drawDrawable(&drawable);
-    EXPECT_EQ(1, canvas.mDrawCounter);  //make sure the layer was composed
+    EXPECT_EQ(1, canvas.mDrawCounter);  // make sure the layer was composed
 
     // clean up layer pointer, so we can safely destruct RenderNode
     layerNode->setLayerSurface(nullptr);
@@ -1129,15 +1127,14 @@
                           getTotalMatrix());
             } else {
                 // Second invocation is preparing the matrix for an elevated RenderNodeDrawable.
-                EXPECT_EQ(SkMatrix::MakeTrans(TRANSLATE_X, TRANSLATE_Y),
-                          matrix);
-                EXPECT_EQ(SkMatrix::MakeTrans(TRANSLATE_X, TRANSLATE_Y),
-                          getTotalMatrix());
+                EXPECT_EQ(SkMatrix::MakeTrans(TRANSLATE_X, TRANSLATE_Y), matrix);
+                EXPECT_EQ(SkMatrix::MakeTrans(TRANSLATE_X, TRANSLATE_Y), getTotalMatrix());
             }
         }
 
     protected:
         int mDrawCounter = 0;
+
     private:
         bool mFirstDidConcat = true;
     };
@@ -1174,14 +1171,14 @@
     public:
         VectorDrawableTestCanvas() : TestCanvasBase(CANVAS_WIDTH, CANVAS_HEIGHT) {}
         void onDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst,
-                const SkPaint* paint, SrcRectConstraint constraint) override {
+                              const SkPaint* paint, SrcRectConstraint constraint) override {
             const int index = mDrawCounter++;
             switch (index) {
                 case 0:
                     EXPECT_EQ(dst, SkRect::MakeWH(CANVAS_WIDTH, CANVAS_HEIGHT));
                     break;
                 case 1:
-                    EXPECT_EQ(dst, SkRect::MakeWH(CANVAS_WIDTH/2, CANVAS_HEIGHT));
+                    EXPECT_EQ(dst, SkRect::MakeWH(CANVAS_WIDTH / 2, CANVAS_HEIGHT));
                     break;
                 default:
                     ADD_FAILURE();
@@ -1191,17 +1188,18 @@
 
     VectorDrawable::Group* group = new VectorDrawable::Group();
     sp<VectorDrawableRoot> vectorDrawable(new VectorDrawableRoot(group));
-    vectorDrawable->mutateStagingProperties()->setScaledSize(CANVAS_WIDTH/10, CANVAS_HEIGHT/10);
+    vectorDrawable->mutateStagingProperties()->setScaledSize(CANVAS_WIDTH / 10, CANVAS_HEIGHT / 10);
 
-    auto node = TestUtils::createSkiaNode(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
-            [&](RenderProperties& props, SkiaRecordingCanvas& canvas) {
-                vectorDrawable->mutateStagingProperties()->setBounds(SkRect::MakeWH(CANVAS_WIDTH,
-                        CANVAS_HEIGHT));
-                canvas.drawVectorDrawable(vectorDrawable.get());
-                vectorDrawable->mutateStagingProperties()->setBounds(SkRect::MakeWH(CANVAS_WIDTH/2,
-                        CANVAS_HEIGHT));
-                canvas.drawVectorDrawable(vectorDrawable.get());
-            });
+    auto node =
+            TestUtils::createSkiaNode(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
+                                      [&](RenderProperties& props, SkiaRecordingCanvas& canvas) {
+                                          vectorDrawable->mutateStagingProperties()->setBounds(
+                                                  SkRect::MakeWH(CANVAS_WIDTH, CANVAS_HEIGHT));
+                                          canvas.drawVectorDrawable(vectorDrawable.get());
+                                          vectorDrawable->mutateStagingProperties()->setBounds(
+                                                  SkRect::MakeWH(CANVAS_WIDTH / 2, CANVAS_HEIGHT));
+                                          canvas.drawVectorDrawable(vectorDrawable.get());
+                                      });
 
     VectorDrawableTestCanvas canvas;
     RenderNodeDrawable drawable(node.get(), &canvas, true);
diff --git a/libs/hwui/tests/unit/RenderNodeTests.cpp b/libs/hwui/tests/unit/RenderNodeTests.cpp
index 75fb0ef..1cd9bd8 100644
--- a/libs/hwui/tests/unit/RenderNodeTests.cpp
+++ b/libs/hwui/tests/unit/RenderNodeTests.cpp
@@ -295,7 +295,8 @@
     canvasContext->destroy();
 }
 
-RENDERTHREAD_TEST(RenderNode, prepareTree_HwLayer_AVD_enqueueDamage) {
+// TODO: Is this supposed to work in SkiaGL/SkiaVK?
+RENDERTHREAD_TEST(DISABLED_RenderNode, prepareTree_HwLayer_AVD_enqueueDamage) {
     VectorDrawable::Group* group = new VectorDrawable::Group();
     sp<VectorDrawableRoot> vectorDrawable(new VectorDrawableRoot(group));
 
diff --git a/libs/hwui/tests/unit/ShaderCacheTests.cpp b/libs/hwui/tests/unit/ShaderCacheTests.cpp
index 1433aa0..87981f1 100644
--- a/libs/hwui/tests/unit/ShaderCacheTests.cpp
+++ b/libs/hwui/tests/unit/ShaderCacheTests.cpp
@@ -14,17 +14,17 @@
  * limitations under the License.
  */
 
-#include <gtest/gtest.h>
-#include <dirent.h>
 #include <cutils/properties.h>
-#include <cstdint>
+#include <dirent.h>
 #include <errno.h>
+#include <gtest/gtest.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <sys/types.h>
 #include <utils/Log.h>
-#include "pipeline/skia/ShaderCache.h"
+#include <cstdint>
 #include "FileBlobCache.h"
+#include "pipeline/skia/ShaderCache.h"
 
 using namespace android::uirenderer::skiapipeline;
 
@@ -66,7 +66,6 @@
 } /* namespace uirenderer */
 } /* namespace android */
 
-
 namespace {
 
 std::string getExternalStorageFolder() {
@@ -82,14 +81,12 @@
     return false;
 }
 
-inline bool
-checkShader(const sk_sp<SkData>& shader1, const sk_sp<SkData>& shader2) {
-    return nullptr != shader1 && nullptr != shader2 && shader1->size() == shader2->size()
-            && 0 == memcmp(shader1->data(), shader2->data(), shader1->size());
+inline bool checkShader(const sk_sp<SkData>& shader1, const sk_sp<SkData>& shader2) {
+    return nullptr != shader1 && nullptr != shader2 && shader1->size() == shader2->size() &&
+           0 == memcmp(shader1->data(), shader2->data(), shader1->size());
 }
 
-inline bool
-checkShader(const sk_sp<SkData>& shader, const char* program) {
+inline bool checkShader(const sk_sp<SkData>& shader, const char* program) {
     sk_sp<SkData> shader2 = SkData::MakeWithCString(program);
     return checkShader(shader, shader2);
 }
@@ -116,32 +113,31 @@
     }
 }
 
-
 #define GrProgramDescTest(a) (*SkData::MakeWithCString(#a).get())
 
 TEST(ShaderCacheTest, testWriteAndRead) {
     if (!folderExist(getExternalStorageFolder())) {
-        //don't run the test if external storage folder is not available
+        // don't run the test if external storage folder is not available
         return;
     }
-    std::string cacheFile1 =  getExternalStorageFolder() + "/shaderCacheTest1";
-    std::string cacheFile2 =  getExternalStorageFolder() + "/shaderCacheTest2";
+    std::string cacheFile1 = getExternalStorageFolder() + "/shaderCacheTest1";
+    std::string cacheFile2 = getExternalStorageFolder() + "/shaderCacheTest2";
 
-    //remove any test files from previous test run
+    // remove any test files from previous test run
     int deleteFile = remove(cacheFile1.c_str());
     ASSERT_TRUE(0 == deleteFile || ENOENT == errno);
     std::srand(0);
 
-    //read the cache from a file that does not exist
+    // read the cache from a file that does not exist
     ShaderCache::get().setFilename(cacheFile1.c_str());
-    ShaderCacheTestUtils::setSaveDelay(ShaderCache::get(), 0); //disable deferred save
+    ShaderCacheTestUtils::setSaveDelay(ShaderCache::get(), 0);  // disable deferred save
     ShaderCache::get().initShaderDiskCache();
 
-    //read a key - should not be found since the cache is empty
+    // read a key - should not be found since the cache is empty
     sk_sp<SkData> outVS;
     ASSERT_EQ(ShaderCache::get().load(GrProgramDescTest(432)), sk_sp<SkData>());
 
-    //write to the in-memory cache without storing on disk and verify we read the same values
+    // write to the in-memory cache without storing on disk and verify we read the same values
     sk_sp<SkData> inVS;
     setShader(inVS, "sassas");
     ShaderCache::get().store(GrProgramDescTest(100), *inVS.get());
@@ -152,23 +148,23 @@
     ASSERT_NE((outVS = ShaderCache::get().load(GrProgramDescTest(432))), sk_sp<SkData>());
     ASSERT_TRUE(checkShader(outVS, "someVS"));
 
-    //store content to disk and release in-memory cache
+    // store content to disk and release in-memory cache
     ShaderCacheTestUtils::terminate(ShaderCache::get(), true);
 
-    //change to a file that does not exist and verify load fails
+    // change to a file that does not exist and verify load fails
     ShaderCache::get().setFilename(cacheFile2.c_str());
     ShaderCache::get().initShaderDiskCache();
     ASSERT_EQ(ShaderCache::get().load(GrProgramDescTest(432)), sk_sp<SkData>());
     ShaderCacheTestUtils::terminate(ShaderCache::get(), false);
 
-    //load again content from disk from an existing file and check the data is read correctly
+    // load again content from disk from an existing file and check the data is read correctly
     ShaderCache::get().setFilename(cacheFile1.c_str());
     ShaderCache::get().initShaderDiskCache();
     sk_sp<SkData> outVS2;
     ASSERT_NE((outVS2 = ShaderCache::get().load(GrProgramDescTest(432))), sk_sp<SkData>());
     ASSERT_TRUE(checkShader(outVS2, "someVS"));
 
-    //change data, store to disk, read back again and verify data has been changed
+    // change data, store to disk, read back again and verify data has been changed
     setShader(inVS, "ewData1");
     ShaderCache::get().store(GrProgramDescTest(432), *inVS.get());
     ShaderCacheTestUtils::terminate(ShaderCache::get(), true);
@@ -176,9 +172,8 @@
     ASSERT_NE((outVS2 = ShaderCache::get().load(GrProgramDescTest(432))), sk_sp<SkData>());
     ASSERT_TRUE(checkShader(outVS2, "ewData1"));
 
-
-    //write and read big data chunk (50K)
-    size_t dataSize = 50*1024;
+    // write and read big data chunk (50K)
+    size_t dataSize = 50 * 1024;
     std::vector<uint8_t> dataBuffer(dataSize);
     genRandomData(dataBuffer);
     setShader(inVS, dataBuffer);
@@ -194,31 +189,31 @@
 
 TEST(ShaderCacheTest, testCacheValidation) {
     if (!folderExist(getExternalStorageFolder())) {
-        //don't run the test if external storage folder is not available
+        // don't run the test if external storage folder is not available
         return;
     }
-    std::string cacheFile1 =  getExternalStorageFolder() + "/shaderCacheTest1";
-    std::string cacheFile2 =  getExternalStorageFolder() + "/shaderCacheTest2";
+    std::string cacheFile1 = getExternalStorageFolder() + "/shaderCacheTest1";
+    std::string cacheFile2 = getExternalStorageFolder() + "/shaderCacheTest2";
 
-    //remove any test files from previous test run
+    // remove any test files from previous test run
     int deleteFile = remove(cacheFile1.c_str());
     ASSERT_TRUE(0 == deleteFile || ENOENT == errno);
     std::srand(0);
 
-    //generate identity and read the cache from a file that does not exist
+    // generate identity and read the cache from a file that does not exist
     ShaderCache::get().setFilename(cacheFile1.c_str());
-    ShaderCacheTestUtils::setSaveDelay(ShaderCache::get(), 0); //disable deferred save
+    ShaderCacheTestUtils::setSaveDelay(ShaderCache::get(), 0);  // disable deferred save
     std::vector<uint8_t> identity(1024);
     genRandomData(identity);
-    ShaderCache::get().initShaderDiskCache(identity.data(), identity.size() *
-                                           sizeof(decltype(identity)::value_type));
+    ShaderCache::get().initShaderDiskCache(
+            identity.data(), identity.size() * sizeof(decltype(identity)::value_type));
 
     // generate random content in cache and store to disk
     constexpr size_t numBlob(10);
     constexpr size_t keySize(1024);
     constexpr size_t dataSize(50 * 1024);
 
-    std::vector< std::pair<sk_sp<SkData>, sk_sp<SkData>> > blobVec(numBlob);
+    std::vector<std::pair<sk_sp<SkData>, sk_sp<SkData>>> blobVec(numBlob);
     for (auto& blob : blobVec) {
         std::vector<uint8_t> keyBuffer(keySize);
         std::vector<uint8_t> dataBuffer(dataSize);
@@ -237,47 +232,47 @@
     // change to a file that does not exist and verify validation fails
     ShaderCache::get().setFilename(cacheFile2.c_str());
     ShaderCache::get().initShaderDiskCache();
-    ASSERT_FALSE( ShaderCacheTestUtils::validateCache(ShaderCache::get(), identity) );
+    ASSERT_FALSE(ShaderCacheTestUtils::validateCache(ShaderCache::get(), identity));
     ShaderCacheTestUtils::terminate(ShaderCache::get(), false);
 
     // restore the original file and verify validation succeeds
     ShaderCache::get().setFilename(cacheFile1.c_str());
-    ShaderCache::get().initShaderDiskCache(identity.data(), identity.size() *
-                                           sizeof(decltype(identity)::value_type));
-    ASSERT_TRUE( ShaderCacheTestUtils::validateCache(ShaderCache::get(), identity) );
+    ShaderCache::get().initShaderDiskCache(
+            identity.data(), identity.size() * sizeof(decltype(identity)::value_type));
+    ASSERT_TRUE(ShaderCacheTestUtils::validateCache(ShaderCache::get(), identity));
     for (const auto& blob : blobVec) {
         auto outVS = ShaderCache::get().load(*blob.first.get());
-        ASSERT_TRUE( checkShader(outVS, blob.second) );
+        ASSERT_TRUE(checkShader(outVS, blob.second));
     }
 
     // generate error identity and verify load fails
     ShaderCache::get().initShaderDiskCache(identity.data(), -1);
     for (const auto& blob : blobVec) {
-        ASSERT_EQ( ShaderCache::get().load(*blob.first.get()), sk_sp<SkData>() );
+        ASSERT_EQ(ShaderCache::get().load(*blob.first.get()), sk_sp<SkData>());
     }
-    ShaderCache::get().initShaderDiskCache(nullptr, identity.size() *
-                                           sizeof(decltype(identity)::value_type));
+    ShaderCache::get().initShaderDiskCache(
+            nullptr, identity.size() * sizeof(decltype(identity)::value_type));
     for (const auto& blob : blobVec) {
-        ASSERT_EQ( ShaderCache::get().load(*blob.first.get()), sk_sp<SkData>() );
+        ASSERT_EQ(ShaderCache::get().load(*blob.first.get()), sk_sp<SkData>());
     }
 
     // verify the cache validation again after load fails
-    ShaderCache::get().initShaderDiskCache(identity.data(), identity.size() *
-                                           sizeof(decltype(identity)::value_type));
-    ASSERT_TRUE( ShaderCacheTestUtils::validateCache(ShaderCache::get(), identity) );
+    ShaderCache::get().initShaderDiskCache(
+            identity.data(), identity.size() * sizeof(decltype(identity)::value_type));
+    ASSERT_TRUE(ShaderCacheTestUtils::validateCache(ShaderCache::get(), identity));
     for (const auto& blob : blobVec) {
         auto outVS = ShaderCache::get().load(*blob.first.get());
-        ASSERT_TRUE( checkShader(outVS, blob.second) );
+        ASSERT_TRUE(checkShader(outVS, blob.second));
     }
 
     // generate another identity and verify load fails
     for (auto& data : identity) {
         data += std::rand();
     }
-    ShaderCache::get().initShaderDiskCache(identity.data(), identity.size() *
-                                           sizeof(decltype(identity)::value_type));
+    ShaderCache::get().initShaderDiskCache(
+            identity.data(), identity.size() * sizeof(decltype(identity)::value_type));
     for (const auto& blob : blobVec) {
-        ASSERT_EQ( ShaderCache::get().load(*blob.first.get()), sk_sp<SkData>() );
+        ASSERT_EQ(ShaderCache::get().load(*blob.first.get()), sk_sp<SkData>());
     }
 
     ShaderCacheTestUtils::terminate(ShaderCache::get(), false);
diff --git a/libs/hwui/tests/unit/SkiaDisplayListTests.cpp b/libs/hwui/tests/unit/SkiaDisplayListTests.cpp
index 415f9e8..53bf84f 100644
--- a/libs/hwui/tests/unit/SkiaDisplayListTests.cpp
+++ b/libs/hwui/tests/unit/SkiaDisplayListTests.cpp
@@ -100,16 +100,35 @@
     GLFunctorDrawable functorDrawable(&functor, nullptr, &dummyCanvas);
     skiaDL.mChildFunctors.push_back(&functorDrawable);
 
+    int functor2 = WebViewFunctor_create(TestUtils::createMockFunctor(RenderMode::OpenGL_ES),
+                                         RenderMode::OpenGL_ES);
+    auto& counts = TestUtils::countsForFunctor(functor2);
+    skiaDL.mChildFunctors.push_back(
+            skiaDL.allocateDrawable<GLFunctorDrawable>(functor2, &dummyCanvas));
+    WebViewFunctor_release(functor2);
+
     SkRect bounds = SkRect::MakeWH(200, 200);
     VectorDrawableRoot vectorDrawable(new VectorDrawable::Group());
     vectorDrawable.mutateStagingProperties()->setBounds(bounds);
     skiaDL.mVectorDrawables.push_back(&vectorDrawable);
 
     // ensure that the functor and vectorDrawable are properly synced
-    skiaDL.syncContents();
+    TestUtils::runOnRenderThread([&](auto&) {
+        skiaDL.syncContents(WebViewSyncData{
+                .applyForceDark = false,
+        });
+    });
 
-    ASSERT_EQ(functor.getLastMode(), DrawGlInfo::kModeSync);
-    ASSERT_EQ(vectorDrawable.mutateProperties()->getBounds(), bounds);
+    EXPECT_EQ(functor.getLastMode(), DrawGlInfo::kModeSync);
+    EXPECT_EQ(counts.sync, 1);
+    EXPECT_EQ(counts.destroyed, 0);
+    EXPECT_EQ(vectorDrawable.mutateProperties()->getBounds(), bounds);
+
+    skiaDL.reset();
+    TestUtils::runOnRenderThread([](auto&) {
+        // Fence
+    });
+    EXPECT_EQ(counts.destroyed, 1);
 }
 
 class ContextFactory : public IContextFactory {
diff --git a/libs/hwui/tests/unit/SkiaPipelineTests.cpp b/libs/hwui/tests/unit/SkiaPipelineTests.cpp
index d16b8be..3c06dab 100644
--- a/libs/hwui/tests/unit/SkiaPipelineTests.cpp
+++ b/libs/hwui/tests/unit/SkiaPipelineTests.cpp
@@ -24,10 +24,10 @@
 #include "DamageAccumulator.h"
 #include "IContextFactory.h"
 #include "SkiaCanvas.h"
-#include "pipeline/skia/SkiaUtils.h"
 #include "pipeline/skia/SkiaDisplayList.h"
 #include "pipeline/skia/SkiaOpenGLPipeline.h"
 #include "pipeline/skia/SkiaRecordingCanvas.h"
+#include "pipeline/skia/SkiaUtils.h"
 #include "renderthread/CanvasContext.h"
 #include "tests/common/TestUtils.h"
 
@@ -51,8 +51,7 @@
     auto surface = SkSurface::MakeRasterN32Premul(1, 1);
     surface->getCanvas()->drawColor(SK_ColorBLUE, SkBlendMode::kSrcOver);
     ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE);
-    pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds,
-                          surface);
+    pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface);
     ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorRED);
 }
 
@@ -84,8 +83,7 @@
     ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE);
 
     // drawFrame will crash if "SkiaPipeline::onPrepareTree" did not clean invalid VD pointer
-    pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds,
-                          surface);
+    pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface);
     ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorRED);
 }
 
@@ -106,12 +104,10 @@
     auto surface = SkSurface::MakeRasterN32Premul(2, 2);
     surface->getCanvas()->drawColor(SK_ColorBLUE, SkBlendMode::kSrcOver);
     ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE);
-    pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, true, contentDrawBounds,
-                          surface);
+    pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, true, contentDrawBounds, surface);
     ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE);
     ASSERT_EQ(TestUtils::getColor(surface, 0, 1), SK_ColorGREEN);
-    pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, false, contentDrawBounds,
-                          surface);
+    pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, false, contentDrawBounds, surface);
     ASSERT_EQ(TestUtils::getColor(surface, 0, 0), (unsigned int)SK_ColorTRANSPARENT);
     ASSERT_EQ(TestUtils::getColor(surface, 0, 1), SK_ColorGREEN);
 }
@@ -130,8 +126,7 @@
     auto surface = SkSurface::MakeRasterN32Premul(2, 2);
     surface->getCanvas()->drawColor(SK_ColorBLUE, SkBlendMode::kSrcOver);
     ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE);
-    pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, true, contentDrawBounds,
-                          surface);
+    pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, true, contentDrawBounds, surface);
     ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE);
     ASSERT_EQ(TestUtils::getColor(surface, 1, 0), SK_ColorBLUE);
     ASSERT_EQ(TestUtils::getColor(surface, 0, 1), SK_ColorRED);
@@ -203,38 +198,32 @@
     ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE);
 
     // Single draw, should be white.
-    pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds,
-                          surface);
+    pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface);
     ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorWHITE);
 
     // 1 Overdraw, should be blue blended onto white.
     renderNodes.push_back(whiteNode);
-    pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds,
-                          surface);
+    pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface);
     ASSERT_EQ(TestUtils::getColor(surface, 0, 0), (unsigned)0xffd0d0ff);
 
     // 2 Overdraw, should be green blended onto white
     renderNodes.push_back(whiteNode);
-    pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds,
-                          surface);
+    pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface);
     ASSERT_EQ(TestUtils::getColor(surface, 0, 0), (unsigned)0xffd0ffd0);
 
     // 3 Overdraw, should be pink blended onto white.
     renderNodes.push_back(whiteNode);
-    pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds,
-                          surface);
+    pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface);
     ASSERT_EQ(TestUtils::getColor(surface, 0, 0), (unsigned)0xffffc0c0);
 
     // 4 Overdraw, should be red blended onto white.
     renderNodes.push_back(whiteNode);
-    pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds,
-                          surface);
+    pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface);
     ASSERT_EQ(TestUtils::getColor(surface, 0, 0), (unsigned)0xffff8080);
 
     // 5 Overdraw, should be red blended onto white.
     renderNodes.push_back(whiteNode);
-    pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds,
-                          surface);
+    pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface);
     ASSERT_EQ(TestUtils::getColor(surface, 0, 0), (unsigned)0xffff8080);
 }
 
@@ -389,7 +378,6 @@
     EXPECT_FALSE(pipeline->isSurfaceReady());
     EXPECT_TRUE(pipeline->setSurface((Surface*)0x01, SwapBehavior::kSwap_default, ColorMode::SRGB));
     EXPECT_TRUE(pipeline->isSurfaceReady());
-    renderThread.destroyGlContext();
+    renderThread.destroyRenderingContext();
     EXPECT_FALSE(pipeline->isSurfaceReady());
 }
-
diff --git a/libs/hwui/tests/unit/TypefaceTests.cpp b/libs/hwui/tests/unit/TypefaceTests.cpp
index b645aeb..1a09b1c 100644
--- a/libs/hwui/tests/unit/TypefaceTests.cpp
+++ b/libs/hwui/tests/unit/TypefaceTests.cpp
@@ -54,9 +54,9 @@
     sk_sp<SkFontMgr> fm(SkFontMgr::RefDefault());
     sk_sp<SkTypeface> typeface(fm->makeFromStream(std::move(fontData)));
     LOG_ALWAYS_FATAL_IF(typeface == nullptr, "Failed to make typeface from %s", fileName);
-    std::shared_ptr<minikin::MinikinFont> font = std::make_shared<MinikinFontSkia>(
-            std::move(typeface), data, st.st_size, fileName, 0,
-            std::vector<minikin::FontVariation>());
+    std::shared_ptr<minikin::MinikinFont> font =
+            std::make_shared<MinikinFontSkia>(std::move(typeface), data, st.st_size, fileName, 0,
+                                              std::vector<minikin::FontVariation>());
     std::vector<minikin::Font> fonts;
     fonts.push_back(minikin::Font::Builder(font).build());
     return std::make_shared<minikin::FontFamily>(std::move(fonts));
diff --git a/libs/hwui/tests/unit/VectorDrawableTests.cpp b/libs/hwui/tests/unit/VectorDrawableTests.cpp
index b11eaa9..5db0028 100644
--- a/libs/hwui/tests/unit/VectorDrawableTests.cpp
+++ b/libs/hwui/tests/unit/VectorDrawableTests.cpp
@@ -85,8 +85,10 @@
              outPath->rCubicTo(8.0, 8.0, 8.0, 8.0, 8.0, 8.0);
              outPath->cubicTo(16.0, 16.0, 9.0, 9.0, 9.0, 9.0);
              outPath->rCubicTo(0.0, 0.0, 9.0, 9.0, 9.0, 9.0);
-             outPath->arcTo(10.0, 10.0, 0.0, SkPath::kLarge_ArcSize, SkPath::kCW_Direction, 10.0, 10.0);
-             outPath->arcTo(10.0, 10.0, 0.0, SkPath::kLarge_ArcSize, SkPath::kCW_Direction, 20.0, 20.0);
+             outPath->arcTo(10.0, 10.0, 0.0, SkPath::kLarge_ArcSize, SkPath::kCW_Direction, 10.0,
+                            10.0);
+             outPath->arcTo(10.0, 10.0, 0.0, SkPath::kLarge_ArcSize, SkPath::kCW_Direction, 20.0,
+                            20.0);
          }},
 
         // Check box VectorDrawable path data
@@ -157,7 +159,8 @@
          },
          [](SkPath* outPath) {
              outPath->moveTo(300.0, 70.0);
-             outPath->arcTo(230.0, 230.0, 0.0, SkPath::kLarge_ArcSize, SkPath::kCCW_Direction, 301.0, 70.0);
+             outPath->arcTo(230.0, 230.0, 0.0, SkPath::kLarge_ArcSize, SkPath::kCCW_Direction,
+                            301.0, 70.0);
              outPath->close();
              outPath->moveTo(300.0, 70.0);
          }},
@@ -236,14 +239,14 @@
 };
 
 const StringPath sStringPaths[] = {
-        {"3e...3", false},     // Not starting with a verb and ill-formatted float
-        {"L.M.F.A.O", false},  // No floats following verbs
-        {"m 1 1", true},       // Valid path data
-        {"\n \t   z", true},   // Valid path data with leading spaces
-        {"1-2e34567", false},  // Not starting with a verb and ill-formatted float
-        {"f 4 5", false},      // Invalid verb
-        {"\r      ", false},   // Empty string
-        {"L1,0 L1,1 L0,1 z M1000", false}    // Not enough floats following verb M.
+        {"3e...3", false},                 // Not starting with a verb and ill-formatted float
+        {"L.M.F.A.O", false},              // No floats following verbs
+        {"m 1 1", true},                   // Valid path data
+        {"\n \t   z", true},               // Valid path data with leading spaces
+        {"1-2e34567", false},              // Not starting with a verb and ill-formatted float
+        {"f 4 5", false},                  // Invalid verb
+        {"\r      ", false},               // Empty string
+        {"L1,0 L1,1 L0,1 z M1000", false}  // Not enough floats following verb M.
 };
 
 static bool hasSameVerbs(const PathData& from, const PathData& to) {
diff --git a/libs/hwui/tests/unit/WebViewFunctorManagerTests.cpp b/libs/hwui/tests/unit/WebViewFunctorManagerTests.cpp
new file mode 100644
index 0000000..c8169af
--- /dev/null
+++ b/libs/hwui/tests/unit/WebViewFunctorManagerTests.cpp
@@ -0,0 +1,154 @@
+/*
+ * 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.
+ */
+
+#include <gtest/gtest.h>
+
+#include "WebViewFunctorManager.h"
+#include "private/hwui/WebViewFunctor.h"
+#include "renderthread/RenderProxy.h"
+#include "tests/common/TestUtils.h"
+
+#include <unordered_map>
+
+using namespace android;
+using namespace android::uirenderer;
+
+TEST(WebViewFunctor, createDestroyGLES) {
+    int functor = WebViewFunctor_create(TestUtils::createMockFunctor(RenderMode::OpenGL_ES),
+                                        RenderMode::OpenGL_ES);
+    ASSERT_NE(-1, functor);
+    WebViewFunctor_release(functor);
+    TestUtils::runOnRenderThreadUnmanaged([](renderthread::RenderThread&) {
+        // Empty, don't care
+    });
+    auto& counts = TestUtils::countsForFunctor(functor);
+    // We never initialized, so contextDestroyed == 0
+    EXPECT_EQ(0, counts.contextDestroyed);
+    EXPECT_EQ(1, counts.destroyed);
+}
+
+TEST(WebViewFunctor, createSyncHandleGLES) {
+    int functor = WebViewFunctor_create(TestUtils::createMockFunctor(RenderMode::OpenGL_ES),
+                                        RenderMode::OpenGL_ES);
+    ASSERT_NE(-1, functor);
+    auto handle = WebViewFunctorManager::instance().handleFor(functor);
+    ASSERT_TRUE(handle);
+    WebViewFunctor_release(functor);
+    EXPECT_FALSE(WebViewFunctorManager::instance().handleFor(functor));
+    TestUtils::runOnRenderThreadUnmanaged([](renderthread::RenderThread&) {
+        // fence
+    });
+    auto& counts = TestUtils::countsForFunctor(functor);
+    EXPECT_EQ(0, counts.sync);
+    EXPECT_EQ(0, counts.contextDestroyed);
+    EXPECT_EQ(0, counts.destroyed);
+
+    TestUtils::runOnRenderThreadUnmanaged([&](auto&) {
+        WebViewSyncData syncData;
+        handle->sync(syncData);
+    });
+
+    EXPECT_EQ(1, counts.sync);
+
+    TestUtils::runOnRenderThreadUnmanaged([&](auto&) {
+        WebViewSyncData syncData;
+        handle->sync(syncData);
+    });
+
+    EXPECT_EQ(2, counts.sync);
+
+    handle.clear();
+
+    TestUtils::runOnRenderThreadUnmanaged([](renderthread::RenderThread&) {
+        // fence
+    });
+
+    EXPECT_EQ(2, counts.sync);
+    EXPECT_EQ(0, counts.contextDestroyed);
+    EXPECT_EQ(1, counts.destroyed);
+}
+
+TEST(WebViewFunctor, createSyncDrawGLES) {
+    int functor = WebViewFunctor_create(TestUtils::createMockFunctor(RenderMode::OpenGL_ES),
+                                        RenderMode::OpenGL_ES);
+    ASSERT_NE(-1, functor);
+    auto handle = WebViewFunctorManager::instance().handleFor(functor);
+    ASSERT_TRUE(handle);
+    WebViewFunctor_release(functor);
+    auto& counts = TestUtils::countsForFunctor(functor);
+    for (int i = 0; i < 5; i++) {
+        TestUtils::runOnRenderThreadUnmanaged([&](auto&) {
+            WebViewSyncData syncData;
+            handle->sync(syncData);
+            DrawGlInfo drawInfo;
+            handle->drawGl(drawInfo);
+            handle->drawGl(drawInfo);
+        });
+    }
+    handle.clear();
+    TestUtils::runOnRenderThreadUnmanaged([](renderthread::RenderThread&) {
+        // fence
+    });
+    EXPECT_EQ(5, counts.sync);
+    EXPECT_EQ(10, counts.glesDraw);
+    EXPECT_EQ(1, counts.contextDestroyed);
+    EXPECT_EQ(1, counts.destroyed);
+}
+
+TEST(WebViewFunctor, contextDestroyed) {
+    int functor = WebViewFunctor_create(TestUtils::createMockFunctor(RenderMode::OpenGL_ES),
+                                        RenderMode::OpenGL_ES);
+    ASSERT_NE(-1, functor);
+    auto handle = WebViewFunctorManager::instance().handleFor(functor);
+    ASSERT_TRUE(handle);
+    WebViewFunctor_release(functor);
+    auto& counts = TestUtils::countsForFunctor(functor);
+    TestUtils::runOnRenderThreadUnmanaged([&](auto&) {
+        WebViewSyncData syncData;
+        handle->sync(syncData);
+        DrawGlInfo drawInfo;
+        handle->drawGl(drawInfo);
+    });
+    EXPECT_EQ(1, counts.sync);
+    EXPECT_EQ(1, counts.glesDraw);
+    EXPECT_EQ(0, counts.contextDestroyed);
+    EXPECT_EQ(0, counts.destroyed);
+    TestUtils::runOnRenderThreadUnmanaged([](auto& rt) {
+        rt.destroyRenderingContext();
+    });
+    EXPECT_EQ(1, counts.sync);
+    EXPECT_EQ(1, counts.glesDraw);
+    EXPECT_EQ(1, counts.contextDestroyed);
+    EXPECT_EQ(0, counts.destroyed);
+    TestUtils::runOnRenderThreadUnmanaged([&](auto&) {
+        WebViewSyncData syncData;
+        handle->sync(syncData);
+        DrawGlInfo drawInfo;
+        handle->drawGl(drawInfo);
+    });
+    EXPECT_EQ(2, counts.sync);
+    EXPECT_EQ(2, counts.glesDraw);
+    EXPECT_EQ(1, counts.contextDestroyed);
+    EXPECT_EQ(0, counts.destroyed);
+    handle.clear();
+    TestUtils::runOnRenderThreadUnmanaged([](renderthread::RenderThread&) {
+        // fence
+    });
+    EXPECT_EQ(2, counts.sync);
+    EXPECT_EQ(2, counts.glesDraw);
+    EXPECT_EQ(2, counts.contextDestroyed);
+    EXPECT_EQ(1, counts.destroyed);
+}
\ No newline at end of file
diff --git a/libs/hwui/tests/unit/main.cpp b/libs/hwui/tests/unit/main.cpp
index aecceb3..63d1540 100644
--- a/libs/hwui/tests/unit/main.cpp
+++ b/libs/hwui/tests/unit/main.cpp
@@ -17,14 +17,14 @@
 #include "gmock/gmock.h"
 #include "gtest/gtest.h"
 
+#include "Properties.h"
 #include "debug/GlesDriver.h"
 #include "debug/NullGlesDriver.h"
 #include "hwui/Typeface.h"
-#include "Properties.h"
 #include "tests/common/LeakChecker.h"
-#include "thread/TaskProcessor.h"
 #include "thread/Task.h"
 #include "thread/TaskManager.h"
+#include "thread/TaskProcessor.h"
 
 #include <signal.h>
 
diff --git a/libs/hwui/utils/Color.h b/libs/hwui/utils/Color.h
index 4daccda..4473ce6 100644
--- a/libs/hwui/utils/Color.h
+++ b/libs/hwui/utils/Color.h
@@ -17,6 +17,7 @@
 #define COLOR_H
 
 #include <math.h>
+#include <cutils/compiler.h>
 #include <system/graphics.h>
 #include <ui/PixelFormat.h>
 
@@ -117,7 +118,7 @@
 
 android::PixelFormat ColorTypeToPixelFormat(SkColorType colorType);
 
-sk_sp<SkColorSpace> DataSpaceToColorSpace(android_dataspace dataspace);
+ANDROID_API sk_sp<SkColorSpace> DataSpaceToColorSpace(android_dataspace dataspace);
 
 struct Lab {
     float L;
diff --git a/location/tests/locationtests/Android.mk b/location/tests/locationtests/Android.mk
index b2fd8ec..3dcf694 100644
--- a/location/tests/locationtests/Android.mk
+++ b/location/tests/locationtests/Android.mk
@@ -12,7 +12,7 @@
 LOCAL_PRIVATE_PLATFORM_APIS := true
 
 LOCAL_STATIC_JAVA_LIBRARIES := \
-    android-support-test \
+    androidx.test.rules \
     core-test-rules \
     guava \
     mockito-target-minus-junit4 \
diff --git a/location/tests/locationtests/AndroidManifest.xml b/location/tests/locationtests/AndroidManifest.xml
index ddb8ea6..5010d3d 100644
--- a/location/tests/locationtests/AndroidManifest.xml
+++ b/location/tests/locationtests/AndroidManifest.xml
@@ -29,7 +29,7 @@
     </application>
 
     <instrumentation
-        android:name="android.support.test.runner.AndroidJUnitRunner"
+        android:name="androidx.test.runner.AndroidJUnitRunner"
         android:targetPackage="com.android.frameworks.locationtests"
         android:label="Frameworks Location Tests" />
 </manifest>
diff --git a/location/tests/locationtests/AndroidTest.xml b/location/tests/locationtests/AndroidTest.xml
index bb6547b..7bddb58 100644
--- a/location/tests/locationtests/AndroidTest.xml
+++ b/location/tests/locationtests/AndroidTest.xml
@@ -22,7 +22,7 @@
     <option name="test-tag" value="FrameworksLocationTests" />
     <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
         <option name="package" value="com.android.frameworks.locationtests" />
-        <option name="runner" value="android.support.test.runner.AndroidJUnitRunner" />
+        <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
         <option name="hidden-api-checks" value="false"/>
     </test>
 </configuration>
diff --git a/media/java/android/media/MediaItem2.java b/media/java/android/media/MediaItem2.java
new file mode 100644
index 0000000..aa2a937
--- /dev/null
+++ b/media/java/android/media/MediaItem2.java
@@ -0,0 +1,324 @@
+/*
+ * 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 static android.media.MediaMetadata.METADATA_KEY_MEDIA_ID;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+import android.util.Log;
+import android.util.Pair;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.Executor;
+
+/**
+ * A class with information on a single media item with the metadata information. Here are use
+ * cases.
+ * <ul>
+ * <li>Specify media items to {@link SessionPlayer2} for playback.
+ * <li>Share media items across the processes.
+ * </ul>
+ * <p>
+ * Subclasses of the session player may only accept certain subclasses of the media items. Check
+ * the player documentation that you're interested in.
+ * <p>
+ * When it's shared across the processes, we cannot guarantee that they contain the right values
+ * because media items are application dependent especially for the metadata.
+ * <p>
+ * This object is thread safe.
+ * <p>
+ * This API is not generally intended for third party application developers.
+ * Use the <a href="{@docRoot}tools/extras/support-library.html">Support Library</a>
+ * {@link androidx.media2.MediaItem} for consistent behavior across all devices.
+ * </p>
+ * @hide
+ */
+public class MediaItem2 implements Parcelable {
+    private static final String TAG = "MediaItem2";
+
+    // intentionally less than long.MAX_VALUE.
+    // Declare this first to avoid 'illegal forward reference'.
+    static final long LONG_MAX = 0x7ffffffffffffffL;
+
+    /**
+     * Used when a position is unknown.
+     *
+     * @see #getEndPosition()
+     */
+    public static final long POSITION_UNKNOWN = LONG_MAX;
+
+    public static final Parcelable.Creator<MediaItem2> CREATOR =
+            new Parcelable.Creator<MediaItem2>() {
+                @Override
+                public MediaItem2 createFromParcel(Parcel in) {
+                    return new MediaItem2(in);
+                }
+
+                @Override
+                public MediaItem2[] newArray(int size) {
+                    return new MediaItem2[size];
+                }
+            };
+
+    // TODO: Use SessionPlayer2.UNKNOWN_TIME instead
+    private static final long UNKNOWN_TIME = -1;
+
+    private final long mStartPositionMs;
+    private final long mEndPositionMs;
+
+    private final Object mLock = new Object();
+
+    @GuardedBy("mLock")
+    private MediaMetadata mMetadata;
+    @GuardedBy("mLock")
+    private final List<Pair<OnMetadataChangedListener, Executor>> mListeners = new ArrayList<>();
+
+    /**
+     * Used by {@link MediaItem2.Builder}.
+     */
+    // Note: Needs to be protected when we want to allow 3rd party player to define customized
+    //       MediaItem2.
+    @SuppressWarnings("WeakerAccess") /* synthetic access */
+    MediaItem2(Builder builder) {
+        this(builder.mMetadata, builder.mStartPositionMs, builder.mEndPositionMs);
+    }
+
+    /**
+     * Used by Parcelable.Creator.
+     */
+    // Note: Needs to be protected when we want to allow 3rd party player to define customized
+    //       MediaItem2.
+    @SuppressWarnings("WeakerAccess") /* synthetic access */
+    MediaItem2(Parcel in) {
+        this(in.readParcelable(MediaItem2.class.getClassLoader()), in.readLong(), in.readLong());
+    }
+
+    @SuppressWarnings("WeakerAccess") /* synthetic access */
+    MediaItem2(MediaItem2 item) {
+        this(item.mMetadata, item.mStartPositionMs, item.mEndPositionMs);
+    }
+
+    @SuppressWarnings("WeakerAccess") /* synthetic access */
+    MediaItem2(@Nullable MediaMetadata metadata, long startPositionMs, long endPositionMs) {
+        if (startPositionMs > endPositionMs) {
+            throw new IllegalArgumentException("Illegal start/end position: "
+                    + startPositionMs + " : " + endPositionMs);
+        }
+        if (metadata != null && metadata.containsKey(MediaMetadata.METADATA_KEY_DURATION)) {
+            long durationMs = metadata.getLong(MediaMetadata.METADATA_KEY_DURATION);
+            if (durationMs != UNKNOWN_TIME && endPositionMs != POSITION_UNKNOWN
+                    && endPositionMs > durationMs) {
+                throw new IllegalArgumentException("endPositionMs shouldn't be greater than"
+                        + " duration in the metdata, endPositionMs=" + endPositionMs
+                        + ", durationMs=" + durationMs);
+            }
+        }
+        mMetadata = metadata;
+        mStartPositionMs = startPositionMs;
+        mEndPositionMs = endPositionMs;
+    }
+
+    @Override
+    public String toString() {
+        final StringBuilder sb = new StringBuilder(getClass().getSimpleName());
+        synchronized (mLock) {
+            sb.append("{mMetadata=").append(mMetadata);
+            sb.append(", mStartPositionMs=").append(mStartPositionMs);
+            sb.append(", mEndPositionMs=").append(mEndPositionMs);
+            sb.append('}');
+        }
+        return sb.toString();
+    }
+
+    /**
+     * Sets metadata. If the metadata is not {@code null}, its id should be matched with this
+     * instance's media id.
+     *
+     * @param metadata metadata to update
+     * @see MediaMetadata#METADATA_KEY_MEDIA_ID
+     */
+    public void setMetadata(@Nullable MediaMetadata metadata) {
+        List<Pair<OnMetadataChangedListener, Executor>> listeners = new ArrayList<>();
+        synchronized (mLock) {
+            if (mMetadata != null && metadata != null
+                    && !TextUtils.equals(getMediaId(), metadata.getString(METADATA_KEY_MEDIA_ID))) {
+                Log.d(TAG, "MediaItem2's media ID shouldn't be changed");
+                return;
+            }
+            mMetadata = metadata;
+            listeners.addAll(mListeners);
+        }
+
+        for (Pair<OnMetadataChangedListener, Executor> pair : listeners) {
+            final OnMetadataChangedListener listener = pair.first;
+            pair.second.execute(new Runnable() {
+                @Override
+                public void run() {
+                    listener.onMetadataChanged(MediaItem2.this);
+                }
+            });
+        }
+    }
+
+    /**
+     * Gets the metadata of the media.
+     *
+     * @return metadata from the session
+     */
+    public @Nullable MediaMetadata getMetadata() {
+        synchronized (mLock) {
+            return mMetadata;
+        }
+    }
+
+    /**
+     * Return the position in milliseconds at which the playback will start.
+     * @return the position in milliseconds at which the playback will start
+     */
+    public long getStartPosition() {
+        return mStartPositionMs;
+    }
+
+    /**
+     * Return the position in milliseconds at which the playback will end.
+     * {@link #POSITION_UNKNOWN} means ending at the end of source content.
+     * @return the position in milliseconds at which the playback will end
+     */
+    public long getEndPosition() {
+        return mEndPositionMs;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeParcelable(mMetadata, 0);
+        dest.writeLong(mStartPositionMs);
+        dest.writeLong(mEndPositionMs);
+    }
+
+    /**
+     * Gets the media id for this item. If it's not {@code null}, it's a persistent unique key
+     * for the underlying media content.
+     *
+     * @return media Id from the session
+     */
+    @Nullable String getMediaId() {
+        synchronized (mLock) {
+            return mMetadata != null
+                    ? mMetadata.getString(METADATA_KEY_MEDIA_ID) : null;
+        }
+    }
+
+    void addOnMetadataChangedListener(Executor executor, OnMetadataChangedListener listener) {
+        synchronized (mLock) {
+            for (Pair<OnMetadataChangedListener, Executor> pair : mListeners) {
+                if (pair.first == listener) {
+                    return;
+                }
+            }
+            mListeners.add(new Pair<>(listener, executor));
+        }
+    }
+
+    void removeOnMetadataChangedListener(OnMetadataChangedListener listener) {
+        synchronized (mLock) {
+            for (int i = mListeners.size() - 1; i >= 0; i--) {
+                if (mListeners.get(i).first == listener) {
+                    mListeners.remove(i);
+                    return;
+                }
+            }
+        }
+    }
+
+    /**
+     * Builder for {@link MediaItem2}.
+     */
+    public static class Builder {
+        @SuppressWarnings("WeakerAccess") /* synthetic access */
+        MediaMetadata mMetadata;
+        @SuppressWarnings("WeakerAccess") /* synthetic access */
+        long mStartPositionMs = 0;
+        @SuppressWarnings("WeakerAccess") /* synthetic access */
+        long mEndPositionMs = POSITION_UNKNOWN;
+
+        /**
+         * Set the metadata of this instance. {@code null} for unset.
+         *
+         * @param metadata metadata
+         * @return this instance for chaining
+         */
+        public @NonNull Builder setMetadata(@Nullable MediaMetadata metadata) {
+            mMetadata = metadata;
+            return this;
+        }
+
+        /**
+         * Sets the start position in milliseconds at which the playback will start.
+         * Any negative number is treated as 0.
+         *
+         * @param position the start position in milliseconds at which the playback will start
+         * @return the same Builder instance.
+         */
+        public @NonNull Builder setStartPosition(long position) {
+            if (position < 0) {
+                position = 0;
+            }
+            mStartPositionMs = position;
+            return this;
+        }
+
+        /**
+         * Sets the end position in milliseconds at which the playback will end.
+         * Any negative number is treated as maximum length of the media item.
+         *
+         * @param position the end position in milliseconds at which the playback will end
+         * @return the same Builder instance.
+         */
+        public @NonNull Builder setEndPosition(long position) {
+            if (position < 0) {
+                position = POSITION_UNKNOWN;
+            }
+            mEndPositionMs = position;
+            return this;
+        }
+
+        /**
+         * Build {@link MediaItem2}.
+         *
+         * @return a new {@link MediaItem2}.
+         */
+        public @NonNull MediaItem2 build() {
+            return new MediaItem2(this);
+        }
+    }
+
+    interface OnMetadataChangedListener {
+        void onMetadataChanged(MediaItem2 item);
+    }
+}
diff --git a/media/java/android/media/MediaMetadataRetriever.java b/media/java/android/media/MediaMetadataRetriever.java
index 00a393a..d656fa3 100644
--- a/media/java/android/media/MediaMetadataRetriever.java
+++ b/media/java/android/media/MediaMetadataRetriever.java
@@ -40,8 +40,7 @@
  * MediaMetadataRetriever class provides a unified interface for retrieving
  * frame and meta data from an input media file.
  */
-public class MediaMetadataRetriever
-{
+public class MediaMetadataRetriever implements AutoCloseable {
     static {
         System.loadLibrary("media_jni");
         native_init();
@@ -672,6 +671,11 @@
     @UnsupportedAppUsage
     private native byte[] getEmbeddedPicture(int pictureType);
 
+    @Override
+    public void close() {
+        release();
+    }
+
     /**
      * Call it when one is done with the object. This method releases the memory
      * allocated internally.
diff --git a/media/java/android/media/ThumbnailUtils.java b/media/java/android/media/ThumbnailUtils.java
index fd14060..f07076a 100644
--- a/media/java/android/media/ThumbnailUtils.java
+++ b/media/java/android/media/ThumbnailUtils.java
@@ -16,33 +16,53 @@
 
 package android.media;
 
+import static android.media.MediaMetadataRetriever.METADATA_KEY_DURATION;
+import static android.media.MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT;
+import static android.media.MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH;
+import static android.media.MediaMetadataRetriever.OPTION_CLOSEST_SYNC;
+import static android.os.Environment.MEDIA_UNKNOWN;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.UnsupportedAppUsage;
 import android.content.ContentResolver;
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
 import android.graphics.Canvas;
+import android.graphics.ImageDecoder;
+import android.graphics.ImageDecoder.ImageInfo;
+import android.graphics.ImageDecoder.Source;
 import android.graphics.Matrix;
+import android.graphics.Point;
 import android.graphics.Rect;
 import android.net.Uri;
+import android.os.CancellationSignal;
+import android.os.Environment;
 import android.os.ParcelFileDescriptor;
-import android.provider.MediaStore.Images;
+import android.provider.MediaStore.ThumbnailConstants;
 import android.util.Log;
+import android.util.Size;
 
-import java.io.FileDescriptor;
-import java.io.FileInputStream;
+import com.android.internal.util.ArrayUtils;
+
+import libcore.io.IoUtils;
+
+import java.io.File;
 import java.io.IOException;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.function.ToIntFunction;
 
 /**
- * Thumbnail generation routines for media provider.
+ * Utilities for generating visual thumbnails from files.
  */
-
 public class ThumbnailUtils {
     private static final String TAG = "ThumbnailUtils";
 
-    /* Maximum pixels size for created bitmap. */
-    private static final int MAX_NUM_PIXELS_THUMBNAIL = 512 * 384;
-    private static final int MAX_NUM_PIXELS_MICRO_THUMBNAIL = 160 * 120;
-    private static final int UNCONSTRAINED = -1;
+    /** @hide */
+    @Deprecated
+    @UnsupportedAppUsage
+    public static final int TARGET_SIZE_MICRO_THUMBNAIL = 96;
 
     /* Options used internally. */
     private static final int OPTIONS_NONE = 0x0;
@@ -54,153 +74,252 @@
      */
     public static final int OPTIONS_RECYCLE_INPUT = 0x2;
 
-    /**
-     * Constant used to indicate the dimension of mini thumbnail.
-     * @hide Only used by media framework and media provider internally.
-     */
-    public static final int TARGET_SIZE_MINI_THUMBNAIL = 320;
+    private static Size convertKind(int kind) {
+        if (kind == ThumbnailConstants.MICRO_KIND) {
+            return Point.convert(ThumbnailConstants.MICRO_SIZE);
+        } else if (kind == ThumbnailConstants.FULL_SCREEN_KIND) {
+            return Point.convert(ThumbnailConstants.FULL_SCREEN_SIZE);
+        } else if (kind == ThumbnailConstants.MINI_KIND) {
+            return Point.convert(ThumbnailConstants.MINI_SIZE);
+        } else {
+            throw new IllegalArgumentException("Unsupported kind: " + kind);
+        }
+    }
+
+    private static class Resizer implements ImageDecoder.OnHeaderDecodedListener {
+        private final Size size;
+        private final CancellationSignal signal;
+
+        public Resizer(Size size, CancellationSignal signal) {
+            this.size = size;
+            this.signal = signal;
+        }
+
+        @Override
+        public void onHeaderDecoded(ImageDecoder decoder, ImageInfo info, Source source) {
+            // One last-ditch check to see if we've been canceled.
+            if (signal != null) signal.throwIfCanceled();
+
+            // We don't know how clients will use the decoded data, so we have
+            // to default to the more flexible "software" option.
+            decoder.setAllocator(ImageDecoder.ALLOCATOR_SOFTWARE);
+
+            // We requested a rough thumbnail size, but the remote size may have
+            // returned something giant, so defensively scale down as needed.
+            final int widthSample = info.getSize().getWidth() / size.getWidth();
+            final int heightSample = info.getSize().getHeight() / size.getHeight();
+            final int sample = Math.max(widthSample, heightSample);
+            if (sample > 1) {
+                decoder.setTargetSampleSize(sample);
+            }
+        }
+    }
 
     /**
-     * Constant used to indicate the dimension of micro thumbnail.
-     * @hide Only used by media framework and media provider internally.
+     * Create a thumbnail for given audio file.
+     *
+     * @param filePath The audio file.
+     * @param kind The desired thumbnail kind, such as
+     *            {@link android.provider.MediaStore.Images.Thumbnails#MINI_KIND}.
      */
-    @UnsupportedAppUsage
-    public static final int TARGET_SIZE_MICRO_THUMBNAIL = 96;
+    @Deprecated
+    public static @Nullable Bitmap createAudioThumbnail(@NonNull String filePath, int kind) {
+        try {
+            return createAudioThumbnail(new File(filePath), convertKind(kind), null);
+        } catch (IOException e) {
+            Log.w(TAG, e);
+            return null;
+        }
+    }
 
     /**
-     * This method first examines if the thumbnail embedded in EXIF is bigger than our target
-     * size. If not, then it'll create a thumbnail from original image. Due to efficiency
-     * consideration, we want to let MediaThumbRequest avoid calling this method twice for
-     * both kinds, so it only requests for MICRO_KIND and set saveImage to true.
+     * Create a thumbnail for given audio file.
      *
-     * This method always returns a "square thumbnail" for MICRO_KIND thumbnail.
-     *
-     * @param filePath the path of image file
-     * @param kind could be MINI_KIND or MICRO_KIND
-     * @return Bitmap, or null on failures
-     *
-     * @hide This method is only used by media framework and media provider internally.
+     * @param file The audio file.
+     * @param size The desired thumbnail size.
+     * @throws IOException If any trouble was encountered while generating or
+     *             loading the thumbnail, or if
+     *             {@link CancellationSignal#cancel()} was invoked.
      */
-    @UnsupportedAppUsage
-    public static Bitmap createImageThumbnail(String filePath, int kind) {
-        boolean wantMini = (kind == Images.Thumbnails.MINI_KIND);
-        int targetSize = wantMini
-                ? TARGET_SIZE_MINI_THUMBNAIL
-                : TARGET_SIZE_MICRO_THUMBNAIL;
-        int maxPixels = wantMini
-                ? MAX_NUM_PIXELS_THUMBNAIL
-                : MAX_NUM_PIXELS_MICRO_THUMBNAIL;
-        SizedThumbnailBitmap sizedThumbnailBitmap = new SizedThumbnailBitmap();
-        Bitmap bitmap = null;
-        String mimeType = MediaFile.getMimeTypeForFile(filePath);
+    public static @NonNull Bitmap createAudioThumbnail(@NonNull File file, @NonNull Size size,
+            @Nullable CancellationSignal signal) throws IOException {
+        // Checkpoint before going deeper
+        if (signal != null) signal.throwIfCanceled();
+
+        final Resizer resizer = new Resizer(size, signal);
+        try (MediaMetadataRetriever retriever = new MediaMetadataRetriever()) {
+            retriever.setDataSource(file.getAbsolutePath());
+            final byte[] raw = retriever.getEmbeddedPicture();
+            if (raw != null) {
+                return ImageDecoder.decodeBitmap(ImageDecoder.createSource(raw), resizer);
+            }
+        } catch (RuntimeException e) {
+            throw new IOException("Failed to create thumbnail", e);
+        }
+
+        // Only poke around for files on external storage
+        if (MEDIA_UNKNOWN.equals(Environment.getExternalStorageState(file))) {
+            throw new IOException("No embedded album art found");
+        }
+
+        // Ignore "Downloads" or top-level directories
+        final File parent = file.getParentFile();
+        final File grandParent = parent != null ? parent.getParentFile() : null;
+        if (parent != null
+                && parent.getName().equals(Environment.DIRECTORY_DOWNLOADS)) {
+            throw new IOException("No thumbnails in Downloads directories");
+        }
+        if (grandParent != null
+                && MEDIA_UNKNOWN.equals(Environment.getExternalStorageState(grandParent))) {
+            throw new IOException("No thumbnails in top-level directories");
+        }
+
+        // If no embedded image found, look around for best standalone file
+        final File[] found = ArrayUtils
+                .defeatNullable(file.getParentFile().listFiles((dir, name) -> {
+                    final String lower = name.toLowerCase();
+                    return (lower.endsWith(".jpg") || lower.endsWith(".png"));
+                }));
+
+        final ToIntFunction<File> score = (f) -> {
+            final String lower = f.getName().toLowerCase();
+            if (lower.equals("albumart.jpg")) return 4;
+            if (lower.startsWith("albumart") && lower.endsWith(".jpg")) return 3;
+            if (lower.contains("albumart") && lower.endsWith(".jpg")) return 2;
+            if (lower.endsWith(".jpg")) return 1;
+            return 0;
+        };
+        final Comparator<File> bestScore = (a, b) -> {
+            return score.applyAsInt(a) - score.applyAsInt(b);
+        };
+
+        final File bestFile = Arrays.asList(found).stream().max(bestScore).orElse(null);
+        if (bestFile == null) {
+            throw new IOException("No album art found");
+        }
+
+        // Checkpoint before going deeper
+        if (signal != null) signal.throwIfCanceled();
+
+        return ImageDecoder.decodeBitmap(ImageDecoder.createSource(bestFile), resizer);
+    }
+
+    /**
+     * Create a thumbnail for given image file.
+     *
+     * @param filePath The image file.
+     * @param kind The desired thumbnail kind, such as
+     *            {@link android.provider.MediaStore.Images.Thumbnails#MINI_KIND}.
+     */
+    @Deprecated
+    public static @Nullable Bitmap createImageThumbnail(@NonNull String filePath, int kind) {
+        try {
+            return createImageThumbnail(new File(filePath), convertKind(kind), null);
+        } catch (IOException e) {
+            Log.w(TAG, e);
+            return null;
+        }
+    }
+
+    /**
+     * Create a thumbnail for given image file.
+     *
+     * @param file The audio file.
+     * @param size The desired thumbnail size.
+     * @throws IOException If any trouble was encountered while generating or
+     *             loading the thumbnail, or if
+     *             {@link CancellationSignal#cancel()} was invoked.
+     */
+    public static @NonNull Bitmap createImageThumbnail(@NonNull File file, @NonNull Size size,
+            @Nullable CancellationSignal signal) throws IOException {
+        // Checkpoint before going deeper
+        if (signal != null) signal.throwIfCanceled();
+
+        final Resizer resizer = new Resizer(size, signal);
+        final String mimeType = MediaFile.getMimeTypeForFile(file.getName());
         if (mimeType.equals("image/heif")
                 || mimeType.equals("image/heif-sequence")
                 || mimeType.equals("image/heic")
                 || mimeType.equals("image/heic-sequence")) {
-            bitmap = createThumbnailFromMetadataRetriever(filePath, targetSize, maxPixels);
-        } else if (MediaFile.isExifMimeType(mimeType)) {
-            createThumbnailFromEXIF(filePath, targetSize, maxPixels, sizedThumbnailBitmap);
-            bitmap = sizedThumbnailBitmap.mBitmap;
-        }
-
-        if (bitmap == null) {
-            FileInputStream stream = null;
-            try {
-                stream = new FileInputStream(filePath);
-                FileDescriptor fd = stream.getFD();
-                BitmapFactory.Options options = new BitmapFactory.Options();
-                options.inSampleSize = 1;
-                options.inJustDecodeBounds = true;
-                BitmapFactory.decodeFileDescriptor(fd, null, options);
-                if (options.mCancel || options.outWidth == -1
-                        || options.outHeight == -1) {
-                    return null;
-                }
-                options.inSampleSize = computeSampleSize(
-                        options, targetSize, maxPixels);
-                options.inJustDecodeBounds = false;
-
-                options.inDither = false;
-                options.inPreferredConfig = Bitmap.Config.ARGB_8888;
-                bitmap = BitmapFactory.decodeFileDescriptor(fd, null, options);
-            } catch (IOException ex) {
-                Log.e(TAG, "", ex);
-            } catch (OutOfMemoryError oom) {
-                Log.e(TAG, "Unable to decode file " + filePath + ". OutOfMemoryError.", oom);
-            } finally {
-                try {
-                    if (stream != null) {
-                        stream.close();
-                    }
-                } catch (IOException ex) {
-                    Log.e(TAG, "", ex);
-                }
+            try (MediaMetadataRetriever retriever = new MediaMetadataRetriever()) {
+                retriever.setDataSource(file.getAbsolutePath());
+                return retriever.getThumbnailImageAtIndex(-1,
+                        new MediaMetadataRetriever.BitmapParams(), size.getWidth(),
+                        size.getWidth() * size.getHeight());
+            } catch (RuntimeException e) {
+                throw new IOException("Failed to create thumbnail", e);
             }
-
+        } else if (MediaFile.isExifMimeType(mimeType)) {
+            final ExifInterface exif = new ExifInterface(file);
+            final byte[] raw = exif.getThumbnailBytes();
+            if (raw != null) {
+                return ImageDecoder.decodeBitmap(ImageDecoder.createSource(raw), resizer);
+            }
         }
 
-        if (kind == Images.Thumbnails.MICRO_KIND) {
-            // now we make it a "square thumbnail" for MICRO_KIND thumbnail
-            bitmap = extractThumbnail(bitmap,
-                    TARGET_SIZE_MICRO_THUMBNAIL,
-                    TARGET_SIZE_MICRO_THUMBNAIL, OPTIONS_RECYCLE_INPUT);
-        }
-        return bitmap;
+        // Checkpoint before going deeper
+        if (signal != null) signal.throwIfCanceled();
+
+        return ImageDecoder.decodeBitmap(ImageDecoder.createSource(file), resizer);
     }
 
     /**
-     * Create a video thumbnail for a video. May return null if the video is
-     * corrupt or the format is not supported.
+     * Create a thumbnail for given video file.
      *
-     * @param filePath the path of video file
-     * @param kind could be MINI_KIND or MICRO_KIND
+     * @param filePath The video file.
+     * @param kind The desired thumbnail kind, such as
+     *            {@link android.provider.MediaStore.Images.Thumbnails#MINI_KIND}.
      */
-    public static Bitmap createVideoThumbnail(String filePath, int kind) {
-        Bitmap bitmap = null;
-        MediaMetadataRetriever retriever = new MediaMetadataRetriever();
+    @Deprecated
+    public static @Nullable Bitmap createVideoThumbnail(@NonNull String filePath, int kind) {
         try {
-            retriever.setDataSource(filePath);
-            // First retrieve album art in metadata if set.
-            byte[] embeddedPicture = retriever.getEmbeddedPicture();
-            if (embeddedPicture != null && embeddedPicture.length > 0) {
-                bitmap = BitmapFactory.decodeByteArray(embeddedPicture, 0, embeddedPicture.length);
-            }
-            // Fall back to first frame of the video.
-            if (bitmap == null) {
-                bitmap = retriever.getFrameAtTime(-1);
-            }
-        } catch (IllegalArgumentException ex) {
-            // Assume this is a corrupt video file
-        } catch (RuntimeException ex) {
-            // Assume this is a corrupt video file.
-        } finally {
-            try {
-                retriever.release();
-            } catch (RuntimeException ex) {
-                // Ignore failures while cleaning up.
-            }
+            return createVideoThumbnail(new File(filePath), convertKind(kind), null);
+        } catch (IOException e) {
+            Log.w(TAG, e);
+            return null;
         }
+    }
 
-        if (bitmap == null) return null;
+    /**
+     * Create a thumbnail for given video file.
+     *
+     * @param file The video file.
+     * @param size The desired thumbnail size.
+     * @throws IOException If any trouble was encountered while generating or
+     *             loading the thumbnail, or if
+     *             {@link CancellationSignal#cancel()} was invoked.
+     */
+    public static @NonNull Bitmap createVideoThumbnail(@NonNull File file, @NonNull Size size,
+            @Nullable CancellationSignal signal) throws IOException {
+        // Checkpoint before going deeper
+        if (signal != null) signal.throwIfCanceled();
 
-        if (kind == Images.Thumbnails.MINI_KIND) {
-            // Scale down the bitmap if it's too large.
-            int width = bitmap.getWidth();
-            int height = bitmap.getHeight();
-            int max = Math.max(width, height);
-            if (max > 512) {
-                float scale = 512f / max;
-                int w = Math.round(scale * width);
-                int h = Math.round(scale * height);
-                bitmap = Bitmap.createScaledBitmap(bitmap, w, h, true);
+        final Resizer resizer = new Resizer(size, signal);
+        try (MediaMetadataRetriever mmr = new MediaMetadataRetriever()) {
+            mmr.setDataSource(file.getAbsolutePath());
+
+            // Try to retrieve thumbnail from metadata
+            final byte[] raw = mmr.getEmbeddedPicture();
+            if (raw != null) {
+                return ImageDecoder.decodeBitmap(ImageDecoder.createSource(raw), resizer);
             }
-        } else if (kind == Images.Thumbnails.MICRO_KIND) {
-            bitmap = extractThumbnail(bitmap,
-                    TARGET_SIZE_MICRO_THUMBNAIL,
-                    TARGET_SIZE_MICRO_THUMBNAIL,
-                    OPTIONS_RECYCLE_INPUT);
+
+            // Fall back to middle of video
+            final int width = Integer.parseInt(mmr.extractMetadata(METADATA_KEY_VIDEO_WIDTH));
+            final int height = Integer.parseInt(mmr.extractMetadata(METADATA_KEY_VIDEO_HEIGHT));
+            final long duration = Long.parseLong(mmr.extractMetadata(METADATA_KEY_DURATION));
+
+            // If we're okay with something larger than native format, just
+            // return a frame without up-scaling it
+            if (size.getWidth() > width && size.getHeight() > height) {
+                return mmr.getFrameAtTime(duration / 2, OPTION_CLOSEST_SYNC);
+            } else {
+                return mmr.getScaledFrameAtTime(duration / 2, OPTION_CLOSEST_SYNC,
+                        size.getWidth(), size.getHeight());
+            }
+        } catch (RuntimeException e) {
+            throw new IOException("Failed to create thumbnail", e);
         }
-        return bitmap;
     }
 
     /**
@@ -242,122 +361,27 @@
         return thumbnail;
     }
 
-    /*
-     * Compute the sample size as a function of minSideLength
-     * and maxNumOfPixels.
-     * minSideLength is used to specify that minimal width or height of a
-     * bitmap.
-     * maxNumOfPixels is used to specify the maximal size in pixels that is
-     * tolerable in terms of memory usage.
-     *
-     * The function returns a sample size based on the constraints.
-     * Both size and minSideLength can be passed in as IImage.UNCONSTRAINED,
-     * which indicates no care of the corresponding constraint.
-     * The functions prefers returning a sample size that
-     * generates a smaller bitmap, unless minSideLength = IImage.UNCONSTRAINED.
-     *
-     * Also, the function rounds up the sample size to a power of 2 or multiple
-     * of 8 because BitmapFactory only honors sample size this way.
-     * For example, BitmapFactory downsamples an image by 2 even though the
-     * request is 3. So we round up the sample size to avoid OOM.
-     */
+    @Deprecated
     @UnsupportedAppUsage
     private static int computeSampleSize(BitmapFactory.Options options,
             int minSideLength, int maxNumOfPixels) {
-        int initialSize = computeInitialSampleSize(options, minSideLength,
-                maxNumOfPixels);
-
-        int roundedSize;
-        if (initialSize <= 8 ) {
-            roundedSize = 1;
-            while (roundedSize < initialSize) {
-                roundedSize <<= 1;
-            }
-        } else {
-            roundedSize = (initialSize + 7) / 8 * 8;
-        }
-
-        return roundedSize;
+        return 1;
     }
 
+    @Deprecated
     @UnsupportedAppUsage
     private static int computeInitialSampleSize(BitmapFactory.Options options,
             int minSideLength, int maxNumOfPixels) {
-        double w = options.outWidth;
-        double h = options.outHeight;
-
-        int lowerBound = (maxNumOfPixels == UNCONSTRAINED) ? 1 :
-                (int) Math.ceil(Math.sqrt(w * h / maxNumOfPixels));
-        int upperBound = (minSideLength == UNCONSTRAINED) ? 128 :
-                (int) Math.min(Math.floor(w / minSideLength),
-                Math.floor(h / minSideLength));
-
-        if (upperBound < lowerBound) {
-            // return the larger one when there is no overlapping zone.
-            return lowerBound;
-        }
-
-        if ((maxNumOfPixels == UNCONSTRAINED) &&
-                (minSideLength == UNCONSTRAINED)) {
-            return 1;
-        } else if (minSideLength == UNCONSTRAINED) {
-            return lowerBound;
-        } else {
-            return upperBound;
-        }
+        return 1;
     }
 
-    /**
-     * Make a bitmap from a given Uri, minimal side length, and maximum number of pixels.
-     * The image data will be read from specified pfd if it's not null, otherwise
-     * a new input stream will be created using specified ContentResolver.
-     *
-     * Clients are allowed to pass their own BitmapFactory.Options used for bitmap decoding. A
-     * new BitmapFactory.Options will be created if options is null.
-     */
-    private static Bitmap makeBitmap(int minSideLength, int maxNumOfPixels,
-            Uri uri, ContentResolver cr, ParcelFileDescriptor pfd,
-            BitmapFactory.Options options) {
-        Bitmap b = null;
-        try {
-            if (pfd == null) pfd = makeInputStream(uri, cr);
-            if (pfd == null) return null;
-            if (options == null) options = new BitmapFactory.Options();
-
-            FileDescriptor fd = pfd.getFileDescriptor();
-            options.inSampleSize = 1;
-            options.inJustDecodeBounds = true;
-            BitmapFactory.decodeFileDescriptor(fd, null, options);
-            if (options.mCancel || options.outWidth == -1
-                    || options.outHeight == -1) {
-                return null;
-            }
-            options.inSampleSize = computeSampleSize(
-                    options, minSideLength, maxNumOfPixels);
-            options.inJustDecodeBounds = false;
-
-            options.inDither = false;
-            options.inPreferredConfig = Bitmap.Config.ARGB_8888;
-            b = BitmapFactory.decodeFileDescriptor(fd, null, options);
-        } catch (OutOfMemoryError ex) {
-            Log.e(TAG, "Got oom exception ", ex);
-            return null;
-        } finally {
-            closeSilently(pfd);
-        }
-        return b;
-    }
-
+    @Deprecated
     @UnsupportedAppUsage
     private static void closeSilently(ParcelFileDescriptor c) {
-      if (c == null) return;
-      try {
-          c.close();
-      } catch (Throwable t) {
-          // do nothing
-      }
+        IoUtils.closeQuietly(c);
     }
 
+    @Deprecated
     @UnsupportedAppUsage
     private static ParcelFileDescriptor makeInputStream(
             Uri uri, ContentResolver cr) {
@@ -371,6 +395,7 @@
     /**
      * Transform source Bitmap to targeted width and height.
      */
+    @Deprecated
     @UnsupportedAppUsage
     private static Bitmap transform(Matrix scaler,
             Bitmap source,
@@ -468,14 +493,7 @@
         return b2;
     }
 
-    /**
-     * SizedThumbnailBitmap contains the bitmap, which is downsampled either from
-     * the thumbnail in exif or the full image.
-     * mThumbnailData, mThumbnailWidth and mThumbnailHeight are set together only if mThumbnail
-     * is not null.
-     *
-     * The width/height of the sized bitmap may be different from mThumbnailWidth/mThumbnailHeight.
-     */
+    @Deprecated
     private static class SizedThumbnailBitmap {
         public byte[] mThumbnailData;
         public Bitmap mBitmap;
@@ -483,81 +501,9 @@
         public int mThumbnailHeight;
     }
 
-    /**
-     * Creates a bitmap by either downsampling from the thumbnail in EXIF or the full image.
-     * The functions returns a SizedThumbnailBitmap,
-     * which contains a downsampled bitmap and the thumbnail data in EXIF if exists.
-     */
+    @Deprecated
     @UnsupportedAppUsage
     private static void createThumbnailFromEXIF(String filePath, int targetSize,
             int maxPixels, SizedThumbnailBitmap sizedThumbBitmap) {
-        if (filePath == null) return;
-
-        ExifInterface exif = null;
-        byte [] thumbData = null;
-        try {
-            exif = new ExifInterface(filePath);
-            thumbData = exif.getThumbnail();
-        } catch (IOException ex) {
-            Log.w(TAG, ex);
-        }
-
-        BitmapFactory.Options fullOptions = new BitmapFactory.Options();
-        BitmapFactory.Options exifOptions = new BitmapFactory.Options();
-        int exifThumbWidth = 0;
-        int fullThumbWidth = 0;
-
-        // Compute exifThumbWidth.
-        if (thumbData != null) {
-            exifOptions.inJustDecodeBounds = true;
-            BitmapFactory.decodeByteArray(thumbData, 0, thumbData.length, exifOptions);
-            exifOptions.inSampleSize = computeSampleSize(exifOptions, targetSize, maxPixels);
-            exifThumbWidth = exifOptions.outWidth / exifOptions.inSampleSize;
-        }
-
-        // Compute fullThumbWidth.
-        fullOptions.inJustDecodeBounds = true;
-        BitmapFactory.decodeFile(filePath, fullOptions);
-        fullOptions.inSampleSize = computeSampleSize(fullOptions, targetSize, maxPixels);
-        fullThumbWidth = fullOptions.outWidth / fullOptions.inSampleSize;
-
-        // Choose the larger thumbnail as the returning sizedThumbBitmap.
-        if (thumbData != null && exifThumbWidth >= fullThumbWidth) {
-            int width = exifOptions.outWidth;
-            int height = exifOptions.outHeight;
-            exifOptions.inJustDecodeBounds = false;
-            sizedThumbBitmap.mBitmap = BitmapFactory.decodeByteArray(thumbData, 0,
-                    thumbData.length, exifOptions);
-            if (sizedThumbBitmap.mBitmap != null) {
-                sizedThumbBitmap.mThumbnailData = thumbData;
-                sizedThumbBitmap.mThumbnailWidth = width;
-                sizedThumbBitmap.mThumbnailHeight = height;
-            }
-        } else {
-            fullOptions.inJustDecodeBounds = false;
-            sizedThumbBitmap.mBitmap = BitmapFactory.decodeFile(filePath, fullOptions);
-        }
-    }
-
-    private static Bitmap createThumbnailFromMetadataRetriever(
-            String filePath, int targetSize, int maxPixels) {
-        if (filePath == null) {
-            return null;
-        }
-        Bitmap thumbnail = null;
-        MediaMetadataRetriever retriever = new MediaMetadataRetriever();
-        try {
-            retriever.setDataSource(filePath);
-            MediaMetadataRetriever.BitmapParams params = new MediaMetadataRetriever.BitmapParams();
-            params.setPreferredConfig(Bitmap.Config.ARGB_8888);
-            thumbnail = retriever.getThumbnailImageAtIndex(-1, params, targetSize, maxPixels);
-        } catch (RuntimeException ex) {
-            // Assume this is a corrupt video file.
-        } finally {
-            if (retriever != null) {
-                retriever.release();
-            }
-        }
-        return thumbnail;
     }
 }
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/native/android/net.c b/native/android/net.c
index e32b787..4cac371 100644
--- a/native/android/net.c
+++ b/native/android/net.c
@@ -84,8 +84,7 @@
     return android_getaddrinfofornet(node, service, hints, netid, 0, res);
 }
 
-int android_res_nquery(net_handle_t network,
-        const char *dname, int ns_class, int ns_type) {
+int android_res_nquery(net_handle_t network, const char *dname, int ns_class, int ns_type) {
     unsigned netid;
     if (!getnetidfromhandle(network, &netid)) {
         return -ENONET;
@@ -94,12 +93,11 @@
     return resNetworkQuery(netid, dname, ns_class, ns_type);
 }
 
-int android_res_nresult(int fd, int *rcode, unsigned char *answer, int anslen) {
+int android_res_nresult(int fd, int *rcode, uint8_t *answer, size_t anslen) {
     return resNetworkResult(fd, rcode, answer, anslen);
 }
 
-int android_res_nsend(net_handle_t network,
-        const unsigned char *msg, int msglen) {
+int android_res_nsend(net_handle_t network, const uint8_t *msg, size_t msglen) {
     unsigned netid;
     if (!getnetidfromhandle(network, &netid)) {
         return -ENONET;
diff --git a/packages/CarSystemUI/Android.bp b/packages/CarSystemUI/Android.bp
index 6874715..9b6ad38 100644
--- a/packages/CarSystemUI/Android.bp
+++ b/packages/CarSystemUI/Android.bp
@@ -81,4 +81,5 @@
         "com.android.keyguard",
     ],
 
+    annotation_processors: ["dagger2-compiler-2.19"],
 }
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/src/com/android/systemui/CarSystemUIFactory.java b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIFactory.java
index f57f26d..7039a2c 100644
--- a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIFactory.java
+++ b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIFactory.java
@@ -17,25 +17,42 @@
 package com.android.systemui;
 
 import android.content.Context;
-import android.util.ArrayMap;
 
 import com.android.internal.widget.LockPatternUtils;
 import com.android.keyguard.ViewMediatorCallback;
-import com.android.systemui.Dependency.DependencyProvider;
 import com.android.systemui.car.CarNotificationEntryManager;
 import com.android.systemui.statusbar.car.CarFacetButtonController;
 import com.android.systemui.statusbar.car.CarStatusBarKeyguardViewManager;
-import com.android.systemui.statusbar.car.hvac.HvacController;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
 import com.android.systemui.volume.CarVolumeDialogComponent;
 import com.android.systemui.volume.VolumeDialogComponent;
 
+import javax.inject.Singleton;
+
+import dagger.Component;
+import dagger.Module;
+import dagger.Provides;
+
 /**
  * Class factory to provide car specific SystemUI components.
  */
 public class CarSystemUIFactory extends SystemUIFactory {
 
+    private CarDependencyComponent mCarDependencyComponent;
+
+    @Override
+    protected void init(Context context) {
+        super.init(context);
+        mCarDependencyComponent = DaggerCarSystemUIFactory_CarDependencyComponent.builder()
+                .contextHolder(new ContextHolder(context))
+                .build();
+    }
+
+    public CarDependencyComponent getCarDependencyComponent() {
+        return mCarDependencyComponent;
+    }
+
     public StatusBarKeyguardViewManager createStatusBarKeyguardViewManager(Context context,
         ViewMediatorCallback viewMediatorCallback, LockPatternUtils lockPatternUtils) {
         return new CarStatusBarKeyguardViewManager(context, viewMediatorCallback, lockPatternUtils);
@@ -46,12 +63,27 @@
     }
 
     @Override
-    public void injectDependencies(ArrayMap<Object, DependencyProvider> providers,
-        Context context) {
-        super.injectDependencies(providers, context);
-        providers.put(NotificationEntryManager.class,
-            () -> new CarNotificationEntryManager(context));
-        providers.put(CarFacetButtonController.class, () -> new CarFacetButtonController(context));
-        providers.put(HvacController.class, () -> new HvacController(context));
+    public NotificationEntryManager provideNotificationEntryManager(Context context) {
+        return new CarNotificationEntryManager(context);
+    }
+
+    @Module
+    protected static class ContextHolder {
+        private Context mContext;
+
+        public ContextHolder(Context context) {
+            mContext = context;
+        }
+
+        @Provides
+        public Context provideContext() {
+            return mContext;
+        }
+    }
+
+    @Singleton
+    @Component(modules = ContextHolder.class)
+    public interface CarDependencyComponent {
+        CarFacetButtonController getCarFacetButtonController();
     }
 }
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarFacetButton.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarFacetButton.java
index cea4ab0..0a20eaa 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarFacetButton.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarFacetButton.java
@@ -26,8 +26,9 @@
 import android.widget.LinearLayout;
 
 import com.android.keyguard.AlphaOptimizedImageButton;
-import com.android.systemui.Dependency;
+import com.android.systemui.CarSystemUIFactory;
 import com.android.systemui.R;
+import com.android.systemui.SystemUIFactory;
 
 /**
  * CarFacetButton is a ui component designed to be used as a shortcut for an app of a defined
@@ -76,8 +77,9 @@
         TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.CarFacetButton);
         setupIntents(typedArray);
         setupIcons(typedArray);
-        CarFacetButtonController carFacetButtonController = Dependency.get(
-                CarFacetButtonController.class);
+        CarSystemUIFactory factory = SystemUIFactory.getInstance();
+        CarFacetButtonController carFacetButtonController = factory.getCarDependencyComponent()
+                .getCarFacetButtonController();
         carFacetButtonController.addFacetButton(this);
     }
 
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarFacetButtonController.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarFacetButtonController.java
index 56db242..7811a1c 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarFacetButtonController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarFacetButtonController.java
@@ -29,11 +29,15 @@
 import java.util.List;
 import java.util.Set;
 
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
 /**
  * CarFacetButtons placed on the nav bar are designed to have visual indication that the active
  * application on screen is associated with it. This is basically a similar concept to a radio
  * button group.
  */
+@Singleton
 public class CarFacetButtonController {
 
     protected HashMap<String, CarFacetButton> mButtonsByCategory = new HashMap<>();
@@ -42,6 +46,7 @@
     protected CarFacetButton mSelectedFacetButton;
     protected Context mContext;
 
+    @Inject
     public CarFacetButtonController(Context context) {
         mContext = context;
     }
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
index 5da236c..7028999c 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
@@ -29,9 +29,11 @@
 
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.systemui.BatteryMeterView;
+import com.android.systemui.CarSystemUIFactory;
 import com.android.systemui.Dependency;
 import com.android.systemui.Prefs;
 import com.android.systemui.R;
+import com.android.systemui.SystemUIFactory;
 import com.android.systemui.classifier.FalsingLog;
 import com.android.systemui.classifier.FalsingManager;
 import com.android.systemui.fragments.FragmentHostManager;
@@ -102,7 +104,9 @@
 
         mHvacController.connectToCarService();
 
-        mCarFacetButtonController = Dependency.get(CarFacetButtonController.class);
+        CarSystemUIFactory factory = SystemUIFactory.getInstance();
+        mCarFacetButtonController = factory.getCarDependencyComponent()
+                .getCarFacetButtonController();
         mDeviceProvisionedController = Dependency.get(DeviceProvisionedController.class);
         mDeviceIsProvisioned = mDeviceProvisionedController.isDeviceProvisioned();
         if (!mDeviceIsProvisioned) {
@@ -239,7 +243,7 @@
     @Override
     protected void makeStatusBarView() {
         super.makeStatusBarView();
-        mHvacController = Dependency.get(HvacController.class);
+        mHvacController = new HvacController(mContext);
 
         mNotificationPanelBackground = getDefaultWallpaper();
         mScrimController.setScrimBehindDrawable(mNotificationPanelBackground);
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/CompanionDeviceManager/AndroidManifest.xml b/packages/CompanionDeviceManager/AndroidManifest.xml
index 34bc4eb..0be71e6 100644
--- a/packages/CompanionDeviceManager/AndroidManifest.xml
+++ b/packages/CompanionDeviceManager/AndroidManifest.xml
@@ -26,6 +26,7 @@
     <uses-permission android:name="android.permission.BLUETOOTH"/>
     <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
     <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
+    <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION"/>
     <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
     <uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>
 
diff --git a/packages/ExtServices/src/android/ext/services/sms/FinancialSmsServiceImpl.java b/packages/ExtServices/src/android/ext/services/sms/FinancialSmsServiceImpl.java
index ab718021..81a63dd 100644
--- a/packages/ExtServices/src/android/ext/services/sms/FinancialSmsServiceImpl.java
+++ b/packages/ExtServices/src/android/ext/services/sms/FinancialSmsServiceImpl.java
@@ -17,14 +17,10 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.database.Cursor;
 import android.database.CursorWindow;
-import android.net.Uri;
 import android.os.Bundle;
 import android.service.sms.FinancialSmsService;
-import android.util.Log;
 
-import java.util.ArrayList;
 /**
  * Service to provide financial apps access to sms messages.
  */
@@ -36,45 +32,6 @@
     @Nullable
     @Override
     public CursorWindow onGetSmsMessages(@NonNull Bundle params) {
-        ArrayList<String> columnNames = params.getStringArrayList(KEY_COLUMN_NAMES);
-        if (columnNames == null || columnNames.size() <= 0) {
-            return null;
-        }
-
-        Uri inbox = Uri.parse("content://sms/inbox");
-
-        try (Cursor cursor = getContentResolver().query(inbox, null, null, null, null);
-                CursorWindow window = new CursorWindow("FinancialSmsMessages")) {
-            int messageCount = cursor.getCount();
-            if (messageCount > 0 && cursor.moveToFirst()) {
-                window.setNumColumns(columnNames.size());
-                for (int row = 0; row < messageCount; row++) {
-                    if (!window.allocRow()) {
-                        Log.e(TAG, "CursorWindow ran out of memory.");
-                        return null;
-                    }
-                    for (int col = 0; col < columnNames.size(); col++) {
-                        String columnName = columnNames.get(col);
-                        int inboxColumnIndex = cursor.getColumnIndexOrThrow(columnName);
-                        String inboxColumnValue = cursor.getString(inboxColumnIndex);
-                        boolean addedToCursorWindow = window.putString(inboxColumnValue, row, col);
-                        if (!addedToCursorWindow) {
-                            Log.e(TAG, "Failed to add:"
-                                    + inboxColumnValue
-                                    + ";column:"
-                                    + columnName);
-                            return null;
-                        }
-                    }
-                    cursor.moveToNext();
-                }
-            } else {
-                Log.w(TAG, "No sms messages.");
-            }
-            return window;
-        } catch (Exception e) {
-            Log.e(TAG, "Failed to get sms messages.");
-            return null;
-        }
+        return null;
     }
 }
diff --git a/packages/SettingsLib/Android.bp b/packages/SettingsLib/Android.bp
index 042808a0..2321790 100644
--- a/packages/SettingsLib/Android.bp
+++ b/packages/SettingsLib/Android.bp
@@ -19,6 +19,7 @@
         "SettingsLibLayoutPreference",
         "SettingsLibActionButtonsPreference",
         "SettingsLibEntityHeaderWidgets",
+        "SettingsLibBarChartPreference"
     ],
 
     // ANDROIDMK TRANSLATION ERROR: unsupported assignment to LOCAL_SHARED_JAVA_LIBRARIES
diff --git a/packages/SettingsLib/BarChartPreference/Android.bp b/packages/SettingsLib/BarChartPreference/Android.bp
new file mode 100644
index 0000000..477e897
--- /dev/null
+++ b/packages/SettingsLib/BarChartPreference/Android.bp
@@ -0,0 +1,13 @@
+android_library {
+    name: "SettingsLibBarChartPreference",
+
+    srcs: ["src/**/*.java"],
+    resource_dirs: ["res"],
+
+    static_libs: [
+          "androidx.preference_preference",
+    ],
+
+    sdk_version: "system_current",
+    min_sdk_version: "21",
+}
diff --git a/packages/SettingsLib/BarChartPreference/AndroidManifest.xml b/packages/SettingsLib/BarChartPreference/AndroidManifest.xml
new file mode 100644
index 0000000..4b9f1ab
--- /dev/null
+++ b/packages/SettingsLib/BarChartPreference/AndroidManifest.xml
@@ -0,0 +1,23 @@
+<?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.
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="com.android.settingslib.widget">
+
+    <uses-sdk android:minSdkVersion="21" />
+
+</manifest>
diff --git a/packages/SettingsLib/BarChartPreference/res/layout/settings_bar_chart.xml b/packages/SettingsLib/BarChartPreference/res/layout/settings_bar_chart.xml
new file mode 100644
index 0000000..1a4d7b7
--- /dev/null
+++ b/packages/SettingsLib/BarChartPreference/res/layout/settings_bar_chart.xml
@@ -0,0 +1,65 @@
+<?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.
+  -->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:settings="http://schemas.android.com/apk/res-auto"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:layout_marginStart="16dp"
+    android:layout_marginEnd="16dp"
+    android:gravity="center"
+    android:orientation="vertical">
+
+    <TextView
+        android:id="@+id/bar_chart_title"
+        android:layout_width="wrap_content"
+        android:layout_height="48dp"
+        android:gravity="center"
+        android:textAppearance="@style/BarChart.Text.HeaderTitle"/>
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:gravity="center|bottom">
+
+        <com.android.settingslib.widget.BarView
+            android:id="@+id/bar_view1"
+            style="@style/BarViewStyle"
+            settings:barColor="#FA7B17"/>
+        <com.android.settingslib.widget.BarView
+            android:id="@+id/bar_view2"
+            style="@style/BarViewStyle"
+            settings:barColor="#F439A0"/>
+        <com.android.settingslib.widget.BarView
+            android:id="@+id/bar_view3"
+            style="@style/BarViewStyle"
+            settings:barColor="#A142F4"/>
+        <com.android.settingslib.widget.BarView
+            android:id="@+id/bar_view4"
+            style="@style/BarViewStyle"
+            settings:barColor="#24C1E0"/>
+    </LinearLayout>
+
+    <Button
+        android:id="@+id/bar_chart_details"
+        style="@android:style/Widget.DeviceDefault.Button.Borderless.Colored"
+        android:layout_width="wrap_content"
+        android:layout_height="48dp"
+        android:gravity="center"/>
+
+</LinearLayout>
diff --git a/packages/SettingsLib/BarChartPreference/res/layout/settings_bar_view.xml b/packages/SettingsLib/BarChartPreference/res/layout/settings_bar_view.xml
new file mode 100644
index 0000000..b053317
--- /dev/null
+++ b/packages/SettingsLib/BarChartPreference/res/layout/settings_bar_view.xml
@@ -0,0 +1,58 @@
+<?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.
+  -->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:gravity="center"
+    android:orientation="vertical">
+
+    <View
+        android:id="@+id/bar_view"
+        android:layout_width="8dp"
+        android:layout_height="wrap_content"
+        android:background="?android:attr/colorAccent"/>
+
+    <ImageView
+        android:id="@+id/icon_view"
+        android:layout_width="@dimen/settings_bar_view_icon_size"
+        android:layout_height="@dimen/settings_bar_view_icon_size"
+        android:scaleType="fitCenter"
+        android:layout_marginTop="12dp"
+        android:layout_marginBottom="12dp"/>
+
+    <TextView
+        android:id="@+id/bar_title"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="10dp"
+        android:layout_marginBottom="2dp"
+        android:singleLine="true"
+        android:ellipsize="marquee"
+        android:textAppearance="@style/BarChart.Text.Title"/>
+
+    <TextView
+        android:id="@+id/bar_summary"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginBottom="12dp"
+        android:singleLine="true"
+        android:ellipsize="marquee"
+        android:textAppearance="@style/BarChart.Text.Summary"/>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/packages/SettingsLib/BarChartPreference/res/values/attrs.xml b/packages/SettingsLib/BarChartPreference/res/values/attrs.xml
new file mode 100644
index 0000000..df3eb0a
--- /dev/null
+++ b/packages/SettingsLib/BarChartPreference/res/values/attrs.xml
@@ -0,0 +1,25 @@
+<?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>
+
+    <declare-styleable name="SettingsBarView">
+        <!-- The color of bar view -->
+        <attr name="barColor" format="color" />
+    </declare-styleable>
+
+</resources>
diff --git a/packages/SettingsLib/BarChartPreference/res/values/dimens.xml b/packages/SettingsLib/BarChartPreference/res/values/dimens.xml
new file mode 100644
index 0000000..7148afa
--- /dev/null
+++ b/packages/SettingsLib/BarChartPreference/res/values/dimens.xml
@@ -0,0 +1,21 @@
+<?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>
+    <dimen name="settings_bar_view_max_height">106dp</dimen>
+    <dimen name="settings_bar_view_icon_size">24dp</dimen>
+</resources>
\ No newline at end of file
diff --git a/packages/SettingsLib/BarChartPreference/res/values/styles.xml b/packages/SettingsLib/BarChartPreference/res/values/styles.xml
new file mode 100644
index 0000000..647d080
--- /dev/null
+++ b/packages/SettingsLib/BarChartPreference/res/values/styles.xml
@@ -0,0 +1,46 @@
+<?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>
+    <style name="BarViewStyle">
+        <item name="android:layout_width">0dp</item>
+        <item name="android:layout_height">wrap_content</item>
+        <item name="android:layout_weight">1</item>
+        <item name="android:layout_marginStart">8dp</item>
+        <item name="android:layout_marginEnd">8dp</item>
+    </style>
+
+    <style name="BarChart.Text"
+           parent="@android:style/TextAppearance.Material.Subhead">
+        <item name="android:fontFamily">@*android:string/config_headlineFontFamilyMedium</item>
+        <item name="android:textColor">?android:attr/textColorPrimary</item>
+    </style>
+
+    <style name="BarChart.Text.HeaderTitle">
+        <item name="android:textSize">14sp</item>
+    </style>
+
+    <style name="BarChart.Text.Title">
+        <item name="android:textSize">22sp</item>
+    </style>
+
+    <style name="BarChart.Text.Summary"
+           parent="@android:style/TextAppearance.Material.Body1">
+        <item name="android:textColor">?android:attr/textColorSecondary</item>
+        <item name="android:textSize">14sp</item>
+    </style>
+</resources>
\ No newline at end of file
diff --git a/packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarChartPreference.java b/packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarChartPreference.java
new file mode 100644
index 0000000..89ebf4d
--- /dev/null
+++ b/packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarChartPreference.java
@@ -0,0 +1,242 @@
+/*
+ * 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.widget;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.Button;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.StringRes;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceViewHolder;
+
+import java.util.Arrays;
+
+/**
+ * This BarChartPreference shows four bar views in this preference at most.
+ *
+ * <p>The following code sample shows a typical use, with an XML layout and code to initialize the
+ * contents of the BarChartPreference:
+ *
+ * <pre>
+ * &lt;com.android.settingslib.widget.BarChartPreference
+ *        android:key="bar_chart"/&gt;
+ * </pre>
+ *
+ * <p>This code sample demonstrates how to initialize the contents of the BarChartPreference defined
+ * in the previous XML layout:
+ *
+ * <pre>
+ * BarViewInfo[] viewsInfo = new BarViewInfo [] {
+ *     new BarViewInfo(icon, 18, res of summary),
+ *     new BarViewInfo(icon, 25, res of summary),
+ *     new BarViewInfo(icon, 10, res of summary),
+ *     new BarViewInfo(icon, 3, res of summary),
+ *  };
+ * </pre>
+ *
+ * <pre>
+ * BarChartPreference preference = ((BarChartPreference) findPreference("bar_chart"));
+ *
+ * preference.setBarChartTitleRes(R.string.title_res);
+ * preference.setBarChartDetailsRes(R.string.details_res);
+ * preference.setBarChartDetailsClickListener(v -> doSomething());
+ * preference.setAllBarViewsData(viewsInfo);
+ * </pre>
+ */
+public class BarChartPreference extends Preference {
+
+    private static final String TAG = "BarChartPreference";
+    private static final int MAXIMUM_BAR_VIEWS = 4;
+    private static final int[] BAR_VIEWS = {
+            R.id.bar_view1,
+            R.id.bar_view2,
+            R.id.bar_view3,
+            R.id.bar_view4
+    };
+
+    private int mMaxBarHeight;
+    private @StringRes int mTitleId;
+    private @StringRes int mDetailsId;
+    private BarViewInfo[] mBarViewsInfo;
+    private View.OnClickListener mDetailsOnClickListener;
+
+    /**
+     * Constructs a new BarChartPreference with the given context's theme.
+     * It sets a layout with settings bar chart style
+     *
+     * @param context The Context the view is running in, through which it can
+     *                access the current theme, resources, etc.
+     */
+    public BarChartPreference(Context context) {
+        super(context);
+        init();
+    }
+
+    /**
+     * Constructs a new BarChartPreference with the given context's theme and the supplied
+     * attribute set.
+     * It sets a layout with settings bar chart style
+     *
+     * @param context the Context the view is running in
+     * @param attrs the attributes of the XML tag that is inflating the view.
+     */
+    public BarChartPreference(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        init();
+    }
+
+    /**
+     * Constructs a new BarChartPreference with the given context's theme, the supplied
+     * attribute set, and default style attribute.
+     * It sets a layout with settings bar chart style
+     *
+     * @param context The Context the view is running in, through which it can
+     *                access the current theme, resources, etc.
+     * @param attrs The attributes of the XML tag that is inflating the view.
+     * @param defStyleAttr An attribute in the current theme that contains a
+     *                     reference to a style resource that supplies default
+     *                     values for the view. Can be 0 to not look for
+     *                     defaults.
+     */
+    public BarChartPreference(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+        init();
+    }
+
+    /**
+     * Constructs a new BarChartPreference with the given context's theme, the supplied
+     * attribute set, and default styles.
+     * It sets a layout with settings bar chart style
+     *
+     * @param context The Context the view is running in, through which it can
+     *                access the current theme, resources, etc.
+     * @param attrs The attributes of the XML tag that is inflating the view.
+     * @param defStyleAttr An attribute in the current theme that contains a
+     *                     reference to a style resource that supplies default
+     *                     values for the view. Can be 0 to not look for
+     *                     defaults.
+     * @param defStyleRes  A resource identifier of a style resource that
+     *                     supplies default values for the view, used only if
+     *                     defStyleAttr is 0 or can not be found in the theme.
+     *                     Can be 0 to not look for defaults.
+     */
+    public BarChartPreference(Context context, AttributeSet attrs, int defStyleAttr,
+            int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+        init();
+    }
+
+    /**
+     * Set the text resource for bar chart title.
+     */
+    public void setBarChartTitle(@StringRes int resId) {
+        mTitleId = resId;
+        notifyChanged();
+    }
+
+    /**
+     * Set the text resource for bar chart details.
+     */
+    public void setBarChartDetails(@StringRes int resId) {
+        mDetailsId = resId;
+        notifyChanged();
+    }
+
+    /**
+     * Register a callback to be invoked when bar chart details view is clicked.
+     */
+    public void setBarChartDetailsClickListener(@Nullable View.OnClickListener clickListener) {
+        mDetailsOnClickListener = clickListener;
+        notifyChanged();
+    }
+
+    /**
+     * Set all bar view information which you'd like to show in preference.
+     *
+     * <p>This method helps you do a sort by {@linkBarViewInfo#mBarNumber} in descending order.
+     *
+     * @param barViewsInfo the barViewsInfo contain at least one {@link BarViewInfo}.
+     */
+    public void setAllBarViewsInfo(@NonNull BarViewInfo[] barViewsInfo) {
+        mBarViewsInfo = barViewsInfo;
+        // Do a sort in descending order, the first element would have max {@link
+        // BarViewInfo#mBarNumber}
+        Arrays.sort(mBarViewsInfo);
+        caculateAllBarViewsHeight();
+        notifyChanged();
+    }
+
+    @Override
+    public void onBindViewHolder(PreferenceViewHolder holder) {
+        super.onBindViewHolder(holder);
+        holder.setDividerAllowedAbove(true);
+        holder.setDividerAllowedBelow(true);
+
+        bindChartTitleView(holder);
+        bindChartDetailsView(holder);
+        updateBarChart(holder);
+    }
+
+    private void init() {
+        setSelectable(false);
+        setLayoutResource(R.layout.settings_bar_chart);
+        mMaxBarHeight = getContext().getResources().getDimensionPixelSize(
+                R.dimen.settings_bar_view_max_height);
+    }
+
+    private void bindChartTitleView(PreferenceViewHolder holder) {
+        final TextView titleView = (TextView) holder.findViewById(R.id.bar_chart_title);
+        titleView.setText(mTitleId);
+    }
+
+    private void bindChartDetailsView(PreferenceViewHolder holder) {
+        final Button detailsView = (Button) holder.findViewById(R.id.bar_chart_details);
+        detailsView.setText(mDetailsId);
+        detailsView.setOnClickListener(mDetailsOnClickListener);
+    }
+
+    private void updateBarChart(PreferenceViewHolder holder) {
+        for (int index = 0; index < MAXIMUM_BAR_VIEWS; index++) {
+            final BarView barView = (BarView) holder.findViewById(BAR_VIEWS[index]);
+
+            // If there is no bar views data can be shown.
+            if (mBarViewsInfo == null || index >= mBarViewsInfo.length) {
+                barView.setVisibility(View.GONE);
+                continue;
+            }
+            barView.setVisibility(View.VISIBLE);
+            barView.updateBarViewUI(mBarViewsInfo[index]);
+        }
+    }
+
+    private void caculateAllBarViewsHeight() {
+        // Since we sorted this array in advance, the first element must have the max {@link
+        // BarViewInfo#mBarNumber}.
+        final int maxBarViewNumber = mBarViewsInfo[0].getBarNumber();
+        // If the max number of bar view is zero, then we don't caculate the unit for bar height.
+        final int unit = maxBarViewNumber == 0 ? 0 : mMaxBarHeight / maxBarViewNumber;
+
+        for (BarViewInfo barView : mBarViewsInfo) {
+            barView.setBarHeight(barView.getBarNumber() * unit);
+        }
+    }
+}
diff --git a/packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarView.java b/packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarView.java
new file mode 100644
index 0000000..6243a2d
--- /dev/null
+++ b/packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarView.java
@@ -0,0 +1,117 @@
+/*
+ * 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.widget;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.util.AttributeSet;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import androidx.annotation.ColorInt;
+import androidx.annotation.VisibleForTesting;
+
+/**
+ * A extension view for bar chart.
+ */
+public class BarView extends LinearLayout {
+
+    private static final String TAG = "BarView";
+
+    private View mBarView;
+    private ImageView mIcon;
+    private TextView mBarTitle;
+    private TextView mBarSummary;
+
+    /**
+     * Constructs a new BarView with the given context's theme.
+     *
+     * @param context The Context the view is running in, through which it can
+     *                access the current theme, resources, etc.
+     */
+    public BarView(Context context) {
+        super(context);
+        init();
+    }
+
+    /**
+     * Constructs a new BarView with the given context's theme and the supplied
+     * attribute set.
+     *
+     * @param context the Context the view is running in
+     * @param attrs the attributes of the XML tag that is inflating the view.
+     */
+    public BarView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        init();
+
+        // Get accent color
+        TypedArray a = context.obtainStyledAttributes(new int[]{android.R.attr.colorAccent});
+        @ColorInt final int colorAccent = a.getColor(0, 0);
+
+        // Get bar color from layout XML
+        a = context.obtainStyledAttributes(attrs, R.styleable.SettingsBarView);
+        @ColorInt final int barColor = a.getColor(R.styleable.SettingsBarView_barColor,
+                colorAccent);
+        a.recycle();
+
+        mBarView.setBackgroundColor(barColor);
+    }
+
+    /**
+     * This helps update the bar view UI with a {@link BarViewInfo}.
+     *
+     * @param barViewInfo A {@link BarViewInfo} saves bar view status.
+     */
+    public void updateBarViewUI(BarViewInfo barViewInfo) {
+        //Set height of bar view
+        mBarView.getLayoutParams().height = barViewInfo.getBarHeight();
+        mIcon.setImageDrawable(barViewInfo.getIcon());
+        // For now, we use the bar number as title.
+        mBarTitle.setText(Integer.toString(barViewInfo.getBarNumber()));
+        mBarSummary.setText(barViewInfo.getSummaryRes());
+    }
+
+    @VisibleForTesting
+    CharSequence getTitle() {
+        return mBarTitle.getText();
+    }
+
+    @VisibleForTesting
+    CharSequence getSummary() {
+        return mBarSummary.getText();
+    }
+
+    private void init() {
+        LayoutInflater.from(getContext()).inflate(R.layout.settings_bar_view, this);
+        setOrientation(LinearLayout.VERTICAL);
+        setGravity(Gravity.CENTER);
+
+        mBarView = findViewById(R.id.bar_view);
+        mIcon = (ImageView) findViewById(R.id.icon_view);
+        mBarTitle = (TextView) findViewById(R.id.bar_title);
+        mBarSummary = (TextView) findViewById(R.id.bar_summary);
+    }
+
+    private void setOnClickListner(View.OnClickListener listener) {
+        mBarView.setOnClickListener(listener);
+    }
+}
diff --git a/packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarViewInfo.java b/packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarViewInfo.java
new file mode 100644
index 0000000..aa83ce9
--- /dev/null
+++ b/packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarViewInfo.java
@@ -0,0 +1,146 @@
+/*
+ * 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.widget;
+
+import android.graphics.drawable.Drawable;
+import android.view.View;
+
+import androidx.annotation.IntRange;
+import androidx.annotation.Nullable;
+import androidx.annotation.StringRes;
+
+import java.util.Comparator;
+
+/**
+ * A class responsible for saving bar view information.
+ */
+public class BarViewInfo implements Comparable<BarViewInfo> {
+
+    private final Drawable mIcon;
+    private View.OnClickListener mListener;
+    private @StringRes int mSummaryRes;
+    // A number indicates this bar's height. The larger number shows a higher bar view.
+    private int mBarNumber;
+    // A real height of bar view.
+    private int mBarHeight;
+
+    /**
+     * Construct a BarViewInfo instance.
+     *
+     * @param icon the icon of bar view.
+     * @param barNumber the number of bar view. The larger number show a more height of bar view.
+     * @param summaryRes the resource identifier of the string resource to be displayed
+     * @return BarViewInfo object.
+     */
+    public BarViewInfo(Drawable icon, @IntRange(from = 0) int barNumber,
+            @StringRes int summaryRes) {
+        mIcon = icon;
+        mBarNumber = barNumber;
+        mSummaryRes = summaryRes;
+    }
+
+    /**
+     * Set number for bar view.
+     *
+     * @param barNumber the number of bar view. The larger number shows a higher bar view.
+     */
+    public void setBarNumber(@IntRange(from = 0) int barNumber) {
+        mBarNumber = barNumber;
+    }
+
+    /**
+     * Set summary resource for bar view
+     *
+     * @param resId the resource identifier of the string resource to be displayed
+     */
+    public void setSummary(@StringRes int resId) {
+        mSummaryRes = resId;
+    }
+
+    /**
+     * Set a click listner for bar view.
+     *
+     * @param listener the click listner is attached on bar view.
+     */
+    public void setClickListener(@Nullable View.OnClickListener listener) {
+        mListener = listener;
+    }
+
+    /**
+     * Get the icon of bar view.
+     *
+     * @return Drawable the icon of bar view.
+     */
+    public Drawable getIcon() {
+        return mIcon;
+    }
+
+    /**
+     * Get the OnClickListener of bar view.
+     *
+     * @return View.OnClickListener the click listner of bar view.
+     */
+    public View.OnClickListener getListener() {
+        return mListener;
+    }
+
+    /**
+     * Get the real height of bar view.
+     *
+     * @return the real height of bar view.
+     */
+    public int getBarHeight() {
+        return mBarHeight;
+    }
+
+    /**
+     * Get summary resource of bar view.
+     *
+     * @return summary resource of bar view.
+     */
+    public int getSummaryRes() {
+        return mSummaryRes;
+    }
+
+    /**
+     * Get the number of app uses this permisssion.
+     *
+     * @return the number of app uses this permission.
+     */
+    public int getBarNumber() {
+        return mBarNumber;
+    }
+
+    @Override
+    public int compareTo(BarViewInfo other) {
+        // Descending order
+        return Comparator.comparingInt((BarViewInfo barViewInfo) -> barViewInfo.mBarNumber)
+                .compare(other, this);
+    }
+
+    /**
+     * Set a real height for bar view.
+     *
+     * <p>This method should not be called by outside. It usually should be called by
+     * {@link BarChartPreference#caculateAllBarViewsHeight}
+     *
+     * @param barHeight the real bar height for bar view.
+     */
+    void setBarHeight(@IntRange(from = 0) int barHeight) {
+        mBarHeight = barHeight;
+    }
+}
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/widget/BarChartPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/BarChartPreferenceTest.java
new file mode 100644
index 0000000..371c3d4
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/BarChartPreferenceTest.java
@@ -0,0 +1,211 @@
+/*
+ * 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.widget;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.view.View;
+import android.widget.TextView;
+
+import androidx.preference.PreferenceViewHolder;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+
+@RunWith(RobolectricTestRunner.class)
+public class BarChartPreferenceTest {
+
+    private Context mContext;
+    private View mBarChartView;
+    private Drawable mIcon;
+    private BarView mBarView1;
+    private BarView mBarView2;
+    private BarView mBarView3;
+    private BarView mBarView4;
+    private PreferenceViewHolder mHolder;
+    private BarChartPreference mPreference;
+
+    @Before
+    public void setUp() {
+        mContext = RuntimeEnvironment.application;
+        mBarChartView = View.inflate(mContext, R.layout.settings_bar_chart, null /* parent */);
+        mHolder = PreferenceViewHolder.createInstanceForTests(mBarChartView);
+        mPreference = new BarChartPreference(mContext, null /* attrs */);
+        mPreference.setBarChartTitle(R.string.debug_app);
+        mPreference.setBarChartDetails(R.string.debug_app);
+
+
+        mIcon = mContext.getDrawable(R.drawable.ic_menu);
+        mBarView1 = (BarView) mBarChartView.findViewById(R.id.bar_view1);
+        mBarView2 = (BarView) mBarChartView.findViewById(R.id.bar_view2);
+        mBarView3 = (BarView) mBarChartView.findViewById(R.id.bar_view3);
+        mBarView4 = (BarView) mBarChartView.findViewById(R.id.bar_view4);
+    }
+
+    @Test
+    public void setBarChartTitleRes_setTitleRes_showInBarChartTitle() {
+        final TextView titleView = (TextView) mBarChartView.findViewById(R.id.bar_chart_title);
+
+        mPreference.setBarChartTitle(R.string.debug_app);
+        mPreference.onBindViewHolder(mHolder);
+
+        assertThat(titleView.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(titleView.getText()).isEqualTo(mContext.getText(R.string.debug_app));
+    }
+
+    @Test
+    public void setBarChartDetailsRes_setDetailsRes_showInBarChartDetails() {
+        final TextView detailsView = (TextView) mBarChartView.findViewById(R.id.bar_chart_details);
+
+        mPreference.setBarChartDetails(R.string.debug_app);
+        mPreference.onBindViewHolder(mHolder);
+
+        assertThat(detailsView.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(detailsView.getText()).isEqualTo(mContext.getText(R.string.debug_app));
+    }
+
+    @Test
+    public void setBarChartDetailsClickListener_setClickListener_detailsViewAttachClickListener() {
+        final TextView detailsView = (TextView) mBarChartView.findViewById(R.id.bar_chart_details);
+
+        mPreference.setBarChartDetailsClickListener(v -> {
+        });
+        mPreference.onBindViewHolder(mHolder);
+
+        assertThat(detailsView.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(detailsView.hasOnClickListeners()).isTrue();
+    }
+
+    @Test
+    public void setAllBarViewsInfo_setOneBarViewInfo_showOneBarView() {
+        final BarViewInfo[] barViewsInfo = new BarViewInfo[]{
+                new BarViewInfo(mIcon, 10 /* barNumber */, R.string.debug_app)
+        };
+
+        mPreference.setAllBarViewsInfo(barViewsInfo);
+        mPreference.onBindViewHolder(mHolder);
+
+        assertThat(mBarView1.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(mBarView1.getTitle()).isEqualTo("10");
+
+        assertThat(mBarView2.getVisibility()).isEqualTo(View.GONE);
+        assertThat(mBarView3.getVisibility()).isEqualTo(View.GONE);
+        assertThat(mBarView4.getVisibility()).isEqualTo(View.GONE);
+    }
+
+    @Test
+    public void setAllBarViewsInfo_setTwoBarViewsInfo_showTwoBarViews() {
+        final BarViewInfo[] barViewsInfo = new BarViewInfo[]{
+                new BarViewInfo(mIcon, 20 /* barNumber */, R.string.debug_app),
+                new BarViewInfo(mIcon, 10 /* barNumber */, R.string.debug_app)
+        };
+
+        mPreference.setAllBarViewsInfo(barViewsInfo);
+        mPreference.onBindViewHolder(mHolder);
+
+        assertThat(mBarView1.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(mBarView1.getTitle()).isEqualTo("20");
+        assertThat(mBarView2.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(mBarView2.getTitle()).isEqualTo("10");
+
+        assertThat(mBarView3.getVisibility()).isEqualTo(View.GONE);
+        assertThat(mBarView4.getVisibility()).isEqualTo(View.GONE);
+    }
+
+    @Test
+    public void setAllBarViewsInfo_setThreeBarViewsInfo_showThreeBarViews() {
+        final BarViewInfo[] barViewsInfo = new BarViewInfo[]{
+                new BarViewInfo(mIcon, 20 /* barNumber */, R.string.debug_app),
+                new BarViewInfo(mIcon, 10 /* barNumber */, R.string.debug_app),
+                new BarViewInfo(mIcon, 5 /* barNumber */, R.string.debug_app)
+        };
+
+        mPreference.setAllBarViewsInfo(barViewsInfo);
+        mPreference.onBindViewHolder(mHolder);
+
+        assertThat(mBarView1.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(mBarView1.getTitle()).isEqualTo("20");
+        assertThat(mBarView2.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(mBarView2.getTitle()).isEqualTo("10");
+        assertThat(mBarView3.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(mBarView3.getTitle()).isEqualTo("5");
+
+        assertThat(mBarView4.getVisibility()).isEqualTo(View.GONE);
+    }
+
+    @Test
+    public void setAllBarViewsInfo_setFourBarViewsInfo_showFourBarViews() {
+        final BarViewInfo[] barViewsInfo = new BarViewInfo[]{
+                new BarViewInfo(mIcon, 20 /* barNumber */, R.string.debug_app),
+                new BarViewInfo(mIcon, 10 /* barNumber */, R.string.debug_app),
+                new BarViewInfo(mIcon, 5 /* barNumber */, R.string.debug_app),
+                new BarViewInfo(mIcon, 2 /* barNumber */, R.string.debug_app),
+        };
+
+        mPreference.setAllBarViewsInfo(barViewsInfo);
+        mPreference.onBindViewHolder(mHolder);
+
+        assertThat(mBarView1.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(mBarView1.getTitle()).isEqualTo("20");
+        assertThat(mBarView2.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(mBarView2.getTitle()).isEqualTo("10");
+        assertThat(mBarView3.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(mBarView3.getTitle()).isEqualTo("5");
+        assertThat(mBarView4.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(mBarView4.getTitle()).isEqualTo("2");
+    }
+
+    @Test
+    public void setAllBarViewsInfo_setFourBarViewsInfo_barViewWasSortedInDescending() {
+        final BarViewInfo[] barViewsInfo = new BarViewInfo[]{
+                new BarViewInfo(mIcon, 30 /* barNumber */, R.string.debug_app),
+                new BarViewInfo(mIcon, 50 /* barNumber */, R.string.debug_app),
+                new BarViewInfo(mIcon, 5 /* barNumber */, R.string.debug_app),
+                new BarViewInfo(mIcon, 10 /* barNumber */, R.string.debug_app),
+        };
+
+        mPreference.setAllBarViewsInfo(barViewsInfo);
+        mPreference.onBindViewHolder(mHolder);
+
+        assertThat(mBarView1.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(mBarView1.getTitle()).isEqualTo("50");
+        assertThat(mBarView2.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(mBarView2.getTitle()).isEqualTo("30");
+        assertThat(mBarView3.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(mBarView3.getTitle()).isEqualTo("10");
+        assertThat(mBarView4.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(mBarView4.getTitle()).isEqualTo("5");
+    }
+
+    @Test
+    public void setAllBarViewsInfo_setValidSummaryRes_barViewShouldShowSummary() {
+        final BarViewInfo[] barViewsInfo = new BarViewInfo[]{
+                new BarViewInfo(mIcon, 10 /* barNumber */, R.string.debug_app),
+        };
+
+        mPreference.setAllBarViewsInfo(barViewsInfo);
+        mPreference.onBindViewHolder(mHolder);
+
+        assertThat(mBarView1.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(mBarView1.getSummary()).isEqualTo(mContext.getText(R.string.debug_app));
+    }
+}
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 5fe08aa..8cfc2a6 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -27,6 +27,7 @@
     <uses-permission android:name="android.permission.READ_SMS" />
     <uses-permission android:name="android.permission.CALL_PHONE" />
     <uses-permission android:name="android.permission.READ_PHONE_STATE" />
+    <uses-permission android:name="android.permission.READ_PRECISE_PHONE_STATE" />
     <uses-permission android:name="android.permission.READ_PRIVILEGED_PHONE_STATE" />
     <uses-permission android:name="android.permission.READ_CONTACTS" />
     <uses-permission android:name="android.permission.WRITE_CONTACTS" />
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index c2e107a..8be67d9 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -115,6 +115,8 @@
         "mockito-target-inline-minus-junit4",
         "testables",
         "truth-prebuilt",
+        "dagger2-2.19",
+        "jsr330"
     ],
     libs: [
         "android.test.runner",
@@ -125,6 +127,7 @@
         "--extra-packages",
         "com.android.keyguard:com.android.systemui",
     ],
+    annotation_processors: ["dagger2-compiler-2.19"],
 }
 
 android_app {
@@ -134,6 +137,7 @@
     ],
 
     platform_apis: true,
+    product_specific: true,
     certificate: "platform",
     privileged: true,
 
diff --git a/packages/SystemUI/docs/dagger.md b/packages/SystemUI/docs/dagger.md
new file mode 100644
index 0000000..565d765f
--- /dev/null
+++ b/packages/SystemUI/docs/dagger.md
@@ -0,0 +1,136 @@
+# Dagger 2 in SystemUI
+*Dagger 2 is a dependency injection framework that compiles annotations to code
+to create dependencies without reflection*
+
+## Recommended reading
+
+Go read about Dagger 2.
+
+TODO: Add some links.
+
+## State of the world
+
+Dagger 2 has been turned on for SystemUI and a early first pass has been taken
+for converting everything in Dependency.java to use Dagger. Since a lot of
+SystemUI depends on Dependency, stubs have been added to Dependency to proxy
+any gets through to the instances provided by dagger, this will allow migration
+of SystemUI through a number of CLs.
+
+### How it works in SystemUI
+
+For the classes that we're using in Dependency and are switching to dagger, the
+equivalent dagger version is using @Singleton and only having one instance.
+To have the single instance span all of SystemUI and be easily accessible for
+other components, there is a single root Component that exists that generates
+these. The component lives in SystemUIFactory and is called SystemUIRootComponent.
+
+```java
+@Singleton
+@Component(modules = {SystemUIFactory.class, DependencyProvider.class, ContextHolder.class})
+public interface SystemUIRootComponent {
+    @Singleton
+    Dependency.DependencyInjector createDependency();
+}
+```
+
+The root modules are what provides the global singleton dependencies across
+SystemUI. ContextHolder is just a wrapper that provides a context.
+SystemUIFactory @Provide dependencies that need to be overridden by SystemUI
+variants (like other form factors). DependencyProvider provides or binds any
+remaining depedencies required.
+
+### Adding injection to a new SystemUI object
+
+Anything that depends on any @Singleton provider from SystemUIRootComponent
+should be declared as a Subcomponent of the root component, this requires
+declaring your own interface for generating your own modules or just the
+object you need injected. The subcomponent also needs to be added to
+SystemUIRootComponent in SystemUIFactory so it can be acquired.
+
+```java
+public interface SystemUIRootComponent {
++    @Singleton
++    Dependency.DependencyInjector createDependency();
+}
+
+public class Dependency extends SystemUI {
+  ...
++  @Subcomponent
++  public interface DependencyInjector {
++      Dependency createSystemUI();
++  }
+}
+```
+
+For objects that extend SystemUI and require injection, you can define an
+injector that creates the injected object for you. This other class should
+be referenced in @string/config_systemUIServiceComponents.
+
+```java
+public static class DependencyCreator implements Injector {
+    @Override
+    public SystemUI apply(Context context) {
+        return SystemUIFactory.getInstance().getRootComponent()
+                .createDependency()
+                .createSystemUI();
+    }
+}
+```
+
+### Adding a new injectable object
+
+First tag the constructor with @Inject. Also tag it with @Singleton if only one
+instance should be created.
+
+```java
+@Singleton
+public class SomethingController {
+  @Inject
+  public SomethingController(Context context,
+    @Named(MAIN_HANDLER_NAME) Handler mainHandler) {
+      // context and mainHandler will be automatically populated.
+  }
+}
+```
+
+If you have an interface class and an implementation class, dagger needs to know
+how to map it. The simplest way to do this is to add a provides method to
+DependencyProvider.
+
+```java
+public class DependencyProvider {
+  ...
+  @Singleton
+  @Provide
+  public SomethingController provideSomethingController(Context context,
+      @Named(MAIN_HANDLER_NAME) Handler mainHandler) {
+    return new SomethingControllerImpl(context, mainHandler);
+  }
+}
+```
+
+If you need to access this from Dependency#get, then add an adapter to Dependency
+that maps to the instance provided by Dagger. The changes should be similar
+to the following diff.
+
+```java
+public class Dependency {
+  ...
+  @Inject Lazy<SomethingController> mSomethingController;
+  ...
+  public void start() {
+    ...
+    mProviders.put(SomethingController.class, mSomethingController::get);
+  }
+}
+```
+
+## TODO List
+
+ - Eliminate usages of Depndency#get
+ - Add support for Fragments to handle injection automatically
+   - (this could be through dagger2-android or something custom)
+ - Reduce number of things with @Provide in DependencyProvider (many can be
+   @Inject instead)
+ - Migrate as many remaining DependencyProvider instances to @Bind
+ - Add links in above TODO
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockPlugin.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockPlugin.java
index 1a18f60..6135aeb 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockPlugin.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockPlugin.java
@@ -62,4 +62,12 @@
      * Notifies that the time zone has changed.
      */
     default void onTimeZoneChanged(TimeZone timeZone) {}
+
+    /**
+     * Indicates whether the keyguard status area (date) should be shown below
+     * the clock.
+     */
+    default boolean shouldShowStatusArea() {
+        return true;
+    }
 }
diff --git a/packages/SystemUI/proguard.flags b/packages/SystemUI/proguard.flags
index ee94aed..cc6848f 100644
--- a/packages/SystemUI/proguard.flags
+++ b/packages/SystemUI/proguard.flags
@@ -17,6 +17,7 @@
 -keep class com.android.systemui.car.CarSystemUIFactory
 -keep class com.android.systemui.SystemUIFactory
 -keep class * extends com.android.systemui.SystemUI
+-keep class * implements com.android.systemui.SystemUI$Injector
 
 -keepclasseswithmembers class * {
     public <init>(android.content.Context, android.util.AttributeSet);
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml b/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml
index 367a9ae..d52866f 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml
@@ -20,19 +20,31 @@
 <!-- This is a view that shows clock information in Keyguard. -->
 <com.android.keyguard.KeyguardClockSwitch
     xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/keyguard_clock_container"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
-    android:layout_gravity="center_horizontal"
-    android:layout_alignParentTop="true">
-    <TextClock
-        android:id="@+id/default_clock_view"
-        android:layout_width="wrap_content"
+    android:layout_gravity="center_horizontal|top">
+    <FrameLayout
+         android:id="@+id/clock_view"
+         android:layout_width="match_parent"
+         android:layout_height="wrap_content"
+         android:layout_gravity="center_horizontal"
+         android:layout_alignParentTop="true">
+        <TextClock
+             android:id="@+id/default_clock_view"
+             android:layout_width="wrap_content"
+             android:layout_height="wrap_content"
+             android:layout_gravity="center_horizontal"
+             android:letterSpacing="0.03"
+             android:textColor="?attr/wallpaperTextColor"
+             android:singleLine="true"
+             style="@style/widget_big"
+             android:format12Hour="@string/keyguard_widget_12_hours_format"
+             android:format24Hour="@string/keyguard_widget_24_hours_format" />
+    </FrameLayout>
+    <include layout="@layout/keyguard_status_area"
+        android:id="@+id/keyguard_status_area"
+        android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:layout_gravity="center_horizontal"
-        android:letterSpacing="0.03"
-        android:textColor="?attr/wallpaperTextColor"
-        android:singleLine="true"
-        style="@style/widget_big"
-        android:format12Hour="@string/keyguard_widget_12_hours_format"
-        android:format24Hour="@string/keyguard_widget_24_hours_format" />
+        android:layout_below="@id/clock_view" />
 </com.android.keyguard.KeyguardClockSwitch>
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_presentation.xml b/packages/SystemUI/res-keyguard/layout/keyguard_presentation.xml
index 7d8a1f5b..a9ba19d 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_presentation.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_presentation.xml
@@ -33,21 +33,11 @@
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:orientation="vertical">
-            <RelativeLayout
+            <include
+                layout="@layout/keyguard_clock_switch"
                 android:id="@+id/keyguard_clock_container"
                 android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                android:layout_gravity="center_horizontal|top">
-                <include layout="@layout/keyguard_clock_switch"
-                         android:id="@+id/clock_view"
-                         android:layout_width="match_parent"
-                         android:layout_height="wrap_content" />
-                <include layout="@layout/keyguard_status_area"
-                         android:id="@+id/keyguard_status_area"
-                         android:layout_width="match_parent"
-                         android:layout_height="wrap_content"
-                         android:layout_below="@id/clock_view" />
-            </RelativeLayout>
+                android:layout_height="wrap_content" />
             <ImageView
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_status_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_status_view.xml
index 67ecf6f..10fea9d 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_status_view.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_status_view.xml
@@ -50,21 +50,11 @@
             android:textSize="13sp"
             android:text="@*android:string/global_action_logout" />
 
-        <RelativeLayout
+        <include
+            layout="@layout/keyguard_clock_switch"
             android:id="@+id/keyguard_clock_container"
             android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:layout_gravity="center_horizontal|top">
-            <include layout="@layout/keyguard_clock_switch"
-                 android:id="@+id/clock_view"
-                 android:layout_width="match_parent"
-                 android:layout_height="wrap_content" />
-            <include layout="@layout/keyguard_status_area"
-                android:id="@+id/keyguard_status_area"
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                android:layout_below="@id/clock_view" />
-        </RelativeLayout>
+            android:layout_height="wrap_content" />
 
         <TextView
             android:id="@+id/owner_info"
diff --git a/packages/SystemUI/res-keyguard/values/styles.xml b/packages/SystemUI/res-keyguard/values/styles.xml
index ffc7b3c..b9966cf 100644
--- a/packages/SystemUI/res-keyguard/values/styles.xml
+++ b/packages/SystemUI/res-keyguard/values/styles.xml
@@ -33,7 +33,7 @@
         <item name="android:gravity">center_horizontal|center_vertical</item>
         <item name="android:background">@null</item>
         <item name="android:textSize">32sp</item>
-        <item name="android:fontFamily">@*android:string/config_headlineFontFamilyLight</item>
+        <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
         <item name="android:textColor">?attr/wallpaperTextColor</item>
         <item name="android:paddingBottom">-16dp</item>
     </style>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 61efbd5..889db66 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -290,7 +290,7 @@
 
     <!-- SystemUI Services: The classes of the stuff to start. -->
     <string-array name="config_systemUIServiceComponents" translatable="false">
-        <item>com.android.systemui.Dependency</item>
+        <item>com.android.systemui.Dependency$DependencyCreator</item>
         <item>com.android.systemui.util.NotificationChannels</item>
         <item>com.android.systemui.statusbar.CommandQueue$CommandQueueStart</item>
         <item>com.android.systemui.keyguard.KeyguardViewMediator</item>
@@ -318,7 +318,7 @@
 
     <!-- SystemUI Services (per user): The classes of the stuff to start for each user. This is a subset of the config_systemUIServiceComponents -->
     <string-array name="config_systemUIServiceComponentsPerUser" translatable="false">
-        <item>com.android.systemui.Dependency</item>
+        <item>com.android.systemui.Dependency$DependencyCreator</item>
         <item>com.android.systemui.util.NotificationChannels</item>
     </string-array>
 
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginEnabler.java b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginEnabler.java
index 74fd13f..01b012d 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginEnabler.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginEnabler.java
@@ -14,12 +14,40 @@
 
 package com.android.systemui.shared.plugins;
 
+import android.annotation.IntDef;
 import android.content.ComponentName;
 
 /**
  * Enables and disables plugins.
  */
 public interface PluginEnabler {
-    void setEnabled(ComponentName component, boolean enabled);
+
+    int ENABLED = 0;
+    int DISABLED_MANUALLY = 1;
+    int DISABLED_INVALID_VERSION = 1;
+    int DISABLED_FROM_EXPLICIT_CRASH = 2;
+    int DISABLED_FROM_SYSTEM_CRASH = 3;
+
+    @IntDef({ENABLED, DISABLED_MANUALLY, DISABLED_INVALID_VERSION, DISABLED_FROM_EXPLICIT_CRASH,
+            DISABLED_FROM_SYSTEM_CRASH})
+    @interface DisableReason {
+    }
+
+    /** Enables plugin via the PackageManager. */
+    void setEnabled(ComponentName component);
+
+    /** Disables a plugin via the PackageManager and records the reason for disabling. */
+    void setDisabled(ComponentName component, @DisableReason int reason);
+
+    /** Returns true if the plugin is enabled in the PackageManager. */
     boolean isEnabled(ComponentName component);
+
+    /**
+     * Returns the reason that a plugin is disabled, (if it is).
+     *
+     * It should return {@link #ENABLED} if the plugin is turned on.
+     * It should return {@link #DISABLED_MANUALLY} if the plugin is off but the reason is unknown.
+     */
+    @DisableReason
+    int getDisableReason(ComponentName componentName);
 }
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstanceManager.java b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstanceManager.java
index 8e7fadb..523720d5 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstanceManager.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstanceManager.java
@@ -136,7 +136,7 @@
         ArrayList<PluginInfo> plugins = new ArrayList<PluginInfo>(mPluginHandler.mPlugins);
         for (PluginInfo info : plugins) {
             if (className.startsWith(info.mPackage)) {
-                disable(info);
+                disable(info, PluginEnabler.DISABLED_FROM_EXPLICIT_CRASH);
                 disableAny = true;
             }
         }
@@ -146,12 +146,13 @@
     public boolean disableAll() {
         ArrayList<PluginInfo> plugins = new ArrayList<PluginInfo>(mPluginHandler.mPlugins);
         for (int i = 0; i < plugins.size(); i++) {
-            disable(plugins.get(i));
+            disable(plugins.get(i), PluginEnabler.DISABLED_FROM_SYSTEM_CRASH);
         }
         return plugins.size() != 0;
     }
 
-    private void disable(PluginInfo info) {
+    private void disable(PluginInfo info,
+            @PluginEnabler.DisableReason int reason) {
         // Live by the sword, die by the sword.
         // Misbehaving plugins get disabled and won't come back until uninstall/reinstall.
 
@@ -162,9 +163,9 @@
             // Don't disable whitelisted plugins as they are a part of the OS.
             return;
         }
-        Log.w(TAG, "Disabling plugin " + info.mPackage + "/" + info.mClass);
-        mManager.getPluginEnabler().setEnabled(new ComponentName(info.mPackage, info.mClass),
-                false);
+        ComponentName pluginComponent = new ComponentName(info.mPackage, info.mClass);
+        Log.w(TAG, "Disabling plugin " + pluginComponent.flattenToShortString());
+        mManager.getPluginEnabler().setDisabled(pluginComponent, reason);
     }
 
     public <T> boolean dependsOn(Plugin p, Class<T> cls) {
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManagerImpl.java b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManagerImpl.java
index dc2a9bd..10b5f1c 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManagerImpl.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManagerImpl.java
@@ -184,6 +184,7 @@
         mListening = true;
         IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
         filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
+        filter.addAction(Intent.ACTION_PACKAGE_REPLACED);
         filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
         filter.addAction(PLUGIN_CHANGED);
         filter.addAction(DISABLE_PLUGIN);
@@ -214,12 +215,13 @@
                 // Don't disable whitelisted plugins as they are a part of the OS.
                 return;
             }
-            getPluginEnabler().setEnabled(component, false);
+            getPluginEnabler().setDisabled(component, PluginEnabler.DISABLED_INVALID_VERSION);
             mContext.getSystemService(NotificationManager.class).cancel(component.getClassName(),
                     SystemMessage.NOTE_PLUGIN);
         } else {
             Uri data = intent.getData();
             String pkg = data.getEncodedSchemeSpecificPart();
+            ComponentName componentName = ComponentName.unflattenFromString(pkg);
             if (mOneShotPackages.contains(pkg)) {
                 int icon = mContext.getResources().getIdentifier("tuner", "drawable",
                         mContext.getPackageName());
@@ -256,6 +258,17 @@
                     Log.v(TAG, "Reloading " + pkg);
                 }
             }
+            if (Intent.ACTION_PACKAGE_REPLACED.equals(intent.getAction())) {
+                @PluginEnabler.DisableReason int disableReason =
+                        getPluginEnabler().getDisableReason(componentName);
+                if (disableReason == PluginEnabler.DISABLED_FROM_EXPLICIT_CRASH
+                        || disableReason == PluginEnabler.DISABLED_FROM_SYSTEM_CRASH
+                        || disableReason == PluginEnabler.DISABLED_INVALID_VERSION) {
+                    Log.i(TAG, "Re-enabling previously disabled plugin that has been "
+                            + "updated: " + componentName.flattenToShortString());
+                    getPluginEnabler().setEnabled(componentName);
+                }
+            }
             if (!Intent.ACTION_PACKAGE_REMOVED.equals(intent.getAction())) {
                 for (PluginInstanceManager manager : mPluginMap.values()) {
                     manager.onPackageChange(pkg);
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
index 0ec9014..22a23a8 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
@@ -7,6 +7,7 @@
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.FrameLayout;
+import android.widget.RelativeLayout;
 import android.widget.TextClock;
 
 import androidx.annotation.VisibleForTesting;
@@ -22,7 +23,7 @@
 /**
  * Switch to show plugin clock when plugin is connected, otherwise it will show default clock.
  */
-public class KeyguardClockSwitch extends FrameLayout {
+public class KeyguardClockSwitch extends RelativeLayout {
     /**
      * Optional/alternative clock injected via plugin.
      */
@@ -31,6 +32,15 @@
      * Default clock.
      */
     private TextClock mClockView;
+    /**
+     * Frame for default and custom clock.
+     */
+    private FrameLayout mClockFrame;
+    /**
+     * Status area (date and other stuff) shown below the clock. Plugin can decide whether
+     * or not to show it below the alternate clock.
+     */
+    private View mKeyguardStatusArea;
 
     private final PluginListener<ClockPlugin> mClockPluginListener =
             new PluginListener<ClockPlugin>() {
@@ -43,11 +53,14 @@
                         // selected clock face. In the future, the user should be able to
                         // pick a clock face from the available plugins.
                         mClockPlugin = plugin;
-                        addView(view, -1,
+                        mClockFrame.addView(view, -1,
                                 new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
                                         ViewGroup.LayoutParams.WRAP_CONTENT));
                         initPluginParams();
                         mClockView.setVisibility(View.GONE);
+                        if (!plugin.shouldShowStatusArea()) {
+                            mKeyguardStatusArea.setVisibility(View.GONE);
+                        }
                     }
                 }
 
@@ -56,6 +69,7 @@
                     if (Objects.equals(plugin, mClockPlugin)) {
                         disconnectPlugin();
                         mClockView.setVisibility(View.VISIBLE);
+                        mKeyguardStatusArea.setVisibility(View.VISIBLE);
                     }
                 }
             };
@@ -72,6 +86,8 @@
     protected void onFinishInflate() {
         super.onFinishInflate();
         mClockView = findViewById(R.id.default_clock_view);
+        mClockFrame = findViewById(R.id.clock_view);
+        mKeyguardStatusArea = findViewById(R.id.keyguard_status_area);
     }
 
     @Override
@@ -185,7 +201,7 @@
         if (mClockPlugin != null) {
             View view = mClockPlugin.getView();
             if (view != null) {
-                removeView(view);
+                mClockFrame.removeView(view);
             }
             mClockPlugin = null;
         }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
index 1e9d288..c6f1726 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
@@ -37,7 +37,7 @@
 import android.util.TypedValue;
 import android.view.View;
 import android.widget.GridLayout;
-import android.widget.RelativeLayout;
+import android.widget.LinearLayout;
 import android.widget.TextView;
 
 import androidx.core.graphics.ColorUtils;
@@ -173,7 +173,7 @@
             mLogoutView.setOnClickListener(this::onLogoutClicked);
         }
 
-        mClockView = findViewById(R.id.clock_view);
+        mClockView = findViewById(R.id.keyguard_clock_container);
         mClockView.setShowCurrentUserTime(true);
         if (KeyguardClockAccessibilityDelegate.isNeeded(mContext)) {
             mClockView.setAccessibilityDelegate(new KeyguardClockAccessibilityDelegate(mContext));
@@ -205,8 +205,8 @@
      * Moves clock, adjusting margins when slice content changes.
      */
     private void onSliceContentChanged() {
-        RelativeLayout.LayoutParams layoutParams =
-                (RelativeLayout.LayoutParams) mClockView.getLayoutParams();
+        LinearLayout.LayoutParams layoutParams =
+                (LinearLayout.LayoutParams) mClockView.getLayoutParams();
         layoutParams.bottomMargin = mPulsing ? mSmallClockPadding : 0;
         mClockView.setLayoutParams(layoutParams);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java
index 327ffcd..4e5af15 100644
--- a/packages/SystemUI/src/com/android/systemui/Dependency.java
+++ b/packages/SystemUI/src/com/android/systemui/Dependency.java
@@ -14,20 +14,15 @@
 
 package com.android.systemui;
 
+import android.annotation.Nullable;
 import android.content.Context;
 import android.content.res.Configuration;
-import android.hardware.SensorManager;
 import android.hardware.SensorPrivacyManager;
 import android.os.Handler;
-import android.os.HandlerThread;
 import android.os.Looper;
-import android.os.Process;
-import android.os.ServiceManager;
-import android.os.UserHandle;
 import android.util.ArrayMap;
 import android.util.DisplayMetrics;
 import android.view.IWindowManager;
-import android.view.WindowManagerGlobal;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.app.ColorDisplayController;
@@ -36,92 +31,86 @@
 import com.android.internal.util.Preconditions;
 import com.android.settingslib.bluetooth.LocalBluetoothManager;
 import com.android.systemui.appops.AppOpsController;
-import com.android.systemui.appops.AppOpsControllerImpl;
 import com.android.systemui.assist.AssistManager;
+import com.android.systemui.bubbles.BubbleController;
 import com.android.systemui.colorextraction.SysuiColorExtractor;
 import com.android.systemui.fragments.FragmentService;
 import com.android.systemui.keyguard.ScreenLifecycle;
 import com.android.systemui.keyguard.WakefulnessLifecycle;
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.plugins.PluginDependencyProvider;
-import com.android.systemui.plugins.PluginInitializerImpl;
 import com.android.systemui.plugins.VolumeDialogController;
 import com.android.systemui.power.EnhancedEstimates;
-import com.android.systemui.power.EnhancedEstimatesImpl;
-import com.android.systemui.power.PowerNotificationWarnings;
 import com.android.systemui.power.PowerUI;
 import com.android.systemui.recents.OverviewProxyService;
 import com.android.systemui.shared.plugins.PluginManager;
-import com.android.systemui.shared.plugins.PluginManagerImpl;
+import com.android.systemui.statusbar.AmbientPulseManager;
 import com.android.systemui.statusbar.DisplayNavigationBarController;
+import com.android.systemui.statusbar.NotificationListener;
+import com.android.systemui.statusbar.NotificationLockscreenUserManager;
+import com.android.systemui.statusbar.NotificationMediaManager;
 import com.android.systemui.statusbar.NotificationRemoteInputManager;
+import com.android.systemui.statusbar.NotificationViewHierarchyManager;
+import com.android.systemui.statusbar.SmartReplyController;
+import com.android.systemui.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.VibratorHelper;
 import com.android.systemui.statusbar.notification.NotificationData.KeyguardEnvironment;
-import com.android.systemui.statusbar.phone.ConfigurationControllerImpl;
-import com.android.systemui.statusbar.phone.DarkIconDispatcherImpl;
-import com.android.systemui.statusbar.phone.KeyguardEnvironmentImpl;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.VisualStabilityManager;
+import com.android.systemui.statusbar.notification.logging.NotificationLogger;
+import com.android.systemui.statusbar.notification.row.NotificationBlockingHelperManager;
+import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
+import com.android.systemui.statusbar.phone.KeyguardDismissUtil;
 import com.android.systemui.statusbar.phone.LightBarController;
 import com.android.systemui.statusbar.phone.LockscreenGestureLogger;
 import com.android.systemui.statusbar.phone.ManagedProfileController;
-import com.android.systemui.statusbar.phone.ManagedProfileControllerImpl;
+import com.android.systemui.statusbar.phone.NotificationGroupAlertTransferHelper;
+import com.android.systemui.statusbar.phone.NotificationGroupManager;
 import com.android.systemui.statusbar.phone.ShadeController;
-import com.android.systemui.statusbar.phone.StatusBar;
 import com.android.systemui.statusbar.phone.StatusBarIconController;
-import com.android.systemui.statusbar.phone.StatusBarIconControllerImpl;
-import com.android.systemui.statusbar.phone.StatusBarRemoteInputCallback;
 import com.android.systemui.statusbar.phone.StatusBarWindowController;
 import com.android.systemui.statusbar.policy.AccessibilityController;
 import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
 import com.android.systemui.statusbar.policy.BatteryController;
-import com.android.systemui.statusbar.policy.BatteryControllerImpl;
 import com.android.systemui.statusbar.policy.BluetoothController;
-import com.android.systemui.statusbar.policy.BluetoothControllerImpl;
 import com.android.systemui.statusbar.policy.CastController;
-import com.android.systemui.statusbar.policy.CastControllerImpl;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.DarkIconDispatcher;
 import com.android.systemui.statusbar.policy.DataSaverController;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
-import com.android.systemui.statusbar.policy.DeviceProvisionedControllerImpl;
 import com.android.systemui.statusbar.policy.ExtensionController;
-import com.android.systemui.statusbar.policy.ExtensionControllerImpl;
 import com.android.systemui.statusbar.policy.FlashlightController;
-import com.android.systemui.statusbar.policy.FlashlightControllerImpl;
 import com.android.systemui.statusbar.policy.HotspotController;
-import com.android.systemui.statusbar.policy.HotspotControllerImpl;
 import com.android.systemui.statusbar.policy.IconLogger;
-import com.android.systemui.statusbar.policy.IconLoggerImpl;
 import com.android.systemui.statusbar.policy.KeyguardMonitor;
-import com.android.systemui.statusbar.policy.KeyguardMonitorImpl;
 import com.android.systemui.statusbar.policy.LocationController;
-import com.android.systemui.statusbar.policy.LocationControllerImpl;
 import com.android.systemui.statusbar.policy.NetworkController;
-import com.android.systemui.statusbar.policy.NetworkControllerImpl;
 import com.android.systemui.statusbar.policy.NextAlarmController;
-import com.android.systemui.statusbar.policy.NextAlarmControllerImpl;
+import com.android.systemui.statusbar.policy.RemoteInputQuickSettingsDisabler;
 import com.android.systemui.statusbar.policy.RotationLockController;
-import com.android.systemui.statusbar.policy.RotationLockControllerImpl;
 import com.android.systemui.statusbar.policy.SecurityController;
-import com.android.systemui.statusbar.policy.SecurityControllerImpl;
+import com.android.systemui.statusbar.policy.SmartReplyConstants;
 import com.android.systemui.statusbar.policy.UserInfoController;
-import com.android.systemui.statusbar.policy.UserInfoControllerImpl;
 import com.android.systemui.statusbar.policy.UserSwitcherController;
 import com.android.systemui.statusbar.policy.ZenModeController;
-import com.android.systemui.statusbar.policy.ZenModeControllerImpl;
 import com.android.systemui.tuner.TunablePadding.TunablePaddingService;
 import com.android.systemui.tuner.TunerService;
-import com.android.systemui.tuner.TunerServiceImpl;
 import com.android.systemui.util.AsyncSensorManager;
 import com.android.systemui.util.leak.GarbageMonitor;
 import com.android.systemui.util.leak.LeakDetector;
 import com.android.systemui.util.leak.LeakReporter;
-import com.android.systemui.volume.VolumeDialogControllerImpl;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.HashMap;
 import java.util.function.Consumer;
 
+import javax.inject.Inject;
+import javax.inject.Named;
+
+import dagger.Lazy;
+import dagger.Subcomponent;
+
 /**
  * Class to handle ugly dependencies throughout sysui until we determine the
  * long-term dependency injection solution.
@@ -143,235 +132,302 @@
     /**
      * Key for getting a background Looper for background work.
      */
-    public static final DependencyKey<Looper> BG_LOOPER = new DependencyKey<>("background_looper");
+    public static final String BG_LOOPER_NAME = "background_looper";
     /**
      * Key for getting a background Handler for background work.
      */
-    public static final DependencyKey<Handler> BG_HANDLER = new DependencyKey<>("background_handler");
+    public static final String BG_HANDLER_NAME = "background_handler";
     /**
      * Key for getting a Handler for receiving time tick broadcasts on.
      */
-    public static final DependencyKey<Handler> TIME_TICK_HANDLER =
-            new DependencyKey<>("time_tick_handler");
+    public static final String TIME_TICK_HANDLER_NAME = "time_tick_handler";
     /**
      * Generic handler on the main thread.
      */
-    public static final DependencyKey<Handler> MAIN_HANDLER = new DependencyKey<>("main_handler");
+    public static final String MAIN_HANDLER_NAME = "main_handler";
 
     /**
      * An email address to send memory leak reports to by default.
      */
-    public static final DependencyKey<String> LEAK_REPORT_EMAIL
-            = new DependencyKey<>("leak_report_email");
+    public static final String LEAK_REPORT_EMAIL_NAME = "leak_report_email";
+
+    /**
+     * Key for getting a background Looper for background work.
+     */
+    public static final DependencyKey<Looper> BG_LOOPER = new DependencyKey<>(BG_LOOPER_NAME);
+    /**
+     * Key for getting a background Handler for background work.
+     */
+    public static final DependencyKey<Handler> BG_HANDLER = new DependencyKey<>(BG_HANDLER_NAME);
+    /**
+     * Key for getting a Handler for receiving time tick broadcasts on.
+     */
+    public static final DependencyKey<Handler> TIME_TICK_HANDLER =
+            new DependencyKey<>(TIME_TICK_HANDLER_NAME);
+    /**
+     * Generic handler on the main thread.
+     */
+    public static final DependencyKey<Handler> MAIN_HANDLER =
+            new DependencyKey<>(MAIN_HANDLER_NAME);
+
+    /**
+     * An email address to send memory leak reports to by default.
+     */
+    public static final DependencyKey<String> LEAK_REPORT_EMAIL =
+            new DependencyKey<>(LEAK_REPORT_EMAIL_NAME);
 
     private final ArrayMap<Object, Object> mDependencies = new ArrayMap<>();
     private final ArrayMap<Object, DependencyProvider> mProviders = new ArrayMap<>();
 
+    @Inject Lazy<ActivityStarter> mActivityStarter;
+    @Inject Lazy<ActivityStarterDelegate> mActivityStarterDelegate;
+    @Inject Lazy<AsyncSensorManager> mAsyncSensorManager;
+    @Inject Lazy<BluetoothController> mBluetoothController;
+    @Inject Lazy<LocationController> mLocationController;
+    @Inject Lazy<RotationLockController> mRotationLockController;
+    @Inject Lazy<NetworkController> mNetworkController;
+    @Inject Lazy<ZenModeController> mZenModeController;
+    @Inject Lazy<HotspotController> mHotspotController;
+    @Inject Lazy<CastController> mCastController;
+    @Inject Lazy<FlashlightController> mFlashlightController;
+    @Inject Lazy<UserSwitcherController> mUserSwitcherController;
+    @Inject Lazy<UserInfoController> mUserInfoController;
+    @Inject Lazy<KeyguardMonitor> mKeyguardMonitor;
+    @Inject Lazy<BatteryController> mBatteryController;
+    @Inject Lazy<ColorDisplayController> mColorDisplayController;
+    @Inject Lazy<ManagedProfileController> mManagedProfileController;
+    @Inject Lazy<NextAlarmController> mNextAlarmController;
+    @Inject Lazy<DataSaverController> mDataSaverController;
+    @Inject Lazy<AccessibilityController> mAccessibilityController;
+    @Inject Lazy<DeviceProvisionedController> mDeviceProvisionedController;
+    @Inject Lazy<PluginManager> mPluginManager;
+    @Inject Lazy<AssistManager> mAssistManager;
+    @Inject Lazy<SecurityController> mSecurityController;
+    @Inject Lazy<LeakDetector> mLeakDetector;
+    @Inject Lazy<LeakReporter> mLeakReporter;
+    @Inject Lazy<GarbageMonitor> mGarbageMonitor;
+    @Inject Lazy<IconLogger> mIconLogger;
+    @Inject Lazy<TunerService> mTunerService;
+    @Inject Lazy<StatusBarWindowController> mStatusBarWindowController;
+    @Inject Lazy<DarkIconDispatcher> mDarkIconDispatcher;
+    @Inject Lazy<ConfigurationController> mConfigurationController;
+    @Inject Lazy<StatusBarIconController> mStatusBarIconController;
+    @Inject Lazy<ScreenLifecycle> mScreenLifecycle;
+    @Inject Lazy<WakefulnessLifecycle> mWakefulnessLifecycle;
+    @Inject Lazy<FragmentService> mFragmentService;
+    @Inject Lazy<ExtensionController> mExtensionController;
+    @Inject Lazy<PluginDependencyProvider> mPluginDependencyProvider;
+    @Inject Lazy<LocalBluetoothManager> mLocalBluetoothManager;
+    @Inject Lazy<VolumeDialogController> mVolumeDialogController;
+    @Inject Lazy<MetricsLogger> mMetricsLogger;
+    @Inject Lazy<AccessibilityManagerWrapper> mAccessibilityManagerWrapper;
+    @Inject Lazy<SysuiColorExtractor> mSysuiColorExtractor;
+    @Inject Lazy<TunablePaddingService> mTunablePaddingService;
+    @Inject Lazy<ForegroundServiceController> mForegroundServiceController;
+    @Inject Lazy<UiOffloadThread> mUiOffloadThread;
+    @Inject Lazy<PowerUI.WarningsUI> mWarningsUI;
+    @Inject Lazy<LightBarController> mLightBarController;
+    @Inject Lazy<IWindowManager> mIWindowManager;
+    @Inject Lazy<OverviewProxyService> mOverviewProxyService;
+    @Inject Lazy<EnhancedEstimates> mEnhancedEstimates;
+    @Inject Lazy<VibratorHelper> mVibratorHelper;
+    @Inject Lazy<IStatusBarService> mIStatusBarService;
+    @Inject Lazy<DisplayMetrics> mDisplayMetrics;
+    @Inject Lazy<LockscreenGestureLogger> mLockscreenGestureLogger;
+    @Inject Lazy<KeyguardEnvironment> mKeyguardEnvironment;
+    @Inject Lazy<ShadeController> mShadeController;
+    @Inject Lazy<NotificationRemoteInputManager.Callback> mNotificationRemoteInputManagerCallback;
+    @Inject Lazy<InitController> mInitController;
+    @Inject Lazy<AppOpsController> mAppOpsController;
+    @Inject Lazy<DisplayNavigationBarController> mDisplayNavigationBarController;
+    @Inject Lazy<StatusBarStateController> mStatusBarStateController;
+    @Inject Lazy<NotificationLockscreenUserManager> mNotificationLockscreenUserManager;
+    @Inject Lazy<NotificationGroupAlertTransferHelper> mNotificationGroupAlertTransferHelper;
+    @Inject Lazy<NotificationGroupManager> mNotificationGroupManager;
+    @Inject Lazy<VisualStabilityManager> mVisualStabilityManager;
+    @Inject Lazy<NotificationGutsManager> mNotificationGutsManager;
+    @Inject Lazy<NotificationMediaManager> mNotificationMediaManager;
+    @Inject Lazy<AmbientPulseManager> mAmbientPulseManager;
+    @Inject Lazy<NotificationBlockingHelperManager> mNotificationBlockingHelperManager;
+    @Inject Lazy<NotificationRemoteInputManager> mNotificationRemoteInputManager;
+    @Inject Lazy<SmartReplyConstants> mSmartReplyConstants;
+    @Inject Lazy<NotificationListener> mNotificationListener;
+    @Inject Lazy<NotificationLogger> mNotificationLogger;
+    @Inject Lazy<NotificationViewHierarchyManager> mNotificationViewHierarchyManager;
+    @Inject Lazy<KeyguardDismissUtil> mKeyguardDismissUtil;
+    @Inject Lazy<SmartReplyController> mSmartReplyController;
+    @Inject Lazy<RemoteInputQuickSettingsDisabler> mRemoteInputQuickSettingsDisabler;
+    @Inject Lazy<BubbleController> mBubbleController;
+    @Inject Lazy<NotificationEntryManager> mNotificationEntryManager;
+    @Inject Lazy<SensorPrivacyManager> mSensorPrivacyManager;
+    @Inject @Named(BG_LOOPER_NAME) Lazy<Looper> mBgLooper;
+    @Inject @Named(BG_HANDLER_NAME) Lazy<Handler> mBgHandler;
+    @Inject @Named(MAIN_HANDLER_NAME) Lazy<Handler> mMainHandler;
+    @Inject @Named(TIME_TICK_HANDLER_NAME) Lazy<Handler> mTimeTickHandler;
+    @Nullable
+    @Inject @Named(LEAK_REPORT_EMAIL_NAME) Lazy<String> mLeakReportEmail;
+
+    @Inject
+    public Dependency() {
+    }
+
     @Override
     public void start() {
         // TODO: Think about ways to push these creation rules out of Dependency to cut down
         // on imports.
-        mProviders.put(TIME_TICK_HANDLER, () -> {
-            HandlerThread thread = new HandlerThread("TimeTick");
-            thread.start();
-            return new Handler(thread.getLooper());
-        });
-        mProviders.put(BG_LOOPER, () -> {
-            HandlerThread thread = new HandlerThread("SysUiBg",
-                    Process.THREAD_PRIORITY_BACKGROUND);
-            thread.start();
-            return thread.getLooper();
-        });
-        mProviders.put(BG_HANDLER, () -> new Handler(getDependency(BG_LOOPER)));
-        mProviders.put(MAIN_HANDLER, () -> new Handler(Looper.getMainLooper()));
-        mProviders.put(ActivityStarter.class, () -> new ActivityStarterDelegate());
-        mProviders.put(ActivityStarterDelegate.class, () ->
-                getDependency(ActivityStarter.class));
+        mProviders.put(TIME_TICK_HANDLER, mTimeTickHandler::get);
+        mProviders.put(BG_LOOPER, mBgLooper::get);
+        mProviders.put(BG_HANDLER, mBgHandler::get);
+        mProviders.put(MAIN_HANDLER, mMainHandler::get);
+        mProviders.put(ActivityStarter.class, mActivityStarter::get);
+        mProviders.put(ActivityStarterDelegate.class, mActivityStarterDelegate::get);
 
-        mProviders.put(AsyncSensorManager.class, () ->
-                new AsyncSensorManager(mContext.getSystemService(SensorManager.class),
-                        getDependency(PluginManager.class)));
+        mProviders.put(AsyncSensorManager.class, mAsyncSensorManager::get);
 
-        mProviders.put(SensorPrivacyManager.class, () ->
-                mContext.getSystemService(SensorPrivacyManager.class));
+        mProviders.put(BluetoothController.class, mBluetoothController::get);
+        mProviders.put(SensorPrivacyManager.class, mSensorPrivacyManager::get);
 
-        mProviders.put(BluetoothController.class, () ->
-                new BluetoothControllerImpl(mContext, getDependency(BG_LOOPER)));
+        mProviders.put(LocationController.class, mLocationController::get);
 
-        mProviders.put(LocationController.class, () ->
-                new LocationControllerImpl(mContext, getDependency(BG_LOOPER)));
+        mProviders.put(RotationLockController.class, mRotationLockController::get);
 
-        mProviders.put(RotationLockController.class, () ->
-                new RotationLockControllerImpl(mContext));
+        mProviders.put(NetworkController.class, mNetworkController::get);
 
-        mProviders.put(NetworkController.class, () ->
-                new NetworkControllerImpl(mContext, getDependency(BG_LOOPER),
-                        getDependency(DeviceProvisionedController.class)));
+        mProviders.put(ZenModeController.class, mZenModeController::get);
 
-        mProviders.put(ZenModeController.class, () ->
-                new ZenModeControllerImpl(mContext, getDependency(MAIN_HANDLER)));
+        mProviders.put(HotspotController.class, mHotspotController::get);
 
-        mProviders.put(HotspotController.class, () ->
-                new HotspotControllerImpl(mContext));
+        mProviders.put(CastController.class, mCastController::get);
 
-        mProviders.put(CastController.class, () ->
-                new CastControllerImpl(mContext));
+        mProviders.put(FlashlightController.class, mFlashlightController::get);
 
-        mProviders.put(FlashlightController.class, () ->
-                new FlashlightControllerImpl(mContext));
+        mProviders.put(KeyguardMonitor.class, mKeyguardMonitor::get);
 
-        mProviders.put(KeyguardMonitor.class, () ->
-                new KeyguardMonitorImpl(mContext));
+        mProviders.put(UserSwitcherController.class, mUserSwitcherController::get);
 
-        mProviders.put(UserSwitcherController.class, () ->
-                new UserSwitcherController(mContext, getDependency(KeyguardMonitor.class),
-                        getDependency(MAIN_HANDLER), getDependency(ActivityStarter.class)));
+        mProviders.put(UserInfoController.class, mUserInfoController::get);
 
-        mProviders.put(UserInfoController.class, () ->
-                new UserInfoControllerImpl(mContext));
+        mProviders.put(BatteryController.class, mBatteryController::get);
 
-        mProviders.put(BatteryController.class, () ->
-                new BatteryControllerImpl(mContext));
+        mProviders.put(ColorDisplayController.class, mColorDisplayController::get);
 
-        mProviders.put(ColorDisplayController.class, () ->
-                new ColorDisplayController(mContext));
+        mProviders.put(ManagedProfileController.class, mManagedProfileController::get);
 
-        mProviders.put(ManagedProfileController.class, () ->
-                new ManagedProfileControllerImpl(mContext));
+        mProviders.put(NextAlarmController.class, mNextAlarmController::get);
 
-        mProviders.put(NextAlarmController.class, () ->
-                new NextAlarmControllerImpl(mContext));
+        mProviders.put(DataSaverController.class, mDataSaverController::get);
 
-        mProviders.put(DataSaverController.class, () ->
-                get(NetworkController.class).getDataSaverController());
+        mProviders.put(AccessibilityController.class, mAccessibilityController::get);
 
-        mProviders.put(AccessibilityController.class, () ->
-                new AccessibilityController(mContext));
+        mProviders.put(DeviceProvisionedController.class, mDeviceProvisionedController::get);
 
-        mProviders.put(DeviceProvisionedController.class, () ->
-                new DeviceProvisionedControllerImpl(mContext));
+        mProviders.put(PluginManager.class, mPluginManager::get);
 
-        mProviders.put(PluginManager.class, () ->
-                new PluginManagerImpl(mContext, new PluginInitializerImpl()));
+        mProviders.put(AssistManager.class, mAssistManager::get);
 
-        mProviders.put(AssistManager.class, () ->
-                new AssistManager(getDependency(DeviceProvisionedController.class), mContext));
+        mProviders.put(SecurityController.class, mSecurityController::get);
 
-        mProviders.put(SecurityController.class, () ->
-                new SecurityControllerImpl(mContext));
+        mProviders.put(LeakDetector.class, mLeakDetector::get);
 
-        mProviders.put(LeakDetector.class, LeakDetector::create);
+        mProviders.put(LEAK_REPORT_EMAIL, mLeakReportEmail::get);
 
-        mProviders.put(LEAK_REPORT_EMAIL, () -> null);
+        mProviders.put(LeakReporter.class, mLeakReporter::get);
 
-        mProviders.put(LeakReporter.class, () -> new LeakReporter(
-                mContext,
-                getDependency(LeakDetector.class),
-                getDependency(LEAK_REPORT_EMAIL)));
+        mProviders.put(GarbageMonitor.class, mGarbageMonitor::get);
 
-        mProviders.put(
-                GarbageMonitor.class,
-                () ->
-                        new GarbageMonitor(
-                                mContext,
-                                getDependency(BG_LOOPER),
-                                getDependency(LeakDetector.class),
-                                getDependency(LeakReporter.class)));
+        mProviders.put(TunerService.class, mTunerService::get);
 
-        mProviders.put(TunerService.class, () ->
-                new TunerServiceImpl(mContext));
+        mProviders.put(StatusBarWindowController.class, mStatusBarWindowController::get);
 
-        mProviders.put(StatusBarWindowController.class, () ->
-                new StatusBarWindowController(mContext));
+        mProviders.put(DarkIconDispatcher.class, mDarkIconDispatcher::get);
 
-        mProviders.put(DarkIconDispatcher.class, () ->
-                new DarkIconDispatcherImpl(mContext));
+        mProviders.put(ConfigurationController.class, mConfigurationController::get);
 
-        mProviders.put(ConfigurationController.class, () ->
-                new ConfigurationControllerImpl(mContext));
+        mProviders.put(StatusBarIconController.class, mStatusBarIconController::get);
 
-        mProviders.put(StatusBarIconController.class, () ->
-                new StatusBarIconControllerImpl(mContext));
+        mProviders.put(ScreenLifecycle.class, mScreenLifecycle::get);
 
-        mProviders.put(ScreenLifecycle.class, () ->
-                new ScreenLifecycle());
+        mProviders.put(WakefulnessLifecycle.class, mWakefulnessLifecycle::get);
 
-        mProviders.put(WakefulnessLifecycle.class, () ->
-                new WakefulnessLifecycle());
+        mProviders.put(FragmentService.class, mFragmentService::get);
 
-        mProviders.put(FragmentService.class, () ->
-                new FragmentService());
+        mProviders.put(ExtensionController.class, mExtensionController::get);
 
-        mProviders.put(ExtensionController.class, () ->
-                new ExtensionControllerImpl(mContext));
+        mProviders.put(PluginDependencyProvider.class, mPluginDependencyProvider::get);
 
-        mProviders.put(PluginDependencyProvider.class, () ->
-                new PluginDependencyProvider(get(PluginManager.class)));
+        mProviders.put(LocalBluetoothManager.class, mLocalBluetoothManager::get);
 
-        mProviders.put(LocalBluetoothManager.class, () ->
-                LocalBluetoothManager.create(mContext, getDependency(BG_HANDLER),
-                        UserHandle.ALL));
+        mProviders.put(VolumeDialogController.class, mVolumeDialogController::get);
 
-        mProviders.put(VolumeDialogController.class, () ->
-                new VolumeDialogControllerImpl(mContext));
+        mProviders.put(MetricsLogger.class, mMetricsLogger::get);
 
-        mProviders.put(MetricsLogger.class, () -> new MetricsLogger());
+        mProviders.put(AccessibilityManagerWrapper.class, mAccessibilityManagerWrapper::get);
 
-        mProviders.put(AccessibilityManagerWrapper.class,
-                () -> new AccessibilityManagerWrapper(mContext));
+        mProviders.put(SysuiColorExtractor.class, mSysuiColorExtractor::get);
 
-        // Creating a new instance will trigger color extraction.
-        // Thankfully this only happens once - during boot - and WallpaperManagerService
-        // loads colors from cache.
-        mProviders.put(SysuiColorExtractor.class, () -> new SysuiColorExtractor(mContext));
+        mProviders.put(TunablePaddingService.class, mTunablePaddingService::get);
 
-        mProviders.put(TunablePaddingService.class, () -> new TunablePaddingService());
+        mProviders.put(ForegroundServiceController.class, mForegroundServiceController::get);
 
-        mProviders.put(ForegroundServiceController.class,
-                () -> new ForegroundServiceControllerImpl(mContext));
+        mProviders.put(UiOffloadThread.class, mUiOffloadThread::get);
 
-        mProviders.put(UiOffloadThread.class, UiOffloadThread::new);
+        mProviders.put(PowerUI.WarningsUI.class, mWarningsUI::get);
 
-        mProviders.put(PowerUI.WarningsUI.class, () -> new PowerNotificationWarnings(mContext));
+        mProviders.put(IconLogger.class, mIconLogger::get);
 
-        mProviders.put(IconLogger.class, () -> new IconLoggerImpl(mContext,
-                getDependency(BG_LOOPER), getDependency(MetricsLogger.class)));
+        mProviders.put(LightBarController.class, mLightBarController::get);
 
-        mProviders.put(LightBarController.class, () -> new LightBarController(mContext));
+        mProviders.put(IWindowManager.class, mIWindowManager::get);
 
-        mProviders.put(IWindowManager.class, () -> WindowManagerGlobal.getWindowManagerService());
+        mProviders.put(OverviewProxyService.class, mOverviewProxyService::get);
 
-        mProviders.put(OverviewProxyService.class, () -> new OverviewProxyService(mContext));
+        mProviders.put(EnhancedEstimates.class, mEnhancedEstimates::get);
 
-        mProviders.put(EnhancedEstimates.class, () -> new EnhancedEstimatesImpl());
+        mProviders.put(VibratorHelper.class, mVibratorHelper::get);
 
-        mProviders.put(VibratorHelper.class, () -> new VibratorHelper(mContext));
+        mProviders.put(IStatusBarService.class, mIStatusBarService::get);
 
-        mProviders.put(IStatusBarService.class, () -> IStatusBarService.Stub.asInterface(
-                ServiceManager.getService(Context.STATUS_BAR_SERVICE)));
+        mProviders.put(DisplayMetrics.class, mDisplayMetrics::get);
 
-        // Single instance of DisplayMetrics, gets updated by StatusBar, but can be used
-        // anywhere it is needed.
-        mProviders.put(DisplayMetrics.class, () -> new DisplayMetrics());
+        mProviders.put(LockscreenGestureLogger.class, mLockscreenGestureLogger::get);
 
-        mProviders.put(LockscreenGestureLogger.class, () -> new LockscreenGestureLogger());
-
-        mProviders.put(KeyguardEnvironment.class, () -> new KeyguardEnvironmentImpl());
-        mProviders.put(ShadeController.class, () ->
-                SysUiServiceProvider.getComponent(mContext, StatusBar.class));
+        mProviders.put(KeyguardEnvironment.class, mKeyguardEnvironment::get);
+        mProviders.put(ShadeController.class, mShadeController::get);
         mProviders.put(NotificationRemoteInputManager.Callback.class,
-                () -> new StatusBarRemoteInputCallback(mContext));
+                mNotificationRemoteInputManagerCallback::get);
 
-        mProviders.put(InitController.class, InitController::new);
+        mProviders.put(InitController.class, mInitController::get);
 
-        mProviders.put(AppOpsController.class, () ->
-                new AppOpsControllerImpl(mContext, getDependency(BG_LOOPER)));
+        mProviders.put(AppOpsController.class, mAppOpsController::get);
 
-        mProviders.put(DisplayNavigationBarController.class, () ->
-                new DisplayNavigationBarController(mContext, getDependency(MAIN_HANDLER)));
+        mProviders.put(DisplayNavigationBarController.class,
+                mDisplayNavigationBarController::get);
 
-        // Put all dependencies above here so the factory can override them if it wants.
-        SystemUIFactory.getInstance().injectDependencies(mProviders, mContext);
+        mProviders.put(StatusBarStateController.class, mStatusBarStateController::get);
+        mProviders.put(NotificationLockscreenUserManager.class,
+                mNotificationLockscreenUserManager::get);
+        mProviders.put(VisualStabilityManager.class, mVisualStabilityManager::get);
+        mProviders.put(NotificationGroupManager.class, mNotificationGroupManager::get);
+        mProviders.put(NotificationGroupAlertTransferHelper.class,
+                mNotificationGroupAlertTransferHelper::get);
+        mProviders.put(NotificationMediaManager.class, mNotificationMediaManager::get);
+        mProviders.put(NotificationGutsManager.class, mNotificationGutsManager::get);
+        mProviders.put(AmbientPulseManager.class, mAmbientPulseManager::get);
+        mProviders.put(NotificationBlockingHelperManager.class,
+                mNotificationBlockingHelperManager::get);
+        mProviders.put(NotificationRemoteInputManager.class,
+                mNotificationRemoteInputManager::get);
+        mProviders.put(SmartReplyConstants.class, mSmartReplyConstants::get);
+        mProviders.put(NotificationListener.class, mNotificationListener::get);
+        mProviders.put(NotificationLogger.class, mNotificationLogger::get);
+        mProviders.put(NotificationViewHierarchyManager.class,
+                mNotificationViewHierarchyManager::get);
+        mProviders.put(KeyguardDismissUtil.class, mKeyguardDismissUtil::get);
+        mProviders.put(SmartReplyController.class, mSmartReplyController::get);
+        mProviders.put(RemoteInputQuickSettingsDisabler.class,
+                mRemoteInputQuickSettingsDisabler::get);
+        mProviders.put(BubbleController.class, mBubbleController::get);
+        mProviders.put(NotificationEntryManager.class, mNotificationEntryManager::get);
 
         sDependency = this;
     }
@@ -484,4 +540,20 @@
             return mDisplayName;
         }
     }
+
+    @Subcomponent
+    public interface DependencyInjector {
+        void createSystemUI(Dependency dependency);
+    }
+
+    public static class DependencyCreator implements Injector {
+        @Override
+        public SystemUI apply(Context context) {
+            Dependency dependency = new Dependency();
+            SystemUIFactory.getInstance().getRootComponent()
+                    .createDependency()
+                    .createSystemUI(dependency);
+            return dependency;
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/DependencyProvider.java b/packages/SystemUI/src/com/android/systemui/DependencyProvider.java
new file mode 100644
index 0000000..e828b23
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/DependencyProvider.java
@@ -0,0 +1,578 @@
+/*
+ * 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;
+
+import static com.android.systemui.Dependency.BG_HANDLER_NAME;
+import static com.android.systemui.Dependency.BG_LOOPER_NAME;
+import static com.android.systemui.Dependency.LEAK_REPORT_EMAIL_NAME;
+import static com.android.systemui.Dependency.MAIN_HANDLER_NAME;
+import static com.android.systemui.Dependency.TIME_TICK_HANDLER_NAME;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.hardware.SensorManager;
+import android.hardware.SensorPrivacyManager;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.Process;
+import android.os.ServiceManager;
+import android.os.UserHandle;
+import android.util.DisplayMetrics;
+import android.util.Log;
+import android.view.IWindowManager;
+import android.view.WindowManagerGlobal;
+
+import com.android.internal.app.ColorDisplayController;
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.statusbar.IStatusBarService;
+import com.android.settingslib.bluetooth.LocalBluetoothManager;
+import com.android.systemui.appops.AppOpsController;
+import com.android.systemui.appops.AppOpsControllerImpl;
+import com.android.systemui.colorextraction.SysuiColorExtractor;
+import com.android.systemui.fragments.FragmentService;
+import com.android.systemui.keyguard.ScreenLifecycle;
+import com.android.systemui.keyguard.WakefulnessLifecycle;
+import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.PluginDependencyProvider;
+import com.android.systemui.plugins.PluginInitializerImpl;
+import com.android.systemui.plugins.VolumeDialogController;
+import com.android.systemui.power.PowerNotificationWarnings;
+import com.android.systemui.power.PowerUI;
+import com.android.systemui.recents.OverviewProxyService;
+import com.android.systemui.shared.plugins.PluginManager;
+import com.android.systemui.shared.plugins.PluginManagerImpl;
+import com.android.systemui.statusbar.DisplayNavigationBarController;
+import com.android.systemui.statusbar.NotificationRemoteInputManager;
+import com.android.systemui.statusbar.VibratorHelper;
+import com.android.systemui.statusbar.phone.ConfigurationControllerImpl;
+import com.android.systemui.statusbar.phone.DarkIconDispatcherImpl;
+import com.android.systemui.statusbar.phone.LightBarController;
+import com.android.systemui.statusbar.phone.LockscreenGestureLogger;
+import com.android.systemui.statusbar.phone.ManagedProfileController;
+import com.android.systemui.statusbar.phone.ManagedProfileControllerImpl;
+import com.android.systemui.statusbar.phone.ShadeController;
+import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.phone.StatusBarIconController;
+import com.android.systemui.statusbar.phone.StatusBarIconControllerImpl;
+import com.android.systemui.statusbar.phone.StatusBarRemoteInputCallback;
+import com.android.systemui.statusbar.phone.StatusBarWindowController;
+import com.android.systemui.statusbar.policy.AccessibilityController;
+import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
+import com.android.systemui.statusbar.policy.BatteryController;
+import com.android.systemui.statusbar.policy.BatteryControllerImpl;
+import com.android.systemui.statusbar.policy.BluetoothController;
+import com.android.systemui.statusbar.policy.BluetoothControllerImpl;
+import com.android.systemui.statusbar.policy.CastController;
+import com.android.systemui.statusbar.policy.CastControllerImpl;
+import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.systemui.statusbar.policy.DarkIconDispatcher;
+import com.android.systemui.statusbar.policy.DataSaverController;
+import com.android.systemui.statusbar.policy.DeviceProvisionedController;
+import com.android.systemui.statusbar.policy.DeviceProvisionedControllerImpl;
+import com.android.systemui.statusbar.policy.ExtensionController;
+import com.android.systemui.statusbar.policy.ExtensionControllerImpl;
+import com.android.systemui.statusbar.policy.FlashlightController;
+import com.android.systemui.statusbar.policy.FlashlightControllerImpl;
+import com.android.systemui.statusbar.policy.HotspotController;
+import com.android.systemui.statusbar.policy.HotspotControllerImpl;
+import com.android.systemui.statusbar.policy.IconLogger;
+import com.android.systemui.statusbar.policy.IconLoggerImpl;
+import com.android.systemui.statusbar.policy.KeyguardMonitor;
+import com.android.systemui.statusbar.policy.KeyguardMonitorImpl;
+import com.android.systemui.statusbar.policy.LocationController;
+import com.android.systemui.statusbar.policy.LocationControllerImpl;
+import com.android.systemui.statusbar.policy.NetworkController;
+import com.android.systemui.statusbar.policy.NetworkControllerImpl;
+import com.android.systemui.statusbar.policy.NextAlarmController;
+import com.android.systemui.statusbar.policy.NextAlarmControllerImpl;
+import com.android.systemui.statusbar.policy.RotationLockController;
+import com.android.systemui.statusbar.policy.RotationLockControllerImpl;
+import com.android.systemui.statusbar.policy.SecurityController;
+import com.android.systemui.statusbar.policy.SecurityControllerImpl;
+import com.android.systemui.statusbar.policy.UserInfoController;
+import com.android.systemui.statusbar.policy.UserInfoControllerImpl;
+import com.android.systemui.statusbar.policy.UserSwitcherController;
+import com.android.systemui.statusbar.policy.ZenModeController;
+import com.android.systemui.statusbar.policy.ZenModeControllerImpl;
+import com.android.systemui.tuner.TunablePadding;
+import com.android.systemui.tuner.TunablePadding.TunablePaddingService;
+import com.android.systemui.tuner.TunerService;
+import com.android.systemui.tuner.TunerServiceImpl;
+import com.android.systemui.util.AsyncSensorManager;
+import com.android.systemui.util.leak.GarbageMonitor;
+import com.android.systemui.util.leak.LeakDetector;
+import com.android.systemui.util.leak.LeakReporter;
+import com.android.systemui.volume.VolumeDialogControllerImpl;
+
+import javax.inject.Named;
+import javax.inject.Singleton;
+
+import dagger.Module;
+import dagger.Provides;
+
+/**
+ * Provides dependencies for the root component of sysui injection.
+ * See SystemUI/docs/dagger.md
+ */
+@Module
+public class DependencyProvider {
+
+    @Singleton
+    @Provides
+    @Named(TIME_TICK_HANDLER_NAME)
+    public Handler provideHandler() {
+        HandlerThread thread = new HandlerThread("TimeTick");
+        thread.start();
+        return new Handler(thread.getLooper());
+    }
+
+    @Singleton
+    @Provides
+    @Named(BG_LOOPER_NAME)
+    public Looper provideBgLooper() {
+        HandlerThread thread = new HandlerThread("SysUiBg",
+                Process.THREAD_PRIORITY_BACKGROUND);
+        thread.start();
+        return thread.getLooper();
+    }
+
+    @Singleton
+    @Provides
+    @Named(BG_HANDLER_NAME)
+    public Handler provideBgHandler(@Named(BG_LOOPER_NAME) Looper bgLooper) {
+        return new Handler(bgLooper);
+    }
+
+    @Singleton
+    @Provides
+    @Named(MAIN_HANDLER_NAME)
+    public Handler provideMainHandler() {
+        return new Handler(Looper.getMainLooper());
+    }
+
+    @Singleton
+    @Provides
+    public ActivityStarter provideActivityStarter() {
+        return new ActivityStarterDelegate();
+    }
+
+    @Singleton
+    @Provides
+    public InitController provideInitController() {
+        return new InitController();
+    }
+
+    @Singleton
+    @Provides
+    public ActivityStarterDelegate provideActivityStarterDelegate(ActivityStarter starter) {
+        return (ActivityStarterDelegate) starter;
+    }
+
+    @Singleton
+    @Provides
+    public AsyncSensorManager provideAsyncSensorManager(Context context, PluginManager manager) {
+        return new AsyncSensorManager(context.getSystemService(SensorManager.class),
+                manager);
+
+    }
+
+    @Singleton
+    @Provides
+    public BluetoothController provideBluetoothController(Context context,
+            @Named(BG_LOOPER_NAME) Looper looper) {
+        return new BluetoothControllerImpl(context, looper);
+
+    }
+
+    @Singleton
+    @Provides
+    public LocationController provideLocationController(Context context,
+            @Named(BG_LOOPER_NAME) Looper bgLooper) {
+        return new LocationControllerImpl(context, bgLooper);
+
+    }
+
+    @Singleton
+    @Provides
+    public RotationLockController provideRotationLockController(Context context) {
+        return new RotationLockControllerImpl(context);
+
+    }
+
+    @Singleton
+    @Provides
+    public NetworkController provideNetworkController(Context context,
+            @Named(BG_LOOPER_NAME) Looper bgLooper, DeviceProvisionedController controller) {
+        return new NetworkControllerImpl(context, bgLooper,
+                controller);
+
+    }
+
+    @Singleton
+    @Provides
+    public ZenModeController provideZenModeController(Context context,
+            @Named(MAIN_HANDLER_NAME) Handler mainHandler) {
+        return new ZenModeControllerImpl(context, mainHandler);
+
+    }
+
+    @Singleton
+    @Provides
+    public HotspotController provideHotspotController(Context context) {
+        return new HotspotControllerImpl(context);
+
+    }
+
+    @Singleton
+    @Provides
+    public CastController provideCastController(Context context) {
+        return new CastControllerImpl(context);
+
+    }
+
+    @Singleton
+    @Provides
+    public FlashlightController provideFlashlightController(Context context) {
+        return new FlashlightControllerImpl(context);
+
+    }
+
+    @Singleton
+    @Provides
+    public KeyguardMonitor provideKeyguardMonitor(Context context) {
+        return new KeyguardMonitorImpl(context);
+
+    }
+
+    @Singleton
+    @Provides
+    public UserSwitcherController provideUserSwitcherController(Context context,
+            KeyguardMonitor keyguardMonitor, @Named(MAIN_HANDLER_NAME) Handler mainHandler,
+            ActivityStarter activityStarter) {
+        return new UserSwitcherController(context, keyguardMonitor, mainHandler, activityStarter);
+    }
+
+    @Singleton
+    @Provides
+    public UserInfoController provideUserInfoContrller(Context context) {
+        return new UserInfoControllerImpl(context);
+
+    }
+
+    @Singleton
+    @Provides
+    public BatteryController provideBatteryController(Context context) {
+        return new BatteryControllerImpl(context);
+
+    }
+
+    @Singleton
+    @Provides
+    public ColorDisplayController provideColorDisplayController(Context context) {
+        return new ColorDisplayController(context);
+
+    }
+
+    @Singleton
+    @Provides
+    public ManagedProfileController provideManagedProfileController(Context context) {
+        return new ManagedProfileControllerImpl(context);
+
+    }
+
+    @Singleton
+    @Provides
+    public NextAlarmController provideNextAlarmController(Context context) {
+        return new NextAlarmControllerImpl(context);
+
+    }
+
+    @Singleton
+    @Provides
+    public DataSaverController provideDataSaverController(NetworkController networkController) {
+        return networkController.getDataSaverController();
+    }
+
+    @Singleton
+    @Provides
+    public AccessibilityController provideAccessibilityController(Context context) {
+        return new AccessibilityController(context);
+
+    }
+
+    @Singleton
+    @Provides
+    public DeviceProvisionedController provideDeviceProvisionedController(Context context) {
+        return new DeviceProvisionedControllerImpl(context);
+
+    }
+
+    @Singleton
+    @Provides
+    public PluginManager providePluginManager(Context context) {
+        return new PluginManagerImpl(context, new PluginInitializerImpl());
+
+    }
+
+    @Singleton
+    @Provides
+    public SecurityController provideSecurityController(Context context) {
+        return new SecurityControllerImpl(context);
+
+    }
+
+    @Singleton
+    @Provides
+    public LeakDetector provideLeakDetector() {
+        return LeakDetector.create();
+
+    }
+
+    @Singleton
+    @Provides
+    public LeakReporter provideLeakReporter(Context context, LeakDetector detector,
+            @Nullable @Named(LEAK_REPORT_EMAIL_NAME) String email) {
+        return new LeakReporter(context, detector, email);
+    }
+
+    @Singleton
+    @Provides
+    public GarbageMonitor provideGarbageMonitor(Context context,
+            @Named(BG_LOOPER_NAME) Looper bgLooper, LeakDetector detector, LeakReporter reporter) {
+        return new GarbageMonitor(context, bgLooper, detector, reporter);
+    }
+
+    @Singleton
+    @Provides
+    public TunerService provideTunerService(Context context) {
+        return new TunerServiceImpl(context);
+
+    }
+
+    @Singleton
+    @Provides
+    public StatusBarWindowController provideStatusBarWindowController(Context context) {
+        return new StatusBarWindowController(context);
+
+    }
+
+    @Singleton
+    @Provides
+    public DarkIconDispatcher provideDarkIconDispatcher(Context context) {
+        return new DarkIconDispatcherImpl(context);
+    }
+
+    @Singleton
+    @Provides
+    public ConfigurationController provideConfigurationController(Context context) {
+        return new ConfigurationControllerImpl(context);
+
+    }
+
+    @Singleton
+    @Provides
+    public StatusBarIconController provideStatusBarIconController(Context context) {
+        return new StatusBarIconControllerImpl(context);
+
+    }
+
+    @Singleton
+    @Provides
+    public ScreenLifecycle provideScreenLifecycle() {
+        return new ScreenLifecycle();
+    }
+
+    @Singleton
+    @Provides
+    public WakefulnessLifecycle provideWakefulnessLifecycle() {
+        return new WakefulnessLifecycle();
+    }
+
+    @Singleton
+    @Provides
+    public FragmentService provideFragmentService() {
+        return new FragmentService();
+    }
+
+    @Singleton
+    @Provides
+    public ExtensionController provideExtensionController(Context context) {
+        return new ExtensionControllerImpl(context);
+    }
+
+    @Singleton
+    @Provides
+    public PluginDependencyProvider providePluginDependency(PluginManager pluginManager) {
+        return new PluginDependencyProvider(pluginManager);
+    }
+
+    @Singleton
+    @Provides
+    public LocalBluetoothManager provideLocalBluetoothController(Context context,
+            @Named(BG_HANDLER_NAME) Handler bgHandler) {
+        return LocalBluetoothManager.create(context, bgHandler,
+                UserHandle.ALL);
+    }
+
+    @Singleton
+    @Provides
+    public VolumeDialogController provideVolumeDialogController(Context context) {
+        return new VolumeDialogControllerImpl(context);
+
+    }
+
+    @Singleton
+    @Provides
+    public MetricsLogger provideMetricsLogger() {
+        return new MetricsLogger();
+
+    }
+
+    @Singleton
+    @Provides
+    public AccessibilityManagerWrapper provideAccessibilityManagerWrapper(Context context) {
+        return new AccessibilityManagerWrapper(context);
+
+    }
+
+    @Singleton
+    @Provides
+    // Creating a new instance will trigger color extraction.
+    // Thankfully this only happens once - during boot - and WallpaperManagerService
+    // loads colors from cache.
+    public SysuiColorExtractor provideSysuiColorExtractor(Context context) {
+        return new SysuiColorExtractor(context);
+
+    }
+
+    @Singleton
+    @Provides
+    public TunablePadding.TunablePaddingService provideTunablePaddingService() {
+        return new TunablePaddingService();
+
+    }
+
+    @Singleton
+    @Provides
+    public ForegroundServiceController provideForegroundService(Context context) {
+        return new ForegroundServiceControllerImpl(context);
+
+    }
+
+    @Singleton
+    @Provides
+    public UiOffloadThread provideUiOffloadThread() {
+        Log.d("TestTest", "provideUiOffloadThread");
+        return new UiOffloadThread();
+    }
+
+    @Singleton
+    @Provides
+    public PowerUI.WarningsUI provideWarningsUi(Context context) {
+        return new PowerNotificationWarnings(context);
+    }
+
+    @Singleton
+    @Provides
+    public IconLogger provideIconLogger(Context context, @Named(BG_LOOPER_NAME) Looper bgLooper,
+            MetricsLogger logger) {
+        return new IconLoggerImpl(context, bgLooper, logger);
+    }
+
+    @Singleton
+    @Provides
+    public LightBarController provideLightBarController(Context context) {
+        return new LightBarController(context);
+    }
+
+    @Singleton
+    @Provides
+    public IWindowManager provideIWindowManager() {
+        return WindowManagerGlobal.getWindowManagerService();
+    }
+
+    @Singleton
+    @Provides
+    public OverviewProxyService provideOverviewProxyService(Context context) {
+        return new OverviewProxyService(context);
+    }
+
+    @Singleton
+    @Provides
+    public VibratorHelper provideVibratorHelper(Context context) {
+        return new VibratorHelper(context);
+
+    }
+
+    @Singleton
+    @Provides
+    public IStatusBarService provideIStatusBarService() {
+        return IStatusBarService.Stub.asInterface(
+                ServiceManager.getService(Context.STATUS_BAR_SERVICE));
+    }
+
+    @Singleton
+    @Provides
+    // Single instance of DisplayMetrics, gets updated by StatusBar, but can be used
+// anywhere it is needed.
+    public DisplayMetrics provideDisplayMetrics() {
+        return new DisplayMetrics();
+
+    }
+
+    @Singleton
+    @Provides
+    public LockscreenGestureLogger provideLockscreenGestureLogger() {
+        return new LockscreenGestureLogger();
+    }
+
+    @Singleton
+    @Provides
+    public ShadeController provideShadeController(Context context) {
+        return SysUiServiceProvider.getComponent(context, StatusBar.class);
+    }
+
+    @Singleton
+    @Provides
+    public NotificationRemoteInputManager.Callback provideNotificationRemoteInputManager(
+            Context context) {
+        return new StatusBarRemoteInputCallback(context);
+
+    }
+
+    @Singleton
+    @Provides
+    public AppOpsController provideAppOpsController(Context context,
+            @Named(BG_LOOPER_NAME) Looper bgLooper) {
+        return new AppOpsControllerImpl(context, bgLooper);
+
+    }
+
+    @Singleton
+    @Provides
+    public DisplayNavigationBarController provideDisplayNavigationBarController(Context context,
+            @Named(MAIN_HANDLER_NAME) Handler mainHandler) {
+        return new DisplayNavigationBarController(context, mainHandler);
+    }
+
+    @Singleton
+    @Provides
+    public SensorPrivacyManager provideSensorPrivacyManager(Context context) {
+        return context.getSystemService(SensorPrivacyManager.class);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUI.java b/packages/SystemUI/src/com/android/systemui/SystemUI.java
index 30fbef6..78a1246 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUI.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUI.java
@@ -24,6 +24,7 @@
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.Map;
+import java.util.function.Function;
 
 public abstract class SystemUI implements SysUiServiceProvider {
     public Context mContext;
@@ -61,4 +62,7 @@
 
         n.addExtras(extras);
     }
+
+    public interface Injector extends Function<Context, SystemUI> {
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
index 92aa652..1d8a21d 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
@@ -170,7 +170,11 @@
             Class cls;
             try {
                 cls = Class.forName(clsName);
-                mServices[i] = (SystemUI) cls.newInstance();
+                Object o = cls.newInstance();
+                if (o instanceof SystemUI.Injector) {
+                    o = ((SystemUI.Injector) o).apply(this);
+                }
+                mServices[i] = (SystemUI) o;
             } catch(ClassNotFoundException ex){
                 throw new RuntimeException(ex);
             } catch (IllegalAccessException ex) {
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
index 867c917..9bc91ee 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
@@ -16,9 +16,11 @@
 
 package com.android.systemui;
 
+import static com.android.systemui.Dependency.LEAK_REPORT_EMAIL_NAME;
+
+import android.annotation.Nullable;
 import android.app.AlarmManager;
 import android.content.Context;
-import android.util.ArrayMap;
 import android.util.Log;
 import android.view.ViewGroup;
 
@@ -26,56 +28,54 @@
 import com.android.internal.util.function.TriConsumer;
 import com.android.internal.widget.LockPatternUtils;
 import com.android.keyguard.ViewMediatorCallback;
-import com.android.systemui.Dependency.DependencyProvider;
-import com.android.systemui.bubbles.BubbleController;
+import com.android.systemui.assist.AssistManager;
 import com.android.systemui.classifier.FalsingManager;
 import com.android.systemui.keyguard.DismissCallbackRegistry;
+import com.android.systemui.power.EnhancedEstimates;
+import com.android.systemui.power.EnhancedEstimatesImpl;
 import com.android.systemui.qs.QSTileHost;
-import com.android.systemui.statusbar.AmbientPulseManager;
 import com.android.systemui.statusbar.KeyguardIndicationController;
 import com.android.systemui.statusbar.NotificationListener;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
 import com.android.systemui.statusbar.NotificationLockscreenUserManagerImpl;
-import com.android.systemui.statusbar.NotificationMediaManager;
-import com.android.systemui.statusbar.NotificationRemoteInputManager;
-import com.android.systemui.statusbar.NotificationViewHierarchyManager;
 import com.android.systemui.statusbar.ScrimView;
-import com.android.systemui.statusbar.SmartReplyController;
-import com.android.systemui.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.notification.NotificationData;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
-import com.android.systemui.statusbar.notification.VisualStabilityManager;
-import com.android.systemui.statusbar.notification.logging.NotificationLogger;
-import com.android.systemui.statusbar.notification.row.NotificationBlockingHelperManager;
-import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
 import com.android.systemui.statusbar.phone.DozeParameters;
 import com.android.systemui.statusbar.phone.KeyguardBouncer;
-import com.android.systemui.statusbar.phone.KeyguardDismissUtil;
+import com.android.systemui.statusbar.phone.KeyguardEnvironmentImpl;
 import com.android.systemui.statusbar.phone.LockIcon;
 import com.android.systemui.statusbar.phone.LockscreenWallpaper;
-import com.android.systemui.statusbar.phone.NotificationGroupAlertTransferHelper;
-import com.android.systemui.statusbar.phone.NotificationGroupManager;
 import com.android.systemui.statusbar.phone.NotificationIconAreaController;
 import com.android.systemui.statusbar.phone.ScrimController;
 import com.android.systemui.statusbar.phone.ScrimState;
 import com.android.systemui.statusbar.phone.StatusBar;
 import com.android.systemui.statusbar.phone.StatusBarIconController;
 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
-import com.android.systemui.statusbar.policy.RemoteInputQuickSettingsDisabler;
-import com.android.systemui.statusbar.policy.SmartReplyConstants;
+import com.android.systemui.statusbar.policy.DeviceProvisionedController;
 import com.android.systemui.volume.VolumeDialogComponent;
 
 import java.util.function.Consumer;
 
+import javax.inject.Named;
+import javax.inject.Singleton;
+
+import dagger.Component;
+import dagger.Module;
+import dagger.Provides;
+
 /**
  * Class factory to provide customizable SystemUI components.
  */
+@Module
 public class SystemUIFactory {
     private static final String TAG = "SystemUIFactory";
 
     static SystemUIFactory mFactory;
+    private SystemUIRootComponent mRootComponent;
 
-    public static SystemUIFactory getInstance() {
-        return mFactory;
+    public static <T extends SystemUIFactory> T getInstance() {
+        return (T) mFactory;
     }
 
     public static void createFromConfig(Context context) {
@@ -88,6 +88,7 @@
             Class<?> cls = null;
             cls = context.getClassLoader().loadClass(clsName);
             mFactory = (SystemUIFactory) cls.newInstance();
+            mFactory.init(context);
         } catch (Throwable t) {
             Log.w(TAG, "Error creating SystemUIFactory component: " + clsName, t);
             throw new RuntimeException(t);
@@ -96,6 +97,18 @@
 
     public SystemUIFactory() {}
 
+    protected void init(Context context) {
+        mRootComponent = DaggerSystemUIFactory_SystemUIRootComponent.builder()
+                .systemUIFactory(this)
+                .dependencyProvider(new com.android.systemui.DependencyProvider())
+                .contextHolder(new ContextHolder(context))
+                .build();
+    }
+
+    public SystemUIRootComponent getRootComponent() {
+        return mRootComponent;
+    }
+
     public StatusBarKeyguardViewManager createStatusBarKeyguardViewManager(Context context,
             ViewMediatorCallback viewMediatorCallback, LockPatternUtils lockPatternUtils) {
         return new StatusBarKeyguardViewManager(context, viewMediatorCallback, lockPatternUtils);
@@ -137,33 +150,70 @@
         return new VolumeDialogComponent(systemUi, context);
     }
 
-    public void injectDependencies(ArrayMap<Object, DependencyProvider> providers,
+    @Singleton
+    @Provides
+    public NotificationData.KeyguardEnvironment provideKeyguardEnvironment(Context context) {
+        return new KeyguardEnvironmentImpl();
+    }
+
+    @Singleton
+    @Provides
+    public NotificationLockscreenUserManager provideNotificationLockscreenUserManager(
             Context context) {
-        providers.put(StatusBarStateController.class, StatusBarStateController::new);
-        providers.put(NotificationLockscreenUserManager.class,
-                () -> new NotificationLockscreenUserManagerImpl(context));
-        providers.put(VisualStabilityManager.class, VisualStabilityManager::new);
-        providers.put(NotificationGroupManager.class, NotificationGroupManager::new);
-        providers.put(NotificationGroupAlertTransferHelper.class,
-                NotificationGroupAlertTransferHelper::new);
-        providers.put(NotificationMediaManager.class, () -> new NotificationMediaManager(context));
-        providers.put(NotificationGutsManager.class, () -> new NotificationGutsManager(context));
-        providers.put(AmbientPulseManager.class, () -> new AmbientPulseManager(context));
-        providers.put(NotificationBlockingHelperManager.class,
-                () -> new NotificationBlockingHelperManager(context));
-        providers.put(NotificationRemoteInputManager.class,
-                () -> new NotificationRemoteInputManager(context));
-        providers.put(SmartReplyConstants.class,
-                () -> new SmartReplyConstants(Dependency.get(Dependency.MAIN_HANDLER), context));
-        providers.put(NotificationListener.class, () -> new NotificationListener(context));
-        providers.put(NotificationLogger.class, NotificationLogger::new);
-        providers.put(NotificationViewHierarchyManager.class,
-                () -> new NotificationViewHierarchyManager(context));
-        providers.put(NotificationEntryManager.class, () -> new NotificationEntryManager(context));
-        providers.put(KeyguardDismissUtil.class, KeyguardDismissUtil::new);
-        providers.put(SmartReplyController.class, () -> new SmartReplyController());
-        providers.put(RemoteInputQuickSettingsDisabler.class,
-                () -> new RemoteInputQuickSettingsDisabler(context));
-        providers.put(BubbleController.class, () -> new BubbleController(context));
+        return new NotificationLockscreenUserManagerImpl(context);
+    }
+
+    @Singleton
+    @Provides
+    public AssistManager provideAssistManager(DeviceProvisionedController controller,
+            Context context) {
+        return new AssistManager(controller, context);
+    }
+
+    @Singleton
+    @Provides
+    public NotificationEntryManager provideNotificationEntryManager(Context context) {
+        return new NotificationEntryManager(context);
+    }
+
+    @Singleton
+    @Provides
+    public EnhancedEstimates provideEnhancedEstimates(Context context) {
+        return new EnhancedEstimatesImpl();
+    }
+
+    @Singleton
+    @Provides
+    @Named(LEAK_REPORT_EMAIL_NAME)
+    @Nullable
+    public String provideLeakReportEmail() {
+        return null;
+    }
+
+    @Singleton
+    @Provides
+    public NotificationListener provideNotificationListener(Context context) {
+        return new NotificationListener(context);
+    }
+
+    @Module
+    protected static class ContextHolder {
+        private Context mContext;
+
+        public ContextHolder(Context context) {
+            mContext = context;
+        }
+
+        @Provides
+        public Context provideContext() {
+            return mContext;
+        }
+    }
+
+    @Singleton
+    @Component(modules = {SystemUIFactory.class, DependencyProvider.class, ContextHolder.class})
+    public interface SystemUIRootComponent {
+        @Singleton
+        Dependency.DependencyInjector createDependency();
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
index 1e91ef3..c8595eb 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
@@ -42,12 +42,16 @@
 import java.util.HashMap;
 import java.util.Map;
 
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
 /**
  * Bubbles are a special type of content that can "float" on top of other apps or System UI.
  * Bubbles can be expanded to show more content.
  *
  * The controller manages addition, removal, and visible state of bubbles on screen.
  */
+@Singleton
 public class BubbleController {
     private static final int MAX_BUBBLES = 5; // TODO: actually enforce this
 
@@ -117,6 +121,7 @@
         void onBubbleExpandChanged(boolean isExpanding, float amount);
     }
 
+    @Inject
     public BubbleController(Context context) {
         mContext = context;
         WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java b/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
index 4fb1bc5..974cd88 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
@@ -35,7 +35,7 @@
     private static final int SIZE = Build.IS_DEBUGGABLE ? 400 : 50;
     static final SimpleDateFormat FORMAT = new SimpleDateFormat("MM-dd HH:mm:ss.SSS");
 
-    private static final int REASONS = 8;
+    private static final int REASONS = 7;
 
     public static final int PULSE_REASON_NONE = -1;
     public static final int PULSE_REASON_INTENT = 0;
@@ -182,7 +182,7 @@
      */
     public static void traceLockScreenWakeUp(boolean wake) {
         if (!ENABLED) return;
-        log("wakeLockScreenWakeUp " + wake);
+        log("wakeLockScreen " + wake);
     }
 
     /**
@@ -191,7 +191,7 @@
      */
     public static void traceWakeDisplay(boolean wake) {
         if (!ENABLED) return;
-        log("wakeLockScreenWakeUp " + wake);
+        log("wakeDisplay " + wake);
     }
 
     public static void traceProximityResult(Context context, boolean near, long millis,
diff --git a/packages/SystemUI/src/com/android/systemui/plugins/PluginEnablerImpl.java b/packages/SystemUI/src/com/android/systemui/plugins/PluginEnablerImpl.java
index e2417f7..6337415 100644
--- a/packages/SystemUI/src/com/android/systemui/plugins/PluginEnablerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/plugins/PluginEnablerImpl.java
@@ -16,28 +16,44 @@
 
 import android.content.ComponentName;
 import android.content.Context;
+import android.content.SharedPreferences;
 import android.content.pm.PackageManager;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.systemui.shared.plugins.PluginEnabler;
 
 public class PluginEnablerImpl implements PluginEnabler {
+    private static final String CRASH_DISABLED_PLUGINS_PREF_FILE = "auto_disabled_plugins_prefs";
 
-    final private PackageManager mPm;
+    private PackageManager mPm;
+    private final SharedPreferences mAutoDisabledPrefs;
 
     public PluginEnablerImpl(Context context) {
-        this(context.getPackageManager());
+        this(context, context.getPackageManager());
     }
 
-    @VisibleForTesting public PluginEnablerImpl(PackageManager pm) {
+    @VisibleForTesting public PluginEnablerImpl(Context context, PackageManager pm) {
+        mAutoDisabledPrefs = context.getSharedPreferences(
+                CRASH_DISABLED_PLUGINS_PREF_FILE, Context.MODE_PRIVATE);
         mPm = pm;
     }
 
     @Override
-    public void setEnabled(ComponentName component, boolean enabled) {
+    public void setEnabled(ComponentName component) {
+        setDisabled(component, ENABLED);
+    }
+
+    @Override
+    public void setDisabled(ComponentName component, @DisableReason int reason) {
+        boolean enabled = reason == ENABLED;
         final int desiredState = enabled ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED
                 : PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
         mPm.setComponentEnabledSetting(component, desiredState, PackageManager.DONT_KILL_APP);
+        if (enabled) {
+            mAutoDisabledPrefs.edit().remove(component.flattenToString()).apply();
+        } else {
+            mAutoDisabledPrefs.edit().putInt(component.flattenToString(), reason).apply();
+        }
     }
 
     @Override
@@ -45,4 +61,12 @@
         return mPm.getComponentEnabledSetting(component)
                 != PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
     }
+
+    @Override
+    public @DisableReason int getDisableReason(ComponentName componentName) {
+        if (isEnabled(componentName)) {
+            return ENABLED;
+        }
+        return mAutoDisabledPrefs.getInt(componentName.flattenToString(), DISABLED_MANUALLY);
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
index 8906665..466c808 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
@@ -183,6 +183,7 @@
     public void updateState(Tile tile) {
         mTile.setIcon(tile.getIcon());
         mTile.setLabel(tile.getLabel());
+        mTile.setSubtitle(tile.getSubtitle());
         mTile.setContentDescription(tile.getContentDescription());
         mTile.setState(tile.getState());
     }
@@ -322,6 +323,14 @@
             return null;
         };
         state.label = mTile.getLabel();
+
+        CharSequence subtitle = mTile.getSubtitle();
+        if (subtitle != null && subtitle.length() > 0) {
+            state.secondaryLabel = subtitle;
+        } else {
+            state.secondaryLabel = null;
+        }
+
         if (mTile.getContentDescription() != null) {
             state.contentDescription = mTile.getContentDescription();
         } else {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/AmbientPulseManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/AmbientPulseManager.java
index 8821679..9bfd4ee 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/AmbientPulseManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/AmbientPulseManager.java
@@ -28,17 +28,22 @@
 import com.android.systemui.statusbar.notification.NotificationData;
 import com.android.systemui.statusbar.notification.row.NotificationInflater.InflationFlag;
 
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
 /**
  * Manager which handles high priority notifications that should "pulse" in when the device is
  * dozing and/or in AOD.  The pulse uses the notification's ambient view and pops in briefly
  * before automatically dismissing the alert.
  */
+@Singleton
 public class AmbientPulseManager extends AlertingNotificationManager {
 
     protected final ArraySet<OnAmbientChangedListener> mListeners = new ArraySet<>();
     @VisibleForTesting
     protected long mExtensionTime;
 
+    @Inject
     public AmbientPulseManager(@NonNull final Context context) {
         Resources resources = context.getResources();
         mAutoDismissNotificationDecay = resources.getInteger(R.integer.ambient_notification_decay);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
index f045548..7d80860 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
@@ -61,10 +61,14 @@
 import java.util.ArrayList;
 import java.util.List;
 
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
 /**
  * Handles tasks and state related to media notifications. For example, there is a 'current' media
  * notification, which this class keeps track of.
  */
+@Singleton
 public class NotificationMediaManager implements Dumpable {
     private static final String TAG = "NotificationMediaManager";
     public static final boolean DEBUG_MEDIA = false;
@@ -157,6 +161,7 @@
         return mEntryManager;
     }
 
+    @Inject
     public NotificationMediaManager(Context context) {
         mContext = context;
         mMediaSessionManager
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
index 9391737..d1556fb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
@@ -59,12 +59,16 @@
 import java.util.ArrayList;
 import java.util.Set;
 
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
 /**
  * Class for handling remote input state over a set of notifications. This class handles things
  * like keeping notifications temporarily that were cancelled as a response to a remote input
  * interaction, keeping track of notifications to remove when NotificationPresenter is collapsed,
  * and handling clicks on remote views.
  */
+@Singleton
 public class NotificationRemoteInputManager implements Dumpable {
     public static final boolean ENABLE_REMOTE_INPUT =
             SystemProperties.getBoolean("debug.enable_remote_input", true);
@@ -229,6 +233,7 @@
         return mShadeController;
     }
 
+    @Inject
     public NotificationRemoteInputManager(Context context) {
         mContext = context;
         mBarService = IStatusBarService.Stub.asInterface(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
index 0702f1b..2524747 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
@@ -41,6 +41,9 @@
 import java.util.List;
 import java.util.Stack;
 
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
 /**
  * NotificationViewHierarchyManager manages updating the view hierarchy of notification views based
  * on their group structure. For example, if a notification becomes bundled with another,
@@ -48,6 +51,7 @@
  * tell NotificationListContainer which notifications to display, and inform it of changes to those
  * notifications that might affect their display.
  */
+@Singleton
 public class NotificationViewHierarchyManager {
     private static final String TAG = "NotificationViewHierarchyManager";
 
@@ -123,6 +127,7 @@
         return mShadeController;
     }
 
+    @Inject
     public NotificationViewHierarchyManager(Context context) {
         Resources res = context.getResources();
         mAlwaysExpandNonGroupedNotification =
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SmartReplyController.java b/packages/SystemUI/src/com/android/systemui/statusbar/SmartReplyController.java
index f5d6904..6f1548d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/SmartReplyController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/SmartReplyController.java
@@ -27,10 +27,14 @@
 
 import java.util.Set;
 
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
 /**
  * Handles when smart replies are added to a notification
  * and clicked upon.
  */
+@Singleton
 public class SmartReplyController {
     private IStatusBarService mBarService;
     private Set<String> mSendingKeys = new ArraySet<>();
@@ -38,7 +42,7 @@
     private final NotificationEntryManager mEntryManager =
             Dependency.get(NotificationEntryManager.class);
 
-
+    @Inject
     public SmartReplyController() {
         mBarService = Dependency.get(IStatusBarService.class);
     }
@@ -88,10 +92,14 @@
         return mSendingKeys.contains(key);
     }
 
-    public void smartRepliesAdded(final NotificationData.Entry entry, int replyCount) {
+    /**
+     * Smart Replies and Actions have been added to the UI.
+     */
+    public void smartSuggestionsAdded(final NotificationData.Entry entry, int replyCount,
+            int actionCount, boolean generatedByAssistant) {
         try {
-            mBarService.onNotificationSmartRepliesAdded(entry.notification.getKey(),
-                    replyCount);
+            mBarService.onNotificationSmartSuggestionsAdded(
+                    entry.notification.getKey(), replyCount, actionCount, generatedByAssistant);
         } catch (RemoteException e) {
             // Nothing to do, system going down
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateController.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateController.java
index 3f84416..087b655 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateController.java
@@ -35,9 +35,13 @@
 import java.util.ArrayList;
 import java.util.Comparator;
 
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
 /**
  * Tracks and reports on {@link StatusBarState}.
  */
+@Singleton
 public class StatusBarStateController implements CallbackController<StateListener> {
     private static final String TAG = "SbStateController";
 
@@ -101,6 +105,10 @@
     public static final int RANK_STACK_SCROLLER = 2;
     public static final int RANK_SHELF = 3;
 
+    @Inject
+    public StatusBarStateController() {
+    }
+
     public int getState() {
         return mState;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java
new file mode 100644
index 0000000..49f1a8d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java
@@ -0,0 +1,104 @@
+/*
+ * 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.notification;
+
+import android.app.Notification;
+import android.os.SystemClock;
+import android.service.notification.StatusBarNotification;
+import android.util.Log;
+import android.view.View;
+
+import com.android.systemui.DejankUtils;
+import com.android.systemui.bubbles.BubbleController;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.phone.ShadeController;
+
+/**
+ * Click handler for generic clicks on notifications. Clicks on specific areas (expansion caret,
+ * app ops icon, etc) are handled elsewhere.
+ */
+public final class NotificationClicker implements View.OnClickListener {
+    private static final String TAG = "NotificationClicker";
+
+    private final ShadeController mShadeController;
+    private final BubbleController mBubbleController;
+    private final NotificationActivityStarter mNotificationActivityStarter;
+
+    public NotificationClicker(ShadeController shadeController,
+            BubbleController bubbleController,
+            NotificationActivityStarter notificationActivityStarter) {
+        mShadeController = shadeController;
+        mBubbleController = bubbleController;
+        mNotificationActivityStarter = notificationActivityStarter;
+    }
+
+    @Override
+    public void onClick(final View v) {
+        if (!(v instanceof ExpandableNotificationRow)) {
+            Log.e(TAG, "NotificationClicker called on a view that is not a notification row.");
+            return;
+        }
+
+        mShadeController.wakeUpIfDozing(SystemClock.uptimeMillis(), v);
+
+        final ExpandableNotificationRow row = (ExpandableNotificationRow) v;
+        final StatusBarNotification sbn = row.getStatusBarNotification();
+        if (sbn == null) {
+            Log.e(TAG, "NotificationClicker called on an unclickable notification,");
+            return;
+        }
+
+        // Check if the notification is displaying the menu, if so slide notification back
+        if (isMenuVisible(row)) {
+            row.animateTranslateNotification(0);
+            return;
+        } else if (row.isChildInGroup() && isMenuVisible(row.getNotificationParent())) {
+            row.getNotificationParent().animateTranslateNotification(0);
+            return;
+        } else if (row.isSummaryWithChildren() && row.areChildrenExpanded()) {
+            // We never want to open the app directly if the user clicks in between
+            // the notifications.
+            return;
+        }
+
+        // Mark notification for one frame.
+        row.setJustClicked(true);
+        DejankUtils.postAfterTraversal(() -> row.setJustClicked(false));
+
+        // If it was a bubble we should close it
+        if (row.getEntry().isBubble()) {
+            mBubbleController.collapseStack();
+        }
+
+        mNotificationActivityStarter.onNotificationClicked(sbn, row);
+    }
+
+    private boolean isMenuVisible(ExpandableNotificationRow row) {
+        return row.getProvider() != null && row.getProvider().isMenuVisible();
+    }
+
+    /**
+     * Attaches the click listener to the row if appropriate.
+     */
+    public void register(ExpandableNotificationRow row, StatusBarNotification sbn) {
+        Notification notification = sbn.getNotification();
+        if (notification.contentIntent != null || notification.fullScreenIntent != null) {
+            row.setOnClickListener(this);
+        } else {
+            row.setOnClickListener(null);
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
index aab3fc4..d2a5864 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
@@ -15,6 +15,7 @@
  */
 package com.android.systemui.statusbar.notification;
 
+import static com.android.internal.util.Preconditions.checkNotNull;
 import static com.android.systemui.bubbles.BubbleController.DEBUG_DEMOTE_TO_NOTIF;
 import static com.android.systemui.statusbar.NotificationRemoteInputManager.ENABLE_REMOTE_INPUT;
 import static com.android.systemui.statusbar.NotificationRemoteInputManager.FORCE_REMOTE_INPUT_HISTORY;
@@ -36,7 +37,6 @@
 import android.os.PowerManager;
 import android.os.RemoteException;
 import android.os.ServiceManager;
-import android.os.SystemClock;
 import android.os.UserHandle;
 import android.provider.Settings;
 import android.service.dreams.DreamService;
@@ -49,7 +49,6 @@
 import android.util.ArraySet;
 import android.util.EventLog;
 import android.util.Log;
-import android.view.View;
 import android.view.ViewGroup;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -57,7 +56,6 @@
 import com.android.internal.statusbar.IStatusBarService;
 import com.android.internal.statusbar.NotificationVisibility;
 import com.android.internal.util.NotificationMessagingUtil;
-import com.android.systemui.DejankUtils;
 import com.android.systemui.Dependency;
 import com.android.systemui.Dumpable;
 import com.android.systemui.EventLogTags;
@@ -115,7 +113,6 @@
     private final NotificationMessagingUtil mMessagingUtil;
     protected final Context mContext;
     protected final HashMap<String, NotificationData.Entry> mPendingNotifications = new HashMap<>();
-    private final NotificationClicker mNotificationClicker = new NotificationClicker();
 
     private final NotificationGroupManager mGroupManager =
             Dependency.get(NotificationGroupManager.class);
@@ -146,7 +143,6 @@
     protected IStatusBarService mBarService;
     private NotificationPresenter mPresenter;
     private Callback mCallback;
-    private NotificationActivityStarter mNotificationActivityStarter;
     protected PowerManager mPowerManager;
     private NotificationListenerService.RankingMap mLatestRankingMap;
     protected HeadsUpManager mHeadsUpManager;
@@ -161,63 +157,7 @@
     private ExpandableNotificationRow.OnAppOpsClickListener mOnAppOpsClickListener;
     private NotificationViewHierarchyManager.StatusBarStateListener mStatusBarStateListener;
     @Nullable private AlertTransferListener mAlertTransferListener;
-
-    private final class NotificationClicker implements View.OnClickListener {
-
-        @Override
-        public void onClick(final View v) {
-            if (!(v instanceof ExpandableNotificationRow)) {
-                Log.e(TAG, "NotificationClicker called on a view that is not a notification row.");
-                return;
-            }
-
-            getShadeController().wakeUpIfDozing(SystemClock.uptimeMillis(), v);
-
-            final ExpandableNotificationRow row = (ExpandableNotificationRow) v;
-            final StatusBarNotification sbn = row.getStatusBarNotification();
-            if (sbn == null) {
-                Log.e(TAG, "NotificationClicker called on an unclickable notification,");
-                return;
-            }
-
-            // Check if the notification is displaying the menu, if so slide notification back
-            if (isMenuVisible(row)) {
-                row.animateTranslateNotification(0);
-                return;
-            } else if (row.isChildInGroup() && isMenuVisible(row.getNotificationParent())) {
-                row.getNotificationParent().animateTranslateNotification(0);
-                return;
-            } else if (row.isSummaryWithChildren() && row.areChildrenExpanded()) {
-                // We never want to open the app directly if the user clicks in between
-                // the notifications.
-                return;
-            }
-
-            // Mark notification for one frame.
-            row.setJustClicked(true);
-            DejankUtils.postAfterTraversal(() -> row.setJustClicked(false));
-
-            // If it was a bubble we should close it
-            if (row.getEntry().isBubble()) {
-                mBubbleController.collapseStack();
-            }
-
-            mNotificationActivityStarter.onNotificationClicked(sbn, row);
-        }
-
-        private boolean isMenuVisible(ExpandableNotificationRow row) {
-            return row.getProvider() != null && row.getProvider().isMenuVisible();
-        }
-
-        public void register(ExpandableNotificationRow row, StatusBarNotification sbn) {
-            Notification notification = sbn.getNotification();
-            if (notification.contentIntent != null || notification.fullScreenIntent != null) {
-                row.setOnClickListener(this);
-            } else {
-                row.setOnClickListener(null);
-            }
-        }
-    }
+    @Nullable private NotificationClicker mNotificationClicker;
 
     private final DeviceProvisionedController.DeviceProvisionedListener
             mDeviceProvisionedListener =
@@ -269,6 +209,10 @@
         mAlertTransferListener = listener;
     }
 
+    public void setNotificationClicker(NotificationClicker clicker) {
+        mNotificationClicker = clicker;
+    }
+
     /**
      * Our dependencies can have cyclic references, so some need to be lazy
      */
@@ -355,11 +299,6 @@
         mOnAppOpsClickListener = mGutsManager::openGuts;
     }
 
-    public void setNotificationActivityStarter(
-            NotificationActivityStarter notificationActivityStarter) {
-        mNotificationActivityStarter = notificationActivityStarter;
-    }
-
     public NotificationData getNotificationData() {
         return mNotificationData;
     }
@@ -757,7 +696,7 @@
         row.setIsLowPriority(isLowPriority);
         row.setLowPriorityStateUpdated(isUpdate && (wasLowPriority != isLowPriority));
         // bind the click event to the content area
-        mNotificationClicker.register(row, sbn);
+        checkNotNull(mNotificationClicker).register(row, sbn);
 
         // Extract target SDK version.
         try {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java
index fce7980..abb7b416 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java
@@ -25,10 +25,14 @@
 
 import java.util.ArrayList;
 
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
 /**
  * A manager that ensures that notifications are visually stable. It will suppress reorderings
  * and reorder at the right time when they are out of view.
  */
+@Singleton
 public class VisualStabilityManager implements OnHeadsUpChangedListener {
 
     private final ArrayList<Callback> mCallbacks =  new ArrayList<>();
@@ -42,6 +46,10 @@
     private ArraySet<View> mAddedChildren = new ArraySet<>();
     private boolean mPulsing;
 
+    @Inject
+    public VisualStabilityManager() {
+    }
+
     /**
      * Add a callback to invoke when reordering is allowed again.
      * @param callback
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
index 9f02e54..eb1fc30 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
@@ -40,10 +40,14 @@
 import java.util.Collection;
 import java.util.Collections;
 
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
 /**
  * Handles notification logging, in particular, logging which notifications are visible and which
  * are not.
  */
+@Singleton
 public class NotificationLogger implements StateListener {
     private static final String TAG = "NotificationLogger";
 
@@ -145,6 +149,7 @@
         }
     };
 
+    @Inject
     public NotificationLogger() {
         mBarService = IStatusBarService.Stub.asInterface(
                 ServiceManager.getService(Context.STATUS_BAR_SERVICE));
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
index 1d79152..8b0a682 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
@@ -800,7 +800,7 @@
     }
 
     @Override
-    public void performRemoveAnimation(long duration, long delay,
+    public long performRemoveAnimation(long duration, long delay,
             float translationDirection, boolean isHeadsUpAnimation, float endLocation,
             Runnable onFinishedRunnable, AnimatorListenerAdapter animationListener) {
         enableAppearDrawing(true);
@@ -812,6 +812,7 @@
         } else if (onFinishedRunnable != null) {
             onFinishedRunnable.run();
         }
+        return 0;
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index 91d08ff..a58c7cd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -2612,6 +2612,29 @@
     }
 
     @Override
+    public long performRemoveAnimation(long duration, long delay, float translationDirection,
+            boolean isHeadsUpAnimation, float endLocation, Runnable onFinishedRunnable,
+            AnimatorListenerAdapter animationListener) {
+        if (mMenuRow.isMenuVisible()) {
+            Animator anim = getTranslateViewAnimator(0f, null /* listener */);
+            if (anim != null) {
+                anim.addListener(new AnimatorListenerAdapter() {
+                    @Override
+                    public void onAnimationEnd(Animator animation) {
+                        ExpandableNotificationRow.super.performRemoveAnimation(
+                                duration, delay, translationDirection, isHeadsUpAnimation,
+                                endLocation, onFinishedRunnable, animationListener);
+                    }
+                });
+                anim.start();
+                return anim.getDuration();
+            }
+        }
+        return super.performRemoveAnimation(duration, delay, translationDirection,
+                isHeadsUpAnimation, endLocation, onFinishedRunnable, animationListener);
+    }
+
+    @Override
     protected void onAppearAnimationFinished(boolean wasAppearing) {
         super.onAppearAnimationFinished(wasAppearing);
         if (wasAppearing) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
index b1fa6a5..d1a89b4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
@@ -312,16 +312,19 @@
      * @param duration The duration of the remove animation.
      * @param delay The delay of the animation
      * @param translationDirection The direction value from [-1 ... 1] indicating in which the
- *                             animation should be performed. A value of -1 means that The
- *                             remove animation should be performed upwards,
- *                             such that the  child appears to be going away to the top. 1
- *                             Should mean the opposite.
+     *                             animation should be performed. A value of -1 means that The
+     *                             remove animation should be performed upwards,
+     *                             such that the  child appears to be going away to the top. 1
+     *                             Should mean the opposite.
      * @param isHeadsUpAnimation Is this a headsUp animation.
      * @param endLocation The location where the horizonal heads up disappear animation should end.
      * @param onFinishedRunnable A runnable which should be run when the animation is finished.
      * @param animationListener An animation listener to add to the animation.
+     *
+     * @return The additional delay, in milliseconds, that this view needs to add before the
+     * animation starts.
      */
-    public abstract void performRemoveAnimation(long duration,
+    public abstract long performRemoveAnimation(long duration,
             long delay, float translationDirection, boolean isHeadsUpAnimation, float endLocation,
             Runnable onFinishedRunnable,
             AnimatorListenerAdapter animationListener);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManager.java
index 16796dd..607d96d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManager.java
@@ -34,10 +34,14 @@
 import java.util.HashSet;
 import java.util.Set;
 
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
 /**
  * Manager for the notification blocking helper - tracks and helps create the blocking helper
  * affordance.
  */
+@Singleton
 public class NotificationBlockingHelperManager {
     /** Enables debug logging and always makes the blocking helper show up after a dismiss. */
     private static final boolean DEBUG = false;
@@ -54,6 +58,7 @@
      */
     private boolean mIsShadeExpanded;
 
+    @Inject
     public NotificationBlockingHelperManager(Context context) {
         mContext = context;
         mNonBlockablePkgs = new HashSet<>();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
index 6bc39c8..02a310c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
@@ -1474,9 +1474,19 @@
         if (mExpandedChild != null) {
             mExpandedSmartReplyView =
                     applySmartReplyView(mExpandedChild, smartRepliesAndActions, entry);
-            if (mExpandedSmartReplyView != null && smartRepliesAndActions.smartReplies != null) {
-                mSmartReplyController.smartRepliesAdded(
-                        entry, smartRepliesAndActions.smartReplies.choices.length);
+            if (mExpandedSmartReplyView != null) {
+                if (smartRepliesAndActions.smartReplies != null
+                        || smartRepliesAndActions.smartActions != null) {
+                    int numSmartReplies = smartRepliesAndActions.smartReplies == null
+                            ? 0 : smartRepliesAndActions.smartReplies.choices.length;
+                    int numSmartActions = smartRepliesAndActions.smartActions == null
+                            ? 0 : smartRepliesAndActions.smartActions.actions.size();
+                    boolean fromAssistant = smartRepliesAndActions.smartReplies == null
+                            ? smartRepliesAndActions.smartActions.fromAssistant
+                            : smartRepliesAndActions.smartReplies.fromAssistant;
+                    mSmartReplyController.smartSuggestionsAdded(entry, numSmartReplies,
+                            numSmartActions, fromAssistant);
+                }
             }
         }
         if (mHeadsUpChild != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
index 2e45527..ac4e583 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
@@ -57,10 +57,14 @@
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
 /**
  * Handles various NotificationGuts related tasks, such as binding guts to a row, opening and
  * closing guts, and keeping track of the currently exposed notification guts.
  */
+@Singleton
 public class NotificationGutsManager implements Dumpable, NotificationLifetimeExtender {
     private static final String TAG = "NotificationGutsManager";
 
@@ -91,6 +95,7 @@
     @VisibleForTesting
     protected String mKeyToRemoveOnGutsClosed;
 
+    @Inject
     public NotificationGutsManager(Context context) {
         mContext = context;
         mAccessibilityManager = (AccessibilityManager)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/StackScrollerDecorView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/StackScrollerDecorView.java
index 1b40c06..eaa2eaf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/StackScrollerDecorView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/StackScrollerDecorView.java
@@ -190,12 +190,13 @@
     }
 
     @Override
-    public void performRemoveAnimation(long duration, long delay,
+    public long performRemoveAnimation(long duration, long delay,
             float translationDirection, boolean isHeadsUpAnimation, float endLocation,
             Runnable onFinishedRunnable,
             AnimatorListenerAdapter animationListener) {
         // TODO: Use duration
         setContentVisible(false);
+        return 0;
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index dbe6e8e..67a5cd9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -16,10 +16,8 @@
 
 package com.android.systemui.statusbar.notification.stack;
 
-import static com.android.systemui.statusbar.notification.ActivityLaunchAnimator
-        .ExpandAnimationParameters;
-import static com.android.systemui.statusbar.notification.stack.StackStateAnimator
-        .ANIMATION_DURATION_SWIPE;
+import static com.android.systemui.statusbar.notification.ActivityLaunchAnimator.ExpandAnimationParameters;
+import static com.android.systemui.statusbar.notification.stack.StackStateAnimator.ANIMATION_DURATION_SWIPE;
 import static com.android.systemui.statusbar.phone.NotificationIconAreaController.LOW_PRIORITY;
 
 import android.animation.Animator;
@@ -651,6 +649,8 @@
                 < mSections[NUM_SECTIONS - 1].getCurrentBounds().bottom
                 || mAmbientState.isDark())) {
             drawBackground(canvas);
+        } else if (mInHeadsUpPinnedMode || mHeadsUpAnimatingAway) {
+            drawHeadsUpBackground(canvas);
         }
 
         if (DEBUG) {
@@ -749,6 +749,32 @@
                 mCornerRadius, mCornerRadius, mBackgroundPaint);
     }
 
+    private void drawHeadsUpBackground(Canvas canvas) {
+        int left = mSidePaddings;
+        int right = getWidth() - mSidePaddings;
+
+        float top = getHeight();
+        float bottom = 0;
+        int childCount = getChildCount();
+        for (int i = 0; i < childCount; i++) {
+            View child = getChildAt(i);
+            if (child.getVisibility() != View.GONE
+                    && child instanceof ExpandableNotificationRow) {
+                ExpandableNotificationRow row = (ExpandableNotificationRow) child;
+                if ((row.isPinned() || row.isHeadsUpAnimatingAway()) && row.getTranslation() < 0) {
+                    top = Math.min(top, row.getTranslationY());
+                    bottom = Math.max(bottom, row.getTranslationY() + row.getActualHeight());
+                }
+            }
+        }
+
+        if (top < bottom) {
+            canvas.drawRoundRect(
+                    left, top, right, bottom,
+                    mCornerRadius, mCornerRadius, mBackgroundPaint);
+        }
+    }
+
     @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     private void updateBackgroundDimming() {
         // No need to update the background color if it's not being drawn.
@@ -2157,19 +2183,7 @@
             }
             return;
         }
-        NotificationSection firstSection = getFirstVisibleSection();
-        int top = 0;
-        if (firstSection != null) {
-            ActivatableNotificationView firstView = firstSection.getFirstVisibleChild();
-            // Round Y up to avoid seeing the background during animation
-            int finalTranslationY = (int) Math.ceil(ViewState.getFinalTranslationY(firstView));
-            if (mAnimateNextBackgroundTop || firstSection.isTargetTop(finalTranslationY)) {
-                // we're ending up at the same location as we are now, lets just skip the animation
-                top = finalTranslationY;
-            } else {
-                top = (int) Math.ceil(firstView.getTranslationY());
-            }
-        }
+        int top = getSectionTopOrFinalTop(getFirstVisibleSection(), mAnimateNextBackgroundTop);
         NotificationSection lastSection = getLastVisibleSection();
         ActivatableNotificationView lastView =
                 mShelf.hasItemsInStableShelf() && mShelf.getVisibility() != GONE
@@ -2177,21 +2191,8 @@
                         : lastSection == null ? null : lastSection.getLastVisibleChild();
         int bottom;
         if (lastView != null) {
-            int finalTranslationY;
-            if (lastView == mShelf) {
-                finalTranslationY = (int) mShelf.getTranslationY();
-            } else {
-                finalTranslationY = (int) ViewState.getFinalTranslationY(lastView);
-            }
-            int finalHeight = ExpandableViewState.getFinalActualHeight(lastView);
-            int finalBottom = finalTranslationY + finalHeight - lastView.getClipBottomAmount();
-            if (mAnimateNextBackgroundBottom || lastSection.isTargetBottom(finalBottom)) {
-                // we're ending up at the same location as we are now, lets just skip the animation
-                bottom = finalBottom;
-            } else {
-                bottom = (int) (lastView.getTranslationY() + lastView.getActualHeight()
-                        - lastView.getClipBottomAmount());
-            }
+            bottom = getSectionBottomOrFinalBottom(
+                    lastSection, lastView, mAnimateNextBackgroundBottom);
         } else {
             top = mTopPadding;
             bottom = top;
@@ -2207,6 +2208,57 @@
         setSectionBoundsByPriority(left, right, top, bottom, mSections[0], mSections[1]);
     }
 
+    private int getSectionTopOrFinalTop(
+            @Nullable NotificationSection section, boolean alreadyAnimating) {
+        int top = 0;
+        if (section != null) {
+            ActivatableNotificationView firstView = section.getFirstVisibleChild();
+            // Round Y up to avoid seeing the background during animation
+            int finalTranslationY = (int) Math.ceil(ViewState.getFinalTranslationY(firstView));
+            if (alreadyAnimating || section.isTargetTop(finalTranslationY)) {
+                // we're ending up at the same location as we are now, let's just skip the animation
+                top = finalTranslationY;
+            } else {
+                top = (int) Math.ceil(firstView.getTranslationY());
+            }
+        }
+        return top;
+    }
+
+    private int getSectionBottomOrFinalBottom(
+            @Nullable NotificationSection section, boolean alreadyAnimating) {
+        return section == null ? 0
+                : getSectionBottomOrFinalBottom(
+                        section, section.getLastVisibleChild(), alreadyAnimating);
+    }
+
+    private int getSectionBottomOrFinalBottom(
+            NotificationSection section,
+            ActivatableNotificationView lastView,
+            boolean alreadyAnimating) {
+        int bottom = 0;
+        if (lastView != null) {
+            float finalTranslationY;
+            if (lastView == mShelf) {
+                finalTranslationY = mShelf.getTranslationY();
+            } else {
+                finalTranslationY = ViewState.getFinalTranslationY(lastView);
+            }
+            int finalHeight = ExpandableViewState.getFinalActualHeight(lastView);
+            // Round Y down to avoid seeing the background during animation
+            int finalBottom = (int) Math.floor(
+                    finalTranslationY + finalHeight - lastView.getClipBottomAmount());
+            if (alreadyAnimating || section.isTargetBottom(finalBottom)) {
+                // we're ending up at the same location as we are now, lets just skip the animation
+                bottom = finalBottom;
+            } else {
+                bottom = (int) (lastView.getTranslationY() + lastView.getActualHeight()
+                        - lastView.getClipBottomAmount());
+            }
+        }
+        return bottom;
+    }
+
     private void setSectionBoundsByPriority(int left, int right, int top, int bottom,
             NotificationSection highPrioritySection, NotificationSection lowPrioritySection) {
         if (NotificationUtils.useNewInterruptionModel(mContext)) {
@@ -2214,13 +2266,14 @@
             ActivatableNotificationView lastChildAboveGap = getLastHighPriorityChild();
             ActivatableNotificationView firstChildBelowGap = getFirstLowPriorityChild();
             if (lastChildAboveGap != null && firstChildBelowGap != null) {
-                int gapTop =
-                        (int) Math.max(top,
-                                Math.min(lastChildAboveGap.getTranslationY()
-                                                + lastChildAboveGap.getActualHeight(),
-                                        bottom));
-                int gapBottom = (int) Math.max(top,
-                        Math.min(firstChildBelowGap.getTranslationY(), bottom));
+                int gapTop = getSectionBottomOrFinalBottom(
+                        highPrioritySection, mAnimateNextSectionBoundsChange);
+                gapTop = Math.max(top, Math.min(gapTop, bottom));
+
+                int gapBottom = getSectionTopOrFinalTop(
+                        lowPrioritySection, mAnimateNextSectionBoundsChange);
+                gapBottom = Math.max(top, Math.min(gapBottom, bottom));
+
                 highPrioritySection.getBounds().set(left, top, right, gapTop);
                 lowPrioritySection.getBounds().set(left, gapBottom, right, bottom);
             } else if (lastChildAboveGap != null) {
@@ -5574,15 +5627,21 @@
             if (translatingParentView != null && row == translatingParentView) {
                 mSwipeHelper.clearExposedMenuView();
                 mSwipeHelper.clearTranslatingParentView();
+                if (row instanceof ExpandableNotificationRow) {
+                    mHeadsUpManager.setMenuShown(
+                            ((ExpandableNotificationRow) row).getEntry(), false);
+
+                }
             }
         }
 
         @Override
         public void onMenuShown(View row) {
             if (row instanceof ExpandableNotificationRow) {
+                ExpandableNotificationRow notificationRow = (ExpandableNotificationRow) row;
                 MetricsLogger.action(mContext, MetricsEvent.ACTION_REVEAL_GEAR,
-                        ((ExpandableNotificationRow) row).getStatusBarNotification()
-                                .getPackageName());
+                        notificationRow.getStatusBarNotification().getPackageName());
+                mHeadsUpManager.setMenuShown(notificationRow.getEntry(), true);
             }
             mSwipeHelper.onMenuShown(row);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
index d690547..19fce48 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
@@ -406,13 +406,8 @@
 
                 }
                 changingView.performRemoveAnimation(ANIMATION_DURATION_APPEAR_DISAPPEAR,
-                        0 /* delay */, translationDirection,  false /* isHeadsUpAppear */,
-                        0, new Runnable() {
-                    @Override
-                    public void run() {
-                        removeTransientView(changingView);
-                    }
-                }, null);
+                        0 /* delay */, translationDirection, false /* isHeadsUpAppear */,
+                        0, () -> removeTransientView(changingView), null);
             } else if (event.animationType ==
                 NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_REMOVE_SWIPED_OUT) {
                 if (Math.abs(changingView.getTranslation()) == changingView.getWidth()
@@ -507,10 +502,11 @@
                     // We need to add the global animation listener, since once no animations are
                     // running anymore, the panel will instantly hide itself. We need to wait until
                     // the animation is fully finished for this though.
-                    changingView.performRemoveAnimation(ANIMATION_DURATION_HEADS_UP_DISAPPEAR
-                                    + ANIMATION_DELAY_HEADS_UP, extraDelay, 0.0f,
-                            true /* isHeadsUpAppear */, targetLocation, endRunnable,
-                            getGlobalAnimationFinishedListener());
+                    long removeAnimationDelay = changingView.performRemoveAnimation(
+                            ANIMATION_DURATION_HEADS_UP_DISAPPEAR + ANIMATION_DELAY_HEADS_UP,
+                            extraDelay, 0.0f, true /* isHeadsUpAppear */, targetLocation,
+                            endRunnable, getGlobalAnimationFinishedListener());
+                    mAnimationProperties.delay += removeAnimationDelay;
                 } else if (endRunnable != null) {
                     endRunnable.run();
                 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
index aa0b7b6..f4cfd41 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
@@ -264,6 +264,17 @@
         }
     }
 
+    /**
+     * Sets whether an entry's menu row is exposed and therefore it should stick in the heads up
+     * area if it's pinned until it's hidden again.
+     */
+    public void setMenuShown(@NonNull NotificationData.Entry entry, boolean menuShown) {
+        HeadsUpEntry headsUpEntry = getHeadsUpEntry(entry.key);
+        if (headsUpEntry instanceof HeadsUpEntryPhone && entry.isRowPinned()) {
+            ((HeadsUpEntryPhone) headsUpEntry).setMenuShownPinned(menuShown);
+        }
+    }
+
     ///////////////////////////////////////////////////////////////////////////////////////////////
     //  HeadsUpManager public methods overrides:
 
@@ -469,6 +480,14 @@
     //  HeadsUpEntryPhone:
 
     protected class HeadsUpEntryPhone extends HeadsUpManager.HeadsUpEntry {
+
+        private boolean mMenuShownPinned;
+
+        @Override
+        protected boolean isSticky() {
+            return super.isSticky() || mMenuShownPinned;
+        }
+
         public void setEntry(@NonNull final NotificationData.Entry entry) {
            Runnable removeHeadsUpRunnable = () -> {
                 if (!mVisualStabilityManager.isReorderingAllowed()) {
@@ -510,6 +529,25 @@
                 updateEntry(false /* updatePostTime */);
             }
         }
+
+        public void setMenuShownPinned(boolean menuShownPinned) {
+            if (mMenuShownPinned == menuShownPinned) {
+                return;
+            }
+
+            mMenuShownPinned = menuShownPinned;
+            if (menuShownPinned) {
+                removeAutoRemovalCallbacks();
+            } else {
+                updateEntry(false /* updatePostTime */);
+            }
+        }
+
+        @Override
+        public void reset() {
+            super.reset();
+            mMenuShownPinned = false;
+        }
     }
 
     public interface AnimationStateHandler {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardDismissUtil.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardDismissUtil.java
index b3d0bf8..e541e14 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardDismissUtil.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardDismissUtil.java
@@ -20,15 +20,23 @@
 
 import com.android.systemui.plugins.ActivityStarter.OnDismissAction;
 
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
 /**
  * Executes actions that require the screen to be unlocked. Delegates the actual handling to an
  * implementation passed via {@link #setDismissHandler}.
  */
+@Singleton
 public class KeyguardDismissUtil implements KeyguardDismissHandler {
     private static final String TAG = "KeyguardDismissUtil";
 
     private volatile KeyguardDismissHandler mDismissHandler;
 
+    @Inject
+    public KeyguardDismissUtil() {
+    }
+
     /** Sets the actual {@link DismissHandler} implementation. */
     public void setDismissHandler(KeyguardDismissHandler dismissHandler) {
         mDismissHandler = dismissHandler;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java
index dd81c4e..6732bbe 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java
@@ -42,11 +42,15 @@
 import java.util.ArrayList;
 import java.util.Objects;
 
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
 /**
  * A helper class dealing with the alert interactions between {@link NotificationGroupManager},
  * {@link HeadsUpManager}, {@link AmbientPulseManager}. In particular, this class deals with keeping
  * the correct notification in a group alerting based off the group suppression.
  */
+@Singleton
 public class NotificationGroupAlertTransferHelper implements OnHeadsUpChangedListener,
         OnAmbientChangedListener, StateListener {
 
@@ -73,6 +77,7 @@
 
     private boolean mIsDozing;
 
+    @Inject
     public NotificationGroupAlertTransferHelper() {
         Dependency.get(StatusBarStateController.class).addCallback(this);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
index 8f4369a..3c1c076 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
@@ -39,9 +39,13 @@
 import java.util.Map;
 import java.util.Objects;
 
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
 /**
  * A class to handle notifications and their corresponding groups.
  */
+@Singleton
 public class NotificationGroupManager implements OnHeadsUpChangedListener,
         OnAmbientChangedListener, StateListener {
 
@@ -54,6 +58,7 @@
     private AmbientPulseManager mAmbientPulseManager = Dependency.get(AmbientPulseManager.class);
     private boolean mIsUpdatingUnchangedGroup;
 
+    @Inject
     public NotificationGroupManager() {
         Dependency.get(StatusBarStateController.class).addCallback(this);
     }
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 75e5cba..1e70912 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -187,6 +187,7 @@
 import com.android.systemui.statusbar.VibratorHelper;
 import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
 import com.android.systemui.statusbar.notification.NotificationActivityStarter;
+import com.android.systemui.statusbar.notification.NotificationClicker;
 import com.android.systemui.statusbar.notification.NotificationData;
 import com.android.systemui.statusbar.notification.NotificationData.Entry;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
@@ -630,8 +631,6 @@
         mBubbleController = Dependency.get(BubbleController.class);
         mBubbleController.setExpandListener(mBubbleExpandListener);
 
-        mGroupAlertTransferHelper.bind(mEntryManager, mGroupManager);
-
         mColorExtractor.addOnColorsChangedListener(this);
         mStatusBarStateController.addCallback(this, StatusBarStateController.RANK_STATUS_BAR);
 
@@ -1018,7 +1017,7 @@
         return new QSFragment();
     }
 
-    protected void setUpPresenter() {
+    private void setUpPresenter() {
         // Set up the initial notification state.
         mActivityLaunchAnimator = new ActivityLaunchAnimator(
                 mStatusBarWindow, this, mNotificationPanel,
@@ -1036,7 +1035,10 @@
         mNotificationActivityStarter = new StatusBarNotificationActivityStarter(
                 mContext, mNotificationPanel, mPresenter, mHeadsUpManager, mActivityLaunchAnimator);
         mGutsManager.setNotificationActivityStarter(mNotificationActivityStarter);
-        mEntryManager.setNotificationActivityStarter(mNotificationActivityStarter);
+
+        mGroupAlertTransferHelper.bind(mEntryManager, mGroupManager);
+        mEntryManager.setNotificationClicker(new NotificationClicker(
+                this, Dependency.get(BubbleController.class), mNotificationActivityStarter));
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
index e7280643..a02c9d5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
@@ -16,8 +16,7 @@
 
 package com.android.systemui.statusbar.policy;
 
-import static com.android.systemui.statusbar.notification.row.NotificationInflater
-        .FLAG_CONTENT_VIEW_HEADS_UP;
+import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_HEADS_UP;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -32,7 +31,6 @@
 import com.android.systemui.R;
 import com.android.systemui.statusbar.AlertingNotificationManager;
 import com.android.systemui.statusbar.notification.NotificationData;
-import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.NotificationInflater.InflationFlag;
 
 import java.io.FileDescriptor;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputQuickSettingsDisabler.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputQuickSettingsDisabler.java
index c2933e1..2a10db6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputQuickSettingsDisabler.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputQuickSettingsDisabler.java
@@ -27,9 +27,13 @@
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.phone.StatusBar;
 
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
 /**
  * Let {@link RemoteInputView} to control the visibility of QuickSetting.
  */
+@Singleton
 public class RemoteInputQuickSettingsDisabler
         implements ConfigurationController.ConfigurationListener {
 
@@ -39,6 +43,7 @@
     private int mLastOrientation;
     @VisibleForTesting CommandQueue mCommandQueue;
 
+    @Inject
     public RemoteInputQuickSettingsDisabler(Context context) {
         mContext = context;
         mCommandQueue = SysUiServiceProvider.getComponent(context, CommandQueue.class);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyConstants.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyConstants.java
index 71d6e54..6193159 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyConstants.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyConstants.java
@@ -16,6 +16,8 @@
 
 package com.android.systemui.statusbar.policy;
 
+import static com.android.systemui.Dependency.MAIN_HANDLER_NAME;
+
 import android.content.Context;
 import android.content.res.Resources;
 import android.database.ContentObserver;
@@ -27,6 +29,11 @@
 
 import com.android.systemui.R;
 
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.inject.Singleton;
+
+@Singleton
 public final class SmartReplyConstants extends ContentObserver {
 
     private static final String TAG = "SmartReplyConstants";
@@ -47,7 +54,8 @@
     private final Context mContext;
     private final KeyValueListParser mParser = new KeyValueListParser(',');
 
-    public SmartReplyConstants(Handler handler, Context context) {
+    @Inject
+    public SmartReplyConstants(@Named(MAIN_HANDLER_NAME) Handler handler, Context context) {
         super(handler);
 
         mContext = context;
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/PluginFragment.java b/packages/SystemUI/src/com/android/systemui/tuner/PluginFragment.java
index dae1472..0a29e04 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/PluginFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/PluginFragment.java
@@ -184,7 +184,11 @@
                         mInfo.services[i].name);
 
                 if (mPluginEnabler.isEnabled(componentName) != isEnabled) {
-                    mPluginEnabler.setEnabled(componentName, isEnabled);
+                    if (isEnabled) {
+                        mPluginEnabler.setEnabled(componentName);
+                    } else {
+                        mPluginEnabler.setDisabled(componentName, PluginEnabler.DISABLED_MANUALLY);
+                    }
                     shouldSendBroadcast = true;
                 }
             }
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java
index 7ca5423..fb2ceac 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java
@@ -35,6 +35,7 @@
 import android.testing.TestableLooper.RunWithLooper;
 import android.text.TextPaint;
 import android.view.LayoutInflater;
+import android.widget.FrameLayout;
 import android.widget.TextClock;
 
 import com.android.systemui.SysuiTestCase;
@@ -51,10 +52,14 @@
 import org.mockito.MockitoAnnotations;
 
 @SmallTest
-@RunWithLooper
 @RunWith(AndroidTestingRunner.class)
+// Need to run on the main thread because KeyguardSliceView$Row init checks for
+// the main thread before acquiring a wake lock. This class is constructed when
+// the keyguard_clcok_switch layout is inflated.
+@RunWithLooper(setAsMainLooper = true)
 public class KeyguardClockSwitchTest extends SysuiTestCase {
     private PluginManager mPluginManager;
+    private FrameLayout mClockContainer;
 
     @Mock
     TextClock mClockView;
@@ -67,6 +72,7 @@
         LayoutInflater layoutInflater = LayoutInflater.from(getContext());
         mKeyguardClockSwitch =
                 (KeyguardClockSwitch) layoutInflater.inflate(R.layout.keyguard_clock_switch, null);
+        mClockContainer = mKeyguardClockSwitch.findViewById(R.id.clock_view);
         MockitoAnnotations.initMocks(this);
         when(mClockView.getPaint()).thenReturn(mock(TextPaint.class));
     }
@@ -97,7 +103,7 @@
         listener.onPluginConnected(plugin, null);
 
         verify(mClockView).setVisibility(GONE);
-        assertThat(plugin.getView().getParent()).isEqualTo(mKeyguardClockSwitch);
+        assertThat(plugin.getView().getParent()).isEqualTo(mClockContainer);
     }
 
     @Test
@@ -120,7 +126,7 @@
         when(plugin2.getView()).thenReturn(new TextClock(getContext()));
         listener.onPluginConnected(plugin2, null);
         // THEN only the view from the second plugin should be a child of KeyguardClockSwitch.
-        assertThat(plugin2.getView().getParent()).isEqualTo(mKeyguardClockSwitch);
+        assertThat(plugin2.getView().getParent()).isEqualTo(mClockContainer);
         assertThat(plugin1.getView().getParent()).isNull();
     }
 
@@ -161,7 +167,7 @@
         // WHEN the first plugin is disconnected
         listener.onPluginDisconnected(plugin1);
         // THEN the view from the second plugin is still a child of KeyguardClockSwitch.
-        assertThat(plugin2.getView().getParent()).isEqualTo(mKeyguardClockSwitch);
+        assertThat(plugin2.getView().getParent()).isEqualTo(mClockContainer);
         assertThat(plugin1.getView().getParent()).isNull();
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/TestableDependency.java b/packages/SystemUI/tests/src/com/android/systemui/TestableDependency.java
index 0c8d137..18bf75e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/TestableDependency.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/TestableDependency.java
@@ -29,9 +29,10 @@
             mComponents = ((SysuiTestableContext) context).getComponents();
         }
         mContext = context;
-        if (SystemUIFactory.getInstance() == null) {
-            SystemUIFactory.createFromConfig(context);
-        }
+        SystemUIFactory.createFromConfig(context);
+        SystemUIFactory.getInstance().getRootComponent()
+                .createDependency()
+                .createSystemUI(this);
         start();
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceManagerTest.java
index 5cc3b3c..4583770 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceManagerTest.java
@@ -17,6 +17,7 @@
 import static junit.framework.Assert.assertFalse;
 import static junit.framework.Assert.assertNotNull;
 import static junit.framework.Assert.assertTrue;
+
 import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.anyInt;
 import static org.mockito.Matchers.eq;
@@ -26,22 +27,6 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
-import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.plugins.Plugin;
-import com.android.systemui.plugins.PluginEnablerImpl;
-import com.android.systemui.plugins.PluginListener;
-import com.android.systemui.shared.plugins.PluginInstanceManager.PluginInfo;
-import com.android.systemui.shared.plugins.VersionInfo.InvalidVersionException;
-import com.android.systemui.plugins.annotations.Requires;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Mockito;
-
 import android.app.Activity;
 import android.app.NotificationManager;
 import android.content.BroadcastReceiver;
@@ -60,6 +45,21 @@
 import android.support.test.runner.AndroidJUnit4;
 import android.test.suitebuilder.annotation.SmallTest;
 
+import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.plugins.Plugin;
+import com.android.systemui.plugins.PluginListener;
+import com.android.systemui.plugins.annotations.Requires;
+import com.android.systemui.shared.plugins.PluginInstanceManager.PluginInfo;
+import com.android.systemui.shared.plugins.VersionInfo.InvalidVersionException;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mockito;
+
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
@@ -79,6 +79,9 @@
     private PluginInstanceManager mPluginInstanceManager;
     private PluginManagerImpl mMockManager;
     private VersionInfo mMockVersionInfo;
+    private PluginEnabler mMockEnabler;
+    ComponentName mTestPluginComponentName =
+            new ComponentName(WHITELISTED_PACKAGE, TestPlugin.class.getName());
 
     @Before
     public void setup() throws Exception {
@@ -88,9 +91,9 @@
         mMockPm = mock(PackageManager.class);
         mMockListener = mock(PluginListener.class);
         mMockManager = mock(PluginManagerImpl.class);
-        when(mMockManager.getClassLoader(any(), any()))
-                .thenReturn(getClass().getClassLoader());
-        when(mMockManager.getPluginEnabler()).thenReturn(new PluginEnablerImpl(mMockPm));
+        when(mMockManager.getClassLoader(any(), any())).thenReturn(getClass().getClassLoader());
+        mMockEnabler = mock(PluginEnabler.class);
+        when(mMockManager.getPluginEnabler()).thenReturn(mMockEnabler);
         mMockVersionInfo = mock(VersionInfo.class);
         mPluginInstanceManager = new PluginInstanceManager(mContextWrapper, mMockPm, "myAction",
                 mMockListener, true, mHandlerThread.getLooper(), mMockVersionInfo,
@@ -230,18 +233,13 @@
         // Start with an unrelated class.
         boolean result = mPluginInstanceManager.checkAndDisable(Activity.class.getName());
         assertFalse(result);
-        verify(mMockPm, never()).setComponentEnabledSetting(
-                ArgumentCaptor.forClass(ComponentName.class).capture(),
-                ArgumentCaptor.forClass(int.class).capture(),
-                ArgumentCaptor.forClass(int.class).capture());
+        verify(mMockEnabler, never()).setDisabled(any(ComponentName.class), anyInt());
 
         // Now hand it a real class and make sure it disables the plugin.
         result = mPluginInstanceManager.checkAndDisable(TestPlugin.class.getName());
         assertTrue(result);
-        verify(mMockPm).setComponentEnabledSetting(
-                ArgumentCaptor.forClass(ComponentName.class).capture(),
-                ArgumentCaptor.forClass(int.class).capture(),
-                ArgumentCaptor.forClass(int.class).capture());
+        verify(mMockEnabler).setDisabled(
+                mTestPluginComponentName, PluginEnabler.DISABLED_FROM_EXPLICIT_CRASH);
     }
 
     @Test
@@ -250,10 +248,8 @@
 
         mPluginInstanceManager.disableAll();
 
-        verify(mMockPm).setComponentEnabledSetting(
-                ArgumentCaptor.forClass(ComponentName.class).capture(),
-                ArgumentCaptor.forClass(int.class).capture(),
-                ArgumentCaptor.forClass(int.class).capture());
+        verify(mMockEnabler).setDisabled(
+                mTestPluginComponentName, PluginEnabler.DISABLED_FROM_SYSTEM_CRASH);
     }
 
     @Test
@@ -275,8 +271,8 @@
         List<ResolveInfo> list = new ArrayList<>();
         ResolveInfo info = new ResolveInfo();
         info.serviceInfo = mock(ServiceInfo.class);
-        info.serviceInfo.packageName = "com.android.systemui";
-        info.serviceInfo.name = TestPlugin.class.getName();
+        info.serviceInfo.packageName = mTestPluginComponentName.getPackageName();
+        info.serviceInfo.name = mTestPluginComponentName.getClassName();
         when(info.serviceInfo.loadLabel(any())).thenReturn("Test Plugin");
         list.add(info);
         when(mMockPm.queryIntentServices(any(), Mockito.anyInt())).thenReturn(list);
@@ -288,6 +284,7 @@
         ApplicationInfo appInfo = getContext().getApplicationInfo();
         when(mMockPm.getApplicationInfo(Mockito.anyString(), Mockito.anyInt())).thenReturn(
                 appInfo);
+        when(mMockEnabler.isEnabled(mTestPluginComponentName)).thenReturn(true);
     }
 
     private void createPlugin() throws Exception {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginManagerTest.java
index ff1bc8ab..76e68f1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginManagerTest.java
@@ -27,7 +27,6 @@
 import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.net.Uri;
-import android.support.test.runner.AndroidJUnit4;
 import android.test.suitebuilder.annotation.SmallTest;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
@@ -82,17 +81,18 @@
                 .thenReturn(mMockPluginInstance);
 
         mMockPackageManager = mock(PackageManager.class);
-        mPluginManager = new PluginManagerImpl(getContext(), mMockFactory, true,
+        mPluginManager = new PluginManagerImpl(
+                getContext(), mMockFactory, true,
                 mMockExceptionHandler, new PluginInitializerImpl() {
-            @Override
-            public String[] getWhitelistedPlugins(Context context) {
-                return new String[0];
-            }
+                    @Override
+                    public String[] getWhitelistedPlugins(Context context) {
+                        return new String[0];
+                    }
 
-            @Override
-            public PluginEnabler getPluginEnabler(Context context) {
-                return new PluginEnablerImpl(mMockPackageManager);
-            }
+                    @Override
+                    public PluginEnabler getPluginEnabler(Context context) {
+                        return new PluginEnablerImpl(context, mMockPackageManager);
+                    }
         });
         resetExceptionHandler();
         mMockListener = mock(PluginListener.class);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/SmartReplyControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/SmartReplyControllerTest.java
index 8d52ccd..14e611a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/SmartReplyControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/SmartReplyControllerTest.java
@@ -51,6 +51,7 @@
     private static final String TEST_CHOICE_TEXT = "A Reply";
     private static final int TEST_CHOICE_INDEX = 2;
     private static final int TEST_CHOICE_COUNT = 4;
+    private static final int TEST_ACTION_COUNT = 3;
 
     private Notification mNotification;
     private NotificationData.Entry mEntry;
@@ -117,12 +118,14 @@
     }
 
     @Test
-    public void testShowSmartReply_logsToStatusBar() throws RemoteException {
-        mSmartReplyController.smartRepliesAdded(mEntry, TEST_CHOICE_COUNT);
+    public void testShowSmartSuggestions_logsToStatusBar() throws RemoteException {
+        final boolean generatedByAsssistant = true;
+        mSmartReplyController.smartSuggestionsAdded(mEntry, TEST_CHOICE_COUNT, TEST_ACTION_COUNT,
+                generatedByAsssistant);
 
         // Check we log the result to the status bar service.
-        verify(mIStatusBarService).onNotificationSmartRepliesAdded(mSbn.getKey(),
-                TEST_CHOICE_COUNT);
+        verify(mIStatusBarService).onNotificationSmartSuggestionsAdded(mSbn.getKey(),
+                TEST_CHOICE_COUNT, TEST_ACTION_COUNT, generatedByAsssistant);
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
index 904e5b9..7f0e435 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
@@ -231,6 +231,7 @@
         mEntryManager = new TestableNotificationEntryManager(mContext, mBarService);
         Dependency.get(InitController.class).executePostInitTasks();
         mEntryManager.setUpWithPresenter(mPresenter, mListContainer, mCallback, mHeadsUpManager);
+        mEntryManager.setNotificationClicker(mock(NotificationClicker.class));
 
         setUserSentiment(mEntry.key, NotificationListenerService.Ranking.USER_SENTIMENT_NEUTRAL);
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
index c207fef..e5620a5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
@@ -176,6 +176,7 @@
         mDependency.injectTestDependency(MetricsLogger.class, mMetricsLogger);
         mDependency.injectTestDependency(NotificationLogger.class, mNotificationLogger);
         mNotificationLogger = new NotificationLogger();
+        DozeLog.traceDozing(mContext, false /* dozing */);
 
         IPowerManager powerManagerService = mock(IPowerManager.class);
         mPowerManager = new PowerManager(mContext, powerManagerService,
diff --git a/packages/overlays/AccentColorBlackOverlay/Android.mk b/packages/overlays/AccentColorBlackOverlay/Android.mk
index d316fbd..b81ae5bb 100644
--- a/packages/overlays/AccentColorBlackOverlay/Android.mk
+++ b/packages/overlays/AccentColorBlackOverlay/Android.mk
@@ -19,6 +19,7 @@
 
 LOCAL_RRO_THEME := AccentColorBlack
 LOCAL_CERTIFICATE := platform
+LOCAL_PRODUCT_MODULE := true
 
 LOCAL_SRC_FILES := $(call all-subdir-java-files)
 
diff --git a/packages/overlays/AccentColorGreenOverlay/Android.mk b/packages/overlays/AccentColorGreenOverlay/Android.mk
index afc4287..db92157 100644
--- a/packages/overlays/AccentColorGreenOverlay/Android.mk
+++ b/packages/overlays/AccentColorGreenOverlay/Android.mk
@@ -19,6 +19,7 @@
 
 LOCAL_RRO_THEME := AccentColorGreen
 LOCAL_CERTIFICATE := platform
+LOCAL_PRODUCT_MODULE := true
 
 LOCAL_SRC_FILES := $(call all-subdir-java-files)
 
diff --git a/packages/overlays/AccentColorPurpleOverlay/Android.mk b/packages/overlays/AccentColorPurpleOverlay/Android.mk
index 3366169..d7dc497 100644
--- a/packages/overlays/AccentColorPurpleOverlay/Android.mk
+++ b/packages/overlays/AccentColorPurpleOverlay/Android.mk
@@ -19,6 +19,7 @@
 
 LOCAL_RRO_THEME := AccentColorPurple
 LOCAL_CERTIFICATE := platform
+LOCAL_PRODUCT_MODULE := true
 
 LOCAL_SRC_FILES := $(call all-subdir-java-files)
 
diff --git a/packages/overlays/CleanSpec.mk b/packages/overlays/CleanSpec.mk
new file mode 100644
index 0000000..16fbaa20
--- /dev/null
+++ b/packages/overlays/CleanSpec.mk
@@ -0,0 +1,53 @@
+# 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.
+#
+
+# If you don't need to do a full clean build but would like to touch
+# a file or delete some intermediate files, add a clean step to the end
+# of the list.  These steps will only be run once, if they haven't been
+# run before.
+#
+# E.g.:
+#     $(call add-clean-step, touch -c external/sqlite/sqlite3.h)
+#     $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/STATIC_LIBRARIES/libz_intermediates)
+#
+# Always use "touch -c" and "rm -f" or "rm -rf" to gracefully deal with
+# files that are missing or have been moved.
+#
+# Use $(PRODUCT_OUT) to get to the "out/target/product/blah/" directory.
+# Use $(OUT_DIR) to refer to the "out" directory.
+#
+# If you need to re-do something that's already mentioned, just copy
+# the command and add it to the bottom of the list.  E.g., if a change
+# that you made last week required touching a file and a change you
+# made today requires touching the same file, just copy the old
+# touch step and add it to the end of the list.
+#
+# ************************************************
+# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
+# ************************************************
+
+# For example:
+#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/AndroidTests_intermediates)
+#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/core_intermediates)
+#$(call add-clean-step, find $(OUT_DIR) -type f -name "IGTalkSession*" -print0 | xargs -0 rm -f)
+#$(call add-clean-step, rm -rf $(PRODUCT_OUT)/data/*)
+
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/vendor/overlay/AccentColor*)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/vendor/overlay/DisplayCutout*)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/vendor/overlay/IconShape*)
+
+# ************************************************
+# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
+# ************************************************
diff --git a/packages/overlays/DisplayCutoutEmulationCornerOverlay/Android.mk b/packages/overlays/DisplayCutoutEmulationCornerOverlay/Android.mk
index 74c43b4..bf2b631 100644
--- a/packages/overlays/DisplayCutoutEmulationCornerOverlay/Android.mk
+++ b/packages/overlays/DisplayCutoutEmulationCornerOverlay/Android.mk
@@ -4,6 +4,8 @@
 LOCAL_RRO_THEME := DisplayCutoutEmulationCorner
 LOCAL_CERTIFICATE := platform
 
+LOCAL_PRODUCT_MODULE := true
+
 LOCAL_SRC_FILES := $(call all-subdir-java-files)
 
 LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
diff --git a/packages/overlays/DisplayCutoutEmulationDoubleOverlay/Android.mk b/packages/overlays/DisplayCutoutEmulationDoubleOverlay/Android.mk
index d83b30a..7042906 100644
--- a/packages/overlays/DisplayCutoutEmulationDoubleOverlay/Android.mk
+++ b/packages/overlays/DisplayCutoutEmulationDoubleOverlay/Android.mk
@@ -4,6 +4,8 @@
 LOCAL_RRO_THEME := DisplayCutoutEmulationDouble
 LOCAL_CERTIFICATE := platform
 
+LOCAL_PRODUCT_MODULE := true
+
 LOCAL_SRC_FILES := $(call all-subdir-java-files)
 
 LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
diff --git a/packages/overlays/DisplayCutoutEmulationNarrowOverlay/Android.mk b/packages/overlays/DisplayCutoutEmulationNarrowOverlay/Android.mk
index f5afad2..ae69e11 100644
--- a/packages/overlays/DisplayCutoutEmulationNarrowOverlay/Android.mk
+++ b/packages/overlays/DisplayCutoutEmulationNarrowOverlay/Android.mk
@@ -4,6 +4,8 @@
 LOCAL_RRO_THEME := DisplayCutoutEmulationNarrow
 LOCAL_CERTIFICATE := platform
 
+LOCAL_PRODUCT_MODULE := true
+
 LOCAL_SRC_FILES := $(call all-subdir-java-files)
 
 LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
diff --git a/packages/overlays/DisplayCutoutEmulationTallOverlay/Android.mk b/packages/overlays/DisplayCutoutEmulationTallOverlay/Android.mk
index f1f8c27..7dcadfb 100644
--- a/packages/overlays/DisplayCutoutEmulationTallOverlay/Android.mk
+++ b/packages/overlays/DisplayCutoutEmulationTallOverlay/Android.mk
@@ -4,6 +4,8 @@
 LOCAL_RRO_THEME := DisplayCutoutEmulationTall
 LOCAL_CERTIFICATE := platform
 
+LOCAL_PRODUCT_MODULE := true
+
 LOCAL_SRC_FILES := $(call all-subdir-java-files)
 
 LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
diff --git a/packages/overlays/DisplayCutoutEmulationWideOverlay/Android.mk b/packages/overlays/DisplayCutoutEmulationWideOverlay/Android.mk
index d149d8e..3f7be73 100644
--- a/packages/overlays/DisplayCutoutEmulationWideOverlay/Android.mk
+++ b/packages/overlays/DisplayCutoutEmulationWideOverlay/Android.mk
@@ -4,6 +4,8 @@
 LOCAL_RRO_THEME := DisplayCutoutEmulationWide
 LOCAL_CERTIFICATE := platform
 
+LOCAL_PRODUCT_MODULE := true
+
 LOCAL_SRC_FILES := $(call all-subdir-java-files)
 
 LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
diff --git a/packages/overlays/IconShapeRoundedRectOverlay/Android.mk b/packages/overlays/IconShapeRoundedRectOverlay/Android.mk
index a734a6b..08428d1 100644
--- a/packages/overlays/IconShapeRoundedRectOverlay/Android.mk
+++ b/packages/overlays/IconShapeRoundedRectOverlay/Android.mk
@@ -19,6 +19,7 @@
 
 LOCAL_RRO_THEME := IconShapeRoundedRect
 LOCAL_CERTIFICATE := platform
+LOCAL_PRODUCT_MODULE := true
 
 LOCAL_SRC_FILES := $(call all-subdir-java-files)
 
diff --git a/packages/overlays/IconShapeSquareOverlay/Android.mk b/packages/overlays/IconShapeSquareOverlay/Android.mk
index 217da9f..ceb745a 100644
--- a/packages/overlays/IconShapeSquareOverlay/Android.mk
+++ b/packages/overlays/IconShapeSquareOverlay/Android.mk
@@ -19,6 +19,7 @@
 
 LOCAL_RRO_THEME := IconShapeSquare
 LOCAL_CERTIFICATE := platform
+LOCAL_PRODUCT_MODULE := true
 
 LOCAL_SRC_FILES := $(call all-subdir-java-files)
 
diff --git a/packages/overlays/IconShapeSquircleOverlay/Android.mk b/packages/overlays/IconShapeSquircleOverlay/Android.mk
index fd3bfa0..34edc3b 100644
--- a/packages/overlays/IconShapeSquircleOverlay/Android.mk
+++ b/packages/overlays/IconShapeSquircleOverlay/Android.mk
@@ -19,6 +19,7 @@
 
 LOCAL_RRO_THEME := IconShapeSquircle
 LOCAL_CERTIFICATE := platform
+LOCAL_PRODUCT_MODULE := true
 
 LOCAL_SRC_FILES := $(call all-subdir-java-files)
 
diff --git a/packages/overlays/IconShapeTeardropOverlay/Android.mk b/packages/overlays/IconShapeTeardropOverlay/Android.mk
index ea43423..834a1c3 100644
--- a/packages/overlays/IconShapeTeardropOverlay/Android.mk
+++ b/packages/overlays/IconShapeTeardropOverlay/Android.mk
@@ -19,6 +19,7 @@
 
 LOCAL_RRO_THEME := IconShapeTeardrop
 LOCAL_CERTIFICATE := platform
+LOCAL_PRODUCT_MODULE := true
 
 LOCAL_SRC_FILES := $(call all-subdir-java-files)
 
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..fd20437 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(
+                        userId, 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..eb10a04 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 {
@@ -274,7 +284,7 @@
     }
 
     @Override
-    public void dataChanged(String packageName) throws RemoteException {
+    public void dataChangedForUser(int userId, String packageName) throws RemoteException {
         BackupManagerService svc = mService;
         if (svc != null) {
             svc.dataChanged(packageName);
@@ -282,8 +292,13 @@
     }
 
     @Override
-    public void initializeTransports(String[] transportNames, IBackupObserver observer)
-            throws RemoteException {
+    public void dataChanged(String packageName) throws RemoteException {
+        dataChangedForUser(binderGetCallingUserId(), packageName);
+    }
+
+    @Override
+    public void initializeTransportsForUser(
+            int userId, String[] transportNames, IBackupObserver observer) throws RemoteException {
         BackupManagerService svc = mService;
         if (svc != null) {
             svc.initializeTransports(transportNames, observer);
@@ -291,7 +306,7 @@
     }
 
     @Override
-    public void clearBackupData(String transportName, String packageName)
+    public void clearBackupDataForUser(int userId, String transportName, String packageName)
             throws RemoteException {
         BackupManagerService svc = mService;
         if (svc != null) {
@@ -300,7 +315,14 @@
     }
 
     @Override
-    public void agentConnected(String packageName, IBinder agent) throws RemoteException {
+    public void clearBackupData(String transportName, String packageName)
+            throws RemoteException {
+        clearBackupDataForUser(binderGetCallingUserId(), transportName, packageName);
+    }
+
+    @Override
+    public void agentConnectedForUser(int userId, String packageName, IBinder agent)
+            throws RemoteException {
         BackupManagerService svc = mService;
         if (svc != null) {
             svc.agentConnected(packageName, agent);
@@ -308,7 +330,12 @@
     }
 
     @Override
-    public void agentDisconnected(String packageName) throws RemoteException {
+    public void agentConnected(String packageName, IBinder agent) throws RemoteException {
+        agentConnectedForUser(binderGetCallingUserId(), packageName, agent);
+    }
+
+    @Override
+    public void agentDisconnectedForUser(int userId, String packageName) throws RemoteException {
         BackupManagerService svc = mService;
         if (svc != null) {
             svc.agentDisconnected(packageName);
@@ -316,7 +343,13 @@
     }
 
     @Override
-    public void restoreAtInstall(String packageName, int token) throws RemoteException {
+    public void agentDisconnected(String packageName) throws RemoteException {
+        agentDisconnectedForUser(binderGetCallingUserId(), packageName);
+    }
+
+    @Override
+    public void restoreAtInstallForUser(int userId, String packageName, int token)
+            throws RemoteException {
         BackupManagerService svc = mService;
         if (svc != null) {
             svc.restoreAtInstall(packageName, token);
@@ -324,6 +357,11 @@
     }
 
     @Override
+    public void restoreAtInstall(String packageName, int token) throws RemoteException {
+        restoreAtInstallForUser(binderGetCallingUserId(), packageName, token);
+    }
+
+    @Override
     public void setBackupEnabledForUser(@UserIdInt int userId, boolean isEnabled)
             throws RemoteException {
         BackupManagerService svc = mService;
@@ -338,7 +376,7 @@
     }
 
     @Override
-    public void setAutoRestore(boolean doAutoRestore) throws RemoteException {
+    public void setAutoRestoreForUser(int userId, boolean doAutoRestore) throws RemoteException {
         BackupManagerService svc = mService;
         if (svc != null) {
             svc.setAutoRestore(doAutoRestore);
@@ -346,6 +384,11 @@
     }
 
     @Override
+    public void setAutoRestore(boolean doAutoRestore) throws RemoteException {
+        setAutoRestoreForUser(binderGetCallingUserId(), doAutoRestore);
+    }
+
+    @Override
     public void setBackupProvisioned(boolean isProvisioned) throws RemoteException {
         BackupManagerService svc = mService;
         if (svc != null) {
@@ -401,7 +444,8 @@
     }
 
     @Override
-    public void fullTransportBackup(String[] packageNames) throws RemoteException {
+    public void fullTransportBackupForUser(int userId, String[] packageNames)
+            throws RemoteException {
         BackupManagerService svc = mService;
         if (svc != null) {
             svc.fullTransportBackup(packageNames);
@@ -417,9 +461,14 @@
     }
 
     @Override
-    public void acknowledgeFullBackupOrRestore(int token, boolean allow, String curPassword,
-            String encryptionPassword, IFullBackupRestoreObserver observer)
-                    throws RemoteException {
+    public void acknowledgeFullBackupOrRestoreForUser(
+            int userId,
+            int token,
+            boolean allow,
+            String curPassword,
+            String encryptionPassword,
+            IFullBackupRestoreObserver observer)
+            throws RemoteException {
         BackupManagerService svc = mService;
         if (svc != null) {
             svc.acknowledgeAdbBackupOrRestore(token, allow,
@@ -428,30 +477,50 @@
     }
 
     @Override
-    public String getCurrentTransport() throws RemoteException {
+    public void acknowledgeFullBackupOrRestore(int token, boolean allow, String curPassword,
+            String encryptionPassword, IFullBackupRestoreObserver observer)
+                    throws RemoteException {
+        BackupManagerService svc = mService;
+        acknowledgeFullBackupOrRestoreForUser(
+                binderGetCallingUserId(), token, allow, curPassword, encryptionPassword, observer);
+    }
+
+
+    @Override
+    public String getCurrentTransportForUser(int userId) throws RemoteException {
         BackupManagerService svc = mService;
         return (svc != null) ? svc.getCurrentTransport() : null;
     }
 
+    @Override
+    public String getCurrentTransport() throws RemoteException {
+        return getCurrentTransportForUser(binderGetCallingUserId());
+    }
+
     /**
      * 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.
      */
     @Override
     @Nullable
-    public ComponentName getCurrentTransportComponent() {
+    public ComponentName getCurrentTransportComponentForUser(int userId) {
         BackupManagerService svc = mService;
         return (svc != null) ? svc.getCurrentTransportComponent() : null;
     }
 
     @Override
-    public String[] listAllTransports() throws RemoteException {
+    public String[] listAllTransportsForUser(int userId) throws RemoteException {
         BackupManagerService svc = mService;
         return (svc != null) ? svc.listAllTransports() : null;
     }
 
     @Override
-    public ComponentName[] listAllTransportComponents() throws RemoteException {
+    public String[] listAllTransports() throws RemoteException {
+        return listAllTransportsForUser(binderGetCallingUserId());
+    }
+
+    @Override
+    public ComponentName[] listAllTransportComponentsForUser(int userId) throws RemoteException {
         BackupManagerService svc = mService;
         return (svc != null) ? svc.listAllTransportComponents() : null;
     }
@@ -463,7 +532,8 @@
     }
 
     @Override
-    public void updateTransportAttributes(
+    public void updateTransportAttributesForUser(
+            int userId,
             ComponentName transportComponent,
             String name,
             @Nullable Intent configurationIntent,
@@ -483,13 +553,19 @@
     }
 
     @Override
-    public String selectBackupTransport(String transport) throws RemoteException {
+    public String selectBackupTransportForUser(int userId, String transport)
+            throws RemoteException {
         BackupManagerService svc = mService;
         return (svc != null) ? svc.selectBackupTransport(transport) : null;
     }
 
     @Override
-    public void selectBackupTransportAsync(ComponentName transport,
+    public String selectBackupTransport(String transport) throws RemoteException {
+        return selectBackupTransportForUser(binderGetCallingUserId(), transport);
+    }
+
+    @Override
+    public void selectBackupTransportAsyncForUser(int userId, ComponentName transport,
             ISelectBackupTransportCallback listener) throws RemoteException {
         BackupManagerService svc = mService;
         if (svc != null) {
@@ -506,32 +582,58 @@
     }
 
     @Override
-    public Intent getConfigurationIntent(String transport) throws RemoteException {
+    public Intent getConfigurationIntentForUser(int userId, String transport)
+            throws RemoteException {
         BackupManagerService svc = mService;
         return (svc != null) ? svc.getConfigurationIntent(transport) : null;
     }
 
     @Override
-    public String getDestinationString(String transport) throws RemoteException {
+    public Intent getConfigurationIntent(String transport)
+            throws RemoteException {
+        return getConfigurationIntentForUser(binderGetCallingUserId(), transport);
+    }
+
+    @Override
+    public String getDestinationStringForUser(int userId, String transport) throws RemoteException {
         BackupManagerService svc = mService;
         return (svc != null) ? svc.getDestinationString(transport) : null;
     }
 
     @Override
-    public Intent getDataManagementIntent(String transport) throws RemoteException {
+    public String getDestinationString(String transport) throws RemoteException {
+        return getDestinationStringForUser(binderGetCallingUserId(), transport);
+    }
+
+    @Override
+    public Intent getDataManagementIntentForUser(int userId, String transport)
+            throws RemoteException {
         BackupManagerService svc = mService;
         return (svc != null) ? svc.getDataManagementIntent(transport) : null;
     }
 
     @Override
-    public String getDataManagementLabel(String transport) throws RemoteException {
+    public Intent getDataManagementIntent(String transport)
+            throws RemoteException {
+        return getDataManagementIntentForUser(binderGetCallingUserId(), transport);
+    }
+
+    @Override
+    public String getDataManagementLabelForUser(int userId, String transport)
+            throws RemoteException {
         BackupManagerService svc = mService;
         return (svc != null) ? svc.getDataManagementLabel(transport) : null;
     }
 
     @Override
-    public IRestoreSession beginRestoreSession(String packageName, String transportID)
+    public String getDataManagementLabel(String transport)
             throws RemoteException {
+        return getDataManagementLabelForUser(binderGetCallingUserId(), transport);
+    }
+
+    @Override
+    public IRestoreSession beginRestoreSessionForUser(
+            int userId, String packageName, String transportID) throws RemoteException {
         BackupManagerService svc = mService;
         return (svc != null) ? svc.beginRestoreSession(packageName, transportID) : null;
     }
@@ -545,19 +647,19 @@
     }
 
     @Override
-    public long getAvailableRestoreToken(String packageName) {
+    public long getAvailableRestoreTokenForUser(int userId, String packageName) {
         BackupManagerService svc = mService;
         return (svc != null) ? svc.getAvailableRestoreToken(packageName) : 0;
     }
 
     @Override
-    public boolean isAppEligibleForBackup(String packageName) {
+    public boolean isAppEligibleForBackupForUser(int userId, String packageName) {
         BackupManagerService svc = mService;
         return (svc != null) ? svc.isAppEligibleForBackup(packageName) : false;
     }
 
     @Override
-    public String[] filterAppsEligibleForBackup(String[] packages) {
+    public String[] filterAppsEligibleForBackupForUser(int userId, String[] packages) {
         BackupManagerService svc = mService;
         return (svc != null) ? svc.filterAppsEligibleForBackup(packages) : null;
     }
diff --git a/services/backup/java/com/android/server/backup/UserBackupManagerService.java b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
index 796ef40..d357404 100644
--- a/services/backup/java/com/android/server/backup/UserBackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
@@ -251,6 +251,7 @@
     private static final long BUSY_BACKOFF_MIN_MILLIS = 1000 * 60 * 60;  // one hour
     private static final int BUSY_BACKOFF_FUZZ = 1000 * 60 * 60 * 2;  // two hours
 
+    private final @UserIdInt int mUserId;
     private final BackupAgentTimeoutParameters mAgentTimeoutParameters;
     private final TransportManager mTransportManager;
 
@@ -372,10 +373,11 @@
      * Creates an instance of {@link UserBackupManagerService} and initializes state for it. This
      * includes setting up the directories where we keep our bookkeeping and transport management.
      *
-     * @see #createAndInitializeService(Context, Trampoline, HandlerThread, File, File,
+     * @see #createAndInitializeService(int, Context, Trampoline, HandlerThread, File, File,
      *     TransportManager)
      */
     static UserBackupManagerService createAndInitializeService(
+            @UserIdInt int userId,
             Context context,
             Trampoline trampoline,
             HandlerThread backupThread,
@@ -399,12 +401,13 @@
         File dataDir = new File(Environment.getDownloadCacheDirectory(), BACKUP_STAGING_DIR);
 
         return createAndInitializeService(
-                context, trampoline, backupThread, baseStateDir, dataDir, transportManager);
+                userId, context, trampoline, backupThread, baseStateDir, dataDir, transportManager);
     }
 
     /**
      * Creates an instance of {@link UserBackupManagerService}.
      *
+     * @param userId The user which this service is for.
      * @param context The system server context.
      * @param trampoline A reference to the proxy to {@link BackupManagerService}.
      * @param backupThread The thread running backup/restore operations for the user.
@@ -415,6 +418,7 @@
      */
     @VisibleForTesting
     public static UserBackupManagerService createAndInitializeService(
+            @UserIdInt int userId,
             Context context,
             Trampoline trampoline,
             HandlerThread backupThread,
@@ -422,16 +426,18 @@
             File dataDir,
             TransportManager transportManager) {
         return new UserBackupManagerService(
-                context, trampoline, backupThread, baseStateDir, dataDir, transportManager);
+                userId, context, trampoline, backupThread, baseStateDir, dataDir, transportManager);
     }
 
     private UserBackupManagerService(
+            @UserIdInt int userId,
             Context context,
             Trampoline parent,
             HandlerThread backupThread,
             File baseStateDir,
             File dataDir,
             TransportManager transportManager) {
+        mUserId = userId;
         mContext = checkNotNull(context, "context cannot be null");
         mPackageManager = context.getPackageManager();
         mPackageManagerBinder = AppGlobals.getPackageManager();
diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
index 10e713d..e8820ae 100644
--- a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
+++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
@@ -25,6 +25,7 @@
 import android.app.ActivityManagerInternal;
 import android.content.ComponentName;
 import android.content.Context;
+import android.os.Binder;
 import android.os.Bundle;
 import android.os.IBinder;
 import android.os.RemoteException;
@@ -34,7 +35,6 @@
 import android.os.UserManager;
 import android.util.Slog;
 import android.view.contentcapture.ContentCaptureContext;
-import android.view.contentcapture.ContentCaptureEvent;
 import android.view.contentcapture.IContentCaptureManager;
 
 import com.android.internal.annotations.GuardedBy;
@@ -47,7 +47,6 @@
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.ArrayList;
-import java.util.List;
 
 /**
  * A service used to observe the contents of the screen.
@@ -182,30 +181,18 @@
             synchronized (mLock) {
                 final ContentCapturePerUserService service = getServiceForUserLocked(userId);
                 service.startSessionLocked(activityToken, componentName, taskId, displayId,
-                        sessionId, clientContext, flags, mAllowInstantService, result);
+                        sessionId, Binder.getCallingUid(), clientContext, flags,
+                        mAllowInstantService, result);
             }
         }
 
         @Override
-        public void sendEvents(@UserIdInt int userId, @NonNull String sessionId,
-                @NonNull List<ContentCaptureEvent> events) {
-            Preconditions.checkNotNull(sessionId);
-            Preconditions.checkNotNull(events);
-
-            synchronized (mLock) {
-                final ContentCapturePerUserService service = getServiceForUserLocked(userId);
-                service.sendEventsLocked(sessionId, events);
-            }
-        }
-
-        @Override
-        public void finishSession(@UserIdInt int userId, @NonNull String sessionId,
-                @Nullable List<ContentCaptureEvent> events) {
+        public void finishSession(@UserIdInt int userId, @NonNull String sessionId) {
             Preconditions.checkNotNull(sessionId);
 
             synchronized (mLock) {
                 final ContentCapturePerUserService service = getServiceForUserLocked(userId);
-                service.finishSessionLocked(sessionId, events);
+                service.finishSessionLocked(sessionId);
             }
         }
 
diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java
index 8130912..f21b0d8 100644
--- a/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java
+++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java
@@ -16,6 +16,8 @@
 
 package com.android.server.contentcapture;
 
+import static android.service.contentcapture.ContentCaptureService.setClientState;
+
 import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_CONTENT;
 import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_DATA;
 import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_STRUCTURE;
@@ -38,7 +40,6 @@
 import android.util.ArrayMap;
 import android.util.Slog;
 import android.view.contentcapture.ContentCaptureContext;
-import android.view.contentcapture.ContentCaptureEvent;
 import android.view.contentcapture.ContentCaptureSession;
 
 import com.android.internal.annotations.GuardedBy;
@@ -48,7 +49,6 @@
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
-import java.util.List;
 
 /**
  * Per-user instance of {@link ContentCaptureManagerService}.
@@ -114,10 +114,10 @@
     @GuardedBy("mLock")
     public void startSessionLocked(@NonNull IBinder activityToken,
             @NonNull ComponentName componentName, int taskId, int displayId,
-            @NonNull String sessionId, @Nullable ContentCaptureContext clientContext,
-            int flags, boolean bindInstantServiceAllowed, @NonNull IResultReceiver resultReceiver) {
+            @NonNull String sessionId, int uid, @Nullable ContentCaptureContext clientContext,
+            int flags, boolean bindInstantServiceAllowed, @NonNull IResultReceiver clientReceiver) {
         if (!isEnabledLocked()) {
-            sendToClient(resultReceiver, ContentCaptureSession.STATE_DISABLED);
+            setClientState(clientReceiver, ContentCaptureSession.STATE_DISABLED, /* binder=*/ null);
             return;
         }
         final ComponentName serviceComponentName = getServiceComponentName();
@@ -131,35 +131,30 @@
             return;
         }
 
-        ContentCaptureServerSession session = mSessions.get(sessionId);
-        if (session != null) {
-            if (mMaster.debug) {
-                Slog.d(TAG, "startSession(): reusing session " + sessionId + " for "
-                        + componentName);
-            }
-            // TODO(b/111276913): check if local ids match and decide what to do if they don't
-            // TODO(b/111276913): should we call session.notifySessionStartedLocked() again??
-            // if not, move notifySessionStartedLocked() into session constructor
-            sendToClient(resultReceiver, ContentCaptureSession.STATE_ACTIVE);
+        final ContentCaptureServerSession existingSession = mSessions.get(sessionId);
+        if (existingSession != null) {
+            Slog.w(TAG, "startSession(id=" + existingSession + ", token=" + activityToken
+                    + ": ignoring because it already exists for " + existingSession.mActivityToken);
+            setClientState(clientReceiver, ContentCaptureSession.STATE_DISABLED_DUPLICATED_ID,
+                    /* binder=*/ null);
             return;
         }
 
-        session = new ContentCaptureServerSession(getContext(), mUserId, mLock, activityToken,
-                this, serviceComponentName, componentName, taskId, displayId, sessionId,
-                clientContext, flags, bindInstantServiceAllowed, mMaster.verbose);
+        final ContentCaptureServerSession newSession = new ContentCaptureServerSession(getContext(),
+                mUserId, mLock, activityToken, this, serviceComponentName, componentName, taskId,
+                displayId, sessionId, uid, clientContext, flags, bindInstantServiceAllowed,
+                mMaster.verbose);
         if (mMaster.verbose) {
-            Slog.v(TAG, "startSession(): new session for " + componentName + " and id "
-                    + sessionId);
+            Slog.v(TAG, "startSession(): new session for "
+                    + ComponentName.flattenToShortString(componentName) + " and id " + sessionId);
         }
-        mSessions.put(sessionId, session);
-        session.notifySessionStartedLocked();
-        sendToClient(resultReceiver, ContentCaptureSession.STATE_ACTIVE);
+        mSessions.put(sessionId, newSession);
+        newSession.notifySessionStartedLocked(clientReceiver);
     }
 
     // TODO(b/111276913): log metrics
     @GuardedBy("mLock")
-    public void finishSessionLocked(@NonNull String sessionId,
-            @Nullable List<ContentCaptureEvent> events) {
+    public void finishSessionLocked(@NonNull String sessionId) {
         if (!isEnabledLocked()) {
             return;
         }
@@ -171,41 +166,8 @@
             }
             return;
         }
-        if (events != null && !events.isEmpty()) {
-            // TODO(b/111276913): for now we're sending the events and the onDestroy() in 2 separate
-            // calls because it's not clear yet whether we'll change the manager to send events
-            // to the service directly (i.e., without passing through system server). Once we
-            // decide, we might need to split IContentCaptureManager.onSessionLifecycle() in 2
-            // methods, one for start and another for finish (and passing the events to finish),
-            // otherwise the service might receive the 2 calls out of order.
-            session.sendEventsLocked(events);
-        }
-        if (mMaster.verbose) {
-            Slog.v(TAG, "finishSession(" + (events == null ? 0 : events.size()) + " events): "
-                    + session);
-        }
-        session.removeSelfLocked(true);
-    }
-
-    // TODO(b/111276913): need to figure out why some events are sent before session is started;
-    // probably because ContentCaptureManager is not buffering them until it gets the session back
-    @GuardedBy("mLock")
-    public void sendEventsLocked(@NonNull String sessionId,
-            @NonNull List<ContentCaptureEvent> events) {
-        if (!isEnabledLocked()) {
-            return;
-        }
-        final ContentCaptureServerSession session = mSessions.get(sessionId);
-        if (session == null) {
-            if (mMaster.verbose) {
-                Slog.v(TAG, "sendEvents(): no session for " + sessionId);
-            }
-            return;
-        }
-        if (mMaster.verbose) {
-            Slog.v(TAG, "sendEvents(): id=" + sessionId + ", events=" + events.size());
-        }
-        session.sendEventsLocked(events);
+        if (mMaster.verbose) Slog.v(TAG, "finishSession(): id=" + sessionId);
+        session.removeSelfLocked(/* notifyRemoteService= */ true);
     }
 
     @GuardedBy("mLock")
@@ -308,12 +270,4 @@
         }
         return null;
     }
-
-    private static void sendToClient(@NonNull IResultReceiver resultReceiver, int value) {
-        try {
-            resultReceiver.send(value, null);
-        } catch (RemoteException e) {
-            Slog.w(TAG, "Error async reporting result to client: " + e);
-        }
-    }
 }
diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureServerSession.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureServerSession.java
index 181a2da..ba98b95 100644
--- a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureServerSession.java
+++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureServerSession.java
@@ -24,15 +24,14 @@
 import android.service.contentcapture.SnapshotData;
 import android.util.Slog;
 import android.view.contentcapture.ContentCaptureContext;
-import android.view.contentcapture.ContentCaptureEvent;
 import android.view.contentcapture.ContentCaptureSessionId;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.os.IResultReceiver;
 import com.android.internal.util.Preconditions;
 import com.android.server.contentcapture.RemoteContentCaptureService.ContentCaptureServiceCallbacks;
 
 import java.io.PrintWriter;
-import java.util.List;
 
 final class ContentCaptureServerSession implements ContentCaptureServiceCallbacks {
 
@@ -43,18 +42,28 @@
     private final ContentCapturePerUserService mService;
     private final RemoteContentCaptureService mRemoteService;
     private final ContentCaptureContext mContentCaptureContext;
+
+    /**
+     * Canonical session id.
+     */
     private final String mId;
 
+    /**
+     * UID of the app whose contents is being captured.
+     */
+    private final int mUid;
+
     ContentCaptureServerSession(@NonNull Context context, int userId, @NonNull Object lock,
             @NonNull IBinder activityToken, @NonNull ContentCapturePerUserService service,
             @NonNull ComponentName serviceComponentName, @NonNull ComponentName appComponentName,
-            int taskId, int displayId, @NonNull String sessionId,
+            int taskId, int displayId, @NonNull String sessionId, int uid,
             @Nullable ContentCaptureContext clientContext, int flags,
             boolean bindInstantServiceAllowed, boolean verbose) {
         mLock = lock;
         mActivityToken = activityToken;
         mService = service;
         mId = Preconditions.checkNotNull(sessionId);
+        mUid = uid;
         mRemoteService = new RemoteContentCaptureService(context,
                 ContentCaptureService.SERVICE_INTERFACE, serviceComponentName, userId, this,
                 bindInstantServiceAllowed, verbose);
@@ -73,15 +82,8 @@
      * Notifies the {@link ContentCaptureService} that the service started.
      */
     @GuardedBy("mLock")
-    public void notifySessionStartedLocked() {
-        mRemoteService.onSessionLifecycleRequest(mContentCaptureContext, mId);
-    }
-
-    /**
-     * Notifies the {@link ContentCaptureService} of a batch of events.
-     */
-    public void sendEventsLocked(@NonNull List<ContentCaptureEvent> events) {
-        mRemoteService.onContentCaptureEventsRequest(mId, events);
+    public void notifySessionStartedLocked(@NonNull IResultReceiver clientReceiver) {
+        mRemoteService.onSessionStarted(mContentCaptureContext, mId, mUid, clientReceiver);
     }
 
     /**
@@ -118,11 +120,11 @@
     @GuardedBy("mLock")
     public void destroyLocked(boolean notifyRemoteService) {
         if (mService.isVerbose()) {
-            Slog.v(TAG, "destroyLocked(notifyRemoteService=" + notifyRemoteService + ")");
+            Slog.v(TAG, "destroy(notifyRemoteService=" + notifyRemoteService + ")");
         }
         // TODO(b/111276913): must call client to set session as FINISHED_BY_SERVER
         if (notifyRemoteService) {
-            mRemoteService.onSessionLifecycleRequest(/* context= */ null, mId);
+            mRemoteService.onSessionFinished(mId);
         }
     }
 
@@ -133,13 +135,14 @@
             Slog.d(TAG, "onServiceDied() for " + mId);
         }
         synchronized (mLock) {
-            removeSelfLocked(/* notifyRemoteService= */ false);
+            removeSelfLocked(/* notifyRemoteService= */ true);
         }
     }
 
     @GuardedBy("mLock")
     public void dumpLocked(@NonNull String prefix, @NonNull PrintWriter pw) {
         pw.print(prefix); pw.print("id: ");  pw.print(mId); pw.println();
+        pw.print(prefix); pw.print("uid: ");  pw.print(mUid); pw.println();
         pw.print(prefix); pw.print("context: ");  mContentCaptureContext.dump(pw); pw.println();
         pw.print(prefix); pw.print("activity token: "); pw.println(mActivityToken);
         pw.print(prefix); pw.print("has autofill callback: ");
diff --git a/services/contentcapture/java/com/android/server/contentcapture/RemoteContentCaptureService.java b/services/contentcapture/java/com/android/server/contentcapture/RemoteContentCaptureService.java
index b4edf7e..b9b1943 100644
--- a/services/contentcapture/java/com/android/server/contentcapture/RemoteContentCaptureService.java
+++ b/services/contentcapture/java/com/android/server/contentcapture/RemoteContentCaptureService.java
@@ -20,17 +20,14 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.os.IBinder;
-import android.service.contentcapture.ContentCaptureEventsRequest;
 import android.service.contentcapture.IContentCaptureService;
 import android.service.contentcapture.SnapshotData;
 import android.text.format.DateUtils;
 import android.view.contentcapture.ContentCaptureContext;
-import android.view.contentcapture.ContentCaptureEvent;
 
+import com.android.internal.os.IResultReceiver;
 import com.android.server.infra.AbstractMultiplePendingRequestsRemoteService;
 
-import java.util.List;
-
 final class RemoteContentCaptureService
         extends AbstractMultiplePendingRequestsRemoteService<RemoteContentCaptureService,
         IContentCaptureService> {
@@ -67,21 +64,19 @@
 
     /**
      * Called by {@link ContentCaptureServerSession} to generate a call to the
-     * {@link RemoteContentCaptureService} to indicate the session was created (when {@code context}
-     * is not {@code null} or destroyed (when {@code context} is {@code null}).
+     * {@link RemoteContentCaptureService} to indicate the session was created.
      */
-    public void onSessionLifecycleRequest(@Nullable ContentCaptureContext context,
-            @NonNull String sessionId) {
-        scheduleAsyncRequest((s) -> s.onSessionLifecycle(context, sessionId));
+    public void onSessionStarted(@Nullable ContentCaptureContext context,
+            @NonNull String sessionId, int uid, @NonNull IResultReceiver clientReceiver) {
+        scheduleAsyncRequest((s) -> s.onSessionStarted(context, sessionId, uid, clientReceiver));
     }
 
     /**
-     * Called by {@link ContentCaptureServerSession} to send a batch of events to the service.
+     * Called by {@link ContentCaptureServerSession} to generate a call to the
+     * {@link RemoteContentCaptureService} to indicate the session was finished.
      */
-    public void onContentCaptureEventsRequest(@NonNull String sessionId,
-            @NonNull List<ContentCaptureEvent> events) {
-        scheduleAsyncRequest((s) -> s.onContentCaptureEventsRequest(sessionId,
-                new ContentCaptureEventsRequest(events)));
+    public void onSessionFinished(@NonNull String sessionId) {
+        scheduleAsyncRequest((s) -> s.onSessionFinished(sessionId));
     }
 
     /**
diff --git a/services/core/java/com/android/server/AppOpsService.java b/services/core/java/com/android/server/AppOpsService.java
index 8d912fa..f0ec69f 100644
--- a/services/core/java/com/android/server/AppOpsService.java
+++ b/services/core/java/com/android/server/AppOpsService.java
@@ -86,6 +86,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.app.IAppOpsActiveCallback;
 import com.android.internal.app.IAppOpsCallback;
+import com.android.internal.app.IAppOpsNotedCallback;
 import com.android.internal.app.IAppOpsService;
 import com.android.internal.os.Zygote;
 import com.android.internal.util.ArrayUtils;
@@ -456,6 +457,7 @@
     final ArrayMap<String, ArraySet<ModeCallback>> mPackageModeWatchers = new ArrayMap<>();
     final ArrayMap<IBinder, ModeCallback> mModeWatchers = new ArrayMap<>();
     final ArrayMap<IBinder, SparseArray<ActiveCallback>> mActiveWatchers = new ArrayMap<>();
+    final ArrayMap<IBinder, SparseArray<NotedCallback>> mNotedWatchers = new ArrayMap<>();
     final SparseArray<SparseArray<Restriction>> mAudioRestrictions = new SparseArray<>();
 
     final class ModeCallback implements DeathRecipient {
@@ -475,6 +477,7 @@
             try {
                 mCallback.asBinder().linkToDeath(this, 0);
             } catch (RemoteException e) {
+                /*ignored*/
             }
         }
 
@@ -524,6 +527,7 @@
             try {
                 mCallback.asBinder().linkToDeath(this, 0);
             } catch (RemoteException e) {
+                /*ignored*/
             }
         }
 
@@ -552,6 +556,50 @@
         }
     }
 
+    final class NotedCallback implements DeathRecipient {
+        final IAppOpsNotedCallback mCallback;
+        final int mWatchingUid;
+        final int mCallingUid;
+        final int mCallingPid;
+
+        NotedCallback(IAppOpsNotedCallback callback, int watchingUid, int callingUid,
+                int callingPid) {
+            mCallback = callback;
+            mWatchingUid = watchingUid;
+            mCallingUid = callingUid;
+            mCallingPid = callingPid;
+            try {
+                mCallback.asBinder().linkToDeath(this, 0);
+            } catch (RemoteException e) {
+                /*ignored*/
+            }
+        }
+
+        @Override
+        public String toString() {
+            StringBuilder sb = new StringBuilder(128);
+            sb.append("NotedCallback{");
+            sb.append(Integer.toHexString(System.identityHashCode(this)));
+            sb.append(" watchinguid=");
+            UserHandle.formatUid(sb, mWatchingUid);
+            sb.append(" from uid=");
+            UserHandle.formatUid(sb, mCallingUid);
+            sb.append(" pid=");
+            sb.append(mCallingPid);
+            sb.append('}');
+            return sb.toString();
+        }
+
+        void destroy() {
+            mCallback.asBinder().unlinkToDeath(this, 0);
+        }
+
+        @Override
+        public void binderDied() {
+            stopWatchingNoted(mCallback);
+        }
+    }
+
     final ArrayMap<IBinder, ClientState> mClients = new ArrayMap<>();
 
     final class ClientState extends Binder implements DeathRecipient {
@@ -1629,7 +1677,7 @@
             UidState uidState = getUidStateLocked(uid, false);
             if (uidState != null && uidState.opModes != null
                     && uidState.opModes.indexOfKey(code) >= 0) {
-                return uidState.opModes.get(code);
+                return uidState.evalMode(uidState.opModes.get(code));
             }
             Op op = getOpLocked(code, uid, packageName, false, true, false);
             if (op == null) {
@@ -1795,12 +1843,16 @@
             final Ops ops = getOpsRawLocked(uid, packageName, true /* edit */,
                     false /* uidMismatchExpected */);
             if (ops == null) {
+                scheduleOpNotedIfNeededLocked(code, uid, packageName,
+                        AppOpsManager.MODE_IGNORED);
                 if (DEBUG) Slog.d(TAG, "noteOperation: no op for code " + code + " uid " + uid
                         + " package " + packageName);
                 return AppOpsManager.MODE_ERRORED;
             }
             final Op op = getOpLocked(ops, code, true);
             if (isOpRestrictedLocked(uid, code, packageName)) {
+                scheduleOpNotedIfNeededLocked(code, uid, packageName,
+                        AppOpsManager.MODE_IGNORED);
                 return AppOpsManager.MODE_IGNORED;
             }
             final UidState uidState = ops.uidState;
@@ -1820,6 +1872,7 @@
                             + switchCode + " (" + code + ") uid " + uid + " package "
                             + packageName);
                     op.rejectTime[uidState.state] = System.currentTimeMillis();
+                    scheduleOpNotedIfNeededLocked(code, uid, packageName, uidMode);
                     return uidMode;
                 }
             } else {
@@ -1830,6 +1883,7 @@
                             + switchCode + " (" + code + ") uid " + uid + " package "
                             + packageName);
                     op.rejectTime[uidState.state] = System.currentTimeMillis();
+                    scheduleOpNotedIfNeededLocked(op.op, uid, packageName, mode);
                     return mode;
                 }
             }
@@ -1839,6 +1893,8 @@
             op.rejectTime[uidState.state] = 0;
             op.proxyUid = proxyUid;
             op.proxyPackageName = proxyPackageName;
+            scheduleOpNotedIfNeededLocked(code, uid, packageName,
+                    AppOpsManager.MODE_ALLOWED);
             return AppOpsManager.MODE_ALLOWED;
         }
     }
@@ -1886,10 +1942,50 @@
             }
             final int callbackCount = activeCallbacks.size();
             for (int i = 0; i < callbackCount; i++) {
-                // Apps ops are mapped to a singleton
-                if (i == 0) {
-                    activeCallbacks.valueAt(i).destroy();
-                }
+                activeCallbacks.valueAt(i).destroy();
+            }
+        }
+    }
+
+    @Override
+    public void startWatchingNoted(@NonNull int[] ops, @NonNull IAppOpsNotedCallback callback) {
+        int watchedUid = Process.INVALID_UID;
+        final int callingUid = Binder.getCallingUid();
+        final int callingPid = Binder.getCallingPid();
+        if (mContext.checkCallingOrSelfPermission(Manifest.permission.WATCH_APPOPS)
+                != PackageManager.PERMISSION_GRANTED) {
+            watchedUid = callingUid;
+        }
+        Preconditions.checkArgument(!ArrayUtils.isEmpty(ops), "Ops cannot be null or empty");
+        Preconditions.checkArrayElementsInRange(ops, 0, AppOpsManager._NUM_OP - 1,
+                "Invalid op code in: " + Arrays.toString(ops));
+        Preconditions.checkNotNull(callback, "Callback cannot be null");
+        synchronized (this) {
+            SparseArray<NotedCallback> callbacks = mNotedWatchers.get(callback.asBinder());
+            if (callbacks == null) {
+                callbacks = new SparseArray<>();
+                mNotedWatchers.put(callback.asBinder(), callbacks);
+            }
+            final NotedCallback notedCallback = new NotedCallback(callback, watchedUid,
+                    callingUid, callingPid);
+            for (int op : ops) {
+                callbacks.put(op, notedCallback);
+            }
+        }
+    }
+
+    @Override
+    public void stopWatchingNoted(IAppOpsNotedCallback callback) {
+        Preconditions.checkNotNull(callback, "Callback cannot be null");
+        synchronized (this) {
+            final SparseArray<NotedCallback> notedCallbacks =
+                    mNotedWatchers.remove(callback.asBinder());
+            if (notedCallbacks == null) {
+                return;
+            }
+            final int callbackCount = notedCallbacks.size();
+            for (int i = 0; i < callbackCount; i++) {
+                notedCallbacks.valueAt(i).destroy();
             }
         }
     }
@@ -2052,6 +2148,51 @@
         }
     }
 
+    private void scheduleOpNotedIfNeededLocked(int code, int uid, String packageName,
+            int result) {
+        ArraySet<NotedCallback> dispatchedCallbacks = null;
+        final int callbackListCount = mNotedWatchers.size();
+        for (int i = 0; i < callbackListCount; i++) {
+            final SparseArray<NotedCallback> callbacks = mNotedWatchers.valueAt(i);
+            final NotedCallback callback = callbacks.get(code);
+            if (callback != null) {
+                if (callback.mWatchingUid >= 0 && callback.mWatchingUid != uid) {
+                    continue;
+                }
+                if (dispatchedCallbacks == null) {
+                    dispatchedCallbacks = new ArraySet<>();
+                }
+                dispatchedCallbacks.add(callback);
+            }
+        }
+        if (dispatchedCallbacks == null) {
+            return;
+        }
+        mHandler.sendMessage(PooledLambda.obtainMessage(
+                AppOpsService::notifyOpChecked,
+                this, dispatchedCallbacks, code, uid, packageName, result));
+    }
+
+    private void notifyOpChecked(ArraySet<NotedCallback> callbacks,
+            int code, int uid, String packageName, int result) {
+        // There are components watching for checks in our process. The callbacks in
+        // these components may require permissions our remote caller does not have.
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            final int callbackCount = callbacks.size();
+            for (int i = 0; i < callbackCount; i++) {
+                final NotedCallback callback = callbacks.valueAt(i);
+                try {
+                    callback.mCallback.opNoted(code, uid, packageName, result);
+                } catch (RemoteException e) {
+                    /* do nothing */
+                }
+            }
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
     @Override
     public int permissionToOpCode(String permission) {
         if (permission == null) {
@@ -3463,6 +3604,46 @@
                     pw.println(cb);
                 }
             }
+            if (mNotedWatchers.size() > 0 && dumpMode < 0) {
+                needSep = true;
+                boolean printedHeader = false;
+                for (int i = 0; i < mNotedWatchers.size(); i++) {
+                    final SparseArray<NotedCallback> notedWatchers = mNotedWatchers.valueAt(i);
+                    if (notedWatchers.size() <= 0) {
+                        continue;
+                    }
+                    final NotedCallback cb = notedWatchers.valueAt(0);
+                    if (dumpOp >= 0 && notedWatchers.indexOfKey(dumpOp) < 0) {
+                        continue;
+                    }
+                    if (dumpPackage != null && cb.mWatchingUid >= 0
+                            && dumpUid != UserHandle.getAppId(cb.mWatchingUid)) {
+                        continue;
+                    }
+                    if (!printedHeader) {
+                        pw.println("  All op noted watchers:");
+                        printedHeader = true;
+                    }
+                    pw.print("    ");
+                    pw.print(Integer.toHexString(System.identityHashCode(
+                            mNotedWatchers.keyAt(i))));
+                    pw.println(" ->");
+                    pw.print("        [");
+                    final int opCount = notedWatchers.size();
+                    for (i = 0; i < opCount; i++) {
+                        if (i > 0) {
+                            pw.print(' ');
+                        }
+                        pw.print(AppOpsManager.opToName(notedWatchers.keyAt(i)));
+                        if (i < opCount - 1) {
+                            pw.print(',');
+                        }
+                    }
+                    pw.println("]");
+                    pw.print("        ");
+                    pw.println(cb);
+                }
+            }
             if (mClients.size() > 0 && dumpMode < 0) {
                 needSep = true;
                 boolean printedHeader = false;
diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
index a07939e..7bbc543 100644
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ b/services/core/java/com/android/server/BluetoothManagerService.java
@@ -76,6 +76,7 @@
 import java.util.LinkedList;
 import java.util.Locale;
 import java.util.Map;
+import java.util.NoSuchElementException;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.locks.ReentrantReadWriteLock;
 
@@ -1276,7 +1277,11 @@
             if (mService == null) {
                 return;
             }
-            mService.unlinkToDeath(this, 0);
+            try {
+                mService.unlinkToDeath(this, 0);
+            } catch (NoSuchElementException e) {
+                Log.e(TAG, "error unlinking to death", e);
+            }
             mService = null;
             mClassName = null;
 
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 89194e4..66ceae4 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -5714,7 +5714,6 @@
                 // This should never fail.  Specifying an already in use NetID will cause failure.
                 if (networkAgent.isVPN()) {
                     mNMS.createVirtualNetwork(networkAgent.network.netId,
-                            !networkAgent.linkProperties.getDnsServers().isEmpty(),
                             (networkAgent.networkMisc == null ||
                                 !networkAgent.networkMisc.allowBypass));
                 } else {
diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java
index 4e8ef54..d33b617 100644
--- a/services/core/java/com/android/server/LocationManagerService.java
+++ b/services/core/java/com/android/server/LocationManagerService.java
@@ -302,12 +302,14 @@
             AppOpsManager.OnOpChangedListener callback
                     = new AppOpsManager.OnOpChangedInternalListener() {
                 public void onOpChanged(int op, String packageName) {
-                    synchronized (mLock) {
-                        for (Receiver receiver : mReceivers.values()) {
-                            receiver.updateMonitoring(true);
-                        }
-                        applyAllProviderRequirementsLocked();
-                    }
+                            mLocationHandler.post(() -> {
+                                synchronized (mLock) {
+                                    for (Receiver receiver : mReceivers.values()) {
+                                        receiver.updateMonitoring(true);
+                                    }
+                                    applyAllProviderRequirementsLocked();
+                                }
+                            });
                 }
             };
             mAppOps.startWatchingMode(AppOpsManager.OP_COARSE_LOCATION, null,
diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index 8869af4..b0ca2df 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -2314,11 +2314,11 @@
     }
 
     @Override
-    public void createVirtualNetwork(int netId, boolean hasDNS, boolean secure) {
+    public void createVirtualNetwork(int netId, boolean secure) {
         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
 
         try {
-            mNetdService.networkCreateVpn(netId, hasDNS, secure);
+            mNetdService.networkCreateVpn(netId, secure);
         } catch (RemoteException | ServiceSpecificException e) {
             throw new IllegalStateException(e);
         }
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 6c62725..7adcaba 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -1504,6 +1504,10 @@
     public StorageManagerService(Context context) {
         sSelf = this;
 
+        // Snapshot feature flag used for this boot
+        SystemProperties.set(StorageManager.PROP_ISOLATED_STORAGE_SNAPSHOT, Boolean.toString(
+                SystemProperties.getBoolean(StorageManager.PROP_ISOLATED_STORAGE, false)));
+
         mContext = context;
         mCallbacks = new Callbacks(FgThread.get().getLooper());
         mLockPatternUtils = new LockPatternUtils(mContext);
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index 1ef3e94..d07cf78 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -214,6 +214,10 @@
 
     private PreciseCallState mPreciseCallState = new PreciseCallState();
 
+    private int mCallDisconnectCause = DisconnectCause.NOT_VALID;
+
+    private int mCallPreciseDisconnectCause = PreciseDisconnectCause.NOT_VALID;
+
     private boolean mCarrierNetworkChangeState = false;
 
     private PhoneCapability mPhoneCapability = null;
@@ -714,6 +718,14 @@
                             remove(r.binder);
                         }
                     }
+                    if ((events & PhoneStateListener.LISTEN_CALL_DISCONNECT_CAUSES) != 0) {
+                        try {
+                            r.callback.onCallDisconnectCauseChanged(mCallDisconnectCause,
+                                    mCallPreciseDisconnectCause);
+                        } catch (RemoteException ex) {
+                            remove(r.binder);
+                        }
+                    }
                     if ((events & PhoneStateListener.LISTEN_PRECISE_DATA_CONNECTION_STATE) != 0) {
                         try {
                             r.callback.onPreciseDataConnectionStateChanged(
@@ -1491,9 +1503,8 @@
             }
             handleRemoveListLocked();
         }
-        broadcastPreciseCallStateChanged(ringingCallState, foregroundCallState, backgroundCallState,
-                DisconnectCause.NOT_VALID,
-                PreciseDisconnectCause.NOT_VALID);
+        broadcastPreciseCallStateChanged(ringingCallState, foregroundCallState,
+                backgroundCallState);
     }
 
     public void notifyDisconnectCause(int disconnectCause, int preciseDisconnectCause) {
@@ -1501,12 +1512,14 @@
             return;
         }
         synchronized (mRecords) {
-            mPreciseCallState = new PreciseCallState(mRingingCallState, mForegroundCallState,
-                    mBackgroundCallState, disconnectCause, preciseDisconnectCause);
+            mCallDisconnectCause = disconnectCause;
+            mCallPreciseDisconnectCause = preciseDisconnectCause;
             for (Record r : mRecords) {
-                if (r.matchPhoneStateListenerEvent(PhoneStateListener.LISTEN_PRECISE_CALL_STATE)) {
+                if (r.matchPhoneStateListenerEvent(PhoneStateListener
+                        .LISTEN_CALL_DISCONNECT_CAUSES)) {
                     try {
-                        r.callback.onPreciseCallStateChanged(mPreciseCallState);
+                        r.callback.onCallDisconnectCauseChanged(mCallDisconnectCause,
+                                mCallPreciseDisconnectCause);
                     } catch (RemoteException ex) {
                         mRemoveList.add(r.binder);
                     }
@@ -1514,8 +1527,6 @@
             }
             handleRemoveListLocked();
         }
-        broadcastPreciseCallStateChanged(mRingingCallState, mForegroundCallState,
-                mBackgroundCallState, disconnectCause, preciseDisconnectCause);
     }
 
     public void notifyPreciseDataConnectionFailed(String reason, String apnType,
@@ -1737,6 +1748,8 @@
             }
             pw.println("mPreciseDataConnectionState=" + mPreciseDataConnectionState);
             pw.println("mPreciseCallState=" + mPreciseCallState);
+            pw.println("mCallDisconnectCause=" + mCallDisconnectCause);
+            pw.println("mCallPreciseDisconnectCause=" + mCallPreciseDisconnectCause);
             pw.println("mCarrierNetworkChangeState=" + mCarrierNetworkChangeState);
             pw.println("mRingingCallState=" + mRingingCallState);
             pw.println("mForegroundCallState=" + mForegroundCallState);
@@ -1912,13 +1925,11 @@
     }
 
     private void broadcastPreciseCallStateChanged(int ringingCallState, int foregroundCallState,
-            int backgroundCallState, int disconnectCause, int preciseDisconnectCause) {
+            int backgroundCallState) {
         Intent intent = new Intent(TelephonyManager.ACTION_PRECISE_CALL_STATE_CHANGED);
         intent.putExtra(TelephonyManager.EXTRA_RINGING_CALL_STATE, ringingCallState);
         intent.putExtra(TelephonyManager.EXTRA_FOREGROUND_CALL_STATE, foregroundCallState);
         intent.putExtra(TelephonyManager.EXTRA_BACKGROUND_CALL_STATE, backgroundCallState);
-        intent.putExtra(TelephonyManager.EXTRA_DISCONNECT_CAUSE, disconnectCause);
-        intent.putExtra(TelephonyManager.EXTRA_PRECISE_DISCONNECT_CAUSE, preciseDisconnectCause);
         mContext.sendBroadcastAsUser(intent, UserHandle.ALL,
                 android.Manifest.permission.READ_PRECISE_PHONE_STATE);
     }
@@ -2006,6 +2017,11 @@
                             + "LISTEN_PREFERRED_DATA_SUBID_CHANGE");
         }
 
+        if ((events & PhoneStateListener.LISTEN_CALL_DISCONNECT_CAUSES) != 0) {
+            mContext.enforceCallingOrSelfPermission(
+                    android.Manifest.permission.READ_PRECISE_PHONE_STATE, null);
+        }
+
         return true;
     }
 
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 5afb90d..fe632e5 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -1665,7 +1665,7 @@
             AppBindRecord b = s.retrieveAppBindingLocked(service, callerApp);
             ConnectionRecord c = new ConnectionRecord(b, activity,
                     connection, flags, clientLabel, clientIntent,
-                    callerApp.uid, callerApp.processName);
+                    callerApp.uid, callerApp.processName, callingPackage);
 
             IBinder binder = connection.asBinder();
             ArrayList<ConnectionRecord> clist = s.connections.get(binder);
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 739dbbc..3bfd363 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -25,6 +25,7 @@
 import static android.app.ActivityManager.INSTR_FLAG_DISABLE_HIDDEN_API_CHECKS;
 import static android.app.ActivityManager.INSTR_FLAG_MOUNT_EXTERNAL_STORAGE_FULL;
 import static android.app.ActivityManager.PROCESS_STATE_CACHED_ACTIVITY;
+import static android.app.ActivityManager.PROCESS_STATE_CACHED_EMPTY;
 import static android.app.ActivityManager.PROCESS_STATE_LAST_ACTIVITY;
 import static android.app.ActivityManager.PROCESS_STATE_NONEXISTENT;
 import static android.app.ActivityManagerInternal.ALLOW_FULL_ONLY;
@@ -2329,9 +2330,16 @@
                     fos.close();
                     long[] rssAfter = Process.getRss(pid);
                     long end = SystemClock.uptimeMillis();
+                    long time = end - start;
                     EventLog.writeEvent(EventLogTags.AM_COMPACT, pid, name, action,
                             rssBefore[0], rssBefore[1], rssBefore[2], rssBefore[3],
-                            rssAfter[0], rssAfter[1], rssAfter[2], rssAfter[3], end-start);
+                            rssAfter[0], rssAfter[1], rssAfter[2], rssAfter[3], time,
+                            lastCompactAction, lastCompactTime, msg.arg1, msg.arg2);
+                    StatsLog.write(StatsLog.APP_COMPACTED, pid, name, pendingAction,
+                            rssBefore[0], rssBefore[1], rssBefore[2], rssBefore[3],
+                            rssAfter[0], rssAfter[1], rssAfter[2], rssAfter[3], time,
+                            lastCompactAction, lastCompactTime, msg.arg1,
+                            ActivityManager.processStateAmToProto(msg.arg2));
                     synchronized(ActivityManagerService.this) {
                         proc.lastCompactTime = end;
                         proc.lastCompactAction = pendingAction;
@@ -6284,7 +6292,7 @@
 
     ContentProviderConnection incProviderCountLocked(ProcessRecord r,
             final ContentProviderRecord cpr, IBinder externalProcessToken, int callingUid,
-            String callingTag, boolean stable) {
+            String callingPackage, String callingTag, boolean stable) {
         if (r != null) {
             for (int i=0; i<r.conProviders.size(); i++) {
                 ContentProviderConnection conn = r.conProviders.get(i);
@@ -6304,7 +6312,7 @@
                     return conn;
                 }
             }
-            ContentProviderConnection conn = new ContentProviderConnection(cpr, r);
+            ContentProviderConnection conn = new ContentProviderConnection(cpr, r, callingPackage);
             conn.startAssociationIfNeeded();
             if (stable) {
                 conn.stableCount = 1;
@@ -6411,8 +6419,8 @@
     }
 
     private ContentProviderHolder getContentProviderImpl(IApplicationThread caller,
-            String name, IBinder token, int callingUid, String callingTag, boolean stable,
-            int userId) {
+            String name, IBinder token, int callingUid, String callingPackage, String callingTag,
+            boolean stable, int userId) {
         ContentProviderRecord cpr;
         ContentProviderConnection conn = null;
         ProviderInfo cpi = null;
@@ -6535,7 +6543,8 @@
 
                 // In this case the provider instance already exists, so we can
                 // return it right away.
-                conn = incProviderCountLocked(r, cpr, token, callingUid, callingTag, stable);
+                conn = incProviderCountLocked(r, cpr, token, callingUid, callingPackage, callingTag,
+                        stable);
                 if (conn != null && (conn.stableCount+conn.unstableCount) == 1) {
                     if (cpr.proc != null && r.setAdj <= ProcessList.PERCEPTIBLE_APP_ADJ) {
                         // If this is a perceptible app accessing the provider,
@@ -6782,7 +6791,8 @@
                 }
 
                 mProviderMap.putProviderByName(name, cpr);
-                conn = incProviderCountLocked(r, cpr, token, callingUid, callingTag, stable);
+                conn = incProviderCountLocked(r, cpr, token, callingUid, callingPackage, callingTag,
+                        stable);
                 if (conn != null) {
                     conn.waiting = true;
                 }
@@ -6924,7 +6934,8 @@
 
     @Override
     public final ContentProviderHolder getContentProvider(
-            IApplicationThread caller, String name, int userId, boolean stable) {
+            IApplicationThread caller, String callingPackage, String name, int userId,
+            boolean stable) {
         enforceNotIsolatedCaller("getContentProvider");
         if (caller == null) {
             String msg = "null IApplicationThread when getting content provider "
@@ -6934,8 +6945,14 @@
         }
         // The incoming user check is now handled in checkContentProviderPermissionLocked() to deal
         // with cross-user grant.
-        return getContentProviderImpl(caller, name, null, Binder.getCallingUid(), null, stable,
-                userId);
+        final int callingUid = Binder.getCallingUid();
+        if (callingPackage != null && mAppOpsService.checkPackage(callingUid, callingPackage)
+                != AppOpsManager.MODE_ALLOWED) {
+            throw new SecurityException("Given calling package " + callingPackage
+                    + " does not match caller's uid " + callingUid);
+        }
+        return getContentProviderImpl(caller, name, null, callingUid, callingPackage,
+                null, stable, userId);
     }
 
     public ContentProviderHolder getContentProviderExternal(
@@ -6950,7 +6967,8 @@
 
     private ContentProviderHolder getContentProviderExternalUnchecked(String name,
             IBinder token, int callingUid, String callingTag, int userId) {
-        return getContentProviderImpl(null, name, token, callingUid, callingTag, true, userId);
+        return getContentProviderImpl(null, name, token, callingUid, null, callingTag,
+                true, userId);
     }
 
     /**
@@ -15694,7 +15712,7 @@
     }
 
     private final boolean computeOomAdjLocked(ProcessRecord app, int cachedAdj, ProcessRecord TOP_APP,
-            boolean doingAll, long now) {
+            boolean doingAll, long now, boolean cycleReEval) {
         if (mAdjSeq == app.adjSeq) {
             if (app.adjSeq == app.completedAdjSeq) {
                 // This adjustment has already been computed successfully.
@@ -15760,20 +15778,21 @@
                 app.systemNoUi = false;
             }
             if (!app.systemNoUi) {
-              if (mWakefulness == PowerManagerInternal.WAKEFULNESS_AWAKE) {
-                  // screen on, promote UI
-                  app.setCurProcState(ActivityManager.PROCESS_STATE_PERSISTENT_UI);
-                  app.setCurrentSchedulingGroup(ProcessList.SCHED_GROUP_TOP_APP);
-              } else {
-                  // screen off, restrict UI scheduling
-                  app.setCurProcState(ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE);
-                  app.setCurrentSchedulingGroup(ProcessList.SCHED_GROUP_RESTRICTED);
-              }
+                if (mWakefulness == PowerManagerInternal.WAKEFULNESS_AWAKE) {
+                    // screen on, promote UI
+                    app.setCurProcState(ActivityManager.PROCESS_STATE_PERSISTENT_UI);
+                    app.setCurrentSchedulingGroup(ProcessList.SCHED_GROUP_TOP_APP);
+                } else {
+                    // screen off, restrict UI scheduling
+                    app.setCurProcState(ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE);
+                    app.setCurrentSchedulingGroup(ProcessList.SCHED_GROUP_RESTRICTED);
+                }
             }
+            app.setCurRawProcState(app.getCurProcState());
             app.curAdj = app.maxAdj;
             app.completedAdjSeq = app.adjSeq;
             // if curAdj is less than prevAppAdj, then this process was promoted
-            return app.curAdj < prevAppAdj;
+            return app.curAdj < prevAppAdj || app.getCurProcState() < prevProcState;
         }
 
         app.systemNoUi = false;
@@ -16015,8 +16034,13 @@
         // By default, we use the computed adjustment.  It may be changed if
         // there are applications dependent on our services or providers, but
         // this gives us a baseline and makes sure we don't get into an
-        // infinite recursion.
-        app.setCurRawAdj(adj);
+        // infinite recursion. If we're re-evaluating due to cycles, use the previously computed
+        // values.
+        app.setCurRawAdj(!cycleReEval ? adj : Math.min(adj, app.getCurRawAdj()));
+        app.setCurRawProcState(!cycleReEval
+                ? procState
+                : Math.min(procState, app.getCurRawProcState()));
+
         app.hasStartedServices = false;
         app.adjSeq = mAdjSeq;
 
@@ -16118,21 +16142,15 @@
                     boolean trackedProcState = false;
                     if ((cr.flags&Context.BIND_WAIVE_PRIORITY) == 0) {
                         ProcessRecord client = cr.binding.client;
-                        computeOomAdjLocked(client, cachedAdj, TOP_APP, doingAll, now);
-                        if (client.containsCycle) {
-                            // We've detected a cycle. We should retry computeOomAdjLocked later in
-                            // case a later-checked connection from a client  would raise its
-                            // priority legitimately.
-                            app.containsCycle = true;
-                            // If the client has not been completely evaluated, skip using its
-                            // priority. Else use the conservative value for now and look for a
-                            // better state in the next iteration.
-                            if (client.completedAdjSeq < mAdjSeq) {
-                                continue;
-                            }
+                        computeOomAdjLocked(client, cachedAdj, TOP_APP, doingAll, now, cycleReEval);
+
+                        if (shouldSkipDueToCycle(app, client, procState, adj, cycleReEval)) {
+                            continue;
                         }
+
                         int clientAdj = client.getCurRawAdj();
-                        int clientProcState = client.getCurProcState();
+                        int clientProcState = client.getCurRawProcState();
+
                         if (clientProcState >= PROCESS_STATE_CACHED_ACTIVITY) {
                             // If the other app is cached for any reason, for purposes here
                             // we are going to consider it empty.  The specific cached state
@@ -16217,6 +16235,7 @@
                                 }
                                 if (adj >  newAdj) {
                                     adj = newAdj;
+                                    app.setCurRawAdj(adj);
                                     adjType = "service";
                                 }
                             }
@@ -16288,6 +16307,7 @@
                         }
                         if (procState > clientProcState) {
                             procState = clientProcState;
+                            app.setCurRawProcState(procState);
                             if (adjType == null) {
                                 adjType = "service";
                             }
@@ -16319,6 +16339,7 @@
                         if (a != null && adj > ProcessList.FOREGROUND_APP_ADJ
                                 && a.isActivityVisible()) {
                             adj = ProcessList.FOREGROUND_APP_ADJ;
+                            app.setCurRawAdj(adj);
                             if ((cr.flags&Context.BIND_NOT_FOREGROUND) == 0) {
                                 if ((cr.flags&Context.BIND_IMPORTANT) != 0) {
                                     schedGroup = ProcessList.SCHED_GROUP_TOP_APP_BOUND;
@@ -16360,21 +16381,15 @@
                     // Being our own client is not interesting.
                     continue;
                 }
-                computeOomAdjLocked(client, cachedAdj, TOP_APP, doingAll, now);
-                if (client.containsCycle) {
-                    // We've detected a cycle. We should retry computeOomAdjLocked later in
-                    // case a later-checked connection from a client  would raise its
-                    // priority legitimately.
-                    app.containsCycle = true;
-                    // If the client has not been completely evaluated, skip using its
-                    // priority. Else use the conservative value for now and look for a
-                    // better state in the next iteration.
-                    if (client.completedAdjSeq < mAdjSeq) {
-                        continue;
-                    }
+                computeOomAdjLocked(client, cachedAdj, TOP_APP, doingAll, now, cycleReEval);
+
+                if (shouldSkipDueToCycle(app, client, procState, adj, cycleReEval)) {
+                    continue;
                 }
+
                 int clientAdj = client.getCurRawAdj();
-                int clientProcState = client.getCurProcState();
+                int clientProcState = client.getCurRawProcState();
+
                 if (clientProcState >= PROCESS_STATE_CACHED_ACTIVITY) {
                     // If the other app is cached for any reason, for purposes here
                     // we are going to consider it empty.
@@ -16388,6 +16403,7 @@
                     } else {
                         adj = clientAdj > ProcessList.FOREGROUND_APP_ADJ
                                 ? clientAdj : ProcessList.FOREGROUND_APP_ADJ;
+                        app.setCurRawAdj(adj);
                         adjType = "provider";
                     }
                     app.cached &= client.cached;
@@ -16423,6 +16439,7 @@
                 conn.trackProcState(clientProcState, mAdjSeq, now);
                 if (procState > clientProcState) {
                     procState = clientProcState;
+                    app.setCurRawProcState(procState);
                 }
                 if (client.getCurrentSchedulingGroup() > schedGroup) {
                     schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
@@ -16448,6 +16465,7 @@
             if (cpr.hasExternalProcessHandles()) {
                 if (adj > ProcessList.FOREGROUND_APP_ADJ) {
                     adj = ProcessList.FOREGROUND_APP_ADJ;
+                    app.setCurRawAdj(adj);
                     schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
                     app.cached = false;
                     app.adjType = "ext-provider";
@@ -16459,6 +16477,7 @@
                 }
                 if (procState > ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND) {
                     procState = ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND;
+                    app.setCurRawProcState(procState);
                     if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
                         reportOomAdjMessageLocked(TAG_OOM_ADJ,
                                 "Raise procstate to external provider: " + app);
@@ -16603,6 +16622,7 @@
         app.curAdj = app.modifyRawOomAdj(adj);
         app.setCurrentSchedulingGroup(schedGroup);
         app.setCurProcState(procState);
+        app.setCurRawProcState(procState);
         app.setHasForegroundActivities(foregroundActivities);
         app.completedAdjSeq = mAdjSeq;
 
@@ -16610,6 +16630,44 @@
         return app.curAdj < prevAppAdj || app.getCurProcState() < prevProcState;
     }
 
+    /**
+     * Checks if for the given app and client, there's a cycle that should skip over the client
+     * for now or use partial values to evaluate the effect of the client binding.
+     * @param app
+     * @param client
+     * @param procState procstate evaluated so far for this app
+     * @param adj oom_adj evaluated so far for this app
+     * @param cycleReEval whether we're currently re-evaluating due to a cycle, and not the first
+     *                    evaluation.
+     * @return whether to skip using the client connection at this time
+     */
+    private boolean shouldSkipDueToCycle(ProcessRecord app, ProcessRecord client,
+            int procState, int adj, boolean cycleReEval) {
+        if (client.containsCycle) {
+            // We've detected a cycle. We should retry computeOomAdjLocked later in
+            // case a later-checked connection from a client  would raise its
+            // priority legitimately.
+            app.containsCycle = true;
+            // If the client has not been completely evaluated, check if it's worth
+            // using the partial values.
+            if (client.completedAdjSeq < mAdjSeq) {
+                if (cycleReEval) {
+                    // If the partial values are no better, skip until the next
+                    // attempt
+                    if (client.getCurRawProcState() >= procState
+                            && client.getCurRawAdj() >= adj) {
+                        return true;
+                    }
+                    // Else use the client's partial procstate and adj to adjust the
+                    // effect of the binding
+                } else {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
     private static final class RecordPssRunnable implements Runnable {
         private final ActivityManagerService mService;
         private final ProcessRecord mProc;
@@ -17028,12 +17086,16 @@
                      app.curAdj == ProcessList.HOME_APP_ADJ)) {
                     app.reqCompactAction = COMPACT_PROCESS_SOME;
                     mPendingCompactionProcesses.add(app);
-                    mCompactionHandler.sendEmptyMessage(COMPACT_PROCESS_MSG);
+                    mCompactionHandler.sendMessage(
+                            mCompactionHandler.obtainMessage(
+                                COMPACT_PROCESS_MSG, app.curAdj, app.setProcState));
                 } else if (app.setAdj < ProcessList.CACHED_APP_MIN_ADJ &&
                            app.curAdj >= ProcessList.CACHED_APP_MIN_ADJ) {
                     app.reqCompactAction = COMPACT_PROCESS_FULL;
                     mPendingCompactionProcesses.add(app);
-                    mCompactionHandler.sendEmptyMessage(COMPACT_PROCESS_MSG);
+                    mCompactionHandler.sendMessage(
+                            mCompactionHandler.obtainMessage(
+                                COMPACT_PROCESS_MSG, app.curAdj, app.setProcState));
                 }
             }
             ProcessList.setOomAdj(app.pid, app.uid, app.curAdj);
@@ -17472,7 +17534,7 @@
             return false;
         }
 
-        computeOomAdjLocked(app, cachedAdj, TOP_APP, doingAll, now);
+        computeOomAdjLocked(app, cachedAdj, TOP_APP, doingAll, now, false);
 
         return applyOomAdjLocked(app, doingAll, now, SystemClock.elapsedRealtime());
     }
@@ -17847,12 +17909,14 @@
         for (int i=N-1; i>=0; i--) {
             ProcessRecord app = mProcessList.mLruProcesses.get(i);
             app.containsCycle = false;
+            app.setCurRawProcState(PROCESS_STATE_CACHED_EMPTY);
+            app.setCurRawAdj(ProcessList.UNKNOWN_ADJ);
         }
         for (int i=N-1; i>=0; i--) {
             ProcessRecord app = mProcessList.mLruProcesses.get(i);
             if (!app.killedByAm && app.thread != null) {
                 app.procStateChanged = false;
-                computeOomAdjLocked(app, ProcessList.UNKNOWN_ADJ, TOP_APP, true, now);
+                computeOomAdjLocked(app, ProcessList.UNKNOWN_ADJ, TOP_APP, true, now, false);
 
                 // if any app encountered a cycle, we need to perform an additional loop later
                 retryCycles |= app.containsCycle;
@@ -17955,8 +18019,8 @@
             for (int i=0; i<N; i++) {
                 ProcessRecord app = mProcessList.mLruProcesses.get(i);
                 if (!app.killedByAm && app.thread != null && app.containsCycle == true) {
-
-                    if (computeOomAdjLocked(app, ProcessList.UNKNOWN_ADJ, TOP_APP, true, now)) {
+                    if (computeOomAdjLocked(app, ProcessList.UNKNOWN_ADJ, TOP_APP, true, now,
+                            true)) {
                         retryCycles = true;
                     }
                 }
diff --git a/services/core/java/com/android/server/am/ConnectionRecord.java b/services/core/java/com/android/server/am/ConnectionRecord.java
index aa76b3d..af1031e 100644
--- a/services/core/java/com/android/server/am/ConnectionRecord.java
+++ b/services/core/java/com/android/server/am/ConnectionRecord.java
@@ -43,6 +43,7 @@
     final PendingIntent clientIntent; // How to launch the client.
     final int clientUid;            // The identity of this connection's client
     final String clientProcessName; // The source process of this connection's client
+    final String clientPackageName; // The source package of this connection's client
     public AssociationState.SourceState association; // Association tracking
     String stringName;              // Caching of toString.
     boolean serviceDead;            // Well is it?
@@ -96,7 +97,7 @@
             ActivityServiceConnectionsHolder<ConnectionRecord> _activity,
             IServiceConnection _conn, int _flags,
             int _clientLabel, PendingIntent _clientIntent,
-            int _clientUid, String _clientProcessName) {
+            int _clientUid, String _clientProcessName, String _clientPackageName) {
         binding = _binding;
         activity = _activity;
         conn = _conn;
@@ -105,6 +106,7 @@
         clientIntent = _clientIntent;
         clientUid = _clientUid;
         clientProcessName = _clientProcessName;
+        clientPackageName = _clientPackageName;
     }
 
     public void startAssociationIfNeeded() {
@@ -125,7 +127,7 @@
             } else {
                 association = holder.pkg.getAssociationStateLocked(holder.state,
                         binding.service.instanceName.getClassName()).startSource(clientUid,
-                        clientProcessName);
+                        clientProcessName, clientPackageName);
 
             }
         }
diff --git a/services/core/java/com/android/server/am/ContentProviderConnection.java b/services/core/java/com/android/server/am/ContentProviderConnection.java
index f2d4f73..5f184c2 100644
--- a/services/core/java/com/android/server/am/ContentProviderConnection.java
+++ b/services/core/java/com/android/server/am/ContentProviderConnection.java
@@ -32,6 +32,7 @@
 public final class ContentProviderConnection extends Binder {
     public final ContentProviderRecord provider;
     public final ProcessRecord client;
+    public final String clientPackage;
     public AssociationState.SourceState association;
     public final long createTime;
     public int stableCount;
@@ -46,9 +47,11 @@
     public int numStableIncs;
     public int numUnstableIncs;
 
-    public ContentProviderConnection(ContentProviderRecord _provider, ProcessRecord _client) {
+    public ContentProviderConnection(ContentProviderRecord _provider, ProcessRecord _client,
+            String _clientPackage) {
         provider = _provider;
         client = _client;
+        clientPackage = _clientPackage;
         createTime = SystemClock.elapsedRealtime();
     }
 
@@ -69,7 +72,8 @@
                         + provider.name.toShortString() + ": proc=" + provider.proc);
             } else {
                 association = holder.pkg.getAssociationStateLocked(holder.state,
-                        provider.name.getClassName()).startSource(client.uid, client.processName);
+                        provider.name.getClassName()).startSource(client.uid, client.processName,
+                        clientPackage);
 
             }
         }
diff --git a/services/core/java/com/android/server/am/ContentProviderRecord.java b/services/core/java/com/android/server/am/ContentProviderRecord.java
index 2fc4adc..46dfc7c 100644
--- a/services/core/java/com/android/server/am/ContentProviderRecord.java
+++ b/services/core/java/com/android/server/am/ContentProviderRecord.java
@@ -305,7 +305,7 @@
                 } else {
                     mAssociation = holder.pkg.getAssociationStateLocked(holder.state,
                             provider.name.getClassName()).startSource(mOwningUid,
-                            mOwningProcessName);
+                            mOwningProcessName, null);
 
                 }
             }
diff --git a/services/core/java/com/android/server/am/EventLogTags.logtags b/services/core/java/com/android/server/am/EventLogTags.logtags
index fa7a4c5..a71f6af 100644
--- a/services/core/java/com/android/server/am/EventLogTags.logtags
+++ b/services/core/java/com/android/server/am/EventLogTags.logtags
@@ -138,4 +138,4 @@
 30061 am_remove_task (Task ID|1|5), (Stack ID|1|5)
 
 # The task is being compacted
-30063 am_compact (Pid|1|5),(Process Name|3),(Action|3),(BeforeRssTotal|2|2),(BeforeRssFile|2|2),(BeforeRssAnon|2|2),(BeforeRssSwap|2|2),(AfterRssTotal|2|2),(AfterRssFile|2|2),(AfterRssAnon|2|2),(AfterRssSwap|2|2),(Time|2|3)
\ No newline at end of file
+30063 am_compact (Pid|1|5),(Process Name|3),(Action|3),(BeforeRssTotal|2|2),(BeforeRssFile|2|2),(BeforeRssAnon|2|2),(BeforeRssSwap|2|2),(AfterRssTotal|2|2),(AfterRssFile|2|2),(AfterRssAnon|2|2),(AfterRssSwap|2|2),(Time|2|3),(LastAction|1|2),(LastActionTimestamp|2|3),(setAdj|1|2),(procState|1|2)
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index c4b7150..c15b7c7 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -153,6 +153,7 @@
     int trimMemoryLevel;        // Last selected memory trimming level
     private int mCurProcState = PROCESS_STATE_NONEXISTENT; // Currently computed process state
     private int mRepProcState = PROCESS_STATE_NONEXISTENT; // Last reported process state
+    private int mCurRawProcState = PROCESS_STATE_NONEXISTENT; // Temp state during computation
     int setProcState = PROCESS_STATE_NONEXISTENT; // Last set process state in process tracker
     int pssProcState = PROCESS_STATE_NONEXISTENT; // Currently requesting pss for
     int pssStatType;            // The type of stat collection that we are currently requesting
@@ -902,6 +903,7 @@
         if (mRepProcState > newState) {
             mRepProcState = newState;
             setCurProcState(newState);
+            setCurRawProcState(newState);
             for (int ipkg = pkgList.size() - 1; ipkg >= 0; ipkg--) {
                 StatsLog.write(StatsLog.PROCESS_STATE_CHANGED,
                         uid, processName, pkgList.keyAt(ipkg),
@@ -984,6 +986,14 @@
         return mCurProcState;
     }
 
+    void setCurRawProcState(int curRawProcState) {
+        mCurRawProcState = curRawProcState;
+    }
+
+    int getCurRawProcState() {
+        return mCurRawProcState;
+    }
+
     void setReportedProcState(int repProcState) {
         mRepProcState = repProcState;
         for (int ipkg = pkgList.size() - 1; ipkg >= 0; ipkg--) {
diff --git a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
index 4c4a090..d5ede5b 100644
--- a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
+++ b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
@@ -19,8 +19,10 @@
 import android.content.ContentResolver;
 import android.database.ContentObserver;
 import android.net.Uri;
+import android.os.AsyncTask;
 import android.os.Build;
 import android.os.SystemProperties;
+import android.provider.DeviceConfig;
 import android.provider.Settings;
 import android.text.TextUtils;
 import android.util.Slog;
@@ -61,13 +63,18 @@
     // Add the global setting you want to push to native level as experiment flag into this list.
     //
     // NOTE: please grant write permission system property prefix
-    // with format persist.experiment.[experiment_category_name]. in system_server.te and grant read
-    // permission in the corresponding .te file your feature belongs to.
+    // with format persist.device_config.global_settings.[flag_name] in system_server.te and grant
+    // read permission in the corresponding .te file your feature belongs to.
     @VisibleForTesting
     static final String[] sGlobalSettings = new String[] {
             Settings.Global.NATIVE_FLAGS_HEALTH_CHECK_ENABLED,
     };
 
+    // All the flags under the listed DeviceConfig scopes will be synced to native level.
+    //
+    // NOTE: please grant write permission system property prefix
+    // with format persist.device_config.[device_config_scope]. in system_server.te and grant read
+    // permission in the corresponding .te file your feature belongs to.
     @VisibleForTesting
     static final String[] sDeviceConfigScopes = new String[] {
     };
@@ -104,19 +111,31 @@
             ContentObserver co = new ContentObserver(null) {
                 @Override
                 public void onChange(boolean selfChange) {
-                    updatePropertyFromSetting(globalSetting, propName, true);
+                    updatePropertyFromSetting(globalSetting, propName);
                 }
             };
 
             // only updating on starting up when no native flags reset is performed during current
             // booting.
             if (!isNativeFlagsResetPerformed()) {
-                updatePropertyFromSetting(globalSetting, propName, true);
+                updatePropertyFromSetting(globalSetting, propName);
             }
             mContentResolver.registerContentObserver(settingUri, false, co);
         }
 
-        // TODO: address sDeviceConfigScopes after DeviceConfig APIs are available.
+        for (String deviceConfigScope : mDeviceConfigScopes) {
+            DeviceConfig.addOnPropertyChangedListener(
+                    deviceConfigScope,
+                    AsyncTask.THREAD_POOL_EXECUTOR,
+                    (String scope, String name, String value) -> {
+                        String propertyName = makePropertyName(scope, name);
+                        if (propertyName == null) {
+                            log("unable to construct system property for " + scope + "/" + name);
+                            return;
+                        }
+                        setProperty(propertyName, value);
+                    });
+        }
     }
 
     public static SettingsToPropertiesMapper start(ContentResolver contentResolver) {
@@ -184,15 +203,6 @@
         return propertyName;
     }
 
-    private String getSetting(String name, boolean isGlobalSetting) {
-        if (isGlobalSetting) {
-            return Settings.Global.getString(mContentResolver, name);
-        } else {
-            // TODO: complete the code after DeviceConfig APIs implemented.
-            return null;
-        }
-    }
-
     private void setProperty(String key, String value) {
         // Check if need to clear the property
         if (value == null) {
@@ -259,8 +269,8 @@
     }
 
     @VisibleForTesting
-    void updatePropertyFromSetting(String settingName, String propName, boolean isGlobalSetting) {
-        String settingValue = getSetting(settingName, isGlobalSetting);
+    void updatePropertyFromSetting(String settingName, String propName) {
+        String settingValue = Settings.Global.getString(mContentResolver, settingName);
         setProperty(propName, settingValue);
     }
 }
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/display/AutomaticBrightnessController.java b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
index 78b3c15..52eccca 100644
--- a/services/core/java/com/android/server/display/AutomaticBrightnessController.java
+++ b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
@@ -17,6 +17,13 @@
 package com.android.server.display;
 
 import android.annotation.Nullable;
+import android.app.ActivityManager;
+import android.app.ActivityManager.StackInfo;
+import android.app.ActivityTaskManager;
+import android.app.IActivityTaskManager;
+import android.app.TaskStackListener;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManagerInternal;
 import android.hardware.Sensor;
 import android.hardware.SensorEvent;
 import android.hardware.SensorEventListener;
@@ -27,6 +34,8 @@
 import android.os.Looper;
 import android.os.Message;
 import android.os.PowerManager;
+import android.os.Process;
+import android.os.RemoteException;
 import android.os.SystemClock;
 import android.os.Trace;
 import android.util.EventLog;
@@ -34,14 +43,15 @@
 import android.util.Slog;
 import android.util.TimeUtils;
 
+import com.android.internal.os.BackgroundThread;
 import com.android.server.EventLogTags;
+import com.android.server.LocalServices;
 
 import java.io.PrintWriter;
 
 class AutomaticBrightnessController {
     private static final String TAG = "AutomaticBrightnessController";
 
-    private static final boolean DEBUG = false;
     private static final boolean DEBUG_PRETEND_LIGHT_SENSOR_ABSENT = false;
 
     // If true, enables the use of the screen auto-brightness adjustment setting.
@@ -66,6 +76,8 @@
     private static final int MSG_UPDATE_AMBIENT_LUX = 1;
     private static final int MSG_BRIGHTNESS_ADJUSTMENT_SAMPLE = 2;
     private static final int MSG_INVALIDATE_SHORT_TERM_MODEL = 3;
+    private static final int MSG_UPDATE_FOREGROUND_APP = 4;
+    private static final int MSG_UPDATE_FOREGROUND_APP_SYNC = 5;
 
     // Length of the ambient light horizon used to calculate the long term estimate of ambient
     // light.
@@ -126,6 +138,8 @@
     private final HysteresisLevels mAmbientBrightnessThresholds;
     private final HysteresisLevels mScreenBrightnessThresholds;
 
+    private boolean mLoggingEnabled;
+
     // Amount of time to delay auto-brightness after screen on while waiting for
     // the light sensor to warm-up in milliseconds.
     // May be 0 if no warm-up is required.
@@ -192,6 +206,19 @@
     private float mShortTermModelAnchor;
     private float SHORT_TERM_MODEL_THRESHOLD_RATIO = 0.6f;
 
+    // Context-sensitive brightness configurations require keeping track of the foreground app's
+    // package name and category, which is done by registering a TaskStackListener to call back to
+    // us onTaskStackChanged, and then using the ActivityTaskManager to get the foreground app's
+    // package namd and PackageManager to get its category (so might as well cache them).
+    private int mUserId;
+    private String mForegroundAppPackageName;
+    private String mPendingForegroundAppPackageName;
+    private @ApplicationInfo.Category int mForegroundAppCategory;
+    private @ApplicationInfo.Category int mPendingForegroundAppCategory;
+    private TaskStackListenerImpl mTaskStackListener;
+    private IActivityTaskManager mActivityTaskManager;
+    private PackageManagerInternal mPackageManagerInternal;
+
     public AutomaticBrightnessController(Callbacks callbacks, Looper looper,
             SensorManager sensorManager, BrightnessMappingStrategy mapper,
             int lightSensorWarmUpTime, int brightnessMin, int brightnessMax, float dozeScaleFactor,
@@ -226,6 +253,42 @@
         if (!DEBUG_PRETEND_LIGHT_SENSOR_ABSENT) {
             mLightSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_LIGHT);
         }
+
+        mUserId = ActivityManager.getCurrentUser();
+        mActivityTaskManager = ActivityTaskManager.getService();
+        mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
+        mTaskStackListener = new TaskStackListenerImpl();
+        mForegroundAppPackageName = null;
+        mPendingForegroundAppPackageName = null;
+        mForegroundAppCategory = ApplicationInfo.CATEGORY_UNDEFINED;
+        mPendingForegroundAppCategory = ApplicationInfo.CATEGORY_UNDEFINED;
+    }
+
+    /**
+     * Enable/disable logging.
+     *
+     * @param loggingEnabled
+     *      Whether logging should be on/off.
+     *
+     * @return Whether the method succeeded or not.
+     */
+    public boolean setLoggingEnabled(boolean loggingEnabled) {
+        if (mLoggingEnabled == loggingEnabled) {
+            return false;
+        }
+        mBrightnessMapper.setLoggingEnabled(loggingEnabled);
+        mLoggingEnabled = loggingEnabled;
+        return true;
+    }
+
+    /**
+     * Update the current user's ID.
+     *
+     * @param userId
+     *      The current user's ID.
+     */
+    public void onSwitchUser(int userId) {
+        mUserId = userId;
     }
 
     public int getAutomaticScreenBrightness() {
@@ -290,7 +353,7 @@
         }
         final int oldPolicy = mDisplayPolicy;
         mDisplayPolicy = policy;
-        if (DEBUG) {
+        if (mLoggingEnabled) {
             Slog.d(TAG, "Display policy transitioning from " + oldPolicy + " to " + policy);
         }
         if (!isInteractivePolicy(policy) && isInteractivePolicy(oldPolicy)) {
@@ -317,7 +380,7 @@
         mBrightnessMapper.addUserDataPoint(mAmbientLux, brightness);
         mShortTermModelValid = true;
         mShortTermModelAnchor = mAmbientLux;
-        if (DEBUG) {
+        if (mLoggingEnabled) {
             Slog.d(TAG, "ShortTermModel: anchor=" + mShortTermModelAnchor);
         }
         return true;
@@ -330,7 +393,7 @@
     }
 
     private void invalidateShortTermModel() {
-        if (DEBUG) {
+        if (mLoggingEnabled) {
             Slog.d(TAG, "ShortTermModel: invalidate user data");
         }
         mShortTermModelValid = false;
@@ -383,7 +446,11 @@
         pw.println("  mBrightnessAdjustmentSampleOldLux=" + mBrightnessAdjustmentSampleOldLux);
         pw.println("  mBrightnessAdjustmentSampleOldBrightness="
                 + mBrightnessAdjustmentSampleOldBrightness);
-        pw.println("  mShortTermModelValid=" + mShortTermModelValid);
+        pw.println("  mUserId=" + mUserId);
+        pw.println("  mForegroundAppPackageName=" + mForegroundAppPackageName);
+        pw.println("  mPendingForegroundAppPackageName=" + mPendingForegroundAppPackageName);
+        pw.println("  mForegroundAppCategory=" + mForegroundAppCategory);
+        pw.println("  mPendingForegroundAppCategory=" + mPendingForegroundAppCategory);
 
         pw.println();
         mBrightnessMapper.dump(pw);
@@ -399,6 +466,7 @@
                 mLightSensorEnabled = true;
                 mLightSensorEnableTime = SystemClock.uptimeMillis();
                 mCurrentLightSensorRate = mInitialLightSensorRate;
+                registerForegroundAppUpdater();
                 mSensorManager.registerListener(mLightSensorListener, mLightSensor,
                         mCurrentLightSensorRate * 1000, mHandler);
                 return true;
@@ -411,6 +479,7 @@
             mAmbientLightRingBuffer.clear();
             mCurrentLightSensorRate = -1;
             mHandler.removeMessages(MSG_UPDATE_AMBIENT_LUX);
+            unregisterForegroundAppUpdater();
             mSensorManager.unregisterListener(mLightSensorListener);
         }
         return false;
@@ -441,7 +510,7 @@
     private void adjustLightSensorRate(int lightSensorRate) {
         // if the light sensor rate changed, update the sensor listener
         if (lightSensorRate != mCurrentLightSensorRate) {
-            if (DEBUG) {
+            if (mLoggingEnabled) {
                 Slog.d(TAG, "adjustLightSensorRate: " +
                         "previousRate=" + mCurrentLightSensorRate + ", " +
                         "currentRate=" + lightSensorRate);
@@ -458,7 +527,7 @@
     }
 
     private void setAmbientLux(float lux) {
-        if (DEBUG) {
+        if (mLoggingEnabled) {
             Slog.d(TAG, "setAmbientLux(" + lux + ")");
         }
         if (lux < 0) {
@@ -476,7 +545,7 @@
             final float maxAmbientLux =
                 mShortTermModelAnchor + mShortTermModelAnchor * SHORT_TERM_MODEL_THRESHOLD_RATIO;
             if (minAmbientLux < mAmbientLux && mAmbientLux < maxAmbientLux) {
-                if (DEBUG) {
+                if (mLoggingEnabled) {
                     Slog.d(TAG, "ShortTermModel: re-validate user data, ambient lux is " +
                             minAmbientLux + " < " + mAmbientLux + " < " + maxAmbientLux);
                 }
@@ -490,7 +559,7 @@
     }
 
     private float calculateAmbientLux(long now, long horizon) {
-        if (DEBUG) {
+        if (mLoggingEnabled) {
             Slog.d(TAG, "calculateAmbientLux(" + now + ", " + horizon + ")");
         }
         final int N = mAmbientLightRingBuffer.size();
@@ -509,7 +578,7 @@
                 break;
             }
         }
-        if (DEBUG) {
+        if (mLoggingEnabled) {
             Slog.d(TAG, "calculateAmbientLux: selected endIndex=" + endIndex + ", point=(" +
                     mAmbientLightRingBuffer.getTime(endIndex) + ", " +
                     mAmbientLightRingBuffer.getLux(endIndex) + ")");
@@ -527,7 +596,7 @@
             final long startTime = eventTime - now;
             float weight = calculateWeight(startTime, endTime);
             float lux = mAmbientLightRingBuffer.getLux(i);
-            if (DEBUG) {
+            if (mLoggingEnabled) {
                 Slog.d(TAG, "calculateAmbientLux: [" + startTime + ", " + endTime + "]: " +
                         "lux=" + lux + ", " +
                         "weight=" + weight);
@@ -536,7 +605,7 @@
             sum += lux * weight;
             endTime = startTime;
         }
-        if (DEBUG) {
+        if (mLoggingEnabled) {
             Slog.d(TAG, "calculateAmbientLux: " +
                     "totalWeight=" + totalWeight + ", " +
                     "newAmbientLux=" + (sum / totalWeight));
@@ -591,7 +660,7 @@
             final long timeWhenSensorWarmedUp =
                 mLightSensorWarmUpTimeConfig + mLightSensorEnableTime;
             if (time < timeWhenSensorWarmedUp) {
-                if (DEBUG) {
+                if (mLoggingEnabled) {
                     Slog.d(TAG, "updateAmbientLux: Sensor not  ready yet: " +
                             "time=" + time + ", " +
                             "timeWhenSensorWarmedUp=" + timeWhenSensorWarmedUp);
@@ -602,7 +671,7 @@
             }
             setAmbientLux(calculateAmbientLux(time, AMBIENT_LIGHT_SHORT_HORIZON_MILLIS));
             mAmbientLuxValid = true;
-            if (DEBUG) {
+            if (mLoggingEnabled) {
                 Slog.d(TAG, "updateAmbientLux: Initializing: " +
                         "mAmbientLightRingBuffer=" + mAmbientLightRingBuffer + ", " +
                         "mAmbientLux=" + mAmbientLux);
@@ -630,10 +699,10 @@
                         && fastAmbientLux <= mAmbientDarkeningThreshold
                         && nextDarkenTransition <= time)) {
             setAmbientLux(fastAmbientLux);
-            if (DEBUG) {
+            if (mLoggingEnabled) {
                 Slog.d(TAG, "updateAmbientLux: "
                         + ((fastAmbientLux > mAmbientLux) ? "Brightened" : "Darkened") + ": "
-                        + "mAmbientBrighteningThreshold=" + mAmbientBrighteningThreshold + ", "
+                        + "mBrighteningLuxThreshold=" + mAmbientBrighteningThreshold + ", "
                         + "mAmbientLightRingBuffer=" + mAmbientLightRingBuffer + ", "
                         + "mAmbientLux=" + mAmbientLux);
             }
@@ -650,7 +719,7 @@
         // weighted ambient lux or not.
         nextTransitionTime =
                 nextTransitionTime > time ? nextTransitionTime : time + mNormalLightSensorRate;
-        if (DEBUG) {
+        if (mLoggingEnabled) {
             Slog.d(TAG, "updateAmbientLux: Scheduling ambient lux update for " +
                     nextTransitionTime + TimeUtils.formatUptime(nextTransitionTime));
         }
@@ -662,7 +731,8 @@
             return;
         }
 
-        float value = mBrightnessMapper.getBrightness(mAmbientLux);
+        float value = mBrightnessMapper.getBrightness(mAmbientLux, mForegroundAppPackageName,
+                mForegroundAppCategory);
 
         int newScreenAutoBrightness =
                 clampScreenBrightness(Math.round(value * PowerManager.BRIGHTNESS_ON));
@@ -673,7 +743,7 @@
         if (mScreenAutoBrightness != -1
                 && newScreenAutoBrightness > mScreenDarkeningThreshold
                 && newScreenAutoBrightness < mScreenBrighteningThreshold) {
-            if (DEBUG) {
+            if (mLoggingEnabled) {
                 Slog.d(TAG, "ignoring newScreenAutoBrightness: " + mScreenDarkeningThreshold
                         + " < " + newScreenAutoBrightness + " < " + mScreenBrighteningThreshold);
             }
@@ -681,8 +751,7 @@
         }
 
         if (mScreenAutoBrightness != newScreenAutoBrightness) {
-
-            if (DEBUG) {
+            if (mLoggingEnabled) {
                 Slog.d(TAG, "updateAutoBrightness: " +
                         "mScreenAutoBrightness=" + mScreenAutoBrightness + ", " +
                         "newScreenAutoBrightness=" + newScreenAutoBrightness);
@@ -718,18 +787,11 @@
                 BRIGHTNESS_ADJUSTMENT_SAMPLE_DEBOUNCE_MILLIS);
     }
 
-    private void cancelBrightnessAdjustmentSample() {
-        if (mBrightnessAdjustmentSamplePending) {
-            mBrightnessAdjustmentSamplePending = false;
-            mHandler.removeMessages(MSG_BRIGHTNESS_ADJUSTMENT_SAMPLE);
-        }
-    }
-
     private void collectBrightnessAdjustmentSample() {
         if (mBrightnessAdjustmentSamplePending) {
             mBrightnessAdjustmentSamplePending = false;
             if (mAmbientLuxValid && mScreenAutoBrightness >= 0) {
-                if (DEBUG) {
+                if (mLoggingEnabled) {
                     Slog.d(TAG, "Auto-brightness adjustment changed by user: " +
                             "lux=" + mAmbientLux + ", " +
                             "brightness=" + mScreenAutoBrightness + ", " +
@@ -745,6 +807,68 @@
         }
     }
 
+    // Register a TaskStackListener to call back to us onTaskStackChanged, so we can update the
+    // foreground app's package name and category and correct the brightness accordingly.
+    private void registerForegroundAppUpdater() {
+        try {
+            mActivityTaskManager.registerTaskStackListener(mTaskStackListener);
+            // This will not get called until the foreground app changes for the first time, so
+            // call it explicitly to get the current foreground app's info.
+            updateForegroundApp();
+        } catch (RemoteException e) {
+            // Nothing to do.
+        }
+    }
+
+    private void unregisterForegroundAppUpdater() {
+        try {
+            mActivityTaskManager.unregisterTaskStackListener(mTaskStackListener);
+        } catch (RemoteException e) {
+            // Nothing to do.
+        }
+        mForegroundAppPackageName = null;
+        mForegroundAppCategory = ApplicationInfo.CATEGORY_UNDEFINED;
+    }
+
+    // Set the foreground app's package name and category, so brightness can be corrected per app.
+    private void updateForegroundApp() {
+        // The ActivityTaskManager's lock tends to get contended, so this is done in a background
+        // thread and applied via this thread's handler synchronously.
+        BackgroundThread.getHandler().post(new Runnable() {
+            public void run() {
+                try {
+                    // The foreground app is the top activity of the focused tasks stack.
+                    final StackInfo info = mActivityTaskManager.getFocusedStackInfo();
+                    if (info == null || info.topActivity == null) {
+                        return;
+                    }
+                    final String packageName = info.topActivity.getPackageName();
+                    // If the app didn't change, there's nothing to do. Otherwise, we have to
+                    // update the category and re-apply the brightness correction.
+                    if (mForegroundAppPackageName != null
+                            && mForegroundAppPackageName.equals(packageName)) {
+                        return;
+                    }
+                    mPendingForegroundAppPackageName = packageName;
+                    ApplicationInfo app = mPackageManagerInternal.getApplicationInfo(packageName,
+                            0 /* flags */, Process.SYSTEM_UID /* filterCallingUid */, mUserId);
+                    mPendingForegroundAppCategory = app.category;
+                    mHandler.sendEmptyMessage(MSG_UPDATE_FOREGROUND_APP_SYNC);
+                } catch (RemoteException e) {
+                    // Nothing to do.
+                }
+            }
+        });
+    }
+
+    private void updateForegroundAppSync() {
+        mForegroundAppPackageName = mPendingForegroundAppPackageName;
+        mPendingForegroundAppPackageName = null;
+        mForegroundAppCategory = mPendingForegroundAppCategory;
+        mPendingForegroundAppCategory = ApplicationInfo.CATEGORY_UNDEFINED;
+        updateAutoBrightness(true /* sendUpdate */);
+    }
+
     private final class AutomaticBrightnessHandler extends Handler {
         public AutomaticBrightnessHandler(Looper looper) {
             super(looper, null, true /*async*/);
@@ -764,6 +888,14 @@
                 case MSG_INVALIDATE_SHORT_TERM_MODEL:
                     invalidateShortTermModel();
                     break;
+
+                case MSG_UPDATE_FOREGROUND_APP:
+                    updateForegroundApp();
+                    break;
+
+                case MSG_UPDATE_FOREGROUND_APP_SYNC:
+                    updateForegroundAppSync();
+                    break;
             }
         }
     }
@@ -784,6 +916,15 @@
         }
     };
 
+    // Call back whenever the tasks stack changes, which includes tasks being created, removed, and
+    // moving to top.
+    class TaskStackListenerImpl extends TaskStackListener {
+        @Override
+        public void onTaskStackChanged() {
+            mHandler.sendEmptyMessage(MSG_UPDATE_FOREGROUND_APP);
+        }
+    }
+
     /** Callbacks to request updates to the display's power state. */
     interface Callbacks {
         void updateBrightness();
diff --git a/services/core/java/com/android/server/display/BrightnessMappingStrategy.java b/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
index 76c191d..9fce644 100644
--- a/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
+++ b/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
@@ -17,9 +17,11 @@
 package com.android.server.display;
 
 import android.annotation.Nullable;
+import android.content.pm.ApplicationInfo;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
 import android.hardware.display.BrightnessConfiguration;
+import android.hardware.display.BrightnessCorrection;
 import android.os.PowerManager;
 import android.util.MathUtils;
 import android.util.Pair;
@@ -42,11 +44,12 @@
  */
 public abstract class BrightnessMappingStrategy {
     private static final String TAG = "BrightnessMappingStrategy";
-    private static final boolean DEBUG = false;
 
     private static final float LUX_GRAD_SMOOTHING = 0.25f;
     private static final float MAX_GRAD = 1.0f;
 
+    protected boolean mLoggingEnabled;
+
     private static final Plog PLOG = Plog.createSystemPlog(TAG);
 
     @Nullable
@@ -161,6 +164,22 @@
     }
 
     /**
+     * Enable/disable logging.
+     *
+     * @param loggingEnabled
+     *      Whether logging should be on/off.
+     *
+     * @return Whether the method succeeded or not.
+     */
+    public boolean setLoggingEnabled(boolean loggingEnabled) {
+        if (mLoggingEnabled == loggingEnabled) {
+            return false;
+        }
+        mLoggingEnabled = loggingEnabled;
+        return true;
+    }
+
+    /**
      * Sets the {@link BrightnessConfiguration}.
      *
      * @param config The new configuration. If {@code null} is passed, the default configuration is
@@ -170,15 +189,33 @@
     public abstract boolean setBrightnessConfiguration(@Nullable BrightnessConfiguration config);
 
     /**
-     * Returns the desired brightness of the display based on the current ambient lux.
+     * Returns the desired brightness of the display based on the current ambient lux, including
+     * any context-related corrections.
      *
      * The returned brightness will be in the range [0, 1.0], where 1.0 is the display at max
      * brightness and 0 is the display at minimum brightness.
      *
      * @param lux The current ambient brightness in lux.
+     * @param packageName the foreground app package name.
+     * @param category the foreground app package category.
      * @return The desired brightness of the display normalized to the range [0, 1.0].
      */
-    public abstract float getBrightness(float lux);
+    public abstract float getBrightness(float lux, String packageName,
+            @ApplicationInfo.Category int category);
+
+    /**
+     * Returns the desired brightness of the display based on the current ambient lux.
+     *
+     * The returned brightness wil be in the range [0, 1.0], where 1.0 is the display at max
+     * brightness and 0 is the display at minimum brightness.
+     *
+     * @param lux The current ambient brightness in lux.
+     *
+     * @return The desired brightness of the display normalized to the range [0, 1.0].
+     */
+    public float getBrightness(float lux) {
+        return getBrightness(lux, null /* packageName */, ApplicationInfo.CATEGORY_UNDEFINED);
+    }
 
     /**
      * Returns the current auto-brightness adjustment.
@@ -239,13 +276,13 @@
 
     public abstract void dump(PrintWriter pw);
 
-    private static float normalizeAbsoluteBrightness(int brightness) {
+    protected float normalizeAbsoluteBrightness(int brightness) {
         brightness = MathUtils.constrain(brightness,
                 PowerManager.BRIGHTNESS_OFF, PowerManager.BRIGHTNESS_ON);
         return (float) brightness / PowerManager.BRIGHTNESS_ON;
     }
 
-    private static Pair<float[], float[]> insertControlPoint(
+    private Pair<float[], float[]> insertControlPoint(
             float[] luxLevels, float[] brightnessLevels, float lux, float brightness) {
         final int idx = findInsertionPoint(luxLevels, lux);
         final float[] newLuxLevels;
@@ -278,7 +315,7 @@
      * This assumes that {@code arr} is sorted. If all values in {@code arr} are greater
      * than val, then it will return the length of arr as the insertion point.
      */
-    private static int findInsertionPoint(float[] arr, float val) {
+    private int findInsertionPoint(float[] arr, float val) {
         for (int i = 0; i < arr.length; i++) {
             if (val <= arr[i]) {
                 return i;
@@ -287,8 +324,8 @@
         return arr.length;
     }
 
-    private static void smoothCurve(float[] lux, float[] brightness, int idx) {
-        if (DEBUG) {
+    private void smoothCurve(float[] lux, float[] brightness, int idx) {
+        if (mLoggingEnabled) {
             PLOG.logCurve("unsmoothed curve", lux, brightness);
         }
         float prevLux = lux[idx];
@@ -323,19 +360,19 @@
             prevBrightness = newBrightness;
             brightness[i] = newBrightness;
         }
-        if (DEBUG) {
+        if (mLoggingEnabled) {
             PLOG.logCurve("smoothed curve", lux, brightness);
         }
     }
 
-    private static float permissibleRatio(float currLux, float prevLux) {
+    private float permissibleRatio(float currLux, float prevLux) {
         return MathUtils.exp(MAX_GRAD
                 * (MathUtils.log(currLux + LUX_GRAD_SMOOTHING)
                     - MathUtils.log(prevLux + LUX_GRAD_SMOOTHING)));
     }
 
-    private static float inferAutoBrightnessAdjustment(float maxGamma,
-            float desiredBrightness, float currentBrightness) {
+    protected float inferAutoBrightnessAdjustment(float maxGamma, float desiredBrightness,
+            float currentBrightness) {
         float adjustment = 0;
         float gamma = Float.NaN;
         // Extreme edge cases: use a simpler heuristic, as proper gamma correction around the edges
@@ -355,7 +392,7 @@
             adjustment = -MathUtils.log(gamma) / MathUtils.log(maxGamma);
         }
         adjustment = MathUtils.constrain(adjustment, -1, +1);
-        if (DEBUG) {
+        if (mLoggingEnabled) {
             Slog.d(TAG, "inferAutoBrightnessAdjustment: " + maxGamma + "^" + -adjustment + "=" +
                     MathUtils.pow(maxGamma, -adjustment) + " == " + gamma);
             Slog.d(TAG, "inferAutoBrightnessAdjustment: " + currentBrightness + "^" + gamma + "=" +
@@ -364,16 +401,16 @@
         return adjustment;
     }
 
-    private static Pair<float[], float[]> getAdjustedCurve(float[] lux, float[] brightness,
+    protected Pair<float[], float[]> getAdjustedCurve(float[] lux, float[] brightness,
             float userLux, float userBrightness, float adjustment, float maxGamma) {
         float[] newLux = lux;
         float[] newBrightness = Arrays.copyOf(brightness, brightness.length);
-        if (DEBUG) {
+        if (mLoggingEnabled) {
             PLOG.logCurve("unadjusted curve", newLux, newBrightness);
         }
         adjustment = MathUtils.constrain(adjustment, -1, 1);
         float gamma = MathUtils.pow(maxGamma, -adjustment);
-        if (DEBUG) {
+        if (mLoggingEnabled) {
             Slog.d(TAG, "getAdjustedCurve: " + maxGamma + "^" + -adjustment + "=" +
                     MathUtils.pow(maxGamma, -adjustment) + " == " + gamma);
         }
@@ -382,7 +419,7 @@
                 newBrightness[i] = MathUtils.pow(newBrightness[i], gamma);
             }
         }
-        if (DEBUG) {
+        if (mLoggingEnabled) {
             PLOG.logCurve("gamma adjusted curve", newLux, newBrightness);
         }
         if (userLux != -1) {
@@ -390,7 +427,7 @@
                     userBrightness);
             newLux = curve.first;
             newBrightness = curve.second;
-            if (DEBUG) {
+            if (mLoggingEnabled) {
                 PLOG.logCurve("gamma and user adjusted curve", newLux, newBrightness);
                 // This is done for comparison.
                 curve = insertControlPoint(lux, brightness, userLux, userBrightness);
@@ -440,7 +477,7 @@
             mAutoBrightnessAdjustment = 0;
             mUserLux = -1;
             mUserBrightness = -1;
-            if (DEBUG) {
+            if (mLoggingEnabled) {
                 PLOG.start("simple mapping strategy");
             }
             computeSpline();
@@ -452,7 +489,8 @@
         }
 
         @Override
-        public float getBrightness(float lux) {
+        public float getBrightness(float lux, String packageName,
+                @ApplicationInfo.Category int category) {
             return mSpline.interpolate(lux);
         }
 
@@ -467,7 +505,7 @@
             if (adjustment == mAutoBrightnessAdjustment) {
                 return false;
             }
-            if (DEBUG) {
+            if (mLoggingEnabled) {
                 Slog.d(TAG, "setAutoBrightnessAdjustment: " + mAutoBrightnessAdjustment + " => " +
                         adjustment);
                 PLOG.start("auto-brightness adjustment");
@@ -485,7 +523,7 @@
         @Override
         public void addUserDataPoint(float lux, float brightness) {
             float unadjustedBrightness = getUnadjustedBrightness(lux);
-            if (DEBUG){
+            if (mLoggingEnabled) {
                 Slog.d(TAG, "addUserDataPoint: (" + lux + "," + brightness + ")");
                 PLOG.start("add user data point")
                         .logPoint("user data point", lux, brightness)
@@ -494,7 +532,7 @@
             float adjustment = inferAutoBrightnessAdjustment(mMaxGamma,
                     brightness /* desiredBrightness */,
                     unadjustedBrightness /* currentBrightness */);
-            if (DEBUG) {
+            if (mLoggingEnabled) {
                 Slog.d(TAG, "addUserDataPoint: " + mAutoBrightnessAdjustment + " => " +
                         adjustment);
             }
@@ -507,7 +545,7 @@
         @Override
         public void clearUserDataPoints() {
             if (mUserLux != -1) {
-                if (DEBUG) {
+                if (mLoggingEnabled) {
                     Slog.d(TAG, "clearUserDataPoints: " + mAutoBrightnessAdjustment + " => 0");
                     PLOG.start("clear user data points")
                             .logPoint("user data point", mUserLux, mUserBrightness);
@@ -614,7 +652,7 @@
             mBacklightToNitsSpline = Spline.createSpline(normalizedBacklight, nits);
 
             mDefaultConfig = config;
-            if (DEBUG) {
+            if (mLoggingEnabled) {
                 PLOG.start("physical mapping strategy");
             }
             mConfig = config;
@@ -629,7 +667,7 @@
             if (config.equals(mConfig)) {
                 return false;
             }
-            if (DEBUG) {
+            if (mLoggingEnabled) {
                 PLOG.start("brightness configuration");
             }
             mConfig = config;
@@ -638,9 +676,17 @@
         }
 
         @Override
-        public float getBrightness(float lux) {
+        public float getBrightness(float lux, String packageName,
+                @ApplicationInfo.Category int category) {
             float nits = mBrightnessSpline.interpolate(lux);
             float backlight = mNitsToBacklightSpline.interpolate(nits);
+            // Correct the brightness according to the current application and its category, but
+            // only if no user data point is set (as this will oevrride the user setting).
+            if (mUserLux == -1) {
+                backlight = correctBrightness(backlight, packageName, category);
+            } else if (mLoggingEnabled) {
+                Slog.d(TAG, "user point set, correction not applied");
+            }
             return backlight;
         }
 
@@ -655,7 +701,7 @@
             if (adjustment == mAutoBrightnessAdjustment) {
                 return false;
             }
-            if (DEBUG) {
+            if (mLoggingEnabled) {
                 Slog.d(TAG, "setAutoBrightnessAdjustment: " + mAutoBrightnessAdjustment + " => " +
                         adjustment);
                 PLOG.start("auto-brightness adjustment");
@@ -673,7 +719,7 @@
         @Override
         public void addUserDataPoint(float lux, float brightness) {
             float unadjustedBrightness = getUnadjustedBrightness(lux);
-            if (DEBUG){
+            if (mLoggingEnabled) {
                 Slog.d(TAG, "addUserDataPoint: (" + lux + "," + brightness + ")");
                 PLOG.start("add user data point")
                         .logPoint("user data point", lux, brightness)
@@ -682,7 +728,7 @@
             float adjustment = inferAutoBrightnessAdjustment(mMaxGamma,
                     brightness /* desiredBrightness */,
                     unadjustedBrightness /* currentBrightness */);
-            if (DEBUG) {
+            if (mLoggingEnabled) {
                 Slog.d(TAG, "addUserDataPoint: " + mAutoBrightnessAdjustment + " => " +
                         adjustment);
             }
@@ -695,7 +741,7 @@
         @Override
         public void clearUserDataPoints() {
             if (mUserLux != -1) {
-                if (DEBUG) {
+                if (mLoggingEnabled) {
                     Slog.d(TAG, "clearUserDataPoints: " + mAutoBrightnessAdjustment + " => 0");
                     PLOG.start("clear user data points")
                             .logPoint("user data point", mUserLux, mUserBrightness);
@@ -758,5 +804,21 @@
             Spline spline = Spline.createSpline(curve.first, curve.second);
             return mNitsToBacklightSpline.interpolate(spline.interpolate(lux));
         }
+
+        private float correctBrightness(float brightness, String packageName, int category) {
+            if (packageName != null) {
+                BrightnessCorrection correction = mConfig.getCorrectionByPackageName(packageName);
+                if (correction != null) {
+                    return correction.apply(brightness);
+                }
+            }
+            if (category != ApplicationInfo.CATEGORY_UNDEFINED) {
+                BrightnessCorrection correction = mConfig.getCorrectionByCategory(category);
+                if (correction != null) {
+                    return correction.apply(brightness);
+                }
+            }
+            return brightness;
+        }
     }
 }
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index cf8d21b..b1ba05c 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -1296,58 +1296,21 @@
             return;
         }
         display.configureDisplayLocked(t, device, info.state == Display.STATE_OFF);
-
+        final int viewportType;
         // Update the corresponding viewport.
-        DisplayViewport internalViewport = getInternalViewportLocked();
         if ((info.flags & DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY) != 0) {
-            populateViewportLocked(internalViewport, display, device);
-        }
-        DisplayViewport externalViewport = getExternalViewportLocked();
-        if (info.touch == DisplayDeviceInfo.TOUCH_EXTERNAL) {
-            populateViewportLocked(externalViewport, display, device);
-        } else if (!externalViewport.valid) {
-            // TODO (b/116850516) move this logic into InputReader
-            externalViewport.copyFrom(internalViewport);
-            externalViewport.type = DisplayViewport.VIEWPORT_EXTERNAL;
+            viewportType = VIEWPORT_INTERNAL;
+        } else if (info.touch == DisplayDeviceInfo.TOUCH_EXTERNAL) {
+            viewportType = VIEWPORT_EXTERNAL;
+        } else if (info.touch == DisplayDeviceInfo.TOUCH_VIRTUAL
+                && !TextUtils.isEmpty(info.uniqueId)) {
+            viewportType = VIEWPORT_VIRTUAL;
+        } else {
+            Slog.wtf(TAG, "Unable to populate viewport for display device: " + info);
+            return;
         }
 
-        if (info.touch == DisplayDeviceInfo.TOUCH_VIRTUAL && !TextUtils.isEmpty(info.uniqueId)) {
-            final DisplayViewport viewport = getVirtualViewportLocked(info.uniqueId);
-            populateViewportLocked(viewport, display, device);
-        }
-    }
-
-    /** Get the virtual device viewport that has the specified uniqueId.
-     * If such viewport does not exist, create it. */
-    private DisplayViewport getVirtualViewportLocked(@NonNull String uniqueId) {
-        DisplayViewport viewport;
-        final int count = mViewports.size();
-        for (int i = 0; i < count; i++) {
-            viewport = mViewports.get(i);
-            if (uniqueId.equals(viewport.uniqueId)) {
-                if (viewport.type != VIEWPORT_VIRTUAL) {
-                    Slog.wtf(TAG, "Found a viewport with uniqueId '"  + uniqueId
-                            + "' but it has type " + DisplayViewport.typeToString(viewport.type)
-                            + " (expected VIRTUAL)");
-                    continue;
-                }
-                return viewport;
-            }
-        }
-
-        viewport = new DisplayViewport();
-        viewport.uniqueId = uniqueId;
-        viewport.type = VIEWPORT_VIRTUAL;
-        mViewports.add(viewport);
-        return viewport;
-    }
-
-    private DisplayViewport getInternalViewportLocked() {
-        return getViewportByTypeLocked(VIEWPORT_INTERNAL);
-    }
-
-    private DisplayViewport getExternalViewportLocked() {
-        return getViewportByTypeLocked(VIEWPORT_EXTERNAL);
+        populateViewportLocked(viewportType, display.getDisplayIdLocked(), device, info.uniqueId);
     }
 
     /**
@@ -1355,35 +1318,44 @@
      * @param viewportType - either INTERNAL or EXTERNAL
      * @return the viewport with the requested type
      */
-    private DisplayViewport getViewportByTypeLocked(int viewportType) {
-        // Only allow a single INTERNAL or EXTERNAL viewport, which makes this function possible.
-        // TODO (b/116824030) allow multiple EXTERNAL viewports and remove this function.
-        // Creates the viewport if none exists.
-        if (viewportType != VIEWPORT_INTERNAL && viewportType != VIEWPORT_EXTERNAL) {
+    private DisplayViewport getViewportLocked(int viewportType, String uniqueId) {
+        if (viewportType != VIEWPORT_INTERNAL && viewportType != VIEWPORT_EXTERNAL
+                && viewportType != VIEWPORT_VIRTUAL) {
             Slog.wtf(TAG, "Cannot call getViewportByTypeLocked for type "
                     + DisplayViewport.typeToString(viewportType));
             return null;
         }
+
+        // Only allow a single INTERNAL or EXTERNAL viewport by forcing their uniqueIds
+        // to be identical (in particular, empty).
+        // TODO (b/116824030) allow multiple EXTERNAL viewports and remove this function.
+        if (viewportType != VIEWPORT_VIRTUAL) {
+            uniqueId = "";
+        }
+
         DisplayViewport viewport;
         final int count = mViewports.size();
         for (int i = 0; i < count; i++) {
             viewport = mViewports.get(i);
-            if (viewport.type == viewportType) {
+            if (viewport.type == viewportType && uniqueId.equals(viewport.uniqueId)) {
                 return viewport;
             }
         }
 
+        // Creates the viewport if none exists.
         viewport = new DisplayViewport();
         viewport.type = viewportType;
+        viewport.uniqueId = uniqueId;
         mViewports.add(viewport);
         return viewport;
     }
 
-    private static void populateViewportLocked(DisplayViewport viewport,
-            LogicalDisplay display, DisplayDevice device) {
-        viewport.valid = true;
-        viewport.displayId = display.getDisplayIdLocked();
+    private void populateViewportLocked(int viewportType,
+            int displayId, DisplayDevice device, String uniqueId) {
+        final DisplayViewport viewport = getViewportLocked(viewportType, uniqueId);
         device.populateViewportLocked(viewport);
+        viewport.valid = true;
+        viewport.displayId = displayId;
     }
 
     private LogicalDisplay findLogicalDisplayForDeviceLocked(DisplayDevice device) {
@@ -2172,6 +2144,14 @@
                     mContext.getPackageName());
         }
 
+        void setAutoBrightnessLoggingEnabled(boolean enabled) {
+            if (mDisplayPowerController != null) {
+                synchronized (mSyncRoot) {
+                    mDisplayPowerController.setAutoBrightnessLoggingEnabled(enabled);
+                }
+            }
+        }
+
         private boolean validatePackageName(int uid, String packageName) {
             if (packageName != null) {
                 String[] packageNames = mContext.getPackageManager().getPackagesForUid(uid);
diff --git a/services/core/java/com/android/server/display/DisplayManagerShellCommand.java b/services/core/java/com/android/server/display/DisplayManagerShellCommand.java
index 27cad1e..abbfc7b 100644
--- a/services/core/java/com/android/server/display/DisplayManagerShellCommand.java
+++ b/services/core/java/com/android/server/display/DisplayManagerShellCommand.java
@@ -17,14 +17,9 @@
 package com.android.server.display;
 
 import android.content.Intent;
-import android.os.RemoteException;
-import android.os.ResultReceiver;
-import android.os.ShellCallback;
 import android.os.ShellCommand;
-import android.util.Slog;
 
 import java.io.PrintWriter;
-import java.lang.NumberFormatException;
 
 class DisplayManagerShellCommand extends ShellCommand {
     private static final String TAG = "DisplayManagerShellCommand";
@@ -46,6 +41,10 @@
                 return setBrightness();
             case "reset-brightness-configuration":
                 return resetBrightnessConfiguration();
+            case "ab-logging-enable":
+                return setAutoBrightnessLoggingEnabled(true);
+            case "ab-logging-disable":
+                return setAutoBrightnessLoggingEnabled(false);
             default:
                 return handleDefaultCommands(cmd);
         }
@@ -62,6 +61,10 @@
         pw.println("    Sets the current brightness to BRIGHTNESS (a number between 0 and 1).");
         pw.println("  reset-brightness-configuration");
         pw.println("    Reset the brightness to its default configuration.");
+        pw.println("  ab-logging-enable");
+        pw.println("    Enable auto-brightness logging.");
+        pw.println("  ab-logging-disable");
+        pw.println("    Disable auto-brightness logging.");
         pw.println();
         Intent.printIntentArgsHelp(pw , "");
     }
@@ -89,4 +92,9 @@
         mService.resetBrightnessConfiguration();
         return 0;
     }
+
+    private int setAutoBrightnessLoggingEnabled(boolean enabled) {
+        mService.setAutoBrightnessLoggingEnabled(enabled);
+        return 0;
+    }
 }
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 249270b..c9ed9f7 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -523,6 +523,9 @@
     public void onSwitchUser(@UserIdInt int newUserId) {
         handleSettingsChange(true /* userSwitch */);
         mBrightnessTracker.onSwitchUser(newUserId);
+        if (mAutomaticBrightnessController != null) {
+            mAutomaticBrightnessController.onSwitchUser(newUserId);
+        }
     }
 
     public ParceledListSlice<AmbientBrightnessDayStats> getAmbientBrightnessStats(
@@ -1836,4 +1839,10 @@
             mHandler.sendMessage(msg);
         }
     }
+
+    void setAutoBrightnessLoggingEnabled(boolean enabled) {
+        if (mAutomaticBrightnessController != null) {
+            mAutomaticBrightnessController.setLoggingEnabled(enabled);
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/display/PersistentDataStore.java b/services/core/java/com/android/server/display/PersistentDataStore.java
index 89cef62..9aec43b 100644
--- a/services/core/java/com/android/server/display/PersistentDataStore.java
+++ b/services/core/java/com/android/server/display/PersistentDataStore.java
@@ -16,13 +16,6 @@
 
 package com.android.server.display;
 
-import com.android.internal.util.FastXmlSerializer;
-import com.android.internal.util.XmlUtils;
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
-
 import android.annotation.Nullable;
 import android.graphics.Point;
 import android.hardware.display.BrightnessConfiguration;
@@ -30,13 +23,20 @@
 import android.util.AtomicFile;
 import android.util.Slog;
 import android.util.SparseArray;
-import android.util.Pair;
 import android.util.SparseLongArray;
 import android.util.TimeUtils;
 import android.util.Xml;
 import android.view.Display;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.FastXmlSerializer;
+import com.android.internal.util.XmlUtils;
+
+import libcore.io.IoUtils;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
 
 import java.io.BufferedInputStream;
 import java.io.BufferedOutputStream;
@@ -50,12 +50,9 @@
 import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
 import java.util.HashMap;
-import java.util.List;
 import java.util.Map;
 import java.util.Objects;
 
-import libcore.io.IoUtils;
-
 /**
  * Manages persistent state recorded by the display manager service as an XML file.
  * Caller must acquire lock on the data store before accessing it.
@@ -110,14 +107,9 @@
 
     private static final String TAG_BRIGHTNESS_CONFIGURATIONS = "brightness-configurations";
     private static final String TAG_BRIGHTNESS_CONFIGURATION = "brightness-configuration";
-    private static final String TAG_BRIGHTNESS_CURVE = "brightness-curve";
-    private static final String TAG_BRIGHTNESS_POINT = "brightness-point";
     private static final String ATTR_USER_SERIAL = "user-serial";
     private static final String ATTR_PACKAGE_NAME = "package-name";
     private static final String ATTR_TIME_STAMP = "timestamp";
-    private static final String ATTR_LUX = "lux";
-    private static final String ATTR_NITS = "nits";
-    private static final String ATTR_DESCRIPTION = "description";
 
     // Remembered Wifi display devices.
     private ArrayList<WifiDisplay> mRememberedWifiDisplays = new ArrayList<WifiDisplay>();
@@ -646,7 +638,8 @@
                     }
 
                     try {
-                        BrightnessConfiguration config = loadConfigurationFromXml(parser);
+                        BrightnessConfiguration config =
+                                BrightnessConfiguration.loadFromXml(parser);
                         if (userSerial >= 0 && config != null) {
                             mConfigurations.put(userSerial, config);
                             if (timeStamp != -1) {
@@ -663,56 +656,6 @@
             }
         }
 
-        private static BrightnessConfiguration loadConfigurationFromXml(XmlPullParser parser)
-                throws IOException, XmlPullParserException {
-            final int outerDepth = parser.getDepth();
-            String description = null;
-            Pair<float[], float[]> curve = null;
-            while (XmlUtils.nextElementWithin(parser, outerDepth)) {
-                if (TAG_BRIGHTNESS_CURVE.equals(parser.getName())) {
-                    description = parser.getAttributeValue(null, ATTR_DESCRIPTION);
-                    curve = loadCurveFromXml(parser);
-                }
-            }
-            if (curve == null) {
-                return null;
-            }
-            final BrightnessConfiguration.Builder builder = new BrightnessConfiguration.Builder(
-                    curve.first, curve.second);
-            builder.setDescription(description);
-            return builder.build();
-        }
-
-        private static Pair<float[], float[]> loadCurveFromXml(XmlPullParser parser)
-                throws IOException, XmlPullParserException {
-            final int outerDepth = parser.getDepth();
-            List<Float> luxLevels = new ArrayList<>();
-            List<Float> nitLevels = new ArrayList<>();
-            while (XmlUtils.nextElementWithin(parser, outerDepth)) {
-                if (TAG_BRIGHTNESS_POINT.equals(parser.getName())) {
-                    luxLevels.add(loadFloat(parser.getAttributeValue(null, ATTR_LUX)));
-                    nitLevels.add(loadFloat(parser.getAttributeValue(null, ATTR_NITS)));
-                }
-            }
-            final int N = luxLevels.size();
-            float[] lux = new float[N];
-            float[] nits = new float[N];
-            for (int i = 0; i < N; i++) {
-                lux[i] = luxLevels.get(i);
-                nits[i] = nitLevels.get(i);
-            }
-            return Pair.create(lux, nits);
-        }
-
-        private static float loadFloat(String val) {
-            try {
-                return Float.parseFloat(val);
-            } catch (NullPointerException | NumberFormatException e) {
-                Slog.e(TAG, "Failed to parse float loading brightness config", e);
-                return Float.NEGATIVE_INFINITY;
-            }
-        }
-
         public void saveToXml(XmlSerializer serializer) throws IOException {
             for (int i = 0; i < mConfigurations.size(); i++) {
                 final int userSerial = mConfigurations.keyAt(i);
@@ -728,27 +671,11 @@
                 if (timestamp != -1) {
                     serializer.attribute(null, ATTR_TIME_STAMP, Long.toString(timestamp));
                 }
-                saveConfigurationToXml(serializer, config);
+                config.saveToXml(serializer);
                 serializer.endTag(null, TAG_BRIGHTNESS_CONFIGURATION);
             }
         }
 
-        private static void saveConfigurationToXml(XmlSerializer serializer,
-                BrightnessConfiguration config) throws IOException {
-            serializer.startTag(null, TAG_BRIGHTNESS_CURVE);
-            if (config.getDescription() != null) {
-                serializer.attribute(null, ATTR_DESCRIPTION, config.getDescription());
-            }
-            final Pair<float[], float[]> curve = config.getCurve();
-            for (int i = 0; i < curve.first.length; i++) {
-                serializer.startTag(null, TAG_BRIGHTNESS_POINT);
-                serializer.attribute(null, ATTR_LUX, Float.toString(curve.first[i]));
-                serializer.attribute(null, ATTR_NITS, Float.toString(curve.second[i]));
-                serializer.endTag(null, TAG_BRIGHTNESS_POINT);
-            }
-            serializer.endTag(null, TAG_BRIGHTNESS_CURVE);
-        }
-
         public void dump(final PrintWriter pw, final String prefix) {
             for (int i = 0; i < mConfigurations.size(); i++) {
                 final int userSerial = mConfigurations.keyAt(i);
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index c0d3fdf..f468c0bf 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -81,8 +81,10 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Locale;
+import java.util.Map;
 import libcore.util.EmptyArray;
 
 /**
@@ -94,6 +96,31 @@
     private final Locale HONG_KONG = new Locale("zh", "HK");
     private final Locale MACAU = new Locale("zh", "MO");
 
+    private static final Map<String, String> mTerminologyToBibliographicMap;
+    static {
+        mTerminologyToBibliographicMap = new HashMap<>();
+        // NOTE: (TERMINOLOGY_CODE, BIBLIOGRAPHIC_CODE)
+        mTerminologyToBibliographicMap.put("sqi", "alb"); // Albanian
+        mTerminologyToBibliographicMap.put("hye", "arm"); // Armenian
+        mTerminologyToBibliographicMap.put("eus", "baq"); // Basque
+        mTerminologyToBibliographicMap.put("mya", "bur"); // Burmese
+        mTerminologyToBibliographicMap.put("ces", "cze"); // Czech
+        mTerminologyToBibliographicMap.put("nld", "dut"); // Dutch
+        mTerminologyToBibliographicMap.put("kat", "geo"); // Georgian
+        mTerminologyToBibliographicMap.put("deu", "ger"); // German
+        mTerminologyToBibliographicMap.put("ell", "gre"); // Greek
+        mTerminologyToBibliographicMap.put("fra", "fre"); // French
+        mTerminologyToBibliographicMap.put("isl", "ice"); // Icelandic
+        mTerminologyToBibliographicMap.put("mkd", "mac"); // Macedonian
+        mTerminologyToBibliographicMap.put("mri", "mao"); // Maori
+        mTerminologyToBibliographicMap.put("msa", "may"); // Malay
+        mTerminologyToBibliographicMap.put("fas", "per"); // Persian
+        mTerminologyToBibliographicMap.put("ron", "rum"); // Romanian
+        mTerminologyToBibliographicMap.put("slk", "slo"); // Slovak
+        mTerminologyToBibliographicMap.put("bod", "tib"); // Tibetan
+        mTerminologyToBibliographicMap.put("cym", "wel"); // Welsh
+    }
+
     static final String PERMISSION = "android.permission.HDMI_CEC";
 
     // The reason code to initiate initializeCec().
@@ -177,7 +204,18 @@
                 // Chinese used in Taiwan/Hong Kong/Macau.
                 return "chi";
             } else {
-                return locale.getISO3Language();
+                String language = locale.getISO3Language();
+
+                // locale.getISO3Language() returns terminology code and need to
+                // send it as bibliographic code instead since the Bibliographic
+                // codes of ISO/FDIS 639-2 shall be used.
+                // NOTE: Chinese also has terminology/bibliographic code "zho" and "chi"
+                // But, as it depends on the locale, is not handled here.
+                if (mTerminologyToBibliographicMap.containsKey(language)) {
+                    language = mTerminologyToBibliographicMap.get(language);
+                }
+
+                return language;
             }
         }
     }
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 67293b9..3552e66 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -1685,6 +1685,7 @@
         return getInputMethodList(false /* isVrOnly */);
     }
 
+    @Override
     public List<InputMethodInfo> getVrInputMethodList() {
         return getInputMethodList(true /* isVrOnly */);
     }
diff --git a/services/core/java/com/android/server/job/JobConcurrencyManager.java b/services/core/java/com/android/server/job/JobConcurrencyManager.java
index ee1d847..827c0f1 100644
--- a/services/core/java/com/android/server/job/JobConcurrencyManager.java
+++ b/services/core/java/com/android/server/job/JobConcurrencyManager.java
@@ -44,17 +44,11 @@
      * We manipulate this array until we arrive at what jobs should be running on
      * what JobServiceContext.
      */
-    JobStatus[] mTmpAssignContextIdToJobMap = new JobStatus[MAX_JOB_CONTEXTS_COUNT];
+    JobStatus[] mRecycledAssignContextIdToJobMap = new JobStatus[MAX_JOB_CONTEXTS_COUNT];
 
-    /**
-     * Indicates whether we need to act on this jobContext id
-     */
-    boolean[] mTmpAssignAct = new boolean[MAX_JOB_CONTEXTS_COUNT];
+    boolean[] mRecycledSlotChanged = new boolean[MAX_JOB_CONTEXTS_COUNT];
 
-    /**
-     * The uid whose jobs we would like to assign to a context.
-     */
-    int[] mTmpAssignPreferredUidForContext = new int[MAX_JOB_CONTEXTS_COUNT];
+    int[] mRecycledPreferredUidForContext = new int[MAX_JOB_CONTEXTS_COUNT];
 
     JobConcurrencyManager(JobSchedulerService service) {
         mService = service;
@@ -99,28 +93,30 @@
                 break;
         }
 
-        JobStatus[] contextIdToJobMap = mTmpAssignContextIdToJobMap;
-        boolean[] act = mTmpAssignAct;
-        int[] preferredUidForContext = mTmpAssignPreferredUidForContext;
-        int numActive = 0;
-        int numForeground = 0;
+        // To avoid GC churn, we recycle the arrays.
+        JobStatus[] contextIdToJobMap = mRecycledAssignContextIdToJobMap;
+        boolean[] slotChanged = mRecycledSlotChanged;
+        int[] preferredUidForContext = mRecycledPreferredUidForContext;
+
+        int numTotalRunningJobs = 0;
+        int numForegroundJobs = 0;
         for (int i=0; i<MAX_JOB_CONTEXTS_COUNT; i++) {
             final JobServiceContext js = mService.mActiveServices.get(i);
             final JobStatus status = js.getRunningJobLocked();
             if ((contextIdToJobMap[i] = status) != null) {
-                numActive++;
+                numTotalRunningJobs++;
                 if (status.lastEvaluatedPriority >= JobInfo.PRIORITY_TOP_APP) {
-                    numForeground++;
+                    numForegroundJobs++;
                 }
             }
-            act[i] = false;
+            slotChanged[i] = false;
             preferredUidForContext[i] = js.getPreferredUid();
         }
         if (DEBUG) {
             Slog.d(TAG, printContextIdToJobMap(contextIdToJobMap, "running jobs initial"));
         }
         for (int i=0; i<pendingJobs.size(); i++) {
-            JobStatus nextPending = pendingJobs.get(i);
+            final JobStatus nextPending = pendingJobs.get(i);
 
             // If job is already running, go to next job.
             int jobRunningContext = findJobContextIdFromMap(nextPending, contextIdToJobMap);
@@ -131,23 +127,32 @@
             final int priority = mService.evaluateJobPriorityLocked(nextPending);
             nextPending.lastEvaluatedPriority = priority;
 
-            // Find a context for nextPending. The context should be available OR
+            // Find an available slot for nextPending. The context should be available OR
             // it should have lowest priority among all running jobs
             // (sharing the same Uid as nextPending)
-            int minPriority = Integer.MAX_VALUE;
-            int minPriorityContextId = -1;
+            int minPriorityForPreemption = Integer.MAX_VALUE;
+            int selectedContextId = -1;
+            boolean startingJob = false;
             for (int j=0; j<MAX_JOB_CONTEXTS_COUNT; j++) {
                 JobStatus job = contextIdToJobMap[j];
                 int preferredUid = preferredUidForContext[j];
                 if (job == null) {
-                    if ((numActive < mService.mMaxActiveJobs ||
-                            (priority >= JobInfo.PRIORITY_TOP_APP &&
-                                    numForeground < mConstants.FG_JOB_COUNT)) &&
-                            (preferredUid == nextPending.getUid() ||
-                                    preferredUid == JobServiceContext.NO_PREFERRED_UID)) {
+                    final boolean totalCountOk = numTotalRunningJobs < mService.mMaxActiveJobs;
+                    final boolean fgCountOk = (priority >= JobInfo.PRIORITY_TOP_APP)
+                            && (numForegroundJobs < mConstants.FG_JOB_COUNT);
+                    final boolean preferredUidOkay = (preferredUid == nextPending.getUid())
+                            || (preferredUid == JobServiceContext.NO_PREFERRED_UID);
+
+                    // TODO: The following check is slightly wrong.
+                    // Depending on how the pending jobs are sorted, we sometimes cap the total
+                    // job count at mMaxActiveJobs (when all jobs are FG jobs), or
+                    // at [mMaxActiveJobs + FG_JOB_COUNT] (when there are mMaxActiveJobs BG jobs
+                    // and then FG_JOB_COUNT FG jobs.)
+                    if ((totalCountOk || fgCountOk) && preferredUidOkay) {
                         // This slot is free, and we haven't yet hit the limit on
                         // concurrent jobs...  we can just throw the job in to here.
-                        minPriorityContextId = j;
+                        selectedContextId = j;
+                        startingJob = true;
                         break;
                     }
                     // No job on this context, but nextPending can't run here because
@@ -158,30 +163,39 @@
                 if (job.getUid() != nextPending.getUid()) {
                     continue;
                 }
-                if (mService.evaluateJobPriorityLocked(job) >= nextPending.lastEvaluatedPriority) {
+
+                final int jobPriority = mService.evaluateJobPriorityLocked(job);
+                if (jobPriority >= nextPending.lastEvaluatedPriority) {
                     continue;
                 }
-                if (minPriority > nextPending.lastEvaluatedPriority) {
-                    minPriority = nextPending.lastEvaluatedPriority;
-                    minPriorityContextId = j;
+
+                // TODO lastEvaluatedPriority should be evaluateJobPriorityLocked. (double check it)
+                if (minPriorityForPreemption > nextPending.lastEvaluatedPriority) {
+                    minPriorityForPreemption = nextPending.lastEvaluatedPriority;
+                    selectedContextId = j;
+                    // In this case, we're just going to preempt a low priority job, we're not
+                    // actually starting a job, so don't set startingJob.
                 }
             }
-            if (minPriorityContextId != -1) {
-                contextIdToJobMap[minPriorityContextId] = nextPending;
-                act[minPriorityContextId] = true;
-                numActive++;
+            if (selectedContextId != -1) {
+                contextIdToJobMap[selectedContextId] = nextPending;
+                slotChanged[selectedContextId] = true;
+            }
+            if (startingJob) {
+                // Increase the counters when we're going to start a job.
+                numTotalRunningJobs++;
                 if (priority >= JobInfo.PRIORITY_TOP_APP) {
-                    numForeground++;
+                    numForegroundJobs++;
                 }
             }
         }
         if (DEBUG) {
             Slog.d(TAG, printContextIdToJobMap(contextIdToJobMap, "running jobs final"));
         }
-        tracker.noteConcurrency(numActive, numForeground);
+        tracker.noteConcurrency(numTotalRunningJobs, numForegroundJobs);
         for (int i=0; i<MAX_JOB_CONTEXTS_COUNT; i++) {
             boolean preservePreferredUid = false;
-            if (act[i]) {
+            if (slotChanged[i]) {
                 JobStatus js = activeServices.get(i).getRunningJobLocked();
                 if (js != null) {
                     if (DEBUG) {
@@ -195,7 +209,7 @@
                     final JobStatus pendingJob = contextIdToJobMap[i];
                     if (DEBUG) {
                         Slog.d(TAG, "About to run job on context "
-                                + String.valueOf(i) + ", job: " + pendingJob);
+                                + i + ", job: " + pendingJob);
                     }
                     for (int ic=0; ic<controllers.size(); ic++) {
                         controllers.get(ic).prepareForExecutionLocked(pendingJob);
diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java
index 10dc156..3f9d928 100644
--- a/services/core/java/com/android/server/job/JobSchedulerService.java
+++ b/services/core/java/com/android/server/job/JobSchedulerService.java
@@ -1261,6 +1261,9 @@
     @Override
     public void onDeviceIdleStateChanged(boolean deviceIdle) {
         synchronized (mLock) {
+            if (DEBUG) {
+                Slog.d(TAG, "Doze state changed: " + deviceIdle);
+            }
             if (deviceIdle) {
                 // When becoming idle, make sure no jobs are actively running,
                 // except those using the idle exemption flag.
@@ -1829,6 +1832,9 @@
                         }
                     } break;
                     case MSG_CHECK_JOB:
+                        if (DEBUG) {
+                            Slog.d(TAG, "MSG_CHECK_JOB");
+                        }
                         if (mReportedActive) {
                             // if jobs are currently being run, queue all ready jobs for execution.
                             queueReadyJobsForExecutionLocked();
@@ -1838,6 +1844,9 @@
                         }
                         break;
                     case MSG_CHECK_JOB_GREEDY:
+                        if (DEBUG) {
+                            Slog.d(TAG, "MSG_CHECK_JOB_GREEDY");
+                        }
                         queueReadyJobsForExecutionLocked();
                         break;
                     case MSG_STOP_JOB:
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index 93b6620..b7bb2c6 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -27,8 +27,10 @@
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.ActivityInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.ServiceInfo;
 import android.content.pm.UserInfo;
 import android.database.ContentObserver;
 import android.media.AudioManager;
@@ -634,11 +636,16 @@
      * <p>The contents of this object is guarded by {@link #mLock}.
      */
     final class FullUserRecord implements MediaSessionStack.OnMediaButtonSessionChangedListener {
+        public static final int COMPONENT_TYPE_BROADCAST = 0;
+        public static final int COMPONENT_TYPE_ACTIVITY = 1;
+        public static final int COMPONENT_TYPE_SERVICE = 2;
         private static final String COMPONENT_NAME_USER_ID_DELIM = ",";
+
         private final int mFullUserId;
         private final MediaSessionStack mPriorityStack;
         private PendingIntent mLastMediaButtonReceiver;
         private ComponentName mRestoredMediaButtonReceiver;
+        private int mRestoredMediaButtonReceiverComponentType;
         private int mRestoredMediaButtonReceiverUserId;
 
         private IOnVolumeKeyLongPressListener mOnVolumeKeyLongPressListener;
@@ -655,17 +662,23 @@
             mFullUserId = fullUserId;
             mPriorityStack = new MediaSessionStack(mAudioPlayerStateMonitor, this);
             // Restore the remembered media button receiver before the boot.
-            String mediaButtonReceiver = Settings.Secure.getStringForUser(mContentResolver,
+            String mediaButtonReceiverInfo = Settings.Secure.getStringForUser(mContentResolver,
                     Settings.System.MEDIA_BUTTON_RECEIVER, mFullUserId);
-            if (mediaButtonReceiver == null) {
+            if (mediaButtonReceiverInfo == null) {
                 return;
             }
-            String[] tokens = mediaButtonReceiver.split(COMPONENT_NAME_USER_ID_DELIM);
-            if (tokens == null || tokens.length != 2) {
+            String[] tokens = mediaButtonReceiverInfo.split(COMPONENT_NAME_USER_ID_DELIM);
+            if (tokens == null || (tokens.length != 2 && tokens.length != 3)) {
                 return;
             }
             mRestoredMediaButtonReceiver = ComponentName.unflattenFromString(tokens[0]);
             mRestoredMediaButtonReceiverUserId = Integer.parseInt(tokens[1]);
+            if (tokens.length == 3) {
+                mRestoredMediaButtonReceiverComponentType = Integer.parseInt(tokens[2]);
+            } else {
+                mRestoredMediaButtonReceiverComponentType =
+                        getComponentType(mRestoredMediaButtonReceiver);
+            }
         }
 
         public void destroySessionsForUserLocked(int userId) {
@@ -696,6 +709,8 @@
             pw.println(indent + "Callback: " + mCallback);
             pw.println(indent + "Last MediaButtonReceiver: " + mLastMediaButtonReceiver);
             pw.println(indent + "Restored MediaButtonReceiver: " + mRestoredMediaButtonReceiver);
+            pw.println(indent + "Restored MediaButtonReceiverComponentType: "
+                    + mRestoredMediaButtonReceiverComponentType);
             mPriorityStack.dump(pw, indent);
         }
 
@@ -722,17 +737,21 @@
             PendingIntent receiver = record.getMediaButtonReceiver();
             mLastMediaButtonReceiver = receiver;
             mRestoredMediaButtonReceiver = null;
-            String componentName = "";
+
+            String mediaButtonReceiverInfo = "";
             if (receiver != null) {
                 ComponentName component = receiver.getIntent().getComponent();
                 if (component != null
                         && record.getPackageName().equals(component.getPackageName())) {
-                    componentName = component.flattenToString();
+                    String componentName = component.flattenToString();
+                    int componentType = getComponentType(component);
+                    mediaButtonReceiverInfo = String.join(COMPONENT_NAME_USER_ID_DELIM,
+                            componentName, String.valueOf(record.getUserId()),
+                            String.valueOf(componentType));
                 }
             }
             Settings.Secure.putStringForUser(mContentResolver,
-                    Settings.System.MEDIA_BUTTON_RECEIVER,
-                    componentName + COMPONENT_NAME_USER_ID_DELIM + record.getUserId(),
+                    Settings.System.MEDIA_BUTTON_RECEIVER, mediaButtonReceiverInfo,
                     mFullUserId);
         }
 
@@ -762,6 +781,32 @@
             return isGlobalPriorityActiveLocked()
                     ? mGlobalPrioritySession : mPriorityStack.getMediaButtonSession();
         }
+
+        private int getComponentType(ComponentName componentName) {
+            PackageManager pm = getContext().getPackageManager();
+            try {
+                ActivityInfo activityInfo = pm.getActivityInfo(componentName,
+                        PackageManager.MATCH_DIRECT_BOOT_AWARE
+                                | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
+                                | PackageManager.GET_ACTIVITIES);
+                if (activityInfo != null) {
+                    return COMPONENT_TYPE_ACTIVITY;
+                }
+            } catch (NameNotFoundException e) {
+            }
+            try {
+                ServiceInfo serviceInfo = pm.getServiceInfo(componentName,
+                        PackageManager.MATCH_DIRECT_BOOT_AWARE
+                                | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
+                                | PackageManager.GET_SERVICES);
+                if (serviceInfo != null) {
+                    return COMPONENT_TYPE_SERVICE;
+                }
+            } catch (NameNotFoundException e) {
+            }
+            // Pick legacy behavior for BroadcastReceiver or unknown.
+            return COMPONENT_TYPE_BROADCAST;
+        }
     }
 
     final class SessionsListenerRecord implements IBinder.DeathRecipient {
@@ -1580,14 +1625,32 @@
                     } else {
                         ComponentName receiver =
                                 mCurrentFullUserRecord.mRestoredMediaButtonReceiver;
+                        int componentType = mCurrentFullUserRecord
+                                .mRestoredMediaButtonReceiverComponentType;
+                        UserHandle userHandle = UserHandle.of(mCurrentFullUserRecord
+                                .mRestoredMediaButtonReceiverUserId);
                         if (DEBUG_KEY_EVENT) {
                             Log.d(TAG, "Sending " + keyEvent + " to the restored intent "
-                                    + receiver);
+                                    + receiver + ", type=" + componentType);
                         }
                         mediaButtonIntent.setComponent(receiver);
-                        getContext().sendBroadcastAsUser(mediaButtonIntent,
-                                UserHandle.of(mCurrentFullUserRecord
-                                      .mRestoredMediaButtonReceiverUserId));
+                        try {
+                            switch (componentType) {
+                                case FullUserRecord.COMPONENT_TYPE_ACTIVITY:
+                                    getContext().startActivityAsUser(mediaButtonIntent, userHandle);
+                                    break;
+                                case FullUserRecord.COMPONENT_TYPE_SERVICE:
+                                    getContext().startForegroundServiceAsUser(mediaButtonIntent,
+                                            userHandle);
+                                    break;
+                                default:
+                                    // Legacy behavior for other cases.
+                                    getContext().sendBroadcastAsUser(mediaButtonIntent, userHandle);
+                            }
+                        } catch (Exception e) {
+                            Log.w(TAG, "Error sending media button to the restored intent "
+                                    + receiver + ", type=" + componentType, e);
+                        }
                         if (mCurrentFullUserRecord.mCallback != null) {
                             mCurrentFullUserRecord.mCallback
                                     .onMediaKeyEventDispatchedToMediaButtonReceiver(
diff --git a/services/core/java/com/android/server/notification/NotificationDelegate.java b/services/core/java/com/android/server/notification/NotificationDelegate.java
index 84bb13e..ee60daa 100644
--- a/services/core/java/com/android/server/notification/NotificationDelegate.java
+++ b/services/core/java/com/android/server/notification/NotificationDelegate.java
@@ -45,7 +45,12 @@
     void onNotificationExpansionChanged(String key, boolean userAction, boolean expanded);
     void onNotificationDirectReplied(String key);
     void onNotificationSettingsViewed(String key);
-    void onNotificationSmartRepliesAdded(String key, int replyCount);
+
+    /**
+     * Notifies that smart replies and actions have been added to the UI.
+     */
+    void onNotificationSmartSuggestionsAdded(String key, int smartReplyCount, int smartActionCount,
+            boolean generatedByAssistant);
 
     /**
      * Notifies a smart reply is sent.
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index bad259b..8fa3c46 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -762,7 +762,13 @@
                         .setType(MetricsEvent.TYPE_ACTION)
                         .setSubtype(actionIndex)
                         .addTaggedData(MetricsEvent.NOTIFICATION_SHADE_INDEX, nv.rank)
-                        .addTaggedData(MetricsEvent.NOTIFICATION_SHADE_COUNT, nv.count));
+                        .addTaggedData(MetricsEvent.NOTIFICATION_SHADE_COUNT, nv.count)
+                        .addTaggedData(MetricsEvent.NOTIFICATION_ACTION_IS_SMART,
+                                (Notification.Action.SEMANTIC_ACTION_CONTEXTUAL_SUGGESTION
+                                        == action.getSemanticAction()) ? 1 : 0)
+                        .addTaggedData(
+                                MetricsEvent.NOTIFICATION_SMART_SUGGESTION_ASSISTANT_GENERATED,
+                                generatedByAssistant ? 1 : 0));
                 EventLogTags.writeNotificationActionClicked(key, actionIndex,
                         r.getLifespanMs(now), r.getFreshnessMs(now), r.getExposureMs(now),
                         nv.rank, nv.count);
@@ -837,15 +843,20 @@
                         if (DBG) Slog.d(TAG, "Marking notification as visible " + nv.key);
                         reportSeen(r);
 
-                        // If the newly visible notification has smart replies
+                        // If the newly visible notification has smart suggestions
                         // then log that the user has seen them.
-                        if (r.getNumSmartRepliesAdded() > 0
+                        if ((r.getNumSmartRepliesAdded() > 0 || r.getNumSmartActionsAdded() > 0)
                                 && !r.hasSeenSmartReplies()) {
                             r.setSeenSmartReplies(true);
                             LogMaker logMaker = r.getLogMaker()
                                     .setCategory(MetricsEvent.SMART_REPLY_VISIBLE)
                                     .addTaggedData(MetricsEvent.NOTIFICATION_SMART_REPLY_COUNT,
-                                            r.getNumSmartRepliesAdded());
+                                            r.getNumSmartRepliesAdded())
+                                    .addTaggedData(MetricsEvent.NOTIFICATION_SMART_ACTION_COUNT,
+                                            r.getNumSmartActionsAdded())
+                                    .addTaggedData(
+                                            MetricsEvent.NOTIFICATION_SMART_SUGGESTION_ASSISTANT_GENERATED,
+                                            r.getSuggestionsGeneratedByAssistant());
                             mMetricsLogger.write(logMaker);
                         }
                     }
@@ -908,11 +919,14 @@
         }
 
         @Override
-        public void onNotificationSmartRepliesAdded(String key, int replyCount) {
+        public void onNotificationSmartSuggestionsAdded(String key, int smartReplyCount,
+                int smartActionCount, boolean generatedByAssistant) {
             synchronized (mNotificationLock) {
                 NotificationRecord r = mNotificationsByKey.get(key);
                 if (r != null) {
-                    r.setNumSmartRepliesAdded(replyCount);
+                    r.setNumSmartRepliesAdded(smartReplyCount);
+                    r.setNumSmartActionsAdded(smartActionCount);
+                    r.setSuggestionsGeneratedByAssistant(generatedByAssistant);
                 }
             }
         }
@@ -920,6 +934,7 @@
         @Override
         public void onNotificationSmartReplySent(String key, int replyIndex, CharSequence reply,
                 boolean generatedByAssistant) {
+
             synchronized (mNotificationLock) {
                 NotificationRecord r = mNotificationsByKey.get(key);
                 if (r != null) {
@@ -2632,6 +2647,10 @@
          * Note that since notification posting is done asynchronously, this will not return
          * notifications that are in the process of being posted.
          *
+         * From {@link Build.VERSION_CODES#Q}, will also return notifications you've posted as
+         * an app's notification delegate via
+         * {@link NotificationManager#notifyAsPackage(String, String, int, Notification)}.
+         *
          * @returns A list of all the package's notifications, in natural order.
          */
         @Override
@@ -2674,16 +2693,18 @@
 
         private StatusBarNotification sanitizeSbn(String pkg, int userId,
                 StatusBarNotification sbn) {
-            if (sbn.getPackageName().equals(pkg) && sbn.getUserId() == userId) {
-                // We could pass back a cloneLight() but clients might get confused and
-                // try to send this thing back to notify() again, which would not work
-                // very well.
-                return new StatusBarNotification(
-                        sbn.getPackageName(),
-                        sbn.getOpPkg(),
-                        sbn.getId(), sbn.getTag(), sbn.getUid(), sbn.getInitialPid(),
-                        sbn.getNotification().clone(),
-                        sbn.getUser(), sbn.getOverrideGroupKey(), sbn.getPostTime());
+            if (sbn.getUserId() == userId) {
+                if (sbn.getPackageName().equals(pkg) || sbn.getOpPkg().equals(pkg)) {
+                    // We could pass back a cloneLight() but clients might get confused and
+                    // try to send this thing back to notify() again, which would not work
+                    // very well.
+                    return new StatusBarNotification(
+                            sbn.getPackageName(),
+                            sbn.getOpPkg(),
+                            sbn.getId(), sbn.getTag(), sbn.getUid(), sbn.getInitialPid(),
+                            sbn.getNotification().clone(),
+                            sbn.getUser(), sbn.getOverrideGroupKey(), sbn.getPostTime());
+                }
             }
             return null;
         }
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index 39451d4..89ec38d 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -178,6 +178,8 @@
     private boolean mTextChanged;
     private boolean mRecordedInterruption;
     private int mNumberOfSmartRepliesAdded;
+    private int mNumberOfSmartActionsAdded;
+    private boolean mSuggestionsGeneratedByAssistant;
     private boolean mHasSeenSmartReplies;
     /**
      * Whether this notification (and its channels) should be considered user locked. Used in
@@ -1140,6 +1142,22 @@
         return mNumberOfSmartRepliesAdded;
     }
 
+    public void setNumSmartActionsAdded(int noActions) {
+        mNumberOfSmartActionsAdded = noActions;
+    }
+
+    public int getNumSmartActionsAdded() {
+        return mNumberOfSmartActionsAdded;
+    }
+
+    public void setSuggestionsGeneratedByAssistant(boolean generatedByAssistant) {
+        mSuggestionsGeneratedByAssistant = generatedByAssistant;
+    }
+
+    public boolean getSuggestionsGeneratedByAssistant() {
+        return mSuggestionsGeneratedByAssistant;
+    }
+
     public boolean hasSeenSmartReplies() {
         return mHasSeenSmartReplies;
     }
diff --git a/services/core/java/com/android/server/om/IdmapManager.java b/services/core/java/com/android/server/om/IdmapManager.java
index 6d59827..16143d3 100644
--- a/services/core/java/com/android/server/om/IdmapManager.java
+++ b/services/core/java/com/android/server/om/IdmapManager.java
@@ -60,7 +60,6 @@
 
     boolean createIdmap(@NonNull final PackageInfo targetPackage,
             @NonNull final PackageInfo overlayPackage, int userId) {
-        // unused userId: see comment in OverlayManagerServiceImpl.removeIdmapIfPossible
         if (DEBUG) {
             Slog.d(TAG, "create idmap for " + targetPackage.packageName + " and "
                     + overlayPackage.packageName);
@@ -70,16 +69,19 @@
         final String overlayPath = overlayPackage.applicationInfo.getBaseCodePath();
         try {
             if (FEATURE_FLAG_IDMAP2) {
-                mIdmap2Service.createIdmap(targetPath, overlayPath, userId);
+                if (mIdmap2Service.verifyIdmap(overlayPath, userId)) {
+                    return true;
+                }
+                return mIdmap2Service.createIdmap(targetPath, overlayPath, userId) != null;
             } else {
                 mInstaller.idmap(targetPath, overlayPath, sharedGid);
+                return true;
             }
         } catch (Exception e) {
             Slog.w(TAG, "failed to generate idmap for " + targetPath + " and "
                     + overlayPath + ": " + e.getMessage());
             return false;
         }
-        return true;
     }
 
     boolean removeIdmap(@NonNull final OverlayInfo oi, final int userId) {
@@ -88,15 +90,15 @@
         }
         try {
             if (FEATURE_FLAG_IDMAP2) {
-                mIdmap2Service.removeIdmap(oi.baseCodePath, userId);
+                return mIdmap2Service.removeIdmap(oi.baseCodePath, userId);
             } else {
                 mInstaller.removeIdmap(oi.baseCodePath);
+                return true;
             }
         } catch (Exception e) {
             Slog.w(TAG, "failed to remove idmap for " + oi.baseCodePath + ": " + e.getMessage());
             return false;
         }
-        return true;
     }
 
     boolean idmapExists(@NonNull final OverlayInfo oi) {
diff --git a/services/core/java/com/android/server/om/OverlayManagerService.java b/services/core/java/com/android/server/om/OverlayManagerService.java
index d471904..a7f1146 100644
--- a/services/core/java/com/android/server/om/OverlayManagerService.java
+++ b/services/core/java/com/android/server/om/OverlayManagerService.java
@@ -223,8 +223,8 @@
     public OverlayManagerService(@NonNull final Context context,
             @NonNull final Installer installer) {
         super(context);
-        mSettingsFile =
-            new AtomicFile(new File(Environment.getDataSystemDirectory(), "overlays.xml"), "overlays");
+        mSettingsFile = new AtomicFile(
+                new File(Environment.getDataSystemDirectory(), "overlays.xml"), "overlays");
         mPackageManager = new PackageManagerHelper();
         mUserManager = UserManagerService.getInstance();
         IdmapManager im = new IdmapManager(installer);
@@ -717,9 +717,9 @@
         final Map<String, List<String>> pendingChanges = new ArrayMap<>(targetPackageNames.size());
         synchronized (mLock) {
             final List<String> frameworkOverlays =
-                mImpl.getEnabledOverlayPackageNames("android", userId);
-            final int N = targetPackageNames.size();
-            for (int i = 0; i < N; i++) {
+                    mImpl.getEnabledOverlayPackageNames("android", userId);
+            final int n = targetPackageNames.size();
+            for (int i = 0; i < n; i++) {
                 final String targetPackageName = targetPackageNames.get(i);
                 List<String> list = new ArrayList<>();
                 if (!"android".equals(targetPackageName)) {
@@ -730,8 +730,8 @@
             }
         }
 
-        final int N = targetPackageNames.size();
-        for (int i = 0; i < N; i++) {
+        final int n = targetPackageNames.size();
+        for (int i = 0; i < n; i++) {
             final String targetPackageName = targetPackageNames.get(i);
             if (DEBUG) {
                 Slog.d(TAG, "-> Updating overlay: target=" + targetPackageName + " overlays=["
@@ -789,7 +789,7 @@
             if (!mSettingsFile.getBaseFile().exists()) {
                 return;
             }
-            try (final FileInputStream stream = mSettingsFile.openRead()) {
+            try (FileInputStream stream = mSettingsFile.openRead()) {
                 mSettings.restore(stream);
 
                 // We might have data for dying users if the device was
@@ -915,8 +915,8 @@
 
             if (!verbose) {
                 int count = 0;
-                final int N = mCache.size();
-                for (int i = 0; i < N; i++) {
+                final int n = mCache.size();
+                for (int i = 0; i < n; i++) {
                     final int userId = mCache.keyAt(i);
                     count += mCache.get(userId).size();
                 }
@@ -929,8 +929,8 @@
                 return;
             }
 
-            final int N = mCache.size();
-            for (int i = 0; i < N; i++) {
+            final int n = mCache.size();
+            for (int i = 0; i < n; i++) {
                 final int userId = mCache.keyAt(i);
                 pw.println(TAB1 + "User " + userId);
                 final HashMap<String, PackageInfo> map = mCache.get(userId);
diff --git a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
index 112059d..b0d2704 100644
--- a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
+++ b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
@@ -55,8 +55,8 @@
  */
 final class OverlayManagerServiceImpl {
     // Flags to use in conjunction with updateState.
-    private static final int FLAG_TARGET_IS_UPGRADING = 1<<0;
-    private static final int FLAG_OVERLAY_IS_UPGRADING = 1<<1;
+    private static final int FLAG_TARGET_IS_UPGRADING = 1 << 0;
+    private static final int FLAG_OVERLAY_IS_UPGRADING = 1 << 1;
 
     private final PackageManagerHelper mPackageManager;
     private final IdmapManager mIdmapManager;
@@ -89,8 +89,8 @@
         // a change in priority is only relevant for static RROs: specifically,
         // a regular RRO should not have its state reset only because a change
         // in priority
-        if (theTruth.isStaticOverlayPackage() &&
-                theTruth.overlayPriority != oldSettings.priority) {
+        if (theTruth.isStaticOverlayPackage()
+                && theTruth.overlayPriority != oldSettings.priority) {
             return true;
         }
         return false;
@@ -294,8 +294,8 @@
             final int userId, final int flags) {
         boolean modified = false;
         final List<OverlayInfo> ois = mSettings.getOverlaysForTarget(targetPackageName, userId);
-        final int N = ois.size();
-        for (int i = 0; i < N; i++) {
+        final int n = ois.size();
+        for (int i = 0; i < n; i++) {
             final OverlayInfo oi = ois.get(i);
             final PackageInfo overlayPackage = mPackageManager.getPackageInfo(oi.packageName,
                     userId);
@@ -610,8 +610,8 @@
         final List<OverlayInfo> overlays = mSettings.getOverlaysForTarget(targetPackageName,
                 userId);
         final List<String> paths = new ArrayList<>(overlays.size());
-        final int N = overlays.size();
-        for (int i = 0; i < N; i++) {
+        final int n = overlays.size();
+        for (int i = 0; i < n; i++) {
             final OverlayInfo oi = overlays.get(i);
             if (oi.isEnabled()) {
                 paths.add(oi.packageName);
@@ -632,8 +632,8 @@
                 userId);
 
         // Static RROs targeting to "android", ie framework-res.apk, are handled by native layers.
-        if (targetPackage != null && overlayPackage != null &&
-                !("android".equals(targetPackageName)
+        if (targetPackage != null && overlayPackage != null
+                && !("android".equals(targetPackageName)
                         && overlayPackage.isStaticOverlayPackage())) {
             mIdmapManager.createIdmap(targetPackage, overlayPackage, userId);
         }
@@ -663,7 +663,7 @@
 
     private @OverlayInfo.State int calculateNewState(@Nullable final PackageInfo targetPackage,
             @Nullable final PackageInfo overlayPackage, final int userId, final int flags)
-        throws OverlayManagerSettings.BadKeyException {
+            throws OverlayManagerSettings.BadKeyException {
 
         if ((flags & FLAG_TARGET_IS_UPGRADING) != 0) {
             return STATE_TARGET_UPGRADING;
diff --git a/services/core/java/com/android/server/om/OverlayManagerSettings.java b/services/core/java/com/android/server/om/OverlayManagerSettings.java
index 572d368..ee06746 100644
--- a/services/core/java/com/android/server/om/OverlayManagerSettings.java
+++ b/services/core/java/com/android/server/om/OverlayManagerSettings.java
@@ -290,21 +290,22 @@
             return;
         }
 
-        final int N = mItems.size();
-        for (int i = 0; i < N; i++) {
+        final int n = mItems.size();
+        for (int i = 0; i < n; i++) {
             final SettingsItem item = mItems.get(i);
             pw.println(item.mPackageName + ":" + item.getUserId() + " {");
             pw.increaseIndent();
 
-            pw.print("mPackageName.......: "); pw.println(item.mPackageName);
-            pw.print("mUserId............: "); pw.println(item.getUserId());
-            pw.print("mTargetPackageName.: "); pw.println(item.getTargetPackageName());
-            pw.print("mBaseCodePath......: "); pw.println(item.getBaseCodePath());
-            pw.print("mState.............: "); pw.println(OverlayInfo.stateToString(item.getState()));
-            pw.print("mIsEnabled.........: "); pw.println(item.isEnabled());
-            pw.print("mIsStatic..........: "); pw.println(item.isStatic());
-            pw.print("mPriority..........: "); pw.println(item.mPriority);
-            pw.print("mCategory..........: "); pw.println(item.mCategory);
+            pw.println("mPackageName.......: " + item.mPackageName);
+            pw.println("mUserId............: " + item.getUserId());
+            pw.println("mTargetPackageName.: " + item.getTargetPackageName());
+            pw.println("mBaseCodePath......: " + item.getBaseCodePath());
+            pw.println("mState.............: " + OverlayInfo.stateToString(item.getState()));
+            pw.println("mState.............: " + OverlayInfo.stateToString(item.getState()));
+            pw.println("mIsEnabled.........: " + item.isEnabled());
+            pw.println("mIsStatic..........: " + item.isStatic());
+            pw.println("mPriority..........: " + item.mPriority);
+            pw.println("mCategory..........: " + item.mCategory);
 
             pw.decreaseIndent();
             pw.println("}");
@@ -400,8 +401,8 @@
             xml.startTag(null, TAG_OVERLAYS);
             XmlUtils.writeIntAttribute(xml, ATTR_VERSION, CURRENT_VERSION);
 
-            final int N = table.size();
-            for (int i = 0; i < N; i++) {
+            final int n = table.size();
+            for (int i = 0; i < n; i++) {
                 final SettingsItem item = table.get(i);
                 persistRow(xml, item);
             }
@@ -542,8 +543,8 @@
     }
 
     private int select(@NonNull final String packageName, final int userId) {
-        final int N = mItems.size();
-        for (int i = 0; i < N; i++) {
+        final int n = mItems.size();
+        for (int i = 0; i < n; i++) {
             final SettingsItem item = mItems.get(i);
             if (item.mUserId == userId && item.mPackageName.equals(packageName)) {
                 return i;
diff --git a/services/core/java/com/android/server/om/OverlayManagerShellCommand.java b/services/core/java/com/android/server/om/OverlayManagerShellCommand.java
index d576d33..e2b1cba 100644
--- a/services/core/java/com/android/server/om/OverlayManagerShellCommand.java
+++ b/services/core/java/com/android/server/om/OverlayManagerShellCommand.java
@@ -121,8 +121,8 @@
         for (final String targetPackageName : allOverlays.keySet()) {
             out.println(targetPackageName);
             List<OverlayInfo> overlaysForTarget = allOverlays.get(targetPackageName);
-            final int N = overlaysForTarget.size();
-            for (int i = 0; i < N; i++) {
+            final int n = overlaysForTarget.size();
+            for (int i = 0; i < n; i++) {
                 final OverlayInfo oi = overlaysForTarget.get(i);
                 String status;
                 switch (oi.state) {
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/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
index 51619cf..164af38 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -143,6 +143,13 @@
         LOCATION_PERMISSIONS.add(Manifest.permission.ACCESS_COARSE_LOCATION);
     }
 
+    private static final Set<String> ALWAYS_LOCATION_PERMISSIONS = new ArraySet<>();
+    static {
+        ALWAYS_LOCATION_PERMISSIONS.add(Manifest.permission.ACCESS_FINE_LOCATION);
+        ALWAYS_LOCATION_PERMISSIONS.add(Manifest.permission.ACCESS_COARSE_LOCATION);
+        ALWAYS_LOCATION_PERMISSIONS.add(Manifest.permission.ACCESS_BACKGROUND_LOCATION);
+    }
+
     private static final Set<String> ACTIVITY_RECOGNITION_PERMISSIONS = new ArraySet<>();
     static {
         ACTIVITY_RECOGNITION_PERMISSIONS.add(Manifest.permission.ACTIVITY_RECOGNITION);
@@ -690,7 +697,7 @@
         // Companion devices
         grantSystemFixedPermissionsToSystemPackage(
                 CompanionDeviceManager.COMPANION_DEVICE_DISCOVERY_PACKAGE_NAME, userId,
-                LOCATION_PERMISSIONS);
+                ALWAYS_LOCATION_PERMISSIONS);
 
         // Ringtone Picker
         grantSystemFixedPermissionsToSystemPackage(
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index 6e4c00e..02d8c0b 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -1254,12 +1254,13 @@
     }
 
     @Override
-    public void onNotificationSmartRepliesAdded(String key, int replyCount)
-            throws RemoteException {
+    public void onNotificationSmartSuggestionsAdded(String key, int smartReplyCount,
+            int smartActionCount, boolean generatedByAssistant) {
         enforceStatusBarService();
         long identity = Binder.clearCallingIdentity();
         try {
-            mNotificationDelegate.onNotificationSmartRepliesAdded(key, replyCount);
+            mNotificationDelegate.onNotificationSmartSuggestionsAdded(key, smartReplyCount,
+                    smartActionCount, generatedByAssistant);
         } finally {
             Binder.restoreCallingIdentity(identity);
         }
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 64ff9cf..7c61e37 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,45 @@
         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 (fallbackConnection == null) {
+            Slog.w(TAG, "Fallback wallpaper connection has not been created yet!!");
+            return;
+        }
+        if (supportsMultiDisplay(systemConnection)
+                && fallbackConnection.getConnectedEngineSize() != 0) {
+            fallbackConnection.forEachDisplayConnector(
+                    WallpaperConnection.DisplayConnector::disconnectLocked);
+            fallbackConnection.mDisplayConnector.clear();
+        } else {
+            // TODO(b/121181553) Handle wallpaper service disconnect case.
+            if (fallbackConnection.mService == null) {
+                Slog.w(TAG, "There is no fallback wallpaper service");
+                return;
+            }
+            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 +945,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 +973,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 +1048,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 +1107,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 +1128,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 +1619,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 +1648,7 @@
             for (String filename : sPerUserFiles) {
                 new File(wallpaperDir, filename).delete();
             }
-            mUserRestorecon.remove(userId);
+            mUserRestorecon.delete(userId);
         }
     }
 
@@ -1789,7 +1875,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 +1912,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 +1930,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 +1956,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 +2024,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 +2256,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 +2397,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 +2520,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 +2542,7 @@
         return true;
     }
 
-    void detachWallpaperLocked(WallpaperData wallpaper) {
+    private void detachWallpaperLocked(WallpaperData wallpaper) {
         if (wallpaper.connection != null) {
             if (wallpaper.connection.mReply != null) {
                 try {
@@ -2473,7 +2552,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 +2561,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 +2676,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 +2834,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 +2924,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 +2959,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 +3053,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 +3138,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 +3163,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 +3181,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/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java
index 2663d99..6755c73 100644
--- a/services/core/java/com/android/server/wm/ActivityStack.java
+++ b/services/core/java/com/android/server/wm/ActivityStack.java
@@ -4914,7 +4914,6 @@
 
         // Update override configurations of all tasks in the stack.
         final Rect taskBounds = tempTaskBounds != null ? tempTaskBounds : bounds;
-        final Rect insetBounds = tempTaskInsetBounds != null ? tempTaskInsetBounds : taskBounds;
 
         mTmpBounds.clear();
         mTmpInsetBounds.clear();
@@ -4922,7 +4921,7 @@
         for (int i = mTaskHistory.size() - 1; i >= 0; i--) {
             final TaskRecord task = mTaskHistory.get(i);
             if (task.isResizeable()) {
-                task.updateOverrideConfiguration(taskBounds, insetBounds);
+                task.updateOverrideConfiguration(taskBounds, tempTaskInsetBounds);
             }
 
             if (task.hasDisplayedBounds()) {
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 8b8cadc..6d3c693 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -505,23 +505,6 @@
         // TODO: Make it can take screenshot on external display
         mScreenshotHelper = displayContent.isDefaultDisplay
                 ? new ScreenshotHelper(mContext) : null;
-    }
-
-    void systemReady() {
-        mSystemGestures.systemReady();
-    }
-
-    private int getDisplayId() {
-        return mDisplayContent.getDisplayId();
-    }
-
-    void onDisplayRemoved() {
-        mDisplayContent.unregisterPointerEventListener(mSystemGestures);
-    }
-
-    void configure(int width, int height, int shortSizeDp) {
-        // Allow the navigation bar to move on non-square small devices (phones).
-        mNavigationBarCanMove = width != height && shortSizeDp < 600;
 
         if (mDisplayContent.isDefaultDisplay) {
             mHasStatusBar = true;
@@ -541,6 +524,23 @@
         }
     }
 
+    void systemReady() {
+        mSystemGestures.systemReady();
+    }
+
+    private int getDisplayId() {
+        return mDisplayContent.getDisplayId();
+    }
+
+    void onDisplayRemoved() {
+        mDisplayContent.unregisterPointerEventListener(mSystemGestures);
+    }
+
+    void configure(int width, int height, int shortSizeDp) {
+        // Allow the navigation bar to move on non-square small devices (phones).
+        mNavigationBarCanMove = width != height && shortSizeDp < 600;
+    }
+
     void updateConfigurationDependentBehaviors() {
         mNavBarOpacityMode = mContext.getResources().getInteger(R.integer.config_navBarOpacityMode);
     }
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 801e5f2..dcade2f0 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -794,8 +794,9 @@
     private void handleResizingWindows() {
         for (int i = mWmService.mResizingWindows.size() - 1; i >= 0; i--) {
             WindowState win = mWmService.mResizingWindows.get(i);
-            if (win.mAppFreezing) {
-                // Don't remove this window until rotation has completed.
+            if (win.mAppFreezing || win.getDisplayContent().mWaitingForConfig) {
+                // Don't remove this window until rotation has completed and is not waiting for the
+                // complete configuration.
                 continue;
             }
             win.reportResized();
diff --git a/services/core/java/com/android/server/wm/TaskRecord.java b/services/core/java/com/android/server/wm/TaskRecord.java
index 5bb6440..8c80009 100644
--- a/services/core/java/com/android/server/wm/TaskRecord.java
+++ b/services/core/java/com/android/server/wm/TaskRecord.java
@@ -1716,7 +1716,7 @@
         updateTaskDescription();
     }
 
-    private void adjustForMinimalTaskDimensions(Rect bounds) {
+    private void adjustForMinimalTaskDimensions(Rect bounds, Rect previousBounds) {
         if (bounds == null) {
             return;
         }
@@ -1747,9 +1747,8 @@
             return;
         }
 
-        final Rect configBounds = getRequestedOverrideBounds();
         if (adjustWidth) {
-            if (!configBounds.isEmpty() && bounds.right == configBounds.right) {
+            if (!previousBounds.isEmpty() && bounds.right == previousBounds.right) {
                 bounds.left = bounds.right - minWidth;
             } else {
                 // Either left bounds match, or neither match, or the previous bounds were
@@ -1758,7 +1757,7 @@
             }
         }
         if (adjustHeight) {
-            if (!configBounds.isEmpty() && bounds.bottom == configBounds.bottom) {
+            if (!previousBounds.isEmpty() && bounds.bottom == previousBounds.bottom) {
                 bounds.top = bounds.bottom - minHeight;
             } else {
                 // Either top bounds match, or neither match, or the previous bounds were
@@ -2113,11 +2112,15 @@
     // TODO(b/113900640): remove this once ActivityRecord is changed to not need it anymore.
     void computeResolvedOverrideConfiguration(Configuration inOutConfig, Configuration parentConfig,
             Configuration overrideConfig) {
+        // Save previous bounds because adjustForMinimalTaskDimensions uses that to determine if it
+        // changes left bound vs. right bound, or top bound vs. bottom bound.
+        mTmpBounds.set(inOutConfig.windowConfiguration.getBounds());
+
         inOutConfig.setTo(overrideConfig);
 
         Rect outOverrideBounds = inOutConfig.windowConfiguration.getBounds();
         if (outOverrideBounds != null && !outOverrideBounds.isEmpty()) {
-            adjustForMinimalTaskDimensions(outOverrideBounds);
+            adjustForMinimalTaskDimensions(outOverrideBounds, mTmpBounds);
 
             int windowingMode = overrideConfig.windowConfiguration.getWindowingMode();
             if (windowingMode == WINDOWING_MODE_UNDEFINED) {
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/Android.mk b/services/robotests/Android.mk
index 0c9c85a..9159f0d 100644
--- a/services/robotests/Android.mk
+++ b/services/robotests/Android.mk
@@ -41,7 +41,8 @@
 
 LOCAL_MODULE := FrameworksServicesRoboTests
 
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_SRC_FILES := $(call all-java-files-under, src) \
+    $(call all-java-files-under, backup/src)
 
 LOCAL_RESOURCE_DIR := \
     $(LOCAL_PATH)/res
@@ -81,4 +82,13 @@
 
 LOCAL_TEST_PACKAGE := FrameworksServicesLib
 
-include external/robolectric-shadows/run_robotests.mk
\ No newline at end of file
+LOCAL_ROBOTEST_FILES := $(call find-files-in-subdirs,$(LOCAL_PATH)/src,*Test.java,.) \
+    $(call find-files-in-subdirs,$(LOCAL_PATH)/backup/src,*Test.java,.)
+
+include external/robolectric-shadows/run_robotests.mk
+
+###################################################################
+# include subdir Android.mk files
+###################################################################
+include $(CLEAR_VARS)
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/services/robotests/backup/Android.mk b/services/robotests/backup/Android.mk
new file mode 100644
index 0000000..cc59b0c
--- /dev/null
+++ b/services/robotests/backup/Android.mk
@@ -0,0 +1,84 @@
+# 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.
+
+LOCAL_PATH := $(call my-dir)
+
+###################################################################
+# BackupFrameworksServicesLib app just for Robolectric test target      #
+###################################################################
+include $(CLEAR_VARS)
+
+LOCAL_PACKAGE_NAME := BackupFrameworksServicesLib
+LOCAL_PRIVATE_PLATFORM_APIS := true
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_PRIVILEGED_MODULE := true
+
+LOCAL_STATIC_JAVA_LIBRARIES := \
+    bmgrlib \
+    bu \
+    services.backup \
+    services.core \
+    services.net
+
+include $(BUILD_PACKAGE)
+
+###################################################################
+# BackupFrameworksServicesLib Robolectric test target.                  #
+###################################################################
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := BackupFrameworksServicesRoboTests
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src) \
+    $(call all-java-files-under, ../src/com/android/server/testing/shadows)
+
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
+
+LOCAL_JAVA_RESOURCE_DIRS := config
+
+# Include the testing libraries
+LOCAL_JAVA_LIBRARIES := \
+    platform-test-annotations \
+    robolectric_android-all-stub \
+    Robolectric_all-target \
+    mockito-robolectric-prebuilt \
+    truth-prebuilt \
+    testng
+
+LOCAL_INSTRUMENTATION_FOR := BackupFrameworksServicesLib
+
+LOCAL_MODULE_TAGS := optional
+
+include $(BUILD_STATIC_JAVA_LIBRARY)
+
+###################################################################
+# BackupFrameworksServicesLib runner target to run the previous target. #
+###################################################################
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := RunBackupFrameworksServicesRoboTests
+
+LOCAL_JAVA_LIBRARIES := \
+    BackupFrameworksServicesRoboTests \
+    platform-test-annotations \
+    robolectric_android-all-stub \
+    Robolectric_all-target \
+    mockito-robolectric-prebuilt \
+    truth-prebuilt \
+    testng
+
+LOCAL_TEST_PACKAGE := BackupFrameworksServicesLib
+
+include external/robolectric-shadows/run_robotests.mk
diff --git a/services/robotests/backup/config/robolectric.properties b/services/robotests/backup/config/robolectric.properties
new file mode 100644
index 0000000..850557a
--- /dev/null
+++ b/services/robotests/backup/config/robolectric.properties
@@ -0,0 +1 @@
+sdk=NEWEST_SDK
\ No newline at end of file
diff --git a/services/robotests/src/android/app/backup/BackupUtilsTest.java b/services/robotests/backup/src/android/app/backup/BackupUtilsTest.java
similarity index 100%
rename from services/robotests/src/android/app/backup/BackupUtilsTest.java
rename to services/robotests/backup/src/android/app/backup/BackupUtilsTest.java
diff --git a/services/robotests/src/android/app/backup/ForwardingBackupAgent.java b/services/robotests/backup/src/android/app/backup/ForwardingBackupAgent.java
similarity index 100%
rename from services/robotests/src/android/app/backup/ForwardingBackupAgent.java
rename to services/robotests/backup/src/android/app/backup/ForwardingBackupAgent.java
diff --git a/services/robotests/src/com/android/commands/bmgr/BmgrTest.java b/services/robotests/backup/src/com/android/commands/bmgr/BmgrTest.java
similarity index 100%
rename from services/robotests/src/com/android/commands/bmgr/BmgrTest.java
rename to services/robotests/backup/src/com/android/commands/bmgr/BmgrTest.java
diff --git a/services/robotests/src/com/android/commands/bu/AdbBackupTest.java b/services/robotests/backup/src/com/android/commands/bu/AdbBackupTest.java
similarity index 100%
rename from services/robotests/src/com/android/commands/bu/AdbBackupTest.java
rename to services/robotests/backup/src/com/android/commands/bu/AdbBackupTest.java
diff --git a/services/robotests/src/com/android/server/backup/BackupAgentTimeoutParametersTest.java b/services/robotests/backup/src/com/android/server/backup/BackupAgentTimeoutParametersTest.java
similarity index 100%
rename from services/robotests/src/com/android/server/backup/BackupAgentTimeoutParametersTest.java
rename to services/robotests/backup/src/com/android/server/backup/BackupAgentTimeoutParametersTest.java
diff --git a/services/robotests/src/com/android/server/backup/BackupManagerConstantsTest.java b/services/robotests/backup/src/com/android/server/backup/BackupManagerConstantsTest.java
similarity index 100%
rename from services/robotests/src/com/android/server/backup/BackupManagerConstantsTest.java
rename to services/robotests/backup/src/com/android/server/backup/BackupManagerConstantsTest.java
diff --git a/services/robotests/backup/src/com/android/server/backup/BackupManagerServiceTest.java b/services/robotests/backup/src/com/android/server/backup/BackupManagerServiceTest.java
new file mode 100644
index 0000000..83f66c5
--- /dev/null
+++ b/services/robotests/backup/src/com/android/server/backup/BackupManagerServiceTest.java
@@ -0,0 +1,1480 @@
+/*
+ * 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.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;
+
+import android.annotation.UserIdInt;
+import android.app.Application;
+import android.app.backup.IBackupManagerMonitor;
+import android.app.backup.IBackupObserver;
+import android.app.backup.IFullBackupRestoreObserver;
+import android.app.backup.ISelectBackupTransportCallback;
+import android.content.Context;
+import android.content.Intent;
+import android.os.IBinder;
+import android.os.ParcelFileDescriptor;
+import android.os.UserHandle;
+import android.platform.test.annotations.Presubmit;
+import android.util.SparseArray;
+
+import com.android.server.backup.testing.TransportData;
+import com.android.server.testing.shadows.ShadowBinder;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+import org.robolectric.shadows.ShadowContextWrapper;
+
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
+/** Tests for the user-aware backup/restore system service {@link BackupManagerService}. */
+@RunWith(RobolectricTestRunner.class)
+@Config(shadows = {ShadowBinder.class})
+@Presubmit
+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 ShadowContextWrapper mShadowContext;
+    private Context mContext;
+    @UserIdInt private int mUserOneId;
+    @UserIdInt private int mUserTwoId;
+    @Mock private UserBackupManagerService mUserOneService;
+    @Mock private UserBackupManagerService mUserTwoService;
+
+    /** Initialize {@link BackupManagerService}. */
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+
+        Application application = RuntimeEnvironment.application;
+        mContext = application;
+        mShadowContext = shadowOf(application);
+
+        // 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;
+    }
+
+    /**
+     * Clean up and reset state that was created for testing {@link BackupManagerService}
+     * operations.
+     */
+    @After
+    public void tearDown() throws Exception {
+        ShadowBinder.reset();
+    }
+
+    /**
+     * 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 {
+        boolean moreDebug = BackupManagerService.MORE_DEBUG;
+
+        assertThat(moreDebug).isFalse();
+    }
+
+    /** 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
+    // ---------------------------------------------
+
+    /** Test that the backup service routes methods correctly to the user that requests it. */
+    @Test
+    public void testDataChanged_onRegisteredUser_callsMethodForUser() throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+
+        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_onRegisteredUser_callsMethodForUser() throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        IBinder agentBinder = mock(IBinder.class);
+
+        backupManagerService.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_onRegisteredUser_callsMethodForUser() throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+
+        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_onRegisteredUser_callsMethodForUser() throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+
+        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);
+    }
+
+    // ---------------------------------------------
+    // Transport tests
+    // ---------------------------------------------
+
+    /** Test that the backup service routes methods correctly to the user that requests it. */
+    @Test
+    public void testInitializeTransports_onRegisteredUser_callsMethodForUser() throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        String[] transports = {TEST_TRANSPORT};
+
+        backupManagerService.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_onRegisteredUser_callsMethodForUser() throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+
+        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_onRegisteredUser_callsMethodForUser() throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+
+        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_onRegisteredUser_callsMethodForUser()
+            throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+
+        backupManagerService.getCurrentTransportComponent();
+
+        verify(mUserOneService).getCurrentTransportComponent();
+    }
+
+    /** Test that the backup service does not route methods for non-registered users. */
+    @Test
+    public void testGetCurrentTransportComponent_onUnknownUser_doesNotPropagateCall()
+            throws Exception {
+        BackupManagerService backupManagerService = createService();
+
+        backupManagerService.getCurrentTransportComponent();
+
+        verify(mUserOneService, never()).getCurrentTransportComponent();
+    }
+
+    /** Test that the backup service routes methods correctly to the user that requests it. */
+    @Test
+    public void testListAllTransports_onRegisteredUser_callsMethodForUser() throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+
+        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 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();
+
+        backupManagerService.updateTransportAttributes(
+                transport.getTransportComponent(),
+                transport.transportName,
+                configurationIntent,
+                "currentDestinationString",
+                dataManagementIntent,
+                "dataManagementLabel");
+
+        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,
+                        configurationIntent,
+                        "currentDestinationString",
+                        dataManagementIntent,
+                        "dataManagementLabel");
+    }
+
+    /** Test that the backup service routes methods correctly to the user that requests it. */
+    @Test
+    public void testSelectBackupTransport_onRegisteredUser_callsMethodForUser() throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+
+        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_onRegisteredUser_callsMethodForUser() throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        TransportData transport = backupTransport();
+        ISelectBackupTransportCallback callback = mock(ISelectBackupTransportCallback.class);
+
+        backupManagerService.selectBackupTransportAsync(
+                transport.getTransportComponent(), callback);
+
+        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_onRegisteredUser_callsMethodForUser() throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+
+        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_onRegisteredUser_callsMethodForUser() throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+
+        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_onRegisteredUser_callsMethodForUser() throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+
+        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_onRegisteredUser_callsMethodForUser() throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+
+        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 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 testSetBackupEnabled_withoutPermission_throwsSecurityExceptionForNonCallingUser() {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+
+        expectThrows(
+                SecurityException.class,
+                () -> backupManagerService.setBackupEnabled(mUserTwoId, true));
+    }
+
+    /**
+     * 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 testSetBackupEnabled_withPermission_propagatesForNonCallingUser() {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        backupManagerService.startServiceForUser(mUserTwoId, mUserTwoService);
+        setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ true);
+
+        backupManagerService.setBackupEnabled(mUserTwoId, true);
+
+        verify(mUserTwoService).setBackupEnabled(true);
+    }
+
+    /** Test that the backup service routes methods correctly to the user that requests it. */
+    @Test
+    public void testSetBackupEnabled_onRegisteredUser_callsMethodForUser() throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+
+        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 testSetAutoRestore_onRegisteredUser_callsMethodForUser() throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+
+        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 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_throwsSecurityExceptionForNonCallingUser() {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+
+        expectThrows(
+                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_onRegisteredUser_callsMethodForUser() throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+
+        backupManagerService.isBackupEnabled(mUserOneId);
+
+        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();
+    }
+
+    // ---------------------------------------------
+    // Backup tests
+    // ---------------------------------------------
+
+    /** Test that the backup service routes methods correctly to the user that requests it. */
+    @Test
+    public void testIsAppEligibleForBackup_onRegisteredUser_callsMethodForUser() throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+
+        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_onRegisteredUser_callsMethodForUser()
+            throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        String[] packages = {TEST_PACKAGE};
+
+        backupManagerService.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
+    public void testBackupNow_withoutPermission_throwsSecurityExceptionForNonCallingUser() {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+
+        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_onRegisteredUser_callsMethodForUser() throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+
+        backupManagerService.backupNow(mUserOneId);
+
+        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 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_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,
+                () ->
+                        backupManagerService.requestBackup(
+                                mUserTwoId, packages, observer, monitor, 0));
+    }
+
+    /**
+     * 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 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);
+
+        backupManagerService.requestBackup(mUserTwoId, packages, observer, monitor, /* flags */ 0);
+
+        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 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();
+
+        backupManagerService.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_onRegisteredUser_callsMethodForUser() throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+
+        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_onRegisteredUser_callsMethodForUser() throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        String[] packages = {TEST_PACKAGE};
+
+        backupManagerService.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);
+    }
+
+    // ---------------------------------------------
+    // Restore tests
+    // ---------------------------------------------
+
+    /** Test that the backup service routes methods correctly to the user that requests it. */
+    @Test
+    public void testRestoreAtInstall_onRegisteredUser_callsMethodForUser() throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+
+        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_onRegisteredUser_callsMethodForUser() throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+
+        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_onRegisteredUser_callsMethodForUser()
+            throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+
+        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);
+    }
+
+    // ---------------------------------------------
+    // Adb backup/restore tests
+    // ---------------------------------------------
+
+    /** Test that the backup service routes methods correctly to the user that requests it. */
+    @Test
+    public void testSetBackupPassword_onRegisteredUser_callsMethodForUser() throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+
+        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_onRegisteredUser_callsMethodForUser() throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+
+        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 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_throwsSecurityExceptionForNonCallingUser() {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+
+        expectThrows(
+                SecurityException.class,
+                () ->
+                        backupManagerService.adbBackup(
+                                mUserTwoId,
+                                /* parcelFileDescriptor*/ null,
+                                /* includeApks */ true,
+                                /* includeObbs */ true,
+                                /* includeShared */ true,
+                                /* doWidgets */ true,
+                                /* doAllApps */ true,
+                                /* includeSystem */ true,
+                                /* doCompress */ true,
+                                /* doKeyValue */ true,
+                                null));
+    }
+
+    /**
+     * 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_withPermission_propagatesForNonCallingUser() throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        backupManagerService.startServiceForUser(mUserTwoId, mUserTwoService);
+        ParcelFileDescriptor parcelFileDescriptor = getFileDescriptorForAdbTest();
+        setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ true);
+
+        backupManagerService.adbBackup(
+                mUserTwoId,
+                parcelFileDescriptor,
+                /* includeApks */ true,
+                /* includeObbs */ true,
+                /* includeShared */ true,
+                /* doWidgets */ true,
+                /* doAllApps */ true,
+                /* includeSystem */ true,
+                /* doCompress */ true,
+                /* doKeyValue */ true,
+                ADB_TEST_PACKAGES);
+
+        verify(mUserTwoService)
+                .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 routes methods correctly to the user that requests it. */
+    @Test
+    public void testAdbBackup_onRegisteredUser_callsMethodForUser() throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        ParcelFileDescriptor parcelFileDescriptor = getFileDescriptorForAdbTest();
+        setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+
+        backupManagerService.adbBackup(
+                mUserOneId,
+                parcelFileDescriptor,
+                /* includeApks */ true,
+                /* includeObbs */ true,
+                /* includeShared */ true,
+                /* doWidgets */ true,
+                /* doAllApps */ true,
+                /* includeSystem */ true,
+                /* doCompress */ true,
+                /* doKeyValue */ true,
+                ADB_TEST_PACKAGES);
+
+        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,
+                        /* includeObbs */ true,
+                        /* includeShared */ true,
+                        /* doWidgets */ true,
+                        /* doAllApps */ true,
+                        /* includeSystem */ true,
+                        /* doCompress */ true,
+                        /* doKeyValue */ true,
+                        ADB_TEST_PACKAGES);
+    }
+
+    /**
+     * 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_throwsSecurityExceptionForNonCallingUser() {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+
+        expectThrows(
+                SecurityException.class, () -> backupManagerService.adbRestore(mUserTwoId, null));
+    }
+
+    /**
+     * 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_withPermission_propagatesForNonCallingUser() throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        backupManagerService.startServiceForUser(mUserTwoId, mUserTwoService);
+        ParcelFileDescriptor parcelFileDescriptor = getFileDescriptorForAdbTest();
+        setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ true);
+
+        backupManagerService.adbRestore(mUserTwoId, parcelFileDescriptor);
+
+        verify(mUserTwoService).adbRestore(parcelFileDescriptor);
+    }
+
+    /** Test that the backup service routes methods correctly to the user that requests it. */
+    @Test
+    public void testAdbRestore_onRegisteredUser_callsMethodForUser() throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        ParcelFileDescriptor parcelFileDescriptor = getFileDescriptorForAdbTest();
+        setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+
+        backupManagerService.adbRestore(mUserOneId, 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_onRegisteredUser_callsMethodForUser()
+            throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        IFullBackupRestoreObserver observer = mock(IFullBackupRestoreObserver.class);
+
+        backupManagerService.acknowledgeAdbBackupOrRestore(
+                /* token */ 0, /* allow */ true, "currentPassword", "encryptionPassword", observer);
+
+        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,
+                        "currentPassword",
+                        "encryptionPassword",
+                        observer);
+    }
+
+    // ---------------------------------------------
+    //  Service tests
+    // ---------------------------------------------
+
+    /** Test that the backup service routes methods correctly to the user that requests it. */
+    @Test
+    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"};
+
+        backupManagerService.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 {
+        File testFile = new File(mContext.getFilesDir(), "test");
+        testFile.createNewFile();
+        return ParcelFileDescriptor.open(testFile, ParcelFileDescriptor.MODE_READ_WRITE);
+    }
+}
diff --git a/services/robotests/src/com/android/server/backup/KeyValueBackupJobTest.java b/services/robotests/backup/src/com/android/server/backup/KeyValueBackupJobTest.java
similarity index 100%
rename from services/robotests/src/com/android/server/backup/KeyValueBackupJobTest.java
rename to services/robotests/backup/src/com/android/server/backup/KeyValueBackupJobTest.java
diff --git a/services/robotests/src/com/android/server/backup/TransportManagerTest.java b/services/robotests/backup/src/com/android/server/backup/TransportManagerTest.java
similarity index 100%
rename from services/robotests/src/com/android/server/backup/TransportManagerTest.java
rename to services/robotests/backup/src/com/android/server/backup/TransportManagerTest.java
diff --git a/services/robotests/src/com/android/server/backup/UserBackupManagerServiceTest.java b/services/robotests/backup/src/com/android/server/backup/UserBackupManagerServiceTest.java
similarity index 98%
rename from services/robotests/src/com/android/server/backup/UserBackupManagerServiceTest.java
rename to services/robotests/backup/src/com/android/server/backup/UserBackupManagerServiceTest.java
index efbcb96..1e468d4 100644
--- a/services/robotests/src/com/android/server/backup/UserBackupManagerServiceTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/UserBackupManagerServiceTest.java
@@ -87,6 +87,7 @@
     private static final String TAG = "BMSTest";
     private static final String PACKAGE_1 = "some.package.1";
     private static final String PACKAGE_2 = "some.package.2";
+    private static final int USER_ID = 10;
 
     @Mock private TransportManager mTransportManager;
     private HandlerThread mBackupThread;
@@ -979,6 +980,7 @@
         mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
 
         UserBackupManagerService.createAndInitializeService(
+                USER_ID,
                 mContext,
                 new Trampoline(mContext),
                 mBackupThread,
@@ -1000,6 +1002,7 @@
         mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
 
         UserBackupManagerService.createAndInitializeService(
+                USER_ID,
                 mContext,
                 new Trampoline(mContext),
                 mBackupThread,
@@ -1022,6 +1025,7 @@
                 NullPointerException.class,
                 () ->
                         UserBackupManagerService.createAndInitializeService(
+                                USER_ID,
                                 /* context */ null,
                                 new Trampoline(mContext),
                                 mBackupThread,
@@ -1041,6 +1045,7 @@
                 NullPointerException.class,
                 () ->
                         UserBackupManagerService.createAndInitializeService(
+                                USER_ID,
                                 mContext,
                                 /* trampoline */ null,
                                 mBackupThread,
@@ -1060,6 +1065,7 @@
                 NullPointerException.class,
                 () ->
                         UserBackupManagerService.createAndInitializeService(
+                                USER_ID,
                                 mContext,
                                 new Trampoline(mContext),
                                 /* backupThread */ null,
@@ -1079,6 +1085,7 @@
                 NullPointerException.class,
                 () ->
                         UserBackupManagerService.createAndInitializeService(
+                                USER_ID,
                                 mContext,
                                 new Trampoline(mContext),
                                 mBackupThread,
@@ -1089,8 +1096,8 @@
 
     /**
      * Test checking non-null argument on {@link
-     * UserBackupManagerService#createAndInitializeService(Context, Trampoline, HandlerThread, File,
-     * File, TransportManager)}.
+     * UserBackupManagerService#createAndInitializeService(int, Context, Trampoline, HandlerThread,
+     * File, File, TransportManager)}.
      */
     @Test
     public void testCreateAndInitializeService_withNullDataDir_throws() {
@@ -1098,6 +1105,7 @@
                 NullPointerException.class,
                 () ->
                         UserBackupManagerService.createAndInitializeService(
+                                USER_ID,
                                 mContext,
                                 new Trampoline(mContext),
                                 mBackupThread,
@@ -1108,8 +1116,8 @@
 
     /**
      * Test checking non-null argument on {@link
-     * UserBackupManagerService#createAndInitializeService(Context, Trampoline, HandlerThread, File,
-     * File, TransportManager)}.
+     * UserBackupManagerService#createAndInitializeService(int, Context, Trampoline, HandlerThread,
+     * File, File, TransportManager)}.
      */
     @Test
     public void testCreateAndInitializeService_withNullTransportManager_throws() {
@@ -1117,6 +1125,7 @@
                 NullPointerException.class,
                 () ->
                         UserBackupManagerService.createAndInitializeService(
+                                USER_ID,
                                 mContext,
                                 new Trampoline(mContext),
                                 mBackupThread,
@@ -1127,7 +1136,7 @@
 
     private UserBackupManagerService createUserBackupManagerServiceAndRunTasks() {
         return BackupManagerServiceTestUtils.createUserBackupManagerServiceAndRunTasks(
-                mContext, mBackupThread, mBaseStateDir, mDataDir, mTransportManager);
+                USER_ID, mContext, mBackupThread, mBaseStateDir, mDataDir, mTransportManager);
     }
 
     private void setUpPowerManager(UserBackupManagerService backupManagerService) {
diff --git a/services/robotests/src/com/android/server/backup/encryption/chunk/ChunkHashTest.java b/services/robotests/backup/src/com/android/server/backup/encryption/chunk/ChunkHashTest.java
similarity index 100%
rename from services/robotests/src/com/android/server/backup/encryption/chunk/ChunkHashTest.java
rename to services/robotests/backup/src/com/android/server/backup/encryption/chunk/ChunkHashTest.java
diff --git a/services/robotests/src/com/android/server/backup/encryption/chunk/ChunkListingTest.java b/services/robotests/backup/src/com/android/server/backup/encryption/chunk/ChunkListingTest.java
similarity index 100%
rename from services/robotests/src/com/android/server/backup/encryption/chunk/ChunkListingTest.java
rename to services/robotests/backup/src/com/android/server/backup/encryption/chunk/ChunkListingTest.java
diff --git a/services/robotests/src/com/android/server/backup/encryption/chunk/ChunkTest.java b/services/robotests/backup/src/com/android/server/backup/encryption/chunk/ChunkTest.java
similarity index 100%
rename from services/robotests/src/com/android/server/backup/encryption/chunk/ChunkTest.java
rename to services/robotests/backup/src/com/android/server/backup/encryption/chunk/ChunkTest.java
diff --git a/services/robotests/src/com/android/server/backup/encryption/chunk/EncryptedChunkOrderingTest.java b/services/robotests/backup/src/com/android/server/backup/encryption/chunk/EncryptedChunkOrderingTest.java
similarity index 100%
rename from services/robotests/src/com/android/server/backup/encryption/chunk/EncryptedChunkOrderingTest.java
rename to services/robotests/backup/src/com/android/server/backup/encryption/chunk/EncryptedChunkOrderingTest.java
diff --git a/services/robotests/src/com/android/server/backup/encryption/chunking/ChunkEncryptorTest.java b/services/robotests/backup/src/com/android/server/backup/encryption/chunking/ChunkEncryptorTest.java
similarity index 100%
rename from services/robotests/src/com/android/server/backup/encryption/chunking/ChunkEncryptorTest.java
rename to services/robotests/backup/src/com/android/server/backup/encryption/chunking/ChunkEncryptorTest.java
diff --git a/services/robotests/src/com/android/server/backup/encryption/chunking/ChunkHasherTest.java b/services/robotests/backup/src/com/android/server/backup/encryption/chunking/ChunkHasherTest.java
similarity index 100%
rename from services/robotests/src/com/android/server/backup/encryption/chunking/ChunkHasherTest.java
rename to services/robotests/backup/src/com/android/server/backup/encryption/chunking/ChunkHasherTest.java
diff --git a/services/robotests/src/com/android/server/backup/encryption/chunking/EncryptedChunkTest.java b/services/robotests/backup/src/com/android/server/backup/encryption/chunking/EncryptedChunkTest.java
similarity index 100%
rename from services/robotests/src/com/android/server/backup/encryption/chunking/EncryptedChunkTest.java
rename to services/robotests/backup/src/com/android/server/backup/encryption/chunking/EncryptedChunkTest.java
diff --git a/services/robotests/src/com/android/server/backup/encryption/chunking/InlineLengthsEncryptedChunkEncoderTest.java b/services/robotests/backup/src/com/android/server/backup/encryption/chunking/InlineLengthsEncryptedChunkEncoderTest.java
similarity index 100%
rename from services/robotests/src/com/android/server/backup/encryption/chunking/InlineLengthsEncryptedChunkEncoderTest.java
rename to services/robotests/backup/src/com/android/server/backup/encryption/chunking/InlineLengthsEncryptedChunkEncoderTest.java
diff --git a/services/robotests/src/com/android/server/backup/encryption/chunking/LengthlessEncryptedChunkEncoderTest.java b/services/robotests/backup/src/com/android/server/backup/encryption/chunking/LengthlessEncryptedChunkEncoderTest.java
similarity index 100%
rename from services/robotests/src/com/android/server/backup/encryption/chunking/LengthlessEncryptedChunkEncoderTest.java
rename to services/robotests/backup/src/com/android/server/backup/encryption/chunking/LengthlessEncryptedChunkEncoderTest.java
diff --git a/services/robotests/src/com/android/server/backup/encryption/chunking/RawBackupWriterTest.java b/services/robotests/backup/src/com/android/server/backup/encryption/chunking/RawBackupWriterTest.java
similarity index 100%
rename from services/robotests/src/com/android/server/backup/encryption/chunking/RawBackupWriterTest.java
rename to services/robotests/backup/src/com/android/server/backup/encryption/chunking/RawBackupWriterTest.java
diff --git a/services/robotests/src/com/android/server/backup/encryption/chunking/cdc/ContentDefinedChunkerTest.java b/services/robotests/backup/src/com/android/server/backup/encryption/chunking/cdc/ContentDefinedChunkerTest.java
similarity index 100%
rename from services/robotests/src/com/android/server/backup/encryption/chunking/cdc/ContentDefinedChunkerTest.java
rename to services/robotests/backup/src/com/android/server/backup/encryption/chunking/cdc/ContentDefinedChunkerTest.java
diff --git a/services/robotests/src/com/android/server/backup/encryption/chunking/cdc/FingerprintMixerTest.java b/services/robotests/backup/src/com/android/server/backup/encryption/chunking/cdc/FingerprintMixerTest.java
similarity index 100%
rename from services/robotests/src/com/android/server/backup/encryption/chunking/cdc/FingerprintMixerTest.java
rename to services/robotests/backup/src/com/android/server/backup/encryption/chunking/cdc/FingerprintMixerTest.java
diff --git a/services/robotests/src/com/android/server/backup/encryption/chunking/cdc/HkdfTest.java b/services/robotests/backup/src/com/android/server/backup/encryption/chunking/cdc/HkdfTest.java
similarity index 100%
rename from services/robotests/src/com/android/server/backup/encryption/chunking/cdc/HkdfTest.java
rename to services/robotests/backup/src/com/android/server/backup/encryption/chunking/cdc/HkdfTest.java
diff --git a/services/robotests/src/com/android/server/backup/encryption/chunking/cdc/IsChunkBreakpointTest.java b/services/robotests/backup/src/com/android/server/backup/encryption/chunking/cdc/IsChunkBreakpointTest.java
similarity index 100%
rename from services/robotests/src/com/android/server/backup/encryption/chunking/cdc/IsChunkBreakpointTest.java
rename to services/robotests/backup/src/com/android/server/backup/encryption/chunking/cdc/IsChunkBreakpointTest.java
diff --git a/services/robotests/src/com/android/server/backup/encryption/chunking/cdc/RabinFingerprint64Test.java b/services/robotests/backup/src/com/android/server/backup/encryption/chunking/cdc/RabinFingerprint64Test.java
similarity index 100%
rename from services/robotests/src/com/android/server/backup/encryption/chunking/cdc/RabinFingerprint64Test.java
rename to services/robotests/backup/src/com/android/server/backup/encryption/chunking/cdc/RabinFingerprint64Test.java
diff --git a/services/robotests/src/com/android/server/backup/encryption/keys/RecoverableKeyStoreSecondaryKeyManagerTest.java b/services/robotests/backup/src/com/android/server/backup/encryption/keys/RecoverableKeyStoreSecondaryKeyManagerTest.java
similarity index 100%
rename from services/robotests/src/com/android/server/backup/encryption/keys/RecoverableKeyStoreSecondaryKeyManagerTest.java
rename to services/robotests/backup/src/com/android/server/backup/encryption/keys/RecoverableKeyStoreSecondaryKeyManagerTest.java
diff --git a/services/robotests/src/com/android/server/backup/encryption/keys/RecoverableKeyStoreSecondaryKeyTest.java b/services/robotests/backup/src/com/android/server/backup/encryption/keys/RecoverableKeyStoreSecondaryKeyTest.java
similarity index 100%
rename from services/robotests/src/com/android/server/backup/encryption/keys/RecoverableKeyStoreSecondaryKeyTest.java
rename to services/robotests/backup/src/com/android/server/backup/encryption/keys/RecoverableKeyStoreSecondaryKeyTest.java
diff --git a/services/robotests/src/com/android/server/backup/encryption/keys/TertiaryKeyGeneratorTest.java b/services/robotests/backup/src/com/android/server/backup/encryption/keys/TertiaryKeyGeneratorTest.java
similarity index 100%
rename from services/robotests/src/com/android/server/backup/encryption/keys/TertiaryKeyGeneratorTest.java
rename to services/robotests/backup/src/com/android/server/backup/encryption/keys/TertiaryKeyGeneratorTest.java
diff --git a/services/robotests/src/com/android/server/backup/encryption/keys/TertiaryKeyRotationTrackerTest.java b/services/robotests/backup/src/com/android/server/backup/encryption/keys/TertiaryKeyRotationTrackerTest.java
similarity index 100%
rename from services/robotests/src/com/android/server/backup/encryption/keys/TertiaryKeyRotationTrackerTest.java
rename to services/robotests/backup/src/com/android/server/backup/encryption/keys/TertiaryKeyRotationTrackerTest.java
diff --git a/services/robotests/src/com/android/server/backup/encryption/storage/BackupEncryptionDbTest.java b/services/robotests/backup/src/com/android/server/backup/encryption/storage/BackupEncryptionDbTest.java
similarity index 100%
rename from services/robotests/src/com/android/server/backup/encryption/storage/BackupEncryptionDbTest.java
rename to services/robotests/backup/src/com/android/server/backup/encryption/storage/BackupEncryptionDbTest.java
diff --git a/services/robotests/src/com/android/server/backup/encryption/storage/TertiaryKeysTableTest.java b/services/robotests/backup/src/com/android/server/backup/encryption/storage/TertiaryKeysTableTest.java
similarity index 100%
rename from services/robotests/src/com/android/server/backup/encryption/storage/TertiaryKeysTableTest.java
rename to services/robotests/backup/src/com/android/server/backup/encryption/storage/TertiaryKeysTableTest.java
diff --git a/services/robotests/src/com/android/server/backup/fullbackup/AppMetadataBackupWriterTest.java b/services/robotests/backup/src/com/android/server/backup/fullbackup/AppMetadataBackupWriterTest.java
similarity index 100%
rename from services/robotests/src/com/android/server/backup/fullbackup/AppMetadataBackupWriterTest.java
rename to services/robotests/backup/src/com/android/server/backup/fullbackup/AppMetadataBackupWriterTest.java
diff --git a/services/robotests/src/com/android/server/backup/internal/PerformInitializeTaskTest.java b/services/robotests/backup/src/com/android/server/backup/internal/PerformInitializeTaskTest.java
similarity index 100%
rename from services/robotests/src/com/android/server/backup/internal/PerformInitializeTaskTest.java
rename to services/robotests/backup/src/com/android/server/backup/internal/PerformInitializeTaskTest.java
diff --git a/services/robotests/src/com/android/server/backup/keyvalue/AgentExceptionTest.java b/services/robotests/backup/src/com/android/server/backup/keyvalue/AgentExceptionTest.java
similarity index 100%
rename from services/robotests/src/com/android/server/backup/keyvalue/AgentExceptionTest.java
rename to services/robotests/backup/src/com/android/server/backup/keyvalue/AgentExceptionTest.java
diff --git a/services/robotests/src/com/android/server/backup/keyvalue/BackupExceptionTest.java b/services/robotests/backup/src/com/android/server/backup/keyvalue/BackupExceptionTest.java
similarity index 100%
rename from services/robotests/src/com/android/server/backup/keyvalue/BackupExceptionTest.java
rename to services/robotests/backup/src/com/android/server/backup/keyvalue/BackupExceptionTest.java
diff --git a/services/robotests/src/com/android/server/backup/keyvalue/KeyValueBackupReporterTest.java b/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupReporterTest.java
similarity index 100%
rename from services/robotests/src/com/android/server/backup/keyvalue/KeyValueBackupReporterTest.java
rename to services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupReporterTest.java
diff --git a/services/robotests/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java b/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java
similarity index 99%
rename from services/robotests/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java
rename to services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java
index 099127c..cf51e19 100644
--- a/services/robotests/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java
@@ -169,6 +169,7 @@
     private static final PackageData PACKAGE_2 = keyValuePackage(2);
     private static final String BACKUP_AGENT_SHARED_PREFS_SYNCHRONIZER_CLASS =
             "android.app.backup.BackupAgent$SharedPrefsSynchronizer";
+    private static final int USER_ID = 10;
 
     @Mock private TransportManager mTransportManager;
     @Mock private DataChangedJournal mOldJournal;
@@ -224,7 +225,7 @@
         setUpBinderCallerAndApplicationAsSystem(mApplication);
         mBackupManagerService =
                 spy(createUserBackupManagerServiceAndRunTasks(
-                        mContext, mBaseStateDir, mDataDir, mTransportManager));
+                        USER_ID, mContext, mBaseStateDir, mDataDir, mTransportManager));
         setUpBackupManagerServiceBasics(
                 mBackupManagerService,
                 mApplication,
diff --git a/services/robotests/src/com/android/server/backup/keyvalue/TaskExceptionTest.java b/services/robotests/backup/src/com/android/server/backup/keyvalue/TaskExceptionTest.java
similarity index 100%
rename from services/robotests/src/com/android/server/backup/keyvalue/TaskExceptionTest.java
rename to services/robotests/backup/src/com/android/server/backup/keyvalue/TaskExceptionTest.java
diff --git a/services/robotests/src/com/android/server/backup/remote/FutureBackupCallbackTest.java b/services/robotests/backup/src/com/android/server/backup/remote/FutureBackupCallbackTest.java
similarity index 100%
rename from services/robotests/src/com/android/server/backup/remote/FutureBackupCallbackTest.java
rename to services/robotests/backup/src/com/android/server/backup/remote/FutureBackupCallbackTest.java
diff --git a/services/robotests/src/com/android/server/backup/remote/RemoteCallTest.java b/services/robotests/backup/src/com/android/server/backup/remote/RemoteCallTest.java
similarity index 100%
rename from services/robotests/src/com/android/server/backup/remote/RemoteCallTest.java
rename to services/robotests/backup/src/com/android/server/backup/remote/RemoteCallTest.java
diff --git a/services/robotests/src/com/android/server/backup/remote/RemoteResultTest.java b/services/robotests/backup/src/com/android/server/backup/remote/RemoteResultTest.java
similarity index 100%
rename from services/robotests/src/com/android/server/backup/remote/RemoteResultTest.java
rename to services/robotests/backup/src/com/android/server/backup/remote/RemoteResultTest.java
diff --git a/services/robotests/src/com/android/server/backup/remote/ServiceBackupCallbackTest.java b/services/robotests/backup/src/com/android/server/backup/remote/ServiceBackupCallbackTest.java
similarity index 100%
rename from services/robotests/src/com/android/server/backup/remote/ServiceBackupCallbackTest.java
rename to services/robotests/backup/src/com/android/server/backup/remote/ServiceBackupCallbackTest.java
diff --git a/services/robotests/src/com/android/server/backup/restore/ActiveRestoreSessionTest.java b/services/robotests/backup/src/com/android/server/backup/restore/ActiveRestoreSessionTest.java
similarity index 100%
rename from services/robotests/src/com/android/server/backup/restore/ActiveRestoreSessionTest.java
rename to services/robotests/backup/src/com/android/server/backup/restore/ActiveRestoreSessionTest.java
diff --git a/services/robotests/src/com/android/server/backup/testing/BackupManagerServiceTestUtils.java b/services/robotests/backup/src/com/android/server/backup/testing/BackupManagerServiceTestUtils.java
similarity index 95%
rename from services/robotests/src/com/android/server/backup/testing/BackupManagerServiceTestUtils.java
rename to services/robotests/backup/src/com/android/server/backup/testing/BackupManagerServiceTestUtils.java
index 06f6d21..b978570 100644
--- a/services/robotests/src/com/android/server/backup/testing/BackupManagerServiceTestUtils.java
+++ b/services/robotests/backup/src/com/android/server/backup/testing/BackupManagerServiceTestUtils.java
@@ -58,13 +58,17 @@
      * <p>If the class-under-test is going to execute methods as the system, it's a good idea to
      * also call {@link #setUpBinderCallerAndApplicationAsSystem(Application)} before this method.
      *
-     * @see #createUserBackupManagerServiceAndRunTasks(Context, HandlerThread, File, File,
+     * @see #createUserBackupManagerServiceAndRunTasks(int, Context, HandlerThread, File, File,
      *     TransportManager)
      */
     public static UserBackupManagerService createUserBackupManagerServiceAndRunTasks(
-            Context context, File baseStateDir, File dataDir, TransportManager transportManager) {
+            int userId,
+            Context context,
+            File baseStateDir,
+            File dataDir,
+            TransportManager transportManager) {
         return createUserBackupManagerServiceAndRunTasks(
-                context, startBackupThread(null), baseStateDir, dataDir, transportManager);
+                userId, context, startBackupThread(null), baseStateDir, dataDir, transportManager);
     }
 
     /**
@@ -75,6 +79,7 @@
      * also call {@link #setUpBinderCallerAndApplicationAsSystem(Application)} before this method.
      */
     public static UserBackupManagerService createUserBackupManagerServiceAndRunTasks(
+            int userId,
             Context context,
             HandlerThread backupThread,
             File baseStateDir,
@@ -82,6 +87,7 @@
             TransportManager transportManager) {
         UserBackupManagerService backupManagerService =
                 UserBackupManagerService.createAndInitializeService(
+                        userId,
                         context,
                         new Trampoline(context),
                         backupThread,
diff --git a/services/robotests/src/com/android/server/backup/testing/CryptoTestUtils.java b/services/robotests/backup/src/com/android/server/backup/testing/CryptoTestUtils.java
similarity index 100%
rename from services/robotests/src/com/android/server/backup/testing/CryptoTestUtils.java
rename to services/robotests/backup/src/com/android/server/backup/testing/CryptoTestUtils.java
diff --git a/services/robotests/src/com/android/server/backup/testing/PackageData.java b/services/robotests/backup/src/com/android/server/backup/testing/PackageData.java
similarity index 100%
rename from services/robotests/src/com/android/server/backup/testing/PackageData.java
rename to services/robotests/backup/src/com/android/server/backup/testing/PackageData.java
diff --git a/services/robotests/src/com/android/server/backup/testing/TestUtils.java b/services/robotests/backup/src/com/android/server/backup/testing/TestUtils.java
similarity index 100%
rename from services/robotests/src/com/android/server/backup/testing/TestUtils.java
rename to services/robotests/backup/src/com/android/server/backup/testing/TestUtils.java
diff --git a/services/robotests/src/com/android/server/backup/testing/TransportData.java b/services/robotests/backup/src/com/android/server/backup/testing/TransportData.java
similarity index 100%
rename from services/robotests/src/com/android/server/backup/testing/TransportData.java
rename to services/robotests/backup/src/com/android/server/backup/testing/TransportData.java
diff --git a/services/robotests/src/com/android/server/backup/testing/TransportTestUtils.java b/services/robotests/backup/src/com/android/server/backup/testing/TransportTestUtils.java
similarity index 100%
rename from services/robotests/src/com/android/server/backup/testing/TransportTestUtils.java
rename to services/robotests/backup/src/com/android/server/backup/testing/TransportTestUtils.java
diff --git a/services/robotests/src/com/android/server/backup/testing/Utils.java b/services/robotests/backup/src/com/android/server/backup/testing/Utils.java
similarity index 100%
rename from services/robotests/src/com/android/server/backup/testing/Utils.java
rename to services/robotests/backup/src/com/android/server/backup/testing/Utils.java
diff --git a/services/robotests/src/com/android/server/backup/transport/TransportClientManagerTest.java b/services/robotests/backup/src/com/android/server/backup/transport/TransportClientManagerTest.java
similarity index 100%
rename from services/robotests/src/com/android/server/backup/transport/TransportClientManagerTest.java
rename to services/robotests/backup/src/com/android/server/backup/transport/TransportClientManagerTest.java
diff --git a/services/robotests/src/com/android/server/backup/transport/TransportClientTest.java b/services/robotests/backup/src/com/android/server/backup/transport/TransportClientTest.java
similarity index 100%
rename from services/robotests/src/com/android/server/backup/transport/TransportClientTest.java
rename to services/robotests/backup/src/com/android/server/backup/transport/TransportClientTest.java
diff --git a/services/robotests/src/com/android/server/backup/transport/TransportStatsTest.java b/services/robotests/backup/src/com/android/server/backup/transport/TransportStatsTest.java
similarity index 100%
rename from services/robotests/src/com/android/server/backup/transport/TransportStatsTest.java
rename to services/robotests/backup/src/com/android/server/backup/transport/TransportStatsTest.java
diff --git a/services/robotests/src/com/android/server/backup/BackupManagerServiceTest.java b/services/robotests/src/com/android/server/backup/BackupManagerServiceTest.java
deleted file mode 100644
index 58bce1c..0000000
--- a/services/robotests/src/com/android/server/backup/BackupManagerServiceTest.java
+++ /dev/null
@@ -1,744 +0,0 @@
-/*
- * 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.server.backup;
-
-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.verify;
-import static org.robolectric.Shadows.shadowOf;
-import static org.testng.Assert.expectThrows;
-
-import android.annotation.UserIdInt;
-import android.app.Application;
-import android.app.backup.IBackupManagerMonitor;
-import android.app.backup.IBackupObserver;
-import android.app.backup.IFullBackupRestoreObserver;
-import android.app.backup.ISelectBackupTransportCallback;
-import android.content.Context;
-import android.content.Intent;
-import android.os.IBinder;
-import android.os.ParcelFileDescriptor;
-import android.os.UserHandle;
-import android.platform.test.annotations.Presubmit;
-
-import com.android.server.backup.testing.BackupManagerServiceTestUtils;
-import com.android.server.backup.testing.TransportData;
-import com.android.server.testing.shadows.ShadowBinder;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.RuntimeEnvironment;
-import org.robolectric.annotation.Config;
-import org.robolectric.shadows.ShadowContextWrapper;
-
-import java.io.File;
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-
-/** Tests for the user-aware backup/restore system service {@link BackupManagerService}. */
-@RunWith(RobolectricTestRunner.class)
-@Config(shadows = {ShadowBinder.class})
-@Presubmit
-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;
-
-    /** Initialize {@link BackupManagerService}. */
-    @Before
-    public void setUp() throws Exception {
-        MockitoAnnotations.initMocks(this);
-
-        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);
-    }
-
-    /**
-     * Clean up and reset state that was created for testing {@link BackupManagerService}
-     * operations.
-     */
-    @After
-    public void tearDown() throws Exception {
-        ShadowBinder.reset();
-    }
-
-    /**
-     * 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 {
-        boolean moreDebug = BackupManagerService.MORE_DEBUG;
-
-        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.
-
-    // ---------------------------------------------
-    // Backup agent tests
-    // ---------------------------------------------
-
-    /** 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);
-
-        verify(mUserBackupManagerService).dataChanged(TEST_PACKAGE);
-    }
-
-    /** Test that the backup service routes methods correctly to the user that requests it. */
-    @Test
-    public void testAgentConnected_callsAgentConnectedForUser() throws Exception {
-        IBinder agentBinder = mock(IBinder.class);
-
-        mBackupManagerService.agentConnected(TEST_PACKAGE, agentBinder);
-
-        verify(mUserBackupManagerService).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);
-
-        verify(mUserBackupManagerService).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);
-
-        verify(mUserBackupManagerService).opComplete(/* token */ 0, /* result */ 0L);
-    }
-
-    // ---------------------------------------------
-    // Transport tests
-    // ---------------------------------------------
-
-    /** Test that the backup service routes methods correctly to the user that requests it. */
-    @Test
-    public void testInitializeTransports_callsInitializeTransportsForUser() throws Exception {
-        String[] transports = {TEST_TRANSPORT};
-
-        mBackupManagerService.initializeTransports(transports, /* observer */ null);
-
-        verify(mUserBackupManagerService).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);
-
-        verify(mUserBackupManagerService).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();
-
-        verify(mUserBackupManagerService).getCurrentTransport();
-    }
-
-    /** Test that the backup service routes methods correctly to the user that requests it. */
-    @Test
-    public void testGetCurrentTransportComponent_callsGetCurrentTransportComponentForUser()
-            throws Exception {
-        mBackupManagerService.getCurrentTransportComponent();
-
-        verify(mUserBackupManagerService).getCurrentTransportComponent();
-    }
-
-    /** Test that the backup service routes methods correctly to the user that requests it. */
-    @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()
-            throws Exception {
-        mBackupManagerService.listAllTransportComponents();
-
-        verify(mUserBackupManagerService).listAllTransportComponents();
-    }
-
-    /** Test that the backup service routes methods correctly to the user that requests it. */
-    @Test
-    public void testGetTransportWhitelist_callsGetTransportWhitelistForUser() throws Exception {
-        mBackupManagerService.getTransportWhitelist();
-
-        verify(mUserBackupManagerService).getTransportWhitelist();
-    }
-
-    /** Test that the backup service routes methods correctly to the user that requests it. */
-    @Test
-    public void testUpdateTransportAttributes_callsUpdateTransportAttributesForUser()
-            throws Exception {
-        TransportData transport = backupTransport();
-        Intent configurationIntent = new Intent();
-        Intent dataManagementIntent = new Intent();
-
-        mBackupManagerService.updateTransportAttributes(
-                transport.getTransportComponent(),
-                transport.transportName,
-                configurationIntent,
-                "currentDestinationString",
-                dataManagementIntent,
-                "dataManagementLabel");
-
-        verify(mUserBackupManagerService)
-                .updateTransportAttributes(
-                        transport.getTransportComponent(),
-                        transport.transportName,
-                        configurationIntent,
-                        "currentDestinationString",
-                        dataManagementIntent,
-                        "dataManagementLabel");
-    }
-
-    /** 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);
-
-        verify(mUserBackupManagerService).selectBackupTransport(TEST_TRANSPORT);
-    }
-
-    /** Test that the backup service routes methods correctly to the user that requests it. */
-    @Test
-    public void testSelectTransportAsync_callsSelectTransportAsyncForUser() throws Exception {
-        TransportData transport = backupTransport();
-        ISelectBackupTransportCallback callback = mock(ISelectBackupTransportCallback.class);
-
-        mBackupManagerService.selectBackupTransportAsync(
-                transport.getTransportComponent(), callback);
-
-        verify(mUserBackupManagerService)
-                .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);
-
-        verify(mUserBackupManagerService).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);
-
-        verify(mUserBackupManagerService).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);
-
-        verify(mUserBackupManagerService).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);
-
-        verify(mUserBackupManagerService).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
-    public void setBackupEnabled_withoutPermission_throwsSecurityException() {
-        mShadowContext.denyPermissions(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
-
-        expectThrows(
-                SecurityException.class,
-                () -> mBackupManagerService.setBackupEnabled(mUserId, 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
-    public void setBackupEnabled_whenCallingUserIsTargetUser_doesntNeedPermission() {
-        ShadowBinder.setCallingUserHandle(UserHandle.of(mUserId));
-        mShadowContext.denyPermissions(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
-
-        mBackupManagerService.setBackupEnabled(mUserId, 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);
-    }
-
-    /** Test that the backup service routes methods correctly to the user that requests it. */
-    @Test
-    public void setAutoRestore_callsSetAutoRestoreForUser() throws Exception {
-        mBackupManagerService.setAutoRestore(true);
-
-        verify(mUserBackupManagerService).setAutoRestore(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);
-
-        verify(mUserBackupManagerService).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
-    public void testIsBackupEnabled_withoutPermission_throwsSecurityException() {
-        mShadowContext.denyPermissions(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
-
-        expectThrows(
-                SecurityException.class,
-                () -> mBackupManagerService.isBackupEnabled(mUserId));
-    }
-
-    /** 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);
-
-        mBackupManagerService.isBackupEnabled(mUserId);
-
-        verify(mUserBackupManagerService).isBackupEnabled();
-    }
-
-    // ---------------------------------------------
-    // Backup tests
-    // ---------------------------------------------
-
-    /** 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);
-
-        verify(mUserBackupManagerService).isAppEligibleForBackup(TEST_PACKAGE);
-    }
-
-    /** Test that the backup service routes methods correctly to the user that requests it. */
-    @Test
-    public void testFilterAppsEligibleForBackup_callsFilterAppsEligibleForBackupForUser()
-            throws Exception {
-        String[] packages = {TEST_PACKAGE};
-
-        mBackupManagerService.filterAppsEligibleForBackup(packages);
-
-        verify(mUserBackupManagerService).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
-    public void testBackupNow_withoutPermission_throwsSecurityException() {
-        mShadowContext.denyPermissions(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
-
-        expectThrows(
-                SecurityException.class,
-                () -> mBackupManagerService.backupNow(mUserId));
-    }
-
-    /** 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);
-
-        mBackupManagerService.backupNow(mUserId);
-
-        verify(mUserBackupManagerService).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
-    public void testRequestBackup_withoutPermission_throwsSecurityException() {
-        mShadowContext.denyPermissions(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
-        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);
-    }
-
-    /**
-     * 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_throwsSecurityException() {
-        mShadowContext.denyPermissions(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
-
-        expectThrows(
-                SecurityException.class,
-                () -> mBackupManagerService.cancelBackups(mUserId));
-    }
-
-
-    /** 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();
-    }
-
-    /** Test that the backup service routes methods correctly to the user that requests it. */
-    @Test
-    public void testBeginFullBackup_callsBeginFullBackupForUser() throws Exception {
-        FullBackupJob job = new FullBackupJob();
-
-        mBackupManagerService.beginFullBackup(job);
-
-        verify(mUserBackupManagerService).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();
-
-        verify(mUserBackupManagerService).endFullBackup();
-    }
-
-    /** Test that the backup service routes methods correctly to the user that requests it. */
-    @Test
-    public void testFullTransportBackup_callsFullTransportBackupForUser() throws Exception {
-        String[] packages = {TEST_PACKAGE};
-
-        mBackupManagerService.fullTransportBackup(packages);
-
-        verify(mUserBackupManagerService).fullTransportBackup(packages);
-    }
-
-    // ---------------------------------------------
-    // Restore tests
-    // ---------------------------------------------
-
-    /** 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);
-
-        verify(mUserBackupManagerService).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);
-
-        verify(mUserBackupManagerService).beginRestoreSession(TEST_PACKAGE, TEST_TRANSPORT);
-    }
-
-    /** Test that the backup service routes methods correctly to the user that requests it. */
-    @Test
-    public void testGetAvailableRestoreToken_callsGetAvailableRestoreTokenForUser()
-            throws Exception {
-        mBackupManagerService.getAvailableRestoreToken(TEST_PACKAGE);
-
-        verify(mUserBackupManagerService).getAvailableRestoreToken(TEST_PACKAGE);
-    }
-
-    // ---------------------------------------------
-    // Adb backup/restore tests
-    // ---------------------------------------------
-
-    /** 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");
-
-        verify(mUserBackupManagerService).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();
-
-        verify(mUserBackupManagerService).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
-    public void testAdbBackup_withoutPermission_throwsSecurityException() {
-        mShadowContext.denyPermissions(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
-
-        expectThrows(SecurityException.class,
-                () ->
-                        mBackupManagerService.adbBackup(
-                                /* userId */ mUserId,
-                                /* parcelFileDescriptor*/ null,
-                                /* includeApks */ true,
-                                /* includeObbs */ true,
-                                /* includeShared */ true,
-                                /* doWidgets */ true,
-                                /* doAllApps */ true,
-                                /* includeSystem */ true,
-                                /* 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
-    public void testAdbBackup_whenCallingUserIsTargetUser_doesntNeedPermission() throws Exception {
-        ShadowBinder.setCallingUserHandle(UserHandle.of(mUserId));
-        mShadowContext.denyPermissions(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
-
-        ParcelFileDescriptor parcelFileDescriptor = getFileDescriptorForAdbTest();
-
-        mBackupManagerService.adbBackup(
-                /* userId */ mUserId,
-                parcelFileDescriptor,
-                /* includeApks */ true,
-                /* includeObbs */ true,
-                /* includeShared */ true,
-                /* doWidgets */ true,
-                /* doAllApps */ true,
-                /* includeSystem */ true,
-                /* doCompress */ true,
-                /* doKeyValue */ true,
-                ADB_TEST_PACKAGES);
-
-        verify(mUserBackupManagerService)
-                .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 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);
-
-        ParcelFileDescriptor parcelFileDescriptor = getFileDescriptorForAdbTest();
-
-        mBackupManagerService.adbBackup(
-                /* userId */ mUserId,
-                parcelFileDescriptor,
-                /* includeApks */ true,
-                /* includeObbs */ true,
-                /* includeShared */ true,
-                /* doWidgets */ true,
-                /* doAllApps */ true,
-                /* includeSystem */ true,
-                /* doCompress */ true,
-                /* doKeyValue */ true,
-                ADB_TEST_PACKAGES);
-
-        verify(mUserBackupManagerService)
-                .adbBackup(
-                        parcelFileDescriptor,
-                        /* includeApks */ true,
-                        /* includeObbs */ true,
-                        /* includeShared */ true,
-                        /* doWidgets */ true,
-                        /* doAllApps */ true,
-                        /* includeSystem */ true,
-                        /* doCompress */ true,
-                        /* doKeyValue */ true,
-                        ADB_TEST_PACKAGES);
-    }
-
-    /**
-     * Test verifying that {@link BackupManagerService#adbRestore(ParcelFileDescriptor, int)} throws
-     * a {@link SecurityException} if the caller does not have INTERACT_ACROSS_USERS_FULL
-     * permission.
-     */
-    @Test
-    public void testAdbRestore_withoutPermission_throwsSecurityException() {
-        mShadowContext.denyPermissions(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
-
-        expectThrows(SecurityException.class,
-                () -> mBackupManagerService.adbRestore(mUserId, 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
-    public void testAdbRestore_whenCallingUserIsTargetUser_doesntNeedPermission() throws Exception {
-        ShadowBinder.setCallingUserHandle(UserHandle.of(mUserId));
-        mShadowContext.denyPermissions(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
-
-        ParcelFileDescriptor parcelFileDescriptor = getFileDescriptorForAdbTest();
-
-        mBackupManagerService.adbRestore(mUserId, parcelFileDescriptor);
-
-        verify(mUserBackupManagerService).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);
-
-        ParcelFileDescriptor parcelFileDescriptor = getFileDescriptorForAdbTest();
-
-        mBackupManagerService.adbRestore(mUserId, parcelFileDescriptor);
-
-        verify(mUserBackupManagerService).adbRestore(parcelFileDescriptor);
-    }
-
-    /** Test that the backup service routes methods correctly to the user that requests it. */
-    @Test
-    public void testAcknowledgeAdbBackupOrRestore_callsAcknowledgeAdbBackupOrRestoreForUser()
-            throws Exception {
-        IFullBackupRestoreObserver observer = mock(IFullBackupRestoreObserver.class);
-
-        mBackupManagerService.acknowledgeAdbBackupOrRestore(
-                /* token */ 0, /* allow */ true, "currentPassword", "encryptionPassword", observer);
-
-        verify(mUserBackupManagerService)
-                .acknowledgeAdbBackupOrRestore(
-                        /* token */ 0,
-                        /* allow */ true,
-                        "currentPassword",
-                        "encryptionPassword",
-                        observer);
-    }
-
-    // ---------------------------------------------
-    //  Service tests
-    // ---------------------------------------------
-
-    /** Test that the backup service routes methods correctly to the user that requests it. */
-    @Test
-    public void testDump_callsDumpForUser() throws Exception {
-        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);
-
-        verify(mUserBackupManagerService).dump(fileDescriptor, printWriter, args);
-    }
-
-    private ParcelFileDescriptor getFileDescriptorForAdbTest() throws Exception {
-        File testFile = new File(mContext.getFilesDir(), "test");
-        testFile.createNewFile();
-        return ParcelFileDescriptor.open(testFile, ParcelFileDescriptor.MODE_READ_WRITE);
-    }
-}
diff --git a/services/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml
index cf4d3a8..1b5ba26 100644
--- a/services/tests/servicestests/AndroidManifest.xml
+++ b/services/tests/servicestests/AndroidManifest.xml
@@ -25,7 +25,6 @@
     <uses-permission android:name="android.permission.READ_PHONE_STATE" />
     <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
     <uses-permission android:name="android.permission.BROADCAST_STICKY" />
-    <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
     <uses-permission android:name="android.permission.UPDATE_DEVICE_STATS" />
     <uses-permission android:name="android.permission.MANAGE_APP_TOKENS" />
     <uses-permission android:name="android.permission.WAKE_LOCK" />
diff --git a/services/tests/servicestests/src/com/android/server/am/SettingsToPropertiesMapperTest.java b/services/tests/servicestests/src/com/android/server/am/SettingsToPropertiesMapperTest.java
index d965f8a..0fd5921 100644
--- a/services/tests/servicestests/src/com/android/server/am/SettingsToPropertiesMapperTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/SettingsToPropertiesMapperTest.java
@@ -41,11 +41,14 @@
 
 /**
  * Tests for {@link SettingsToPropertiesMapper}
+ *
+ *  Build/Install/Run:
+ *  atest FrameworksServicesTests:SettingsToPropertiesMapperTest
  */
 @RunWith(AndroidJUnit4.class)
 @SmallTest
 public class SettingsToPropertiesMapperTest {
-    private static final String NAME_VALID_CHARACTERS_REGEX = "^[\\w\\.\\-@:]*$";
+    private static final String NAME_VALID_CHARACTERS_REGEX = "^[\\w\\-@:]*$";
     private static final String[] TEST_MAPPING = new String[] {
             Settings.Global.SQLITE_COMPATIBILITY_WAL_FLAGS
     };
@@ -77,7 +80,28 @@
             }
             if (!globalSetting.matches(NAME_VALID_CHARACTERS_REGEX)) {
                 Assert.fail(globalSetting + " contains invalid characters. "
-                        + "Only alphanumeric characters, '.', '-', '@', ':' and '_' are valid.");
+                        + "Only alphanumeric characters, '-', '@', ':' and '_' are valid.");
+            }
+        }
+    }
+
+    @Test
+    public void validateRegisteredDeviceConfigScopes() {
+        HashSet<String> hashSet = new HashSet<>();
+        for (String deviceConfigScope : SettingsToPropertiesMapper.sDeviceConfigScopes) {
+            if (hashSet.contains(deviceConfigScope)) {
+                Assert.fail("deviceConfigScope "
+                        + deviceConfigScope
+                        + " is registered more than once in "
+                        + "SettingsToPropertiesMapper.sDeviceConfigScopes.");
+            }
+            hashSet.add(deviceConfigScope);
+            if (TextUtils.isEmpty(deviceConfigScope)) {
+                Assert.fail("empty deviceConfigScope registered.");
+            }
+            if (!deviceConfigScope.matches(NAME_VALID_CHARACTERS_REGEX)) {
+                Assert.fail(deviceConfigScope + " contains invalid characters. "
+                        + "Only alphanumeric characters, '-', '@', ':' and '_' are valid.");
             }
         }
     }
@@ -98,8 +122,7 @@
                 Settings.Global.SQLITE_COMPATIBILITY_WAL_FLAGS, "testValue2");
         mTestMapper.updatePropertyFromSetting(
                 Settings.Global.SQLITE_COMPATIBILITY_WAL_FLAGS,
-                systemPropertyName,
-                true);
+                systemPropertyName);
         propValue = mTestMapper.systemPropertiesGet(systemPropertyName);
         Assert.assertEquals("testValue2", propValue);
 
@@ -107,8 +130,7 @@
                 Settings.Global.SQLITE_COMPATIBILITY_WAL_FLAGS, null);
         mTestMapper.updatePropertyFromSetting(
                 Settings.Global.SQLITE_COMPATIBILITY_WAL_FLAGS,
-                systemPropertyName,
-                true);
+                systemPropertyName);
         propValue = mTestMapper.systemPropertiesGet(systemPropertyName);
         Assert.assertEquals("", propValue);
     }
diff --git a/services/tests/servicestests/src/com/android/server/appops/AppOpsNotedWatcherTest.java b/services/tests/servicestests/src/com/android/server/appops/AppOpsNotedWatcherTest.java
new file mode 100644
index 0000000..52f434d
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/appops/AppOpsNotedWatcherTest.java
@@ -0,0 +1,100 @@
+/*
+ * 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.server.appops;
+
+import android.Manifest;
+import android.app.AppOpsManager;
+import android.app.AppOpsManager.OnOpNotedListener;
+import android.content.Context;
+import android.os.Process;
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.InOrder;
+
+
+import static org.junit.Assert.fail;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.timeout;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+
+/**
+ * Tests watching noted ops.
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class AppOpsNotedWatcherTest {
+
+    private static final long NOTIFICATION_TIMEOUT_MILLIS = 5000;
+
+    public void testWatchNotedOpsRequiresPermission() {
+        // Create a mock listener
+        final OnOpNotedListener listener = mock(OnOpNotedListener.class);
+
+        // Try to start watching noted ops
+        final AppOpsManager appOpsManager = getContext().getSystemService(AppOpsManager.class);
+        try {
+            appOpsManager.startWatchingNoted(new String[]{AppOpsManager.OPSTR_FINE_LOCATION,
+                    AppOpsManager.OPSTR_RECORD_AUDIO}, listener);
+            fail("Watching noted ops shoudl require " + Manifest.permission.WATCH_APPOPS);
+        } catch (SecurityException expected) {
+            /*ignored*/
+        }
+    }
+
+    @Test
+    public void testWatchNotedOps() {
+        // Create a mock listener
+        final OnOpNotedListener listener = mock(OnOpNotedListener.class);
+
+        // Start watching noted ops
+        final AppOpsManager appOpsManager = getContext().getSystemService(AppOpsManager.class);
+        appOpsManager.startWatchingNoted(new String[]{AppOpsManager.OPSTR_FINE_LOCATION,
+                AppOpsManager.OPSTR_CAMERA}, listener);
+
+        // Note some ops
+        appOpsManager.noteOp(AppOpsManager.OPSTR_FINE_LOCATION, Process.myUid(),
+                getContext().getPackageName());
+        appOpsManager.noteOp(AppOpsManager.OPSTR_CAMERA, Process.myUid(),
+                getContext().getPackageName());
+
+        // Verify that we got called for the ops being noted
+        final InOrder inOrder = inOrder(listener);
+        inOrder.verify(listener, timeout(NOTIFICATION_TIMEOUT_MILLIS)
+                .times(1)).onOpNoted(eq(AppOpsManager.OPSTR_FINE_LOCATION),
+                eq(Process.myUid()), eq(getContext().getPackageName()),
+                eq(AppOpsManager.MODE_ALLOWED));
+        inOrder.verify(listener, timeout(NOTIFICATION_TIMEOUT_MILLIS)
+                .times(1)).onOpNoted(eq(AppOpsManager.OPSTR_CAMERA),
+                eq(Process.myUid()), eq(getContext().getPackageName()),
+                eq(AppOpsManager.MODE_ALLOWED));
+
+        // Stop watching
+        appOpsManager.stopWatchingNoted(listener);
+
+        // This should be the only two callbacks we got
+        verifyNoMoreInteractions(listener);
+    }
+
+    private static Context getContext() {
+        return InstrumentationRegistry.getContext();
+    }
+}
\ No newline at end of file
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..5615dff 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);
     }
@@ -300,6 +302,15 @@
     }
 
     @Test
+    public void dataChangedForUser_forwarded() throws RemoteException {
+        mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+
+        mTrampoline.dataChangedForUser(mUserId, PACKAGE_NAME);
+
+        verify(mBackupManagerServiceMock).dataChanged(PACKAGE_NAME);
+    }
+
+    @Test
     public void dataChanged_forwarded() throws RemoteException {
         mTrampoline.initializeService(UserHandle.USER_SYSTEM);
         mTrampoline.dataChanged(PACKAGE_NAME);
@@ -313,6 +324,15 @@
     }
 
     @Test
+    public void clearBackupDataForUser_forwarded() throws RemoteException {
+        mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+
+        mTrampoline.clearBackupDataForUser(mUserId, TRANSPORT_NAME, PACKAGE_NAME);
+
+        verify(mBackupManagerServiceMock).clearBackupData(TRANSPORT_NAME, PACKAGE_NAME);
+    }
+
+    @Test
     public void clearBackupData_forwarded() throws RemoteException {
         mTrampoline.initializeService(UserHandle.USER_SYSTEM);
         mTrampoline.clearBackupData(TRANSPORT_NAME, PACKAGE_NAME);
@@ -326,6 +346,15 @@
     }
 
     @Test
+    public void agentConnectedForUser_forwarded() throws RemoteException {
+        mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+
+        mTrampoline.agentConnectedForUser(mUserId, PACKAGE_NAME, mAgentMock);
+
+        verify(mBackupManagerServiceMock).agentConnected(PACKAGE_NAME, mAgentMock);
+    }
+
+    @Test
     public void agentConnected_forwarded() throws RemoteException {
         mTrampoline.initializeService(UserHandle.USER_SYSTEM);
         mTrampoline.agentConnected(PACKAGE_NAME, mAgentMock);
@@ -339,6 +368,15 @@
     }
 
     @Test
+    public void agentDisconnectedForUser_forwarded() throws RemoteException {
+        mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+
+        mTrampoline.agentDisconnectedForUser(mUserId, PACKAGE_NAME);
+
+        verify(mBackupManagerServiceMock).agentDisconnected(PACKAGE_NAME);
+    }
+
+    @Test
     public void agentDisconnected_forwarded() throws RemoteException {
         mTrampoline.initializeService(UserHandle.USER_SYSTEM);
         mTrampoline.agentDisconnected(PACKAGE_NAME);
@@ -352,6 +390,15 @@
     }
 
     @Test
+    public void restoreAtInstallForUser_forwarded() throws RemoteException {
+        mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+
+        mTrampoline.restoreAtInstallForUser(mUserId, PACKAGE_NAME, 123);
+
+        verify(mBackupManagerServiceMock).restoreAtInstall(PACKAGE_NAME, 123);
+    }
+
+    @Test
     public void restoreAtInstall_forwarded() throws RemoteException {
         mTrampoline.initializeService(UserHandle.USER_SYSTEM);
         mTrampoline.restoreAtInstall(PACKAGE_NAME, 123);
@@ -390,6 +437,15 @@
     }
 
     @Test
+    public void setAutoRestoreForUser_forwarded() throws RemoteException {
+        mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+
+        mTrampoline.setAutoRestoreForUser(mUserId, true);
+
+        verify(mBackupManagerServiceMock).setAutoRestore(true);
+    }
+
+    @Test
     public void setAutoRestore_forwarded() throws RemoteException {
         mTrampoline.initializeService(UserHandle.USER_SYSTEM);
         mTrampoline.setAutoRestore(true);
@@ -505,14 +561,14 @@
 
     @Test
     public void fullTransportBackup_calledBeforeInitialize_ignored() throws RemoteException {
-        mTrampoline.fullTransportBackup(PACKAGE_NAMES);
+        mTrampoline.fullTransportBackupForUser(mUserId, PACKAGE_NAMES);
         verifyNoMoreInteractions(mBackupManagerServiceMock);
     }
 
     @Test
-    public void fullTransportBackup_forwarded() throws RemoteException {
+    public void fullTransportBackupForUser_forwarded() throws RemoteException {
         mTrampoline.initializeService(UserHandle.USER_SYSTEM);
-        mTrampoline.fullTransportBackup(PACKAGE_NAMES);
+        mTrampoline.fullTransportBackupForUser(mUserId, PACKAGE_NAMES);
         verify(mBackupManagerServiceMock).fullTransportBackup(PACKAGE_NAMES);
     }
 
@@ -538,6 +594,22 @@
     }
 
     @Test
+    public void acknowledgeFullBackupOrRestoreForUser_forwarded() throws RemoteException {
+        mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+
+        mTrampoline.acknowledgeFullBackupOrRestoreForUser(
+                mUserId,
+                123,
+                true,
+                CURRENT_PASSWORD,
+                ENCRYPTION_PASSWORD,
+                mFullBackupRestoreObserverMock);
+
+        verify(mBackupManagerServiceMock).acknowledgeAdbBackupOrRestore(123, true, CURRENT_PASSWORD,
+                ENCRYPTION_PASSWORD, mFullBackupRestoreObserverMock);
+    }
+
+    @Test
     public void acknowledgeFullBackupOrRestore_forwarded() throws RemoteException {
         mTrampoline.initializeService(UserHandle.USER_SYSTEM);
         mTrampoline.acknowledgeFullBackupOrRestore(123, true, CURRENT_PASSWORD, ENCRYPTION_PASSWORD,
@@ -553,6 +625,16 @@
     }
 
     @Test
+    public void getCurrentTransportForUser_forwarded() throws RemoteException {
+        when(mBackupManagerServiceMock.getCurrentTransport()).thenReturn(TRANSPORT_NAME);
+        mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+
+        assertEquals(TRANSPORT_NAME, mTrampoline.getCurrentTransportForUser(mUserId));
+
+        verify(mBackupManagerServiceMock).getCurrentTransport();
+    }
+
+    @Test
     public void getCurrentTransport_forwarded() throws RemoteException {
         when(mBackupManagerServiceMock.getCurrentTransport()).thenReturn(TRANSPORT_NAME);
 
@@ -569,6 +651,16 @@
     }
 
     @Test
+    public void listAllTransportsForUser_forwarded() throws RemoteException {
+        when(mBackupManagerServiceMock.listAllTransports()).thenReturn(TRANSPORTS);
+        mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+
+        assertEquals(TRANSPORTS, mTrampoline.listAllTransportsForUser(mUserId));
+        verify(mBackupManagerServiceMock).listAllTransports();
+    }
+
+
+    @Test
     public void listAllTransports_forwarded() throws RemoteException {
         when(mBackupManagerServiceMock.listAllTransports()).thenReturn(TRANSPORTS);
 
@@ -578,18 +670,19 @@
     }
 
     @Test
-    public void listAllTransportComponents_calledBeforeInitialize_ignored() throws RemoteException {
-        assertNull(mTrampoline.listAllTransportComponents());
+    public void listAllTransportComponentsForUser_calledBeforeInitialize_ignored()
+            throws RemoteException {
+        assertNull(mTrampoline.listAllTransportComponentsForUser(mUserId));
         verifyNoMoreInteractions(mBackupManagerServiceMock);
     }
 
     @Test
-    public void listAllTransportComponents_forwarded() throws RemoteException {
+    public void listAllTransportComponentsForUser_forwarded() throws RemoteException {
         when(mBackupManagerServiceMock.listAllTransportComponents()).thenReturn(
                 TRANSPORT_COMPONENTS);
-
         mTrampoline.initializeService(UserHandle.USER_SYSTEM);
-        assertEquals(TRANSPORT_COMPONENTS, mTrampoline.listAllTransportComponents());
+
+        assertEquals(TRANSPORT_COMPONENTS, mTrampoline.listAllTransportComponentsForUser(mUserId));
         verify(mBackupManagerServiceMock).listAllTransportComponents();
     }
 
@@ -609,19 +702,34 @@
     }
 
     @Test
-    public void describeTransport_calledBeforeInitialize_ignored() throws RemoteException {
-        mTrampoline.updateTransportAttributes(TRANSPORT_COMPONENT_NAME, TRANSPORT_NAME, null,
-                "Transport Destination", null, "Data Management");
+    public void updateTransportAttributesForUser_calledBeforeInitialize_ignored()
+            throws RemoteException {
+        mTrampoline.updateTransportAttributesForUser(
+                mUserId,
+                TRANSPORT_COMPONENT_NAME,
+                TRANSPORT_NAME,
+                null,
+                "Transport Destination",
+                null,
+                "Data Management");
+
         verifyNoMoreInteractions(mBackupManagerServiceMock);
     }
 
     @Test
-    public void describeTransport_forwarded() throws RemoteException {
+    public void updateTransportAttributesForUser_forwarded() throws RemoteException {
         when(mBackupManagerServiceMock.getTransportWhitelist()).thenReturn(TRANSPORTS);
-
         mTrampoline.initializeService(UserHandle.USER_SYSTEM);
-        mTrampoline.updateTransportAttributes(TRANSPORT_COMPONENT_NAME, TRANSPORT_NAME, null,
-                "Transport Destination", null, "Data Management");
+
+        mTrampoline.updateTransportAttributesForUser(
+                mUserId,
+                TRANSPORT_COMPONENT_NAME,
+                TRANSPORT_NAME,
+                null,
+                "Transport Destination",
+                null,
+                "Data Management");
+
         verify(mBackupManagerServiceMock).updateTransportAttributes(TRANSPORT_COMPONENT_NAME,
                 TRANSPORT_NAME, null, "Transport Destination", null, "Data Management");
     }
@@ -633,6 +741,15 @@
     }
 
     @Test
+    public void selectBackupTransportForUser_forwarded() throws RemoteException {
+        mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+
+        mTrampoline.selectBackupTransportForUser(mUserId, TRANSPORT_NAME);
+
+        verify(mBackupManagerServiceMock).selectBackupTransport(TRANSPORT_NAME);
+    }
+
+    @Test
     public void selectBackupTransport_forwarded() throws RemoteException {
         mTrampoline.initializeService(UserHandle.USER_SYSTEM);
         mTrampoline.selectBackupTransport(TRANSPORT_NAME);
@@ -640,9 +757,12 @@
     }
 
     @Test
-    public void selectBackupTransportAsync_calledBeforeInitialize_ignored() throws Exception {
+    public void selectBackupTransportAsyncForUser_calledBeforeInitialize_ignored()
+            throws Exception {
         LinkedBlockingQueue<Integer> q = new LinkedBlockingQueue();
-        mTrampoline.selectBackupTransportAsync(
+
+        mTrampoline.selectBackupTransportAsyncForUser(
+                mUserId,
                 TRANSPORT_COMPONENT_NAME,
                 new ISelectBackupTransportCallback() {
                     @Override
@@ -660,6 +780,7 @@
                         return null;
                     }
                 });
+
         verifyNoMoreInteractions(mBackupManagerServiceMock);
         Integer errorCode = q.poll(5, TimeUnit.SECONDS);
         assertNotNull(errorCode);
@@ -667,17 +788,19 @@
     }
 
     @Test
-    public void selectBackupTransportAsync_calledBeforeInitialize_ignored_nullListener()
+    public void selectBackupTransportAsyncForUser_calledBeforeInitialize_ignored_nullListener()
             throws Exception {
-        mTrampoline.selectBackupTransportAsync(TRANSPORT_COMPONENT_NAME, null);
+        mTrampoline.selectBackupTransportAsyncForUser(mUserId, TRANSPORT_COMPONENT_NAME, null);
+
         verifyNoMoreInteractions(mBackupManagerServiceMock);
         // No crash.
     }
 
     @Test
-    public void selectBackupTransportAsync_calledBeforeInitialize_ignored_listenerThrowException()
+    public void selectBackupTransportAsyncForUser_calledBeforeInitialize_ignored_listenerThrows()
             throws Exception {
-        mTrampoline.selectBackupTransportAsync(
+        mTrampoline.selectBackupTransportAsyncForUser(
+                mUserId,
                 TRANSPORT_COMPONENT_NAME,
                 new ISelectBackupTransportCallback() {
                     @Override
@@ -695,14 +818,17 @@
                         return null;
                     }
                 });
+
         verifyNoMoreInteractions(mBackupManagerServiceMock);
         // No crash.
     }
 
     @Test
-    public void selectBackupTransportAsync_forwarded() throws RemoteException {
+    public void selectBackupTransportAsyncForUser_forwarded() throws RemoteException {
         mTrampoline.initializeService(UserHandle.USER_SYSTEM);
-        mTrampoline.selectBackupTransportAsync(TRANSPORT_COMPONENT_NAME, null);
+
+        mTrampoline.selectBackupTransportAsyncForUser(mUserId, TRANSPORT_COMPONENT_NAME, null);
+
         verify(mBackupManagerServiceMock).selectBackupTransportAsync(TRANSPORT_COMPONENT_NAME,
                 null);
     }
@@ -714,6 +840,19 @@
     }
 
     @Test
+    public void getConfigurationIntentForUser_forwarded() throws RemoteException {
+        Intent configurationIntentStub = new Intent();
+        when(mBackupManagerServiceMock.getConfigurationIntent(TRANSPORT_NAME)).thenReturn(
+                configurationIntentStub);
+        mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+
+        assertEquals(
+                configurationIntentStub,
+                mTrampoline.getConfigurationIntentForUser(mUserId, TRANSPORT_NAME));
+        verify(mBackupManagerServiceMock).getConfigurationIntent(TRANSPORT_NAME);
+    }
+
+    @Test
     public void getConfigurationIntent_forwarded() throws RemoteException {
         Intent configurationIntentStub = new Intent();
         when(mBackupManagerServiceMock.getConfigurationIntent(TRANSPORT_NAME)).thenReturn(
@@ -731,6 +870,18 @@
     }
 
     @Test
+    public void getDestinationStringForUser_forwarded() throws RemoteException {
+        when(mBackupManagerServiceMock.getDestinationString(TRANSPORT_NAME)).thenReturn(
+                DESTINATION_STRING);
+        mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+
+        assertEquals(
+                DESTINATION_STRING,
+                mTrampoline.getDestinationStringForUser(mUserId, TRANSPORT_NAME));
+        verify(mBackupManagerServiceMock).getDestinationString(TRANSPORT_NAME);
+    }
+
+    @Test
     public void getDestinationString_forwarded() throws RemoteException {
         when(mBackupManagerServiceMock.getDestinationString(TRANSPORT_NAME)).thenReturn(
                 DESTINATION_STRING);
@@ -747,6 +898,19 @@
     }
 
     @Test
+    public void getDataManagementIntentForUser_forwarded() throws RemoteException {
+        Intent dataManagementIntent = new Intent();
+        when(mBackupManagerServiceMock.getDataManagementIntent(TRANSPORT_NAME)).thenReturn(
+                dataManagementIntent);
+        mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+
+        assertEquals(
+                dataManagementIntent,
+                mTrampoline.getDataManagementIntentForUser(mUserId, TRANSPORT_NAME));
+        verify(mBackupManagerServiceMock).getDataManagementIntent(TRANSPORT_NAME);
+    }
+
+    @Test
     public void getDataManagementIntent_forwarded() throws RemoteException {
         Intent dataManagementIntent = new Intent();
         when(mBackupManagerServiceMock.getDataManagementIntent(TRANSPORT_NAME)).thenReturn(
@@ -764,6 +928,18 @@
     }
 
     @Test
+    public void getDataManagementLabelForUser_forwarded() throws RemoteException {
+        when(mBackupManagerServiceMock.getDataManagementLabel(TRANSPORT_NAME)).thenReturn(
+                DATA_MANAGEMENT_LABEL);
+        mTrampoline.initializeService(UserHandle.USER_SYSTEM);
+
+        assertEquals(
+                DATA_MANAGEMENT_LABEL,
+                mTrampoline.getDataManagementLabelForUser(mUserId, TRANSPORT_NAME));
+        verify(mBackupManagerServiceMock).getDataManagementLabel(TRANSPORT_NAME);
+    }
+
+    @Test
     public void getDataManagementLabel_forwarded() throws RemoteException {
         when(mBackupManagerServiceMock.getDataManagementLabel(TRANSPORT_NAME)).thenReturn(
                 DATA_MANAGEMENT_LABEL);
@@ -775,14 +951,16 @@
 
     @Test
     public void beginRestoreSession_calledBeforeInitialize_ignored() throws RemoteException {
-        mTrampoline.beginRestoreSession(PACKAGE_NAME, TRANSPORT_NAME);
+        mTrampoline.beginRestoreSessionForUser(mUserId, PACKAGE_NAME, TRANSPORT_NAME);
         verifyNoMoreInteractions(mBackupManagerServiceMock);
     }
 
     @Test
-    public void beginRestoreSession_forwarded() throws RemoteException {
+    public void beginRestoreSessionForUser_forwarded() throws RemoteException {
         mTrampoline.initializeService(UserHandle.USER_SYSTEM);
-        mTrampoline.beginRestoreSession(PACKAGE_NAME, TRANSPORT_NAME);
+
+        mTrampoline.beginRestoreSessionForUser(mUserId, PACKAGE_NAME, TRANSPORT_NAME);
+
         verify(mBackupManagerServiceMock).beginRestoreSession(PACKAGE_NAME, TRANSPORT_NAME);
     }
 
@@ -800,32 +978,34 @@
     }
 
     @Test
-    public void getAvailableRestoreToken_calledBeforeInitialize_ignored() throws RemoteException {
-        assertEquals(0, mTrampoline.getAvailableRestoreToken(PACKAGE_NAME));
+    public void getAvailableRestoreTokenForUser_calledBeforeInitialize_ignored()
+            throws RemoteException {
+        assertEquals(0, mTrampoline.getAvailableRestoreTokenForUser(mUserId, PACKAGE_NAME));
         verifyNoMoreInteractions(mBackupManagerServiceMock);
     }
 
     @Test
-    public void getAvailableRestoreToken_forwarded() throws RemoteException {
+    public void getAvailableRestoreTokenForUser_forwarded() throws RemoteException {
         when(mBackupManagerServiceMock.getAvailableRestoreToken(PACKAGE_NAME)).thenReturn(123L);
-
         mTrampoline.initializeService(UserHandle.USER_SYSTEM);
-        assertEquals(123, mTrampoline.getAvailableRestoreToken(PACKAGE_NAME));
+
+        assertEquals(123, mTrampoline.getAvailableRestoreTokenForUser(mUserId, PACKAGE_NAME));
         verify(mBackupManagerServiceMock).getAvailableRestoreToken(PACKAGE_NAME);
     }
 
     @Test
-    public void isAppEligibleForBackup_calledBeforeInitialize_ignored() throws RemoteException {
-        assertFalse(mTrampoline.isAppEligibleForBackup(PACKAGE_NAME));
+    public void isAppEligibleForBackupForUser_calledBeforeInitialize_ignored()
+            throws RemoteException {
+        assertFalse(mTrampoline.isAppEligibleForBackupForUser(mUserId, PACKAGE_NAME));
         verifyNoMoreInteractions(mBackupManagerServiceMock);
     }
 
     @Test
-    public void isAppEligibleForBackup_forwarded() throws RemoteException {
+    public void isAppEligibleForBackupForUser_forwarded() throws RemoteException {
         when(mBackupManagerServiceMock.isAppEligibleForBackup(PACKAGE_NAME)).thenReturn(true);
-
         mTrampoline.initializeService(UserHandle.USER_SYSTEM);
-        assertTrue(mTrampoline.isAppEligibleForBackup(PACKAGE_NAME));
+
+        assertTrue(mTrampoline.isAppEligibleForBackupForUser(mUserId, PACKAGE_NAME));
         verify(mBackupManagerServiceMock).isAppEligibleForBackup(PACKAGE_NAME);
     }
 
@@ -989,6 +1169,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/display/DisplayManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
index e9bfa8f..abf9040 100644
--- a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
@@ -149,12 +149,11 @@
         verify(mMockInputManagerInternal).setDisplayViewports(viewportCaptor.capture());
         List<DisplayViewport> viewports = viewportCaptor.getValue();
 
-        // Expect to receive 3 viewports: internal, external, and virtual
-        assertEquals(3, viewports.size());
+        // Expect to receive 2 viewports: internal, and virtual
+        assertEquals(2, viewports.size());
 
         DisplayViewport virtualViewport = null;
         DisplayViewport internalViewport = null;
-        DisplayViewport externalViewport = null;
         for (int i = 0; i < viewports.size(); i++) {
             DisplayViewport v = viewports.get(i);
             switch (v.type) {
@@ -163,7 +162,7 @@
                     break;
                 }
                 case DisplayViewport.VIEWPORT_EXTERNAL: {
-                    externalViewport = v;
+                    fail("EXTERNAL viewport should not exist.");
                     break;
                 }
                 case DisplayViewport.VIEWPORT_VIRTUAL: {
@@ -172,14 +171,12 @@
                 }
             }
         }
-        // INTERNAL and EXTERNAL viewports get created upon access
+        // INTERNAL viewport gets created upon access.
         assertNotNull(internalViewport);
-        assertNotNull(externalViewport);
         assertNotNull(virtualViewport);
 
-        // INTERNAL and EXTERNAL
+        // INTERNAL
         assertTrue(internalViewport.valid);
-        assertTrue(externalViewport.valid);
 
         // VIRTUAL
         assertEquals(height, virtualViewport.deviceHeight);
@@ -216,39 +213,16 @@
         verify(mMockInputManagerInternal).setDisplayViewports(viewportCaptor.capture());
         List<DisplayViewport> viewports = viewportCaptor.getValue();
 
-        // Expect to receive 2 viewports: 1 internal, 1 external
-        assertEquals(2, viewports.size());
+        // Expect to receive actual viewports: 1 internal
+        assertEquals(1, viewports.size());
 
-        DisplayViewport internalViewport = null;
-        DisplayViewport externalViewport = null;
-        for (int i = 0; i < viewports.size(); i++) {
-            DisplayViewport v = viewports.get(i);
-            switch (v.type) {
-                case DisplayViewport.VIEWPORT_INTERNAL: {
-                    internalViewport = v;
-                    break;
-                }
-                case DisplayViewport.VIEWPORT_EXTERNAL: {
-                    externalViewport = v;
-                    break;
-                }
-                default: {
-                    fail("Unexpected viewport type: " + DisplayViewport.typeToString(v.type));
-                    break;
-                }
-            }
-        }
-        // INTERNAL and EXTERNAL viewports get created upon access
+        DisplayViewport internalViewport = viewports.get(0);
+
+        // INTERNAL is the only one actual display.
         assertNotNull(internalViewport);
-        assertNotNull(externalViewport);
+        assertEquals(DisplayViewport.VIEWPORT_INTERNAL, internalViewport.type);
         assertTrue(internalViewport.valid);
         assertEquals(displayId, internalViewport.displayId);
-
-        // To simplify comparison, override the type for external Viewport
-        // TODO (b/116850516) remove this
-        externalViewport.type = internalViewport.type;
-        assertEquals(internalViewport, externalViewport);
-        externalViewport.type = DisplayViewport.VIEWPORT_EXTERNAL; // undo the changes above
     }
 
     @Test
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/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java
index e988994..bf4b52e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java
@@ -52,7 +52,6 @@
 import android.provider.Settings;
 import android.view.Surface;
 
-import androidx.test.filters.FlakyTest;
 import androidx.test.filters.SmallTest;
 
 import com.android.internal.util.test.FakeSettingsProvider;
@@ -81,7 +80,6 @@
  */
 @SmallTest
 @Presubmit
-@FlakyTest(detail = "Confirm stable in post-submit before removing")
 public class DisplayRotationTests {
     private static final long UI_HANDLER_WAIT_TIMEOUT_MS = 50;
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java
index f3a125b..8635364 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java
@@ -40,7 +40,6 @@
 import android.view.DisplayInfo;
 
 import androidx.test.InstrumentationRegistry;
-import androidx.test.filters.FlakyTest;
 import androidx.test.filters.MediumTest;
 
 import com.android.server.LocalServices;
@@ -65,7 +64,6 @@
  */
 @MediumTest
 @Presubmit
-@FlakyTest(detail = "Confirm stable in post-submit before removing")
 public class LaunchParamsPersisterTests extends ActivityTestsBase {
     private static final int TEST_USER_ID = 3;
     private static final int ALTERNATIVE_USER_ID = 0;
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java
index 0bd681b..7186e22 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java
@@ -47,7 +47,6 @@
 import android.view.Display;
 import android.view.Gravity;
 
-import androidx.test.filters.FlakyTest;
 import androidx.test.filters.SmallTest;
 
 import com.android.server.wm.LaunchParamsController.LaunchParams;
@@ -64,7 +63,6 @@
  *  atest WmTests:TaskLaunchParamsModifierTests
  */
 @SmallTest
-@FlakyTest(detail = "Confirm stable in post-submit before removing")
 @Presubmit
 public class TaskLaunchParamsModifierTests extends ActivityTestsBase {
 
diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
index 60cb08f..294b750 100644
--- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
@@ -16,6 +16,12 @@
 
 package com.android.server.usb;
 
+import static android.hardware.usb.UsbPortStatus.DATA_ROLE_DEVICE;
+import static android.hardware.usb.UsbPortStatus.DATA_ROLE_HOST;
+import static android.hardware.usb.UsbPortStatus.MODE_AUDIO_ACCESSORY;
+import static android.hardware.usb.UsbPortStatus.POWER_ROLE_SINK;
+import static android.hardware.usb.UsbPortStatus.POWER_ROLE_SOURCE;
+
 import static com.android.internal.usb.DumpUtils.writeAccessory;
 import static com.android.internal.util.dump.DumpUtils.writeStringIfNotNull;
 
@@ -36,6 +42,7 @@
 import android.content.res.Resources;
 import android.debug.AdbManagerInternal;
 import android.debug.IAdbTransport;
+import android.hardware.usb.ParcelableUsbPort;
 import android.hardware.usb.UsbAccessory;
 import android.hardware.usb.UsbConfiguration;
 import android.hardware.usb.UsbConstants;
@@ -294,9 +301,10 @@
         BroadcastReceiver portReceiver = new BroadcastReceiver() {
             @Override
             public void onReceive(Context context, Intent intent) {
-                UsbPort port = intent.getParcelableExtra(UsbManager.EXTRA_PORT);
+                ParcelableUsbPort port = intent.getParcelableExtra(UsbManager.EXTRA_PORT);
                 UsbPortStatus status = intent.getParcelableExtra(UsbManager.EXTRA_PORT_STATUS);
-                mHandler.updateHostState(port, status);
+                mHandler.updateHostState(
+                        port.getUsbPort(context.getSystemService(UsbManager.class)), status);
             }
         };
 
@@ -821,23 +829,20 @@
                     boolean prevHostConnected = mHostConnected;
                     UsbPort port = (UsbPort) args.arg1;
                     UsbPortStatus status = (UsbPortStatus) args.arg2;
-                    mHostConnected = status.getCurrentDataRole() == UsbPort.DATA_ROLE_HOST;
-                    mSourcePower = status.getCurrentPowerRole() == UsbPort.POWER_ROLE_SOURCE;
-                    mSinkPower = status.getCurrentPowerRole() == UsbPort.POWER_ROLE_SINK;
-                    mAudioAccessoryConnected =
-                            (status.getCurrentMode() == UsbPort.MODE_AUDIO_ACCESSORY);
-                    mAudioAccessorySupported = port.isModeSupported(UsbPort.MODE_AUDIO_ACCESSORY);
+                    mHostConnected = status.getCurrentDataRole() == DATA_ROLE_HOST;
+                    mSourcePower = status.getCurrentPowerRole() == POWER_ROLE_SOURCE;
+                    mSinkPower = status.getCurrentPowerRole() == POWER_ROLE_SINK;
+                    mAudioAccessoryConnected = (status.getCurrentMode() == MODE_AUDIO_ACCESSORY);
+                    mAudioAccessorySupported = port.isModeSupported(MODE_AUDIO_ACCESSORY);
                     // Ideally we want to see if PR_SWAP and DR_SWAP is supported.
                     // But, this should be suffice, since, all four combinations are only supported
                     // when PR_SWAP and DR_SWAP are supported.
                     mSupportsAllCombinations = status.isRoleCombinationSupported(
-                            UsbPort.POWER_ROLE_SOURCE, UsbPort.DATA_ROLE_HOST)
-                            && status.isRoleCombinationSupported(UsbPort.POWER_ROLE_SINK,
-                            UsbPort.DATA_ROLE_HOST)
-                            && status.isRoleCombinationSupported(UsbPort.POWER_ROLE_SOURCE,
-                            UsbPort.DATA_ROLE_DEVICE)
-                            && status.isRoleCombinationSupported(UsbPort.POWER_ROLE_SINK,
-                            UsbPort.DATA_ROLE_HOST);
+                            POWER_ROLE_SOURCE, DATA_ROLE_HOST)
+                            && status.isRoleCombinationSupported(POWER_ROLE_SINK, DATA_ROLE_HOST)
+                            && status.isRoleCombinationSupported(POWER_ROLE_SOURCE,
+                            DATA_ROLE_DEVICE)
+                            && status.isRoleCombinationSupported(POWER_ROLE_SINK, DATA_ROLE_HOST);
 
                     args.recycle();
                     updateUsbNotification(false);
diff --git a/services/usb/java/com/android/server/usb/UsbPortManager.java b/services/usb/java/com/android/server/usb/UsbPortManager.java
index 96618f5..6f210e3 100644
--- a/services/usb/java/com/android/server/usb/UsbPortManager.java
+++ b/services/usb/java/com/android/server/usb/UsbPortManager.java
@@ -16,12 +16,22 @@
 
 package com.android.server.usb;
 
+import static android.hardware.usb.UsbPortStatus.DATA_ROLE_DEVICE;
+import static android.hardware.usb.UsbPortStatus.DATA_ROLE_HOST;
+import static android.hardware.usb.UsbPortStatus.MODE_DFP;
+import static android.hardware.usb.UsbPortStatus.MODE_DUAL;
+import static android.hardware.usb.UsbPortStatus.MODE_UFP;
+import static android.hardware.usb.UsbPortStatus.POWER_ROLE_SINK;
+import static android.hardware.usb.UsbPortStatus.POWER_ROLE_SOURCE;
+
 import static com.android.internal.usb.DumpUtils.writePort;
 import static com.android.internal.usb.DumpUtils.writePortStatus;
 
+import android.Manifest;
 import android.annotation.NonNull;
 import android.content.Context;
 import android.content.Intent;
+import android.hardware.usb.ParcelableUsbPort;
 import android.hardware.usb.UsbManager;
 import android.hardware.usb.UsbPort;
 import android.hardware.usb.UsbPortStatus;
@@ -78,13 +88,13 @@
 
     // All non-trivial role combinations.
     private static final int COMBO_SOURCE_HOST =
-            UsbPort.combineRolesAsBit(UsbPort.POWER_ROLE_SOURCE, UsbPort.DATA_ROLE_HOST);
-    private static final int COMBO_SOURCE_DEVICE =
-            UsbPort.combineRolesAsBit(UsbPort.POWER_ROLE_SOURCE, UsbPort.DATA_ROLE_DEVICE);
+            UsbPort.combineRolesAsBit(POWER_ROLE_SOURCE, DATA_ROLE_HOST);
+    private static final int COMBO_SOURCE_DEVICE = UsbPort.combineRolesAsBit(
+            POWER_ROLE_SOURCE, DATA_ROLE_DEVICE);
     private static final int COMBO_SINK_HOST =
-            UsbPort.combineRolesAsBit(UsbPort.POWER_ROLE_SINK, UsbPort.DATA_ROLE_HOST);
-    private static final int COMBO_SINK_DEVICE =
-            UsbPort.combineRolesAsBit(UsbPort.POWER_ROLE_SINK, UsbPort.DATA_ROLE_DEVICE);
+            UsbPort.combineRolesAsBit(POWER_ROLE_SINK, DATA_ROLE_HOST);
+    private static final int COMBO_SINK_DEVICE = UsbPort.combineRolesAsBit(
+            POWER_ROLE_SINK, DATA_ROLE_DEVICE);
 
     // The system context.
     private final Context mContext;
@@ -217,12 +227,12 @@
             final int newMode;
             if ((!canChangePowerRole && currentPowerRole != newPowerRole)
                     || (!canChangeDataRole && currentDataRole != newDataRole)) {
-                if (canChangeMode && newPowerRole == UsbPort.POWER_ROLE_SOURCE
-                        && newDataRole == UsbPort.DATA_ROLE_HOST) {
-                    newMode = UsbPort.MODE_DFP;
-                } else if (canChangeMode && newPowerRole == UsbPort.POWER_ROLE_SINK
-                        && newDataRole == UsbPort.DATA_ROLE_DEVICE) {
-                    newMode = UsbPort.MODE_UFP;
+                if (canChangeMode && newPowerRole == POWER_ROLE_SOURCE
+                        && newDataRole == DATA_ROLE_HOST) {
+                    newMode = MODE_DFP;
+                } else if (canChangeMode && newPowerRole == POWER_ROLE_SINK
+                        && newDataRole == DATA_ROLE_DEVICE) {
+                    newMode = MODE_UFP;
                 } else {
                     logAndPrint(Log.ERROR, pw, "Found mismatch in supported USB role combinations "
                             + "while attempting to change role: " + portInfo
@@ -607,7 +617,7 @@
             IndentingPrintWriter pw) {
         // Only allow mode switch capability for dual role ports.
         // Validate that the current mode matches the supported modes we expect.
-        if ((supportedModes & UsbPort.MODE_DUAL) != UsbPort.MODE_DUAL) {
+        if ((supportedModes & MODE_DUAL) != MODE_DUAL) {
             canChangeMode = false;
             if (currentMode != 0 && currentMode != supportedModes) {
                 logAndPrint(Log.WARN, pw, "Ignoring inconsistent current mode from USB "
@@ -633,16 +643,16 @@
                 // Can only change power role.
                 // Assume data role must remain at its current value.
                 supportedRoleCombinations |= UsbPort.combineRolesAsBit(
-                        UsbPort.POWER_ROLE_SOURCE, currentDataRole);
+                        POWER_ROLE_SOURCE, currentDataRole);
                 supportedRoleCombinations |= UsbPort.combineRolesAsBit(
-                        UsbPort.POWER_ROLE_SINK, currentDataRole);
+                        POWER_ROLE_SINK, currentDataRole);
             } else if (canChangeDataRole) {
                 // Can only change data role.
                 // Assume power role must remain at its current value.
                 supportedRoleCombinations |= UsbPort.combineRolesAsBit(
-                        currentPowerRole, UsbPort.DATA_ROLE_HOST);
+                        currentPowerRole, DATA_ROLE_HOST);
                 supportedRoleCombinations |= UsbPort.combineRolesAsBit(
-                        currentPowerRole, UsbPort.DATA_ROLE_DEVICE);
+                        currentPowerRole, DATA_ROLE_DEVICE);
             } else if (canChangeMode) {
                 // Can only change the mode.
                 // Assume both standard UFP and DFP configurations will become available
@@ -654,7 +664,8 @@
         // Update the port data structures.
         PortInfo portInfo = mPorts.get(portId);
         if (portInfo == null) {
-            portInfo = new PortInfo(portId, supportedModes);
+            portInfo = new PortInfo(mContext.getSystemService(UsbManager.class), portId,
+                    supportedModes);
             portInfo.setStatus(currentMode, canChangeMode,
                     currentPowerRole, canChangePowerRole,
                     currentDataRole, canChangeDataRole,
@@ -701,12 +712,13 @@
         intent.addFlags(
                 Intent.FLAG_RECEIVER_FOREGROUND |
                         Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
-        intent.putExtra(UsbManager.EXTRA_PORT, portInfo.mUsbPort);
+        intent.putExtra(UsbManager.EXTRA_PORT, ParcelableUsbPort.of(portInfo.mUsbPort));
         intent.putExtra(UsbManager.EXTRA_PORT_STATUS, portInfo.mUsbPortStatus);
 
         // Guard against possible reentrance by posting the broadcast from the handler
         // instead of from within the critical section.
-        mHandler.post(() -> mContext.sendBroadcastAsUser(intent, UserHandle.ALL));
+        mHandler.post(() -> mContext.sendBroadcastAsUser(intent, UserHandle.ALL,
+                Manifest.permission.MANAGE_USB));
 
         // Log to statsd
         if (!mConnected.containsKey(portInfo.mUsbPort.getId())
@@ -772,8 +784,8 @@
         // 0 when port is connected. Else reports the last connected duration
         public long mLastConnectDurationMillis;
 
-        public PortInfo(String portId, int supportedModes) {
-            mUsbPort = new UsbPort(portId, supportedModes);
+        PortInfo(@NonNull UsbManager usbManager, @NonNull String portId, int supportedModes) {
+            mUsbPort = new UsbPort(usbManager, portId, supportedModes);
         }
 
         public boolean setStatus(int currentMode, boolean canChangeMode,
diff --git a/services/usb/java/com/android/server/usb/UsbService.java b/services/usb/java/com/android/server/usb/UsbService.java
index f9abedf..9115477 100644
--- a/services/usb/java/com/android/server/usb/UsbService.java
+++ b/services/usb/java/com/android/server/usb/UsbService.java
@@ -16,6 +16,14 @@
 
 package com.android.server.usb;
 
+import static android.hardware.usb.UsbPortStatus.DATA_ROLE_DEVICE;
+import static android.hardware.usb.UsbPortStatus.DATA_ROLE_HOST;
+import static android.hardware.usb.UsbPortStatus.MODE_DFP;
+import static android.hardware.usb.UsbPortStatus.MODE_DUAL;
+import static android.hardware.usb.UsbPortStatus.MODE_UFP;
+import static android.hardware.usb.UsbPortStatus.POWER_ROLE_SINK;
+import static android.hardware.usb.UsbPortStatus.POWER_ROLE_SOURCE;
+
 import android.annotation.NonNull;
 import android.annotation.UserIdInt;
 import android.app.PendingIntent;
@@ -27,6 +35,7 @@
 import android.content.IntentFilter;
 import android.content.pm.PackageManager;
 import android.hardware.usb.IUsbManager;
+import android.hardware.usb.ParcelableUsbPort;
 import android.hardware.usb.UsbAccessory;
 import android.hardware.usb.UsbDevice;
 import android.hardware.usb.UsbManager;
@@ -52,7 +61,9 @@
 import java.io.File;
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
+import java.util.ArrayList;
 import java.util.Collections;
+import java.util.List;
 
 /**
  * UsbService manages all USB related state, including both host and device support.
@@ -489,12 +500,25 @@
     }
 
     @Override
-    public UsbPort[] getPorts() {
+    public List<ParcelableUsbPort> getPorts() {
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);
 
         final long ident = Binder.clearCallingIdentity();
         try {
-            return mPortManager != null ? mPortManager.getPorts() : null;
+            if (mPortManager == null) {
+                return null;
+            } else {
+                final UsbPort[] ports = mPortManager.getPorts();
+
+                final int numPorts = ports.length;
+                ArrayList<ParcelableUsbPort> parcelablePorts = new ArrayList<>();
+                for (int i = 0; i < numPorts; i++) {
+                    parcelablePorts.add(ParcelableUsbPort.of(ports[i]));
+                }
+
+                return parcelablePorts;
+            }
+
         } finally {
             Binder.restoreCallingIdentity(ident);
         }
@@ -588,10 +612,10 @@
                 final int powerRole;
                 switch (args[2]) {
                     case "source":
-                        powerRole = UsbPort.POWER_ROLE_SOURCE;
+                        powerRole = POWER_ROLE_SOURCE;
                         break;
                     case "sink":
-                        powerRole = UsbPort.POWER_ROLE_SINK;
+                        powerRole = POWER_ROLE_SINK;
                         break;
                     case "no-power":
                         powerRole = 0;
@@ -603,10 +627,10 @@
                 final int dataRole;
                 switch (args[3]) {
                     case "host":
-                        dataRole = UsbPort.DATA_ROLE_HOST;
+                        dataRole = DATA_ROLE_HOST;
                         break;
                     case "device":
-                        dataRole = UsbPort.DATA_ROLE_DEVICE;
+                        dataRole = DATA_ROLE_DEVICE;
                         break;
                     case "no-data":
                         dataRole = 0;
@@ -631,13 +655,13 @@
                 final int supportedModes;
                 switch (args[2]) {
                     case "ufp":
-                        supportedModes = UsbPort.MODE_UFP;
+                        supportedModes = MODE_UFP;
                         break;
                     case "dfp":
-                        supportedModes = UsbPort.MODE_DFP;
+                        supportedModes = MODE_DFP;
                         break;
                     case "dual":
-                        supportedModes = UsbPort.MODE_DUAL;
+                        supportedModes = MODE_DUAL;
                         break;
                     case "none":
                         supportedModes = 0;
@@ -658,10 +682,10 @@
                 final boolean canChangeMode = args[2].endsWith("?");
                 switch (canChangeMode ? removeLastChar(args[2]) : args[2]) {
                     case "ufp":
-                        mode = UsbPort.MODE_UFP;
+                        mode = MODE_UFP;
                         break;
                     case "dfp":
-                        mode = UsbPort.MODE_DFP;
+                        mode = MODE_DFP;
                         break;
                     default:
                         pw.println("Invalid mode: " + args[2]);
@@ -671,10 +695,10 @@
                 final boolean canChangePowerRole = args[3].endsWith("?");
                 switch (canChangePowerRole ? removeLastChar(args[3]) : args[3]) {
                     case "source":
-                        powerRole = UsbPort.POWER_ROLE_SOURCE;
+                        powerRole = POWER_ROLE_SOURCE;
                         break;
                     case "sink":
-                        powerRole = UsbPort.POWER_ROLE_SINK;
+                        powerRole = POWER_ROLE_SINK;
                         break;
                     default:
                         pw.println("Invalid power role: " + args[3]);
@@ -684,10 +708,10 @@
                 final boolean canChangeDataRole = args[4].endsWith("?");
                 switch (canChangeDataRole ? removeLastChar(args[4]) : args[4]) {
                     case "host":
-                        dataRole = UsbPort.DATA_ROLE_HOST;
+                        dataRole = DATA_ROLE_HOST;
                         break;
                     case "device":
-                        dataRole = UsbPort.DATA_ROLE_DEVICE;
+                        dataRole = DATA_ROLE_DEVICE;
                         break;
                     default:
                         pw.println("Invalid data role: " + args[4]);
diff --git a/startop/view_compiler/dex_builder.cc b/startop/view_compiler/dex_builder.cc
index 94879d0..a78f7d5 100644
--- a/startop/view_compiler/dex_builder.cc
+++ b/startop/view_compiler/dex_builder.cc
@@ -79,6 +79,9 @@
     case Instruction::Op::kNew:
       out << "kNew";
       return out;
+    case Instruction::Op::kCheckCast:
+      out << "kCheckCast";
+      return out;
   }
 }
 
@@ -329,6 +332,8 @@
       return EncodeBranch(art::Instruction::IF_NEZ, instruction);
     case Instruction::Op::kNew:
       return EncodeNew(instruction);
+    case Instruction::Op::kCheckCast:
+      return EncodeCast(instruction);
   }
 }
 
@@ -422,6 +427,18 @@
   Encode21c(::art::Instruction::NEW_INSTANCE, RegisterValue(*instruction.dest()), type.value());
 }
 
+void MethodBuilder::EncodeCast(const Instruction& instruction) {
+  DCHECK_EQ(Instruction::Op::kCheckCast, instruction.opcode());
+  DCHECK(instruction.dest().has_value());
+  DCHECK(instruction.dest()->is_variable());
+  DCHECK_EQ(1, instruction.args().size());
+
+  const Value& type = instruction.args()[0];
+  DCHECK_LT(RegisterValue(*instruction.dest()), 256);
+  DCHECK(type.is_type());
+  Encode21c(::art::Instruction::CHECK_CAST, RegisterValue(*instruction.dest()), type.value());
+}
+
 size_t MethodBuilder::RegisterValue(const Value& value) const {
   if (value.is_register()) {
     return value.value();
diff --git a/startop/view_compiler/dex_builder.h b/startop/view_compiler/dex_builder.h
index 45596ac..06059c8 100644
--- a/startop/view_compiler/dex_builder.h
+++ b/startop/view_compiler/dex_builder.h
@@ -142,17 +142,18 @@
   // The operation performed by this instruction. These are virtual instructions that do not
   // correspond exactly to DEX instructions.
   enum class Op {
-    kReturn,
-    kReturnObject,
-    kMove,
-    kInvokeVirtual,
-    kInvokeDirect,
-    kInvokeStatic,
-    kInvokeInterface,
     kBindLabel,
     kBranchEqz,
     kBranchNEqz,
-    kNew
+    kCheckCast,
+    kInvokeDirect,
+    kInvokeInterface,
+    kInvokeStatic,
+    kInvokeVirtual,
+    kMove,
+    kNew,
+    kReturn,
+    kReturnObject,
   };
 
   ////////////////////////
@@ -168,6 +169,13 @@
   static inline Instruction OpWithArgs(Op opcode, std::optional<const Value> dest, T... args) {
     return Instruction{opcode, /*method_id=*/0, /*result_is_object=*/false, dest, args...};
   }
+
+  // A cast instruction. Basically, `(type)val`
+  static inline Instruction Cast(Value val, Value type) {
+    DCHECK(type.is_type());
+    return OpWithArgs(Op::kCheckCast, val, type);
+  }
+
   // For method calls.
   template <typename... T>
   static inline Instruction InvokeVirtual(size_t method_id, std::optional<const Value> dest,
@@ -302,6 +310,7 @@
   void EncodeInvoke(const Instruction& instruction, ::art::Instruction::Code opcode);
   void EncodeBranch(art::Instruction::Code op, const Instruction& instruction);
   void EncodeNew(const Instruction& instruction);
+  void EncodeCast(const Instruction& instruction);
 
   // Low-level instruction format encoding. See
   // https://source.android.com/devices/tech/dalvik/instruction-formats for documentation of
diff --git a/startop/view_compiler/dex_builder_test/src/android/startop/test/DexBuilderTest.java b/startop/view_compiler/dex_builder_test/src/android/startop/test/DexBuilderTest.java
index 1508ad9e..42d4161 100644
--- a/startop/view_compiler/dex_builder_test/src/android/startop/test/DexBuilderTest.java
+++ b/startop/view_compiler/dex_builder_test/src/android/startop/test/DexBuilderTest.java
@@ -20,6 +20,7 @@
 import dalvik.system.InMemoryDexClassLoader;
 import dalvik.system.PathClassLoader;
 import java.io.InputStream;
+import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.nio.ByteBuffer;
 import org.junit.Assert;
@@ -151,4 +152,23 @@
     Method method = clazz.getMethod("invokeVirtualReturnObject", String.class, int.class);
     Assert.assertEquals("bc", method.invoke(null, "abc", 1));
   }
+
+  @Test
+  public void castObjectToString() throws Exception {
+    ClassLoader loader = loadDexFile("simple.dex");
+    Class clazz = loader.loadClass("android.startop.test.testcases.SimpleTests");
+    Method method = clazz.getMethod("castObjectToString", Object.class);
+    Assert.assertEquals("abc", method.invoke(null, "abc"));
+    boolean castFailed = false;
+    try {
+      method.invoke(null, 5);
+    } catch (InvocationTargetException e) {
+      if (e.getCause() instanceof ClassCastException) {
+        castFailed = true;
+      } else {
+        throw e;
+      }
+    }
+    Assert.assertTrue(castFailed);
+  }
 }
diff --git a/startop/view_compiler/dex_testcase_generator.cc b/startop/view_compiler/dex_testcase_generator.cc
index 2781aa5..f62ec5dd 100644
--- a/startop/view_compiler/dex_testcase_generator.cc
+++ b/startop/view_compiler/dex_testcase_generator.cc
@@ -269,6 +269,19 @@
     method.Encode();
   }(invokeVirtualReturnObject);
 
+  // Make sure we can cast objects
+  // String castObjectToString(Object o) { return (String)o; }
+  MethodBuilder castObjectToString{cbuilder.CreateMethod(
+      "castObjectToString",
+      Prototype{string_type, TypeDescriptor::FromClassname("java.lang.Object")})};
+  [&](MethodBuilder& method) {
+    const ir::Type* type_def = dex_file.GetOrAddType(string_type.descriptor());
+    method.AddInstruction(
+        Instruction::Cast(Value::Parameter(0), Value::Type(type_def->orig_index)));
+    method.BuildReturn(Value::Parameter(0), /*is_object=*/true);
+    method.Encode();
+  }(castObjectToString);
+
   slicer::MemView image{dex_file.CreateImage()};
   std::ofstream out_file(outdir + "/simple.dex");
   out_file.write(image.ptr<const char>(), image.size());
diff --git a/telephony/java/android/telephony/CellIdentityGsm.java b/telephony/java/android/telephony/CellIdentityGsm.java
index 04c28e5..c8a899b 100644
--- a/telephony/java/android/telephony/CellIdentityGsm.java
+++ b/telephony/java/android/telephony/CellIdentityGsm.java
@@ -16,6 +16,7 @@
 
 package android.telephony;
 
+import android.annotation.Nullable;
 import android.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.telephony.gsm.GsmCellLocation;
@@ -169,6 +170,7 @@
     /**
      * @return a 5 or 6 character string (MCC+MNC), null if any field is unknown.
      */
+    @Nullable
     public String getMobileNetworkOperator() {
         return (mMccStr == null || mMncStr == null) ? null : mMccStr + mMncStr;
     }
diff --git a/telephony/java/android/telephony/CellIdentityLte.java b/telephony/java/android/telephony/CellIdentityLte.java
index 04b6a6c..8e1877d 100644
--- a/telephony/java/android/telephony/CellIdentityLte.java
+++ b/telephony/java/android/telephony/CellIdentityLte.java
@@ -16,6 +16,7 @@
 
 package android.telephony;
 
+import android.annotation.Nullable;
 import android.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.telephony.gsm.GsmCellLocation;
@@ -197,6 +198,7 @@
     /**
      * @return a 5 or 6 character string (MCC+MNC), null if any field is unknown.
      */
+    @Nullable
     public String getMobileNetworkOperator() {
         return (mMccStr == null || mMncStr == null) ? null : mMccStr + mMncStr;
     }
diff --git a/telephony/java/android/telephony/CellIdentityTdscdma.java b/telephony/java/android/telephony/CellIdentityTdscdma.java
index 8b1c1b9..f77c468 100644
--- a/telephony/java/android/telephony/CellIdentityTdscdma.java
+++ b/telephony/java/android/telephony/CellIdentityTdscdma.java
@@ -16,6 +16,7 @@
 
 package android.telephony;
 
+import android.annotation.Nullable;
 import android.os.Parcel;
 import android.telephony.gsm.GsmCellLocation;
 
@@ -116,6 +117,7 @@
     /**
      * @return a 5 or 6 character string (MCC+MNC), null if any field is unknown
      */
+    @Nullable
     public String getMobileNetworkOperator() {
         return (mMccStr == null || mMncStr == null) ? null : mMccStr + mMncStr;
     }
diff --git a/telephony/java/android/telephony/CellIdentityWcdma.java b/telephony/java/android/telephony/CellIdentityWcdma.java
index 3416ffe..31f9e6d 100644
--- a/telephony/java/android/telephony/CellIdentityWcdma.java
+++ b/telephony/java/android/telephony/CellIdentityWcdma.java
@@ -16,6 +16,7 @@
 
 package android.telephony;
 
+import android.annotation.Nullable;
 import android.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.telephony.gsm.GsmCellLocation;
@@ -173,6 +174,7 @@
     /**
      * @return a 5 or 6 character string (MCC+MNC), null if any field is unknown
      */
+    @Nullable
     public String getMobileNetworkOperator() {
         return (mMccStr == null || mMncStr == null) ? null : mMccStr + mMncStr;
     }
diff --git a/telephony/java/android/telephony/DisconnectCause.java b/telephony/java/android/telephony/DisconnectCause.java
index c6887ab..f53cb82 100644
--- a/telephony/java/android/telephony/DisconnectCause.java
+++ b/telephony/java/android/telephony/DisconnectCause.java
@@ -16,12 +16,16 @@
 
 package android.telephony;
 
+import android.annotation.SystemApi;
 import android.annotation.UnsupportedAppUsage;
 
 /**
- * Contains disconnect call causes generated by the framework and the RIL.
+ * Describes the cause of a disconnected call. Those disconnect causes can be converted into a more
+ * generic {@link android.telecom.DisconnectCause} object.
+ *
  * @hide
  */
+@SystemApi
 public class DisconnectCause {
 
     /** The disconnect cause is not valid (Not received a disconnect cause) */
@@ -101,8 +105,8 @@
     /** Unknown error or not specified */
     public static final int ERROR_UNSPECIFIED              = 36;
     /**
-     * Only emergency numbers are allowed, but we tried to dial
-     * a non-emergency number.
+     * Only emergency numbers are allowed, but we tried to dial a non-emergency number.
+     * @hide
      */
     // TODO: This should be the same as NOT_EMERGENCY
     public static final int EMERGENCY_ONLY                 = 37;
@@ -115,8 +119,7 @@
      */
     public static final int DIALED_MMI                     = 39;
     /**
-     * We tried to call a voicemail: URI but the device has no
-     * voicemail number configured.
+     * We tried to call a voicemail: URI but the device has no voicemail number configured.
      */
     public static final int VOICEMAIL_NUMBER_MISSING       = 40;
     /**
@@ -129,6 +132,8 @@
      * needs to be triggered by a *disconnect* event, rather than when
      * the InCallScreen first comes to the foreground.  For now we use
      * the needToShowCallLostDialog field for this (see below.)
+     *
+     * @hide
      */
     public static final int CDMA_CALL_LOST                 = 41;
     /**
@@ -169,62 +174,52 @@
 
     /**
      * Stk Call Control modified DIAL request to USSD request.
-     * {@hide}
      */
     public static final int DIAL_MODIFIED_TO_USSD          = 46;
     /**
      * Stk Call Control modified DIAL request to SS request.
-     * {@hide}
      */
     public static final int DIAL_MODIFIED_TO_SS            = 47;
     /**
      * Stk Call Control modified DIAL request to DIAL with modified data.
-     * {@hide}
      */
     public static final int DIAL_MODIFIED_TO_DIAL          = 48;
 
     /**
      * The call was terminated because CDMA phone service and roaming have already been activated.
-     * {@hide}
      */
     public static final int CDMA_ALREADY_ACTIVATED         = 49;
 
     /**
      * The call was terminated because it is not possible to place a video call while TTY is
      * enabled.
-     * {@hide}
      */
     public static final int VIDEO_CALL_NOT_ALLOWED_WHILE_TTY_ENABLED = 50;
 
     /**
      * The call was terminated because it was pulled to another device.
-     * {@hide}
      */
     public static final int CALL_PULLED = 51;
 
     /**
      * The call was terminated because it was answered on another device.
-     * {@hide}
      */
     public static final int ANSWERED_ELSEWHERE = 52;
 
     /**
      * The call was terminated because the maximum allowable number of calls has been reached.
-     * {@hide}
      */
     public static final int MAXIMUM_NUMBER_OF_CALLS_REACHED = 53;
 
     /**
      * The call was terminated because cellular data has been disabled.
      * Used when in a video call and the user disables cellular data via the settings.
-     * {@hide}
      */
     public static final int DATA_DISABLED = 54;
 
     /**
      * The call was terminated because the data policy has disabled cellular data.
      * Used when in a video call and the user has exceeded the device data limit.
-     * {@hide}
      */
     public static final int DATA_LIMIT_REACHED = 55;
 
@@ -237,7 +232,6 @@
     /**
      * The network does not accept the emergency call request because IMEI was used as
      * identification and this cability is not supported by the network.
-     * {@hide}
      */
     public static final int IMEI_NOT_ACCEPTED = 58;
 
@@ -249,7 +243,6 @@
 
     /**
      * The call has failed because of access class barring.
-     * {@hide}
      */
     public static final int IMS_ACCESS_BLOCKED = 60;
 
@@ -265,51 +258,43 @@
 
     /**
      * Emergency call failed with a temporary fail cause and can be redialed on this slot.
-     * {@hide}
      */
     public static final int EMERGENCY_TEMP_FAILURE = 63;
 
     /**
      * Emergency call failed with a permanent fail cause and should not be redialed on this
-     * slot. 
-     * {@hide}
+     * slot.
      */
     public static final int EMERGENCY_PERM_FAILURE = 64;
 
     /**
      * This cause is used to report a normal event only when no other cause in the normal class
      * applies.
-     * {@hide}
      */
     public static final int NORMAL_UNSPECIFIED = 65;
 
     /**
      * Stk Call Control modified DIAL request to video DIAL request.
-     * {@hide}
      */
     public static final int DIAL_MODIFIED_TO_DIAL_VIDEO = 66;
 
     /**
      * Stk Call Control modified Video DIAL request to SS request.
-     * {@hide}
      */
     public static final int DIAL_VIDEO_MODIFIED_TO_SS = 67;
 
     /**
      * Stk Call Control modified Video DIAL request to USSD request.
-     * {@hide}
      */
     public static final int DIAL_VIDEO_MODIFIED_TO_USSD = 68;
 
     /**
      * Stk Call Control modified Video DIAL request to DIAL request.
-     * {@hide}
      */
     public static final int DIAL_VIDEO_MODIFIED_TO_DIAL = 69;
 
     /**
      * Stk Call Control modified Video DIAL request to Video DIAL request.
-     * {@hide}
      */
     public static final int DIAL_VIDEO_MODIFIED_TO_DIAL_VIDEO = 70;
 
@@ -359,7 +344,10 @@
         // Do nothing.
     }
 
-    /** Returns descriptive string for the specified disconnect cause. */
+    /**
+     * Returns descriptive string for the specified disconnect cause.
+     * @hide
+     */
     @UnsupportedAppUsage
     public static String toString(int cause) {
         switch (cause) {
diff --git a/telephony/java/android/telephony/PhoneStateListener.java b/telephony/java/android/telephony/PhoneStateListener.java
index 0df0daf..9317aa7 100644
--- a/telephony/java/android/telephony/PhoneStateListener.java
+++ b/telephony/java/android/telephony/PhoneStateListener.java
@@ -173,14 +173,14 @@
     public static final int LISTEN_CELL_INFO = 0x00000400;
 
     /**
-     * Listen for precise changes and fails to the device calls (cellular).
+     * Listen for {@link PreciseCallState.State} of ringing, background and foreground calls.
      * {@more}
      * Requires Permission: {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE
      * READ_PRECISE_PHONE_STATE}
      *
      * @hide
      */
-    @UnsupportedAppUsage
+    @SystemApi
     public static final int LISTEN_PRECISE_CALL_STATE                       = 0x00000800;
 
     /**
@@ -320,6 +320,18 @@
      */
     public static final int LISTEN_EMERGENCY_NUMBER_LIST                   = 0x01000000;
 
+    /**
+     * Listen for call disconnect causes which contains {@link DisconnectCause} and
+     * {@link PreciseDisconnectCause}.
+     * {@more}
+     * Requires Permission: {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE
+     * READ_PRECISE_PHONE_STATE}
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int LISTEN_CALL_DISCONNECT_CAUSES                  = 0x02000000;
+
     /*
      * Subscription used to listen to the phone state changes
      * @hide
@@ -530,11 +542,23 @@
 
     /**
      * Callback invoked when precise device call state changes.
+     * @param callState {@link PreciseCallState}
+     * @hide
+     */
+    @SystemApi
+    public void onPreciseCallStateChanged(PreciseCallState callState) {
+        // default implementation empty
+    }
+
+    /**
+     * Callback invoked when call disconnect cause changes.
+     * @param disconnectCause {@link DisconnectCause}.
+     * @param preciseDisconnectCause {@link PreciseDisconnectCause}.
      *
      * @hide
      */
-    @UnsupportedAppUsage
-    public void onPreciseCallStateChanged(PreciseCallState callState) {
+    @SystemApi
+    public void onCallDisconnectCauseChanged(int disconnectCause, int preciseDisconnectCause) {
         // default implementation empty
     }
 
@@ -799,6 +823,15 @@
                     () -> mExecutor.execute(() -> psl.onPreciseCallStateChanged(callState)));
         }
 
+        public void onCallDisconnectCauseChanged(int disconnectCause, int preciseDisconnectCause) {
+            PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
+            if (psl == null) return;
+
+            Binder.withCleanCallingIdentity(
+                    () -> mExecutor.execute(() -> psl.onCallDisconnectCauseChanged(
+                            disconnectCause, preciseDisconnectCause)));
+        }
+
         public void onPreciseDataConnectionStateChanged(
                 PreciseDataConnectionState dataConnectionState) {
             PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
diff --git a/telephony/java/android/telephony/PreciseCallState.java b/telephony/java/android/telephony/PreciseCallState.java
index ed5c26a..59f3e1f 100644
--- a/telephony/java/android/telephony/PreciseCallState.java
+++ b/telephony/java/android/telephony/PreciseCallState.java
@@ -16,29 +16,51 @@
 
 package android.telephony;
 
+import android.annotation.IntDef;
+import android.annotation.SystemApi;
 import android.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.telephony.DisconnectCause;
 import android.telephony.PreciseDisconnectCause;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+
 /**
- * Contains precise call state and call fail causes generated by the
- * framework and the RIL.
+ * Contains precise call states.
  *
  * The following call information is included in returned PreciseCallState:
  *
  * <ul>
- *   <li>Ringing call state.
- *   <li>Foreground call state.
- *   <li>Background call state.
- *   <li>Disconnect cause; generated by the framework.
- *   <li>Precise disconnect cause; generated by the RIL.
+ *   <li>Precise ringing call state.
+ *   <li>Precise foreground call state.
+ *   <li>Precise background call state.
  * </ul>
  *
+ * @see android.telephony.TelephonyManager.CallState which contains generic call states.
+ *
  * @hide
  */
-public class PreciseCallState implements Parcelable {
+@SystemApi
+public final class PreciseCallState implements Parcelable {
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = {"PRECISE_CALL_STATE_"},
+            value = {
+                    PRECISE_CALL_STATE_NOT_VALID,
+                    PRECISE_CALL_STATE_IDLE,
+                    PRECISE_CALL_STATE_ACTIVE,
+                    PRECISE_CALL_STATE_HOLDING,
+                    PRECISE_CALL_STATE_DIALING,
+                    PRECISE_CALL_STATE_ALERTING,
+                    PRECISE_CALL_STATE_INCOMING,
+                    PRECISE_CALL_STATE_WAITING,
+                    PRECISE_CALL_STATE_DISCONNECTED,
+                    PRECISE_CALL_STATE_DISCONNECTING})
+    public @interface State {}
 
     /** Call state is not valid (Not received a call state). */
     public static final int PRECISE_CALL_STATE_NOT_VALID =      -1;
@@ -61,9 +83,9 @@
     /** Call state: Disconnecting. */
     public static final int PRECISE_CALL_STATE_DISCONNECTING =  8;
 
-    private int mRingingCallState = PRECISE_CALL_STATE_NOT_VALID;
-    private int mForegroundCallState = PRECISE_CALL_STATE_NOT_VALID;
-    private int mBackgroundCallState = PRECISE_CALL_STATE_NOT_VALID;
+    private @State int mRingingCallState = PRECISE_CALL_STATE_NOT_VALID;
+    private @State int mForegroundCallState = PRECISE_CALL_STATE_NOT_VALID;
+    private @State int mBackgroundCallState = PRECISE_CALL_STATE_NOT_VALID;
     private int mDisconnectCause = DisconnectCause.NOT_VALID;
     private int mPreciseDisconnectCause = PreciseDisconnectCause.NOT_VALID;
 
@@ -73,8 +95,9 @@
      * @hide
      */
     @UnsupportedAppUsage
-    public PreciseCallState(int ringingCall, int foregroundCall, int backgroundCall,
-            int disconnectCause, int preciseDisconnectCause) {
+    public PreciseCallState(@State int ringingCall, @State int foregroundCall,
+                            @State int backgroundCall, int disconnectCause,
+                            int preciseDisconnectCause) {
         mRingingCallState = ringingCall;
         mForegroundCallState = foregroundCall;
         mBackgroundCallState = backgroundCall;
@@ -92,6 +115,8 @@
 
     /**
      * Construct a PreciseCallState object from the given parcel.
+     *
+     * @hide
      */
     private PreciseCallState(Parcel in) {
         mRingingCallState = in.readInt();
@@ -102,59 +127,23 @@
     }
 
     /**
-     * Get precise ringing call state
-     *
-     * @see PreciseCallState#PRECISE_CALL_STATE_NOT_VALID
-     * @see PreciseCallState#PRECISE_CALL_STATE_IDLE
-     * @see PreciseCallState#PRECISE_CALL_STATE_ACTIVE
-     * @see PreciseCallState#PRECISE_CALL_STATE_HOLDING
-     * @see PreciseCallState#PRECISE_CALL_STATE_DIALING
-     * @see PreciseCallState#PRECISE_CALL_STATE_ALERTING
-     * @see PreciseCallState#PRECISE_CALL_STATE_INCOMING
-     * @see PreciseCallState#PRECISE_CALL_STATE_WAITING
-     * @see PreciseCallState#PRECISE_CALL_STATE_DISCONNECTED
-     * @see PreciseCallState#PRECISE_CALL_STATE_DISCONNECTING
+     * Returns the precise ringing call state.
      */
-    @UnsupportedAppUsage
-    public int getRingingCallState() {
+    public @State int getRingingCallState() {
         return mRingingCallState;
     }
 
     /**
-     * Get precise foreground call state
-     *
-     * @see PreciseCallState#PRECISE_CALL_STATE_NOT_VALID
-     * @see PreciseCallState#PRECISE_CALL_STATE_IDLE
-     * @see PreciseCallState#PRECISE_CALL_STATE_ACTIVE
-     * @see PreciseCallState#PRECISE_CALL_STATE_HOLDING
-     * @see PreciseCallState#PRECISE_CALL_STATE_DIALING
-     * @see PreciseCallState#PRECISE_CALL_STATE_ALERTING
-     * @see PreciseCallState#PRECISE_CALL_STATE_INCOMING
-     * @see PreciseCallState#PRECISE_CALL_STATE_WAITING
-     * @see PreciseCallState#PRECISE_CALL_STATE_DISCONNECTED
-     * @see PreciseCallState#PRECISE_CALL_STATE_DISCONNECTING
+     * Returns the precise foreground call state.
      */
-    @UnsupportedAppUsage
-    public int getForegroundCallState() {
+    public @State int getForegroundCallState() {
         return mForegroundCallState;
     }
 
     /**
-     * Get precise background call state
-     *
-     * @see PreciseCallState#PRECISE_CALL_STATE_NOT_VALID
-     * @see PreciseCallState#PRECISE_CALL_STATE_IDLE
-     * @see PreciseCallState#PRECISE_CALL_STATE_ACTIVE
-     * @see PreciseCallState#PRECISE_CALL_STATE_HOLDING
-     * @see PreciseCallState#PRECISE_CALL_STATE_DIALING
-     * @see PreciseCallState#PRECISE_CALL_STATE_ALERTING
-     * @see PreciseCallState#PRECISE_CALL_STATE_INCOMING
-     * @see PreciseCallState#PRECISE_CALL_STATE_WAITING
-     * @see PreciseCallState#PRECISE_CALL_STATE_DISCONNECTED
-     * @see PreciseCallState#PRECISE_CALL_STATE_DISCONNECTING
+     * Returns the precise background call state.
      */
-    @UnsupportedAppUsage
-    public int getBackgroundCallState() {
+    public @State int getBackgroundCallState() {
         return mBackgroundCallState;
     }
 
@@ -199,6 +188,11 @@
      * @see DisconnectCause#CDMA_NOT_EMERGENCY
      * @see DisconnectCause#CDMA_ACCESS_BLOCKED
      * @see DisconnectCause#ERROR_UNSPECIFIED
+     *
+     * TODO: remove disconnect cause from preciseCallState as there is no link between random
+     * connection disconnect cause with foreground, background or ringing call.
+     *
+     * @hide
      */
     @UnsupportedAppUsage
     public int getDisconnectCause() {
@@ -238,6 +232,11 @@
      * @see PreciseDisconnectCause#CDMA_NOT_EMERGENCY
      * @see PreciseDisconnectCause#CDMA_ACCESS_BLOCKED
      * @see PreciseDisconnectCause#ERROR_UNSPECIFIED
+     *
+     * TODO: remove precise disconnect cause from preciseCallState as there is no link between
+     * random connection disconnect cause with foreground, background or ringing call.
+     *
+     * @hide
      */
     @UnsupportedAppUsage
     public int getPreciseDisconnectCause() {
@@ -272,14 +271,8 @@
 
     @Override
     public int hashCode() {
-        final int prime = 31;
-        int result = 1;
-        result = prime * result + mRingingCallState;
-        result = prime * result + mForegroundCallState;
-        result = prime * result + mBackgroundCallState;
-        result = prime * result + mDisconnectCause;
-        result = prime * result + mPreciseDisconnectCause;
-        return result;
+        return Objects.hash(mRingingCallState, mForegroundCallState, mForegroundCallState,
+                mDisconnectCause, mPreciseDisconnectCause);
     }
 
     @Override
diff --git a/telephony/java/android/telephony/PreciseDisconnectCause.java b/telephony/java/android/telephony/PreciseDisconnectCause.java
index 2acaf34..af88748 100644
--- a/telephony/java/android/telephony/PreciseDisconnectCause.java
+++ b/telephony/java/android/telephony/PreciseDisconnectCause.java
@@ -16,279 +16,329 @@
 
 package android.telephony;
 
+import android.annotation.SystemApi;
+
 /**
- * Contains precise disconnect call causes generated by the
- * framework and the RIL.
- *
+ * Contains precise disconnect call causes generated by the framework and the RIL.
  * @hide
  */
+@SystemApi
 public class PreciseDisconnectCause {
 
-    /** The disconnect cause is not valid (Not received a disconnect cause)*/
+    /** The disconnect cause is not valid (Not received a disconnect cause).*/
     public static final int NOT_VALID                                        = -1;
-    /** No disconnect cause provided. Generally a local disconnect or an incoming missed call */
+    /** No disconnect cause provided. Generally a local disconnect or an incoming missed call. */
     public static final int NO_DISCONNECT_CAUSE_AVAILABLE                    = 0;
     /**
      * The destination cannot be reached because the number, although valid,
-     * is not currently assigned
+     * is not currently assigned.
      */
     public static final int UNOBTAINABLE_NUMBER                              = 1;
-    /** The user cannot be reached because the network through which the call has been
-     *  routed does not serve the destination desired
+    /**
+     * The user cannot be reached because the network through which the call has been routed does
+     * not serve the destination desired.
      */
     public static final int NO_ROUTE_TO_DESTINATION                          = 3;
-    /** The channel most recently identified is not acceptable to the sending entity for
-     *  use in this call
+    /**
+     * The channel most recently identified is not acceptable to the sending entity for use in this
+     * call.
      */
     public static final int CHANNEL_UNACCEPTABLE                             = 6;
-    /** The MS has tried to access a service that the MS's network operator or service
-     *  provider is not prepared to allow
+    /**
+     * The mobile station (MS) has tried to access a service that the MS's network operator or
+     * service provider is not prepared to allow.
      */
     public static final int OPERATOR_DETERMINED_BARRING                      = 8;
-    /** One of the users involved in the call has requested that the call is cleared */
+    /** One of the users involved in the call has requested that the call is cleared. */
     public static final int NORMAL                                           = 16;
-    /** The called user is unable to accept another call */
+    /** The called user is unable to accept another call. */
     public static final int BUSY                                             = 17;
-    /** The user does not respond to a call establishment message with either an alerting
-     *  or connect indication within the prescribed period of time allocated
+    /**
+     * The user does not respond to a call establishment message with either an alerting or connect
+     * indication within the prescribed period of time allocated.
      */
     public static final int NO_USER_RESPONDING                               = 18;
-    /** The user has provided an alerting indication but has not provided a connect
-     *  indication within a prescribed period of time
+    /**
+     * The user has provided an alerting indication but has not provided a connect indication
+     * within a prescribed period of time.
      */
     public static final int NO_ANSWER_FROM_USER                              = 19;
-    /** The equipment sending this cause does not wish to accept this call */
+    /** The equipment sending this cause does not wish to accept this call. */
     public static final int CALL_REJECTED                                    = 21;
-    /** The called number is no longer assigned */
+    /** The called number is no longer assigned. */
     public static final int NUMBER_CHANGED                                   = 22;
-    /** This cause is returned to the network when a mobile station clears an active
-     *  call which is being pre-empted by another call with higher precedence
+    /**
+     * This cause is returned to the network when a mobile station clears an active call which is
+     * being pre-empted by another call with higher precedence.
      */
     public static final int PREEMPTION                                       = 25;
-    /** The destination indicated by the mobile station cannot be reached because
-     *  the interface to the destination is not functioning correctly
+    /**
+     * The destination indicated by the mobile station cannot be reached because the interface to
+     * the destination is not functioning correctly.
      */
     public static final int DESTINATION_OUT_OF_ORDER                         = 27;
-    /** The called party number is not a valid format or is not complete */
+    /** The called party number is not a valid format or is not complete. */
     public static final int INVALID_NUMBER_FORMAT                            = 28;
-    /** The facility requested by user can not be provided by the network */
+    /** The facility requested by user can not be provided by the network. */
     public static final int FACILITY_REJECTED                                = 29;
-    /** Provided in response to a STATUS ENQUIRY message */
+    /** Provided in response to a STATUS ENQUIRY message. */
     public static final int STATUS_ENQUIRY                                   = 30;
-    /** Reports a normal disconnect only when no other normal cause applies */
+    /** Reports a normal disconnect only when no other normal cause applies. */
     public static final int NORMAL_UNSPECIFIED                               = 31;
-    /** There is no channel presently available to handle the call */
+    /** There is no channel presently available to handle the call. */
     public static final int NO_CIRCUIT_AVAIL                                 = 34;
-    /** The network is not functioning correctly and that the condition is likely
-     *  to last a relatively long period of time
+    /**
+     * The network is not functioning correctly and that the condition is likely to last a
+     * relatively long period of time.
      */
     public static final int NETWORK_OUT_OF_ORDER                             = 38;
     /**
-     * The network is not functioning correctly and the condition is not likely to last
-     * a long period of time
+     * The network is not functioning correctly and the condition is not likely to last a long
+     * period of time.
      */
     public static final int TEMPORARY_FAILURE                                = 41;
-    /** The switching equipment is experiencing a period of high traffic */
+    /** The switching equipment is experiencing a period of high traffic. */
     public static final int SWITCHING_CONGESTION                             = 42;
-    /** The network could not deliver access information to the remote user as requested */
+    /** The network could not deliver access information to the remote user as requested. */
     public static final int ACCESS_INFORMATION_DISCARDED                     = 43;
-    /** The channel cannot be provided */
+    /** The channel cannot be provided. */
     public static final int CHANNEL_NOT_AVAIL                                = 44;
-    /** This cause is used to report a resource unavailable event only when no other
-     *  cause in the resource unavailable class applies
+    /**
+     * This cause is used to report a resource unavailable event only when no other cause in the
+     * resource unavailable class applies.
      */
     public static final int RESOURCES_UNAVAILABLE_OR_UNSPECIFIED             = 47;
-    /** The requested quality of service (ITU-T X.213) cannot be provided */
+    /** The requested quality of service (ITU-T X.213) cannot be provided. */
     public static final int QOS_NOT_AVAIL                                    = 49;
-    /** The facility could not be provided by the network because the user has no
-     *  complete subscription
+    /**
+     * The facility could not be provided by the network because the user has no complete
+     * subscription.
      */
     public static final int REQUESTED_FACILITY_NOT_SUBSCRIBED                = 50;
-    /** Incoming calls are not allowed within this CUG */
+    /** Incoming calls are not allowed within this calling user group (CUG). */
     public static final int INCOMING_CALLS_BARRED_WITHIN_CUG                 = 55;
-    /** The mobile station is not authorized to use bearer capability requested */
+    /** The mobile station is not authorized to use bearer capability requested. */
     public static final int BEARER_CAPABILITY_NOT_AUTHORIZED                 = 57;
-    /** The requested bearer capability is not available at this time */
+    /** The requested bearer capability is not available at this time. */
     public static final int BEARER_NOT_AVAIL                                 = 58;
-    /** The service option is not availble at this time */
+    /** The service option is not availble at this time. */
     public static final int SERVICE_OPTION_NOT_AVAILABLE                     = 63;
-    /** The equipment sending this cause does not support the bearer capability requested */
+    /** The equipment sending this cause does not support the bearer capability requested. */
     public static final int BEARER_SERVICE_NOT_IMPLEMENTED                   = 65;
-    /** The call clearing is due to ACM being greater than or equal to ACMmax */
+    /** The call clearing is due to ACM being greater than or equal to ACMmax. */
     public static final int ACM_LIMIT_EXCEEDED                               = 68;
-    /** The equipment sending this cause does not support the requested facility */
+    /** The equipment sending this cause does not support the requested facility. */
     public static final int REQUESTED_FACILITY_NOT_IMPLEMENTED               = 69;
-    /** The equipment sending this cause only supports the restricted version of
-     *  the requested bearer capability
+    /**
+     * The equipment sending this cause only supports the restricted version of the requested bearer
+     * capability.
      */
     public static final int ONLY_DIGITAL_INFORMATION_BEARER_AVAILABLE        = 70;
-    /** The service requested is not implemented at network */
+    /** The service requested is not implemented at network. */
     public static final int SERVICE_OR_OPTION_NOT_IMPLEMENTED                = 79;
-    /** The equipment sending this cause has received a message with a transaction identifier
-     *  which is not currently in use on the MS-network interface
+    /**
+     * The equipment sending this cause has received a message with a transaction identifier
+     * which is not currently in use on the mobile station network interface.
      */
     public static final int INVALID_TRANSACTION_IDENTIFIER                   = 81;
-    /** The called user for the incoming CUG call is not a member of the specified CUG */
+    /**
+     * The called user for the incoming CUG call is not a member of the specified calling user
+     * group (CUG).
+     */
     public static final int USER_NOT_MEMBER_OF_CUG                           = 87;
-    /** The equipment sending this cause has received a request which can't be accomodated */
+    /** The equipment sending this cause has received a request which can't be accomodated. */
     public static final int INCOMPATIBLE_DESTINATION                         = 88;
-    /** This cause is used to report receipt of a message with semantically incorrect contents */
+    /** This cause is used to report receipt of a message with semantically incorrect contents. */
     public static final int SEMANTICALLY_INCORRECT_MESSAGE                   = 95;
-    /** The equipment sending this cause has received a message with a non-semantical
-     *  mandatory IE error
+    /**
+     * The equipment sending this cause has received a message with a non-semantical mandatory
+     * information element (IE) error.
      */
     public static final int INVALID_MANDATORY_INFORMATION                    = 96;
-    /** This is sent in response to a message which is not defined, or defined but not
-     *  implemented by the equipment sending this cause
+    /**
+     * This is sent in response to a message which is not defined, or defined but not implemented
+     * by the equipment sending this cause.
      */
     public static final int MESSAGE_TYPE_NON_IMPLEMENTED                     = 97;
-    /** The equipment sending this cause has received a message not compatible with the
-     *  protocol state
+    /**
+     * The equipment sending this cause has received a message not compatible with the protocol
+     * state.
      */
     public static final int MESSAGE_TYPE_NOT_COMPATIBLE_WITH_PROTOCOL_STATE  = 98;
-    /** The equipment sending this cause has received a message which includes information
-     *  elements not recognized because its identifier is not defined or it is defined but not
-     *  implemented by the equipment sending the cause
+    /**
+     * The equipment sending this cause has received a message which includes information
+     * elements not recognized because its identifier is not defined or it is defined but not
+     * implemented by the equipment sending the cause.
      */
     public static final int INFORMATION_ELEMENT_NON_EXISTENT                 = 99;
-    /** The equipment sending this cause has received a message with conditional IE errors */
+    /** The equipment sending this cause has received a message with conditional IE errors. */
     public static final int CONDITIONAL_IE_ERROR                             = 100;
-    /** The message has been received which is incompatible with the protocol state */
+    /** The message has been received which is incompatible with the protocol state. */
     public static final int MESSAGE_NOT_COMPATIBLE_WITH_PROTOCOL_STATE       = 101;
-    /** The  procedure has been initiated by the expiry of a timer in association with
-     *  3GPP TS 24.008 error handling procedures
+    /**
+     * The procedure has been initiated by the expiry of a timer in association with
+     * 3GPP TS 24.008 error handling procedures.
      */
     public static final int RECOVERY_ON_TIMER_EXPIRED                        = 102;
-    /** This protocol error event is reported only when no other cause in the protocol
-     *  error class applies
+    /**
+     * This protocol error event is reported only when no other cause in the protocol error class
+     * applies.
      */
     public static final int PROTOCOL_ERROR_UNSPECIFIED                       = 111;
-    /** interworking with a network which does not provide causes for actions it takes
-     *  thus, the precise cause for a message which is being sent cannot be ascertained
+    /**
+     * Interworking with a network which does not provide causes for actions it takes thus, the
+     * precise cause for a message which is being sent cannot be ascertained.
      */
     public static final int INTERWORKING_UNSPECIFIED                         = 127;
-    /** The call is restricted */
+    /** The call is restricted. */
     public static final int CALL_BARRED                                      = 240;
-    /** The call is blocked by the Fixed Dialing Number list */
+    /** The call is blocked by the Fixed Dialing Number list. */
     public static final int FDN_BLOCKED                                      = 241;
-    /** The given IMSI is not known at the VLR */
-    /** TS 24.008 cause 4 */
+    /** The given IMSI is not known at the Visitor Location Register (VLR) TS 24.008 cause . */
     public static final int IMSI_UNKNOWN_IN_VLR                              = 242;
     /**
      * The network does not accept emergency call establishment using an IMEI or not accept attach
-     * procedure for emergency services using an IMEI
+     * procedure for emergency services using an IMEI.
      */
     public static final int IMEI_NOT_ACCEPTED                                = 243;
-    /** The call cannot be established because RADIO is OFF */
+    /** The call cannot be established because RADIO is OFF. */
     public static final int RADIO_OFF                                        = 247;
-    /** The call cannot be established because of no cell coverage */
+    /** The call cannot be established because of no cell coverage. */
     public static final int OUT_OF_SRV                                       = 248;
-    /** The call cannot be established because of no valid SIM */
+    /** The call cannot be established because of no valid SIM. */
     public static final int NO_VALID_SIM                                     = 249;
-    /** The call is dropped or failed internally by modem */
+    /** The call is dropped or failed internally by modem. */
     public static final int RADIO_INTERNAL_ERROR                             = 250;
-    /** Call failed because of UE timer expired while waiting for a response from network */
+    /** Call failed because of UE timer expired while waiting for a response from network. */
     public static final int NETWORK_RESP_TIMEOUT                             = 251;
-    /** Call failed because of a network reject */
+    /** Call failed because of a network reject. */
     public static final int NETWORK_REJECT                                   = 252;
-    /** Call failed because of radio access failure. ex. RACH failure */
+    /** Call failed because of radio access failure. ex. RACH failure. */
     public static final int RADIO_ACCESS_FAILURE                             = 253;
-    /** Call failed/dropped because of a RLF */
+    /** Call failed/dropped because of a Radio Link Failure (RLF). */
     public static final int RADIO_LINK_FAILURE                               = 254;
-    /** Call failed/dropped because of radio link lost */
+    /** Call failed/dropped because of radio link lost. */
     public static final int RADIO_LINK_LOST                                  = 255;
-    /** Call failed because of a radio uplink issue */
+    /** Call failed because of a radio uplink issue. */
     public static final int RADIO_UPLINK_FAILURE                             = 256;
-    /** Call failed because of a RRC connection setup failure */
+    /** Call failed because of a RRC (Radio Resource Control) connection setup failure. */
     public static final int RADIO_SETUP_FAILURE                              = 257;
-    /** Call failed/dropped because of RRC connection release from NW */
+    /** Call failed/dropped because of RRC (Radio Resource Control) connection release from NW. */
     public static final int RADIO_RELEASE_NORMAL                             = 258;
-    /** Call failed/dropped because of RRC abnormally released by modem/network */
+    /**
+     * Call failed/dropped because of RRC (Radio Resource Control) abnormally released by
+     * modem/network.
+     */
     public static final int RADIO_RELEASE_ABNORMAL                           = 259;
-    /** Call setup failed because of access class barring */
+    /** Call setup failed because of access class barring. */
     public static final int ACCESS_CLASS_BLOCKED                             = 260;
-    /** Call failed/dropped because of a network detach */
+    /** Call failed/dropped because of a network detach. */
     public static final int NETWORK_DETACH                                   = 261;
 
-    /** MS is locked until next power cycle */
+    /** Mobile station (MS) is locked until next power cycle. */
     public static final int CDMA_LOCKED_UNTIL_POWER_CYCLE                    = 1000;
-    /** Drop call*/
+    /** Drop call. */
     public static final int CDMA_DROP                                        = 1001;
-    /** INTERCEPT order received, MS state idle entered */
+    /** INTERCEPT order received, Mobile station (MS) state idle entered. */
     public static final int CDMA_INTERCEPT                                   = 1002;
-    /** MS has been redirected, call is cancelled */
+    /** Mobile station (MS) has been redirected, call is cancelled. */
     public static final int CDMA_REORDER                                     = 1003;
-    /** Service option rejection */
+    /** Service option rejection. */
     public static final int CDMA_SO_REJECT                                   = 1004;
-    /** Requested service is rejected, retry delay is set */
+    /** Requested service is rejected, retry delay is set. */
     public static final int CDMA_RETRY_ORDER                                 = 1005;
-    /** Unable to obtain access to the CDMA system */
+    /** Unable to obtain access to the CDMA system. */
     public static final int CDMA_ACCESS_FAILURE                              = 1006;
-    /** Not a preempted call */
+    /** Not a preempted call. */
     public static final int CDMA_PREEMPTED                                   = 1007;
-    /** Not an emergency call */
+    /** Not an emergency call. */
     public static final int CDMA_NOT_EMERGENCY                               = 1008;
-    /** Access Blocked by CDMA network */
+    /** Access Blocked by CDMA network. */
     public static final int CDMA_ACCESS_BLOCKED                              = 1009;
 
     /** Mapped from ImsReasonInfo */
+    // TODO: remove ImsReasonInfo from preciseDisconnectCause
     /* The passed argument is an invalid */
+    /** @hide */
     public static final int LOCAL_ILLEGAL_ARGUMENT                           = 1200;
     // The operation is invoked in invalid call state
+    /** @hide */
     public static final int LOCAL_ILLEGAL_STATE                              = 1201;
     // IMS service internal error
+    /** @hide */
     public static final int LOCAL_INTERNAL_ERROR                             = 1202;
     // IMS service goes down (service connection is lost)
+    /** @hide */
     public static final int LOCAL_IMS_SERVICE_DOWN                           = 1203;
     // No pending incoming call exists
+    /** @hide */
     public static final int LOCAL_NO_PENDING_CALL                            = 1204;
     // Service unavailable; by power off
+    /** @hide */
     public static final int LOCAL_POWER_OFF                                  = 1205;
     // Service unavailable; by low battery
+    /** @hide */
     public static final int LOCAL_LOW_BATTERY                                = 1206;
     // Service unavailable; by out of service (data service state)
+    /** @hide */
     public static final int LOCAL_NETWORK_NO_SERVICE                         = 1207;
     /* Service unavailable; by no LTE coverage
      * (VoLTE is not supported even though IMS is registered)
      */
+    /** @hide */
     public static final int LOCAL_NETWORK_NO_LTE_COVERAGE                    = 1208;
     /** Service unavailable; by located in roaming area */
+    /** @hide */
     public static final int LOCAL_NETWORK_ROAMING                            = 1209;
     /** Service unavailable; by IP changed */
+    /** @hide */
     public static final int LOCAL_NETWORK_IP_CHANGED                         = 1210;
     /** Service unavailable; other */
+    /** @hide */
     public static final int LOCAL_SERVICE_UNAVAILABLE                        = 1211;
     /* Service unavailable; IMS connection is lost (IMS is not registered) */
+    /** @hide */
     public static final int LOCAL_NOT_REGISTERED                             = 1212;
     /** Max call exceeded */
+    /** @hide */
     public static final int LOCAL_MAX_CALL_EXCEEDED                          = 1213;
     /** Call decline */
+    /** @hide */
     public static final int LOCAL_CALL_DECLINE                               = 1214;
     /** SRVCC is in progress */
+    /** @hide */
     public static final int LOCAL_CALL_VCC_ON_PROGRESSING                    = 1215;
     /** Resource reservation is failed (QoS precondition) */
+    /** @hide */
     public static final int LOCAL_CALL_RESOURCE_RESERVATION_FAILED           = 1216;
     /** Retry CS call; VoLTE service can't be provided by the network or remote end
      *  Resolve the extra code(EXTRA_CODE_CALL_RETRY_*) if the below code is set
+     *  @hide
      */
     public static final int LOCAL_CALL_CS_RETRY_REQUIRED                     = 1217;
     /** Retry VoLTE call; VoLTE service can't be provided by the network temporarily */
+    /** @hide */
     public static final int LOCAL_CALL_VOLTE_RETRY_REQUIRED                  = 1218;
     /** IMS call is already terminated (in TERMINATED state) */
+    /** @hide */
     public static final int LOCAL_CALL_TERMINATED                            = 1219;
     /** Handover not feasible */
+    /** @hide */
     public static final int LOCAL_HO_NOT_FEASIBLE                            = 1220;
 
     /** 1xx waiting timer is expired after sending INVITE request (MO only) */
+    /** @hide */
     public static final int TIMEOUT_1XX_WAITING                              = 1221;
     /** User no answer during call setup operation (MO/MT)
      *  MO : 200 OK to INVITE request is not received,
      *  MT : No action from user after alerting the call
+     *  @hide
      */
     public static final int TIMEOUT_NO_ANSWER                                = 1222;
     /** User no answer during call update operation (MO/MT)
      *  MO : 200 OK to re-INVITE request is not received,
      *  MT : No action from user after alerting the call
+     *  @hide
      */
     public static final int TIMEOUT_NO_ANSWER_CALL_UPDATE                    = 1223;
 
@@ -296,102 +346,142 @@
      * STATUSCODE (SIP response code) (IMS -> Telephony)
      */
     /** SIP request is redirected */
+    /** @hide */
     public static final int SIP_REDIRECTED                                   = 1300;
     /** 4xx responses */
     /** 400 : Bad Request */
+    /** @hide */
     public static final int SIP_BAD_REQUEST                                  = 1310;
     /** 403 : Forbidden */
+    /** @hide */
     public static final int SIP_FORBIDDEN                                    = 1311;
     /** 404 : Not Found */
+    /** @hide */
     public static final int SIP_NOT_FOUND                                    = 1312;
     /** 415 : Unsupported Media Type
      *  416 : Unsupported URI Scheme
      *  420 : Bad Extension
      */
+    /** @hide */
     public static final int SIP_NOT_SUPPORTED                                = 1313;
     /** 408 : Request Timeout */
+    /** @hide */
     public static final int SIP_REQUEST_TIMEOUT                              = 1314;
     /** 480 : Temporarily Unavailable */
+    /** @hide */
     public static final int SIP_TEMPRARILY_UNAVAILABLE                       = 1315;
     /** 484 : Address Incomplete */
+    /** @hide */
     public static final int SIP_BAD_ADDRESS                                  = 1316;
     /** 486 : Busy Here
      *  600 : Busy Everywhere
      */
+    /** @hide */
     public static final int SIP_BUSY                                         = 1317;
     /** 487 : Request Terminated */
+    /** @hide */
     public static final int SIP_REQUEST_CANCELLED                            = 1318;
     /** 406 : Not Acceptable
      *  488 : Not Acceptable Here
      *  606 : Not Acceptable
      */
+    /** @hide */
     public static final int SIP_NOT_ACCEPTABLE                               = 1319;
     /** 410 : Gone
      *  604 : Does Not Exist Anywhere
      */
+    /** @hide */
     public static final int SIP_NOT_REACHABLE                                = 1320;
     /** Others */
+    /** @hide */
     public static final int SIP_CLIENT_ERROR                                 = 1321;
     /** 481 : Transaction Does Not Exist */
+    /** @hide */
     public static final int SIP_TRANSACTION_DOES_NOT_EXIST                   = 1322;
     /** 5xx responses
      *  501 : Server Internal Error
      */
+    /** @hide */
     public static final int SIP_SERVER_INTERNAL_ERROR                        = 1330;
     /** 503 : Service Unavailable */
+    /** @hide */
     public static final int SIP_SERVICE_UNAVAILABLE                          = 1331;
     /** 504 : Server Time-out */
+    /** @hide */
     public static final int SIP_SERVER_TIMEOUT                               = 1332;
     /** Others */
+    /** @hide */
     public static final int SIP_SERVER_ERROR                                 = 1333;
     /** 6xx responses
      *  603 : Decline
      */
+    /** @hide */
     public static final int SIP_USER_REJECTED                                = 1340;
     /** Others */
+    /** @hide */
     public static final int SIP_GLOBAL_ERROR                                 = 1341;
     /** Emergency failure */
+    /** @hide */
     public static final int EMERGENCY_TEMP_FAILURE                           = 1342;
+    /** @hide */
     public static final int EMERGENCY_PERM_FAILURE                           = 1343;
     /** Media resource initialization failed */
+    /** @hide */
     public static final int MEDIA_INIT_FAILED                                = 1400;
     /** RTP timeout (no audio / video traffic in the session) */
+    /** @hide */
     public static final int MEDIA_NO_DATA                                    = 1401;
     /** Media is not supported; so dropped the call */
+    /** @hide */
     public static final int MEDIA_NOT_ACCEPTABLE                             = 1402;
     /** Unknown media related errors */
+    /** @hide */
     public static final int MEDIA_UNSPECIFIED                                = 1403;
     /** User triggers the call end */
+    /** @hide */
     public static final int USER_TERMINATED                                  = 1500;
     /** No action while an incoming call is ringing */
+    /** @hide */
     public static final int USER_NOANSWER                                    = 1501;
     /** User ignores an incoming call */
+    /** @hide */
     public static final int USER_IGNORE                                      = 1502;
     /** User declines an incoming call */
+    /** @hide */
     public static final int USER_DECLINE                                     = 1503;
     /** Device declines/ends a call due to low battery */
+    /** @hide */
     public static final int LOW_BATTERY                                      = 1504;
     /** Device declines call due to blacklisted call ID */
+    /** @hide */
     public static final int BLACKLISTED_CALL_ID                              = 1505;
     /** The call is terminated by the network or remote user */
+    /** @hide */
     public static final int USER_TERMINATED_BY_REMOTE                        = 1510;
 
     /**
      * UT
      */
+    /** @hide */
     public static final int UT_NOT_SUPPORTED                                 = 1800;
+    /** @hide */
     public static final int UT_SERVICE_UNAVAILABLE                           = 1801;
+    /** @hide */
     public static final int UT_OPERATION_NOT_ALLOWED                         = 1802;
+    /** @hide */
     public static final int UT_NETWORK_ERROR                                 = 1803;
+    /** @hide */
     public static final int UT_CB_PASSWORD_MISMATCH                          = 1804;
 
     /**
      * ECBM
+     * @hide
      */
     public static final int ECBM_NOT_SUPPORTED                               = 1900;
 
     /**
      * Fail code used to indicate that Multi-endpoint is not supported by the Ims framework.
+     * @hide
      */
     public static final int MULTIENDPOINT_NOT_SUPPORTED                      = 1901;
 
@@ -405,56 +495,68 @@
      * active wifi call and at the edge of coverage and there is no qualified LTE network available
      * to handover the call to. We get a handover NOT_TRIGERRED message from the modem. This error
      * code is received as part of the handover message.
+     * @hide
      */
     public static final int CALL_DROP_IWLAN_TO_LTE_UNAVAILABLE               = 2000;
 
     /**
      * MT call has ended due to a release from the network
      * because the call was answered elsewhere
+     * @hide
      */
     public static final int ANSWERED_ELSEWHERE                               = 2100;
 
     /**
      * For MultiEndpoint - Call Pull request has failed
+     * @hide
      */
     public static final int CALL_PULL_OUT_OF_SYNC                            = 2101;
 
     /**
      * For MultiEndpoint - Call has been pulled from primary to secondary
+     * @hide
      */
     public static final int CALL_PULLED                                      = 2102;
 
     /**
      * Supplementary services (HOLD/RESUME) failure error codes.
      * Values for Supplemetary services failure - Failed, Cancelled and Re-Invite collision.
+     * @hide
      */
     public static final int SUPP_SVC_FAILED                                  = 2300;
+    /** @hide */
     public static final int SUPP_SVC_CANCELLED                               = 2301;
+    /** @hide */
     public static final int SUPP_SVC_REINVITE_COLLISION                      = 2302;
 
     /**
      * DPD Procedure received no response or send failed
+     * @hide
      */
     public static final int IWLAN_DPD_FAILURE                                = 2400;
 
     /**
      * Establishment of the ePDG Tunnel Failed
+     * @hide
      */
     public static final int EPDG_TUNNEL_ESTABLISH_FAILURE                    = 2500;
 
     /**
      * Re-keying of the ePDG Tunnel Failed; may not always result in teardown
+     * @hide
      */
     public static final int EPDG_TUNNEL_REKEY_FAILURE                        = 2501;
 
     /**
      * Connection to the packet gateway is lost
+     * @hide
      */
     public static final int EPDG_TUNNEL_LOST_CONNECTION                      = 2502;
 
     /**
      * The maximum number of calls allowed has been reached.  Used in a multi-endpoint scenario
      * where the number of calls across all connected devices has reached the maximum.
+     * @hide
      */
     public static final int MAXIMUM_NUMBER_OF_CALLS_REACHED                  = 2503;
 
@@ -462,21 +564,25 @@
      * Similar to {@link #CODE_LOCAL_CALL_DECLINE}, except indicates that a remote device has
      * declined the call.  Used in a multi-endpoint scenario where a remote device declined an
      * incoming call.
+     * @hide
      */
     public static final int REMOTE_CALL_DECLINE                              = 2504;
 
     /**
      * Indicates the call was disconnected due to the user reaching their data limit.
+     * @hide
      */
     public static final int DATA_LIMIT_REACHED                               = 2505;
 
     /**
      * Indicates the call was disconnected due to the user disabling cellular data.
+     * @hide
      */
     public static final int DATA_DISABLED                                    = 2506;
 
     /**
      * Indicates a call was disconnected due to loss of wifi signal.
+     * @hide
      */
     public static final int WIFI_LOST                                        = 2507;
 
@@ -499,7 +605,7 @@
     public static final int OEM_CAUSE_14                                     = 0xf00e;
     public static final int OEM_CAUSE_15                                     = 0xf00f;
 
-    /** Disconnected due to unspecified reasons */
+    /** Disconnected due to unspecified reasons. */
     public static final int ERROR_UNSPECIFIED                                = 0xffff;
 
     /** Private constructor to avoid class instantiation. */
diff --git a/telephony/java/android/telephony/SubscriptionInfo.java b/telephony/java/android/telephony/SubscriptionInfo.java
index a7e8e8a..a1e8b19 100644
--- a/telephony/java/android/telephony/SubscriptionInfo.java
+++ b/telephony/java/android/telephony/SubscriptionInfo.java
@@ -138,10 +138,15 @@
     private UiccAccessRule[] mAccessRules;
 
     /**
-     * The ID of the SIM card. It is the ICCID of the active profile for a UICC card and the EID
-     * for an eUICC card.
+     * The string ID of the SIM card. It is the ICCID of the active profile for a UICC card and the
+     * EID for an eUICC card.
      */
-    private String mCardId;
+    private String mCardString;
+
+    /**
+     * The card ID of the SIM card. This maps uniquely to the card string.
+     */
+    private int mCardId;
 
     /**
      * Whether the subscription is opportunistic.
@@ -174,9 +179,9 @@
     public SubscriptionInfo(int id, String iccId, int simSlotIndex, CharSequence displayName,
             CharSequence carrierName, int nameSource, int iconTint, String number, int roaming,
             Bitmap icon, String mcc, String mnc, String countryIso, boolean isEmbedded,
-            @Nullable UiccAccessRule[] accessRules, String cardId) {
+            @Nullable UiccAccessRule[] accessRules, String cardString) {
         this(id, iccId, simSlotIndex, displayName, carrierName, nameSource, iconTint, number,
-                roaming, icon, mcc, mnc, countryIso, isEmbedded, accessRules, cardId,
+                roaming, icon, mcc, mnc, countryIso, isEmbedded, accessRules, cardString,
                 false, null, true, TelephonyManager.UNKNOWN_CARRIER_ID);
     }
 
@@ -186,20 +191,22 @@
     public SubscriptionInfo(int id, String iccId, int simSlotIndex, CharSequence displayName,
             CharSequence carrierName, int nameSource, int iconTint, String number, int roaming,
             Bitmap icon, String mcc, String mnc, String countryIso, boolean isEmbedded,
-            @Nullable UiccAccessRule[] accessRules, String cardId, boolean isOpportunistic,
+            @Nullable UiccAccessRule[] accessRules, String cardString, boolean isOpportunistic,
             @Nullable String groupUUID, boolean isMetered, int carrierId) {
         this(id, iccId, simSlotIndex, displayName, carrierName, nameSource, iconTint, number,
-                roaming, icon, mcc, mnc, countryIso, isEmbedded, accessRules, cardId,
+                roaming, icon, mcc, mnc, countryIso, isEmbedded, accessRules, cardString, -1,
                 isOpportunistic, groupUUID, isMetered, false, carrierId);
     }
+
     /**
      * @hide
      */
     public SubscriptionInfo(int id, String iccId, int simSlotIndex, CharSequence displayName,
             CharSequence carrierName, int nameSource, int iconTint, String number, int roaming,
             Bitmap icon, String mcc, String mnc, String countryIso, boolean isEmbedded,
-            @Nullable UiccAccessRule[] accessRules, String cardId, boolean isOpportunistic,
-            @Nullable String groupUUID, boolean isMetered, boolean isGroupDisabled, int carrierid) {
+            @Nullable UiccAccessRule[] accessRules, String cardString, int cardId,
+            boolean isOpportunistic, @Nullable String groupUUID, boolean isMetered,
+            boolean isGroupDisabled, int carrierid) {
         this.mId = id;
         this.mIccId = iccId;
         this.mSimSlotIndex = simSlotIndex;
@@ -215,6 +222,7 @@
         this.mCountryIso = countryIso;
         this.mIsEmbedded = isEmbedded;
         this.mAccessRules = accessRules;
+        this.mCardString = cardString;
         this.mCardId = cardId;
         this.mIsOpportunistic = isOpportunistic;
         this.mGroupUUID = groupUUID;
@@ -523,10 +531,21 @@
     }
 
     /**
-     * @return the ID of the SIM card which contains the subscription.
+     * @return the card string of the SIM card which contains the subscription. The card string is
+     * the ICCID for UICCs or the EID for eUICCs.
+     * @hide
+     * //TODO rename usages in LPA: UiccSlotUtil.java, UiccSlotsManager.java, UiccSlotInfoTest.java
+     */
+    public String getCardString() {
+        return this.mCardString;
+    }
+
+    /**
+     * @return the cardId of the SIM card which contains the subscription.
      * @hide
      */
-    public String getCardId() {
+    @SystemApi
+    public int getCardId() {
         return this.mCardId;
     }
 
@@ -564,7 +583,8 @@
             Bitmap iconBitmap = Bitmap.CREATOR.createFromParcel(source);
             boolean isEmbedded = source.readBoolean();
             UiccAccessRule[] accessRules = source.createTypedArray(UiccAccessRule.CREATOR);
-            String cardId = source.readString();
+            String cardString = source.readString();
+            int cardId = source.readInt();
             boolean isOpportunistic = source.readBoolean();
             String groupUUID = source.readString();
             boolean isMetered = source.readBoolean();
@@ -573,8 +593,8 @@
 
             return new SubscriptionInfo(id, iccId, simSlotIndex, displayName, carrierName,
                     nameSource, iconTint, number, dataRoaming, iconBitmap, mcc, mnc, countryIso,
-                    isEmbedded, accessRules, cardId, isOpportunistic, groupUUID, isMetered,
-                    isGroupDisabled, carrierid);
+                    isEmbedded, accessRules, cardString, cardId, isOpportunistic, groupUUID,
+                    isMetered, isGroupDisabled, carrierid);
         }
 
         @Override
@@ -600,7 +620,8 @@
         mIconBitmap.writeToParcel(dest, flags);
         dest.writeBoolean(mIsEmbedded);
         dest.writeTypedArray(mAccessRules, flags);
-        dest.writeString(mCardId);
+        dest.writeString(mCardString);
+        dest.writeInt(mCardId);
         dest.writeBoolean(mIsOpportunistic);
         dest.writeString(mGroupUUID);
         dest.writeBoolean(mIsMetered);
@@ -631,7 +652,7 @@
     @Override
     public String toString() {
         String iccIdToPrint = givePrintableIccid(mIccId);
-        String cardIdToPrint = givePrintableIccid(mCardId);
+        String cardStringToPrint = givePrintableIccid(mCardString);
         return "{id=" + mId + ", iccId=" + iccIdToPrint + " simSlotIndex=" + mSimSlotIndex
                 + " carrierId=" + mCarrierId + " displayName=" + mDisplayName
                 + " carrierName=" + mCarrierName + " nameSource=" + mNameSource
@@ -639,17 +660,17 @@
                 + " dataRoaming=" + mDataRoaming + " iconBitmap=" + mIconBitmap + " mcc " + mMcc
                 + " mnc " + mMnc + "mCountryIso=" + mCountryIso + " isEmbedded " + mIsEmbedded
                 + " accessRules " + Arrays.toString(mAccessRules)
-                + " cardId=" + cardIdToPrint + " isOpportunistic " + mIsOpportunistic
-                + " mGroupUUID=" + mGroupUUID + " isMetered=" + mIsMetered
-                + " mIsGroupDisabled=" + mIsGroupDisabled + "}";
+                + " cardString=" + cardStringToPrint + " cardId=" + mCardId
+                + " isOpportunistic " + mIsOpportunistic + " mGroupUUID=" + mGroupUUID
+                + " isMetered=" + mIsMetered + " mIsGroupDisabled=" + mIsGroupDisabled + "}";
     }
 
     @Override
     public int hashCode() {
         return Objects.hash(mId, mSimSlotIndex, mNameSource, mIconTint, mDataRoaming, mIsEmbedded,
                 mIsOpportunistic, mGroupUUID, mIsMetered, mIccId, mNumber, mMcc, mMnc,
-                mCountryIso, mCardId, mDisplayName, mCarrierName, mAccessRules, mIsGroupDisabled,
-                mCarrierId);
+                mCountryIso, mCardString, mCardId, mDisplayName, mCarrierName, mAccessRules,
+                mIsGroupDisabled, mCarrierId);
     }
 
     @Override
@@ -680,6 +701,7 @@
                 && Objects.equals(mMcc, toCompare.mMcc)
                 && Objects.equals(mMnc, toCompare.mMnc)
                 && Objects.equals(mCountryIso, toCompare.mCountryIso)
+                && Objects.equals(mCardString, toCompare.mCardString)
                 && Objects.equals(mCardId, toCompare.mCardId)
                 && TextUtils.equals(mDisplayName, toCompare.mDisplayName)
                 && TextUtils.equals(mCarrierName, toCompare.mCarrierName)
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index e0632b1..29d32e9 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -625,8 +625,6 @@
      * The {@link #EXTRA_RINGING_CALL_STATE} extra indicates the ringing call state.
      * The {@link #EXTRA_FOREGROUND_CALL_STATE} extra indicates the foreground call state.
      * The {@link #EXTRA_BACKGROUND_CALL_STATE} extra indicates the background call state.
-     * The {@link #EXTRA_DISCONNECT_CAUSE} extra indicates the disconnect cause.
-     * The {@link #EXTRA_PRECISE_DISCONNECT_CAUSE} extra indicates the precise disconnect cause.
      *
      * <p class="note">
      * Requires the READ_PRECISE_PHONE_STATE permission.
@@ -634,12 +632,10 @@
      * @see #EXTRA_RINGING_CALL_STATE
      * @see #EXTRA_FOREGROUND_CALL_STATE
      * @see #EXTRA_BACKGROUND_CALL_STATE
-     * @see #EXTRA_DISCONNECT_CAUSE
-     * @see #EXTRA_PRECISE_DISCONNECT_CAUSE
      *
      * <p class="note">
      * Requires the READ_PRECISE_PHONE_STATE permission.
-     *
+     * @deprecated use {@link PhoneStateListener#LISTEN_PRECISE_CALL_STATE} instead
      * @hide
      */
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
@@ -647,8 +643,28 @@
             "android.intent.action.PRECISE_CALL_STATE";
 
     /**
-     * The lookup key used with the {@link #ACTION_PRECISE_CALL_STATE_CHANGED} broadcast
-     * for an integer containing the state of the current ringing call.
+     * Broadcast intent action indicating that call disconnect cause has changed.
+     *
+     * <p>
+     * The {@link #EXTRA_DISCONNECT_CAUSE} extra indicates the disconnect cause.
+     * The {@link #EXTRA_PRECISE_DISCONNECT_CAUSE} extra indicates the precise disconnect cause.
+     *
+     * <p class="note">
+     * Requires the READ_PRECISE_PHONE_STATE permission.
+     *
+     * @see #EXTRA_DISCONNECT_CAUSE
+     * @see #EXTRA_PRECISE_DISCONNECT_CAUSE
+     *
+     * @hide
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_CALL_DISCONNECT_CAUSE_CHANGED =
+            "android.intent.action.CALL_DISCONNECT_CAUSE";
+
+    /**
+     * The lookup key used with the {@link #ACTION_PRECISE_CALL_STATE_CHANGED} broadcast and
+     * {@link PhoneStateListener#onPreciseCallStateChanged(PreciseCallState)} for an integer
+     * containing the state of the current ringing call.
      *
      * @see PreciseCallState#PRECISE_CALL_STATE_NOT_VALID
      * @see PreciseCallState#PRECISE_CALL_STATE_IDLE
@@ -670,8 +686,9 @@
     public static final String EXTRA_RINGING_CALL_STATE = "ringing_state";
 
     /**
-     * The lookup key used with the {@link #ACTION_PRECISE_CALL_STATE_CHANGED} broadcast
-     * for an integer containing the state of the current foreground call.
+     * The lookup key used with the {@link #ACTION_PRECISE_CALL_STATE_CHANGED} broadcast and
+     * {@link PhoneStateListener#onPreciseCallStateChanged(PreciseCallState)} for an integer
+     * containing the state of the current foreground call.
      *
      * @see PreciseCallState#PRECISE_CALL_STATE_NOT_VALID
      * @see PreciseCallState#PRECISE_CALL_STATE_IDLE
@@ -693,8 +710,9 @@
     public static final String EXTRA_FOREGROUND_CALL_STATE = "foreground_state";
 
     /**
-     * The lookup key used with the {@link #ACTION_PRECISE_CALL_STATE_CHANGED} broadcast
-     * for an integer containing the state of the current background call.
+     * The lookup key used with the {@link #ACTION_PRECISE_CALL_STATE_CHANGED} broadcast and
+     * {@link PhoneStateListener#onPreciseCallStateChanged(PreciseCallState)} for an integer
+     * containing the state of the current background call.
      *
      * @see PreciseCallState#PRECISE_CALL_STATE_NOT_VALID
      * @see PreciseCallState#PRECISE_CALL_STATE_IDLE
@@ -716,8 +734,9 @@
     public static final String EXTRA_BACKGROUND_CALL_STATE = "background_state";
 
     /**
-     * The lookup key used with the {@link #ACTION_PRECISE_CALL_STATE_CHANGED} broadcast
-     * for an integer containing the disconnect cause.
+     * The lookup key used with the {@link #ACTION_PRECISE_CALL_STATE_CHANGED} broadcast and
+     * {@link PhoneStateListener#onPreciseCallStateChanged(PreciseCallState)} for an integer
+     * containing the disconnect cause.
      *
      * @see DisconnectCause
      *
@@ -730,8 +749,9 @@
     public static final String EXTRA_DISCONNECT_CAUSE = "disconnect_cause";
 
     /**
-     * The lookup key used with the {@link #ACTION_PRECISE_CALL_STATE_CHANGED} broadcast
-     * for an integer containing the disconnect cause provided by the RIL.
+     * The lookup key used with the {@link #ACTION_PRECISE_CALL_STATE_CHANGED} broadcast and
+     * {@link PhoneStateListener#onPreciseCallStateChanged(PreciseCallState)} for an integer
+     * containing the disconnect cause provided by the RIL.
      *
      * @see PreciseDisconnectCause
      *
@@ -4888,7 +4908,7 @@
      */
     @RequiresPermission(android.Manifest.permission.ACCESS_COARSE_LOCATION)
     public void requestCellInfoUpdate(
-            @NonNull Executor executor, @NonNull CellInfoCallback callback) {
+            @NonNull @CallbackExecutor Executor executor, @NonNull CellInfoCallback callback) {
         try {
             ITelephony telephony = getITelephony();
             if (telephony == null) return;
diff --git a/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl b/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl
index 78fc0bc4..00cf9c3 100644
--- a/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl
+++ b/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl
@@ -55,5 +55,6 @@
     void onPreferredDataSubIdChanged(in int subId);
     void onRadioPowerStateChanged(in int state);
     void onEmergencyNumberListChanged(in Map emergencyNumberList);
+    void onCallDisconnectCauseChanged(in int disconnectCause, in int preciseDisconnectCause);
 }
 
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/tools/aapt2/ResourceParser.cpp b/tools/aapt2/ResourceParser.cpp
index c91134c..58702dc 100644
--- a/tools/aapt2/ResourceParser.cpp
+++ b/tools/aapt2/ResourceParser.cpp
@@ -99,7 +99,7 @@
   ResourceId id;
   Visibility::Level visibility_level = Visibility::Level::kUndefined;
   bool allow_new = false;
-  Maybe<Overlayable> overlayable;
+  Maybe<OverlayableItem> overlayable_item;
 
   std::string comment;
   std::unique_ptr<Value> value;
@@ -133,8 +133,8 @@
     }
   }
 
-  if (res->overlayable) {
-    if (!table->SetOverlayable(res->name, res->overlayable.value(), diag)) {
+  if (res->overlayable_item) {
+    if (!table->SetOverlayable(res->name, res->overlayable_item.value(), diag)) {
       return false;
     }
   }
@@ -1059,92 +1059,119 @@
 bool ResourceParser::ParseOverlayable(xml::XmlPullParser* parser, ParsedResource* out_resource) {
   if (out_resource->config != ConfigDescription::DefaultConfig()) {
     diag_->Warn(DiagMessage(out_resource->source)
-                    << "ignoring configuration '" << out_resource->config
-                    << "' for <overlayable> tag");
+                << "ignoring configuration '" << out_resource->config
+                << "' for <overlayable> tag");
   }
 
+  Maybe<StringPiece> overlayable_name = xml::FindNonEmptyAttribute(parser, "name");
+  if (!overlayable_name) {
+    diag_->Error(DiagMessage(out_resource->source)
+                  << "<overlayable> tag must have a 'name' attribute");
+    return false;
+  }
+
+  const std::string kActorUriScheme =
+      android::base::StringPrintf("%s://", Overlayable::kActorScheme);
+  Maybe<StringPiece> overlayable_actor = xml::FindNonEmptyAttribute(parser, "actor");
+  if (overlayable_actor && !util::StartsWith(overlayable_actor.value(), kActorUriScheme)) {
+    diag_->Error(DiagMessage(out_resource->source)
+                 << "specified <overlayable> tag 'actor' attribute must use the scheme '"
+                 << Overlayable::kActorScheme << "'");
+    return false;
+  }
+
+  // Create a overlayable entry grouping that represents this <overlayable>
+  auto overlayable = std::make_shared<Overlayable>(
+      overlayable_name.value(), (overlayable_actor) ? overlayable_actor.value() : "",
+      out_resource->source);
+
   bool error = false;
   std::string comment;
-  Overlayable::PolicyFlags current_policies = Overlayable::Policy::kNone;
+  OverlayableItem::PolicyFlags current_policies = OverlayableItem::Policy::kNone;
   const size_t start_depth = parser->depth();
   while (xml::XmlPullParser::IsGoodEvent(parser->Next())) {
     xml::XmlPullParser::Event event = parser->event();
     if (event == xml::XmlPullParser::Event::kEndElement && parser->depth() == start_depth) {
-      // Break the loop when exiting the overlayable element
+      // Break the loop when exiting the <overlayable>
       break;
     } else if (event == xml::XmlPullParser::Event::kEndElement
                && parser->depth() == start_depth + 1) {
-      // Clear the current policies when exiting the policy element
-      current_policies = Overlayable::Policy::kNone;
+      // Clear the current policies when exiting the <policy> tags
+      current_policies = OverlayableItem::Policy::kNone;
       continue;
     } else if (event == xml::XmlPullParser::Event::kComment) {
-      // Get the comment of individual item elements
+      // Retrieve the comment of individual <item> tags
       comment = parser->comment();
       continue;
     } else if (event != xml::XmlPullParser::Event::kStartElement) {
-      // Skip to the next element
+      // Skip to the start of the next element
       continue;
     }
 
-    const Source item_source = source_.WithLine(parser->line_number());
+    const Source element_source = source_.WithLine(parser->line_number());
     const std::string& element_name = parser->element_name();
     const std::string& element_namespace = parser->element_namespace();
     if (element_namespace.empty() && element_name == "item") {
       // Items specify the name and type of resource that should be overlayable
-      Maybe<StringPiece> maybe_name = xml::FindNonEmptyAttribute(parser, "name");
-      if (!maybe_name) {
-        diag_->Error(DiagMessage(item_source)
-                     << "<item> within an <overlayable> tag must have a 'name' attribute");
+      Maybe<StringPiece> item_name = xml::FindNonEmptyAttribute(parser, "name");
+      if (!item_name) {
+        diag_->Error(DiagMessage(element_source)
+                     << "<item> within an <overlayable> must have a 'name' attribute");
         error = true;
         continue;
       }
 
-      Maybe<StringPiece> maybe_type = xml::FindNonEmptyAttribute(parser, "type");
-      if (!maybe_type) {
-        diag_->Error(DiagMessage(item_source)
-                     << "<item> within an <overlayable> tag must have a 'type' attribute");
+      Maybe<StringPiece> item_type = xml::FindNonEmptyAttribute(parser, "type");
+      if (!item_type) {
+        diag_->Error(DiagMessage(element_source)
+                     << "<item> within an <overlayable> must have a 'type' attribute");
         error = true;
         continue;
       }
 
-      const ResourceType* type = ParseResourceType(maybe_type.value());
+      const ResourceType* type = ParseResourceType(item_type.value());
       if (type == nullptr) {
-        diag_->Error(DiagMessage(item_source)
-                     << "invalid resource type '" << maybe_type.value()
+        diag_->Error(DiagMessage(element_source)
+                     << "invalid resource type '" << item_type.value()
                      << "' in <item> within an <overlayable>");
         error = true;
         continue;
       }
 
-      ParsedResource child_resource;
+      OverlayableItem overlayable_item(overlayable);
+      overlayable_item.policies = current_policies;
+      overlayable_item.comment = comment;
+      overlayable_item.source = element_source;
+
+      ParsedResource child_resource{};
       child_resource.name.type = *type;
-      child_resource.name.entry = maybe_name.value().to_string();
-      child_resource.overlayable = Overlayable{current_policies, item_source, comment};
+      child_resource.name.entry = item_name.value().to_string();
+      child_resource.overlayable_item = overlayable_item;
       out_resource->child_resources.push_back(std::move(child_resource));
 
     } else if (element_namespace.empty() && element_name == "policy") {
-      if (current_policies != Overlayable::Policy::kNone) {
+      if (current_policies != OverlayableItem::Policy::kNone) {
         // If the policy list is not empty, then we are currently inside a policy element
-        diag_->Error(DiagMessage(item_source) << "<policy> blocks cannot be recursively nested");
+        diag_->Error(DiagMessage(element_source) << "<policy> blocks cannot be recursively nested");
         error = true;
         break;
       } else if (Maybe<StringPiece> maybe_type = xml::FindNonEmptyAttribute(parser, "type")) {
         // Parse the polices separated by vertical bar characters to allow for specifying multiple
-        // policies
+        // policies. Items within the policy tag will have the specified policy.
         for (StringPiece part : util::Tokenize(maybe_type.value(), '|')) {
           StringPiece trimmed_part = util::TrimWhitespace(part);
           if (trimmed_part == "public") {
-            current_policies |= Overlayable::Policy::kPublic;
+            current_policies |= OverlayableItem::Policy::kPublic;
           } else if (trimmed_part == "product") {
-            current_policies |= Overlayable::Policy::kProduct;
+            current_policies |= OverlayableItem::Policy::kProduct;
           } else if (trimmed_part == "product_services") {
-            current_policies |= Overlayable::Policy::kProductServices;
+            current_policies |= OverlayableItem::Policy::kProductServices;
           } else if (trimmed_part == "system") {
-            current_policies |= Overlayable::Policy::kSystem;
+            current_policies |= OverlayableItem::Policy::kSystem;
           } else if (trimmed_part == "vendor") {
-            current_policies |= Overlayable::Policy::kVendor;
+            current_policies |= OverlayableItem::Policy::kVendor;
           } else {
-            diag_->Error(DiagMessage(item_source)
+            diag_->Error(DiagMessage(element_source)
                          << "<policy> has unsupported type '" << trimmed_part << "'");
             error = true;
             continue;
@@ -1152,11 +1179,13 @@
         }
       }
     } else if (!ShouldIgnoreElement(element_namespace, element_name)) {
-      diag_->Error(DiagMessage(item_source) << "invalid element <" << element_name << "> "
+      diag_->Error(DiagMessage(element_source) << "invalid element <" << element_name << "> "
                                             << " in <overlayable>");
       error = true;
       break;
     }
+
+    comment.clear();
   }
 
   return !error;
diff --git a/tools/aapt2/ResourceParser_test.cpp b/tools/aapt2/ResourceParser_test.cpp
index 03e6197..debca9c 100644
--- a/tools/aapt2/ResourceParser_test.cpp
+++ b/tools/aapt2/ResourceParser_test.cpp
@@ -892,11 +892,8 @@
 }
 
 TEST_F(ResourceParserTest, ParseOverlayable) {
-  std::string input = R"(<overlayable />)";
-  EXPECT_TRUE(TestParse(input));
-
-  input = R"(
-      <overlayable>
+  std::string input = R"(
+      <overlayable name="Name" actor="overlay://theme">
         <item type="string" name="foo" />
         <item type="drawable" name="bar" />
       </overlayable>)";
@@ -905,24 +902,35 @@
   auto search_result = table_.FindResource(test::ParseNameOrDie("string/foo"));
   ASSERT_TRUE(search_result);
   ASSERT_THAT(search_result.value().entry, NotNull());
-  ASSERT_TRUE(search_result.value().entry->overlayable);
-  EXPECT_THAT(search_result.value().entry->overlayable.value().policies,
-              Eq(Overlayable::Policy::kNone));
+  ASSERT_TRUE(search_result.value().entry->overlayable_item);
+  OverlayableItem& result_overlayable_item = search_result.value().entry->overlayable_item.value();
+  EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name"));
+  EXPECT_THAT(result_overlayable_item.overlayable->actor, Eq("overlay://theme"));
+  EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kNone));
 
   search_result = table_.FindResource(test::ParseNameOrDie("drawable/bar"));
   ASSERT_TRUE(search_result);
   ASSERT_THAT(search_result.value().entry, NotNull());
-  ASSERT_TRUE(search_result.value().entry->overlayable);
-  EXPECT_THAT(search_result.value().entry->overlayable.value().policies,
-              Eq(Overlayable::Policy::kNone));
+  ASSERT_TRUE(search_result.value().entry->overlayable_item);
+  result_overlayable_item = search_result.value().entry->overlayable_item.value();
+  EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name"));
+  EXPECT_THAT(result_overlayable_item.overlayable->actor, Eq("overlay://theme"));
+  EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kNone));
+}
+
+TEST_F(ResourceParserTest, ParseOverlayableRequiresName) {
+  EXPECT_FALSE(TestParse(R"(<overlayable actor="overlay://theme" />)"));
+  EXPECT_TRUE(TestParse(R"(<overlayable name="Name" />)"));
+  EXPECT_TRUE(TestParse(R"(<overlayable name="Name" actor="overlay://theme" />)"));
+}
+
+TEST_F(ResourceParserTest, ParseOverlayableBadActorFail) {
+  EXPECT_FALSE(TestParse(R"(<overlayable name="Name" actor="overley://theme" />)"));
 }
 
 TEST_F(ResourceParserTest, ParseOverlayablePolicy) {
-  std::string input = R"(<overlayable />)";
-  EXPECT_TRUE(TestParse(input));
-
-  input = R"(
-      <overlayable>
+  std::string input = R"(
+      <overlayable name="Name">
         <item type="string" name="foo" />
         <policy type="product">
           <item type="string" name="bar" />
@@ -945,49 +953,55 @@
   auto search_result = table_.FindResource(test::ParseNameOrDie("string/foo"));
   ASSERT_TRUE(search_result);
   ASSERT_THAT(search_result.value().entry, NotNull());
-  ASSERT_TRUE(search_result.value().entry->overlayable);
-  Overlayable& overlayable = search_result.value().entry->overlayable.value();
-  EXPECT_THAT(overlayable.policies, Eq(Overlayable::Policy::kNone));
+  ASSERT_TRUE(search_result.value().entry->overlayable_item);
+  OverlayableItem result_overlayable_item = search_result.value().entry->overlayable_item.value();
+  EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name"));
+  EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kNone));
 
   search_result = table_.FindResource(test::ParseNameOrDie("string/bar"));
   ASSERT_TRUE(search_result);
   ASSERT_THAT(search_result.value().entry, NotNull());
-  ASSERT_TRUE(search_result.value().entry->overlayable);
-  overlayable = search_result.value().entry->overlayable.value();
-  EXPECT_THAT(overlayable.policies, Eq(Overlayable::Policy::kProduct));
+  ASSERT_TRUE(search_result.value().entry->overlayable_item);
+  result_overlayable_item = search_result.value().entry->overlayable_item.value();
+  EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name"));
+  EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kProduct));
 
   search_result = table_.FindResource(test::ParseNameOrDie("string/baz"));
   ASSERT_TRUE(search_result);
   ASSERT_THAT(search_result.value().entry, NotNull());
-  ASSERT_TRUE(search_result.value().entry->overlayable);
-  overlayable = search_result.value().entry->overlayable.value();
-  EXPECT_THAT(overlayable.policies, Eq(Overlayable::Policy::kProductServices));
+  ASSERT_TRUE(search_result.value().entry->overlayable_item);
+  result_overlayable_item = search_result.value().entry->overlayable_item.value();
+  EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name"));
+  EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kProductServices));
 
   search_result = table_.FindResource(test::ParseNameOrDie("string/fiz"));
   ASSERT_TRUE(search_result);
   ASSERT_THAT(search_result.value().entry, NotNull());
-  ASSERT_TRUE(search_result.value().entry->overlayable);
-  overlayable = search_result.value().entry->overlayable.value();
-  EXPECT_THAT(overlayable.policies, Eq(Overlayable::Policy::kSystem));
+  ASSERT_TRUE(search_result.value().entry->overlayable_item);
+  result_overlayable_item = search_result.value().entry->overlayable_item.value();
+  EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name"));
+  EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kSystem));
 
   search_result = table_.FindResource(test::ParseNameOrDie("string/fuz"));
   ASSERT_TRUE(search_result);
   ASSERT_THAT(search_result.value().entry, NotNull());
-  ASSERT_TRUE(search_result.value().entry->overlayable);
-  overlayable = search_result.value().entry->overlayable.value();
-  EXPECT_THAT(overlayable.policies, Eq(Overlayable::Policy::kVendor));
+  ASSERT_TRUE(search_result.value().entry->overlayable_item);
+  result_overlayable_item = search_result.value().entry->overlayable_item.value();
+  EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name"));
+  EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kVendor));
 
   search_result = table_.FindResource(test::ParseNameOrDie("string/faz"));
   ASSERT_TRUE(search_result);
   ASSERT_THAT(search_result.value().entry, NotNull());
-  ASSERT_TRUE(search_result.value().entry->overlayable);
-  overlayable = search_result.value().entry->overlayable.value();
-  EXPECT_THAT(overlayable.policies, Eq(Overlayable::Policy::kPublic));
+  ASSERT_TRUE(search_result.value().entry->overlayable_item);
+  result_overlayable_item = search_result.value().entry->overlayable_item.value();
+  EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name"));
+  EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kPublic));
 }
 
 TEST_F(ResourceParserTest, ParseOverlayableBadPolicyError) {
   std::string input = R"(
-      <overlayable>
+      <overlayable name="Name">
         <policy type="illegal_policy">
           <item type="string" name="foo" />
         </policy>
@@ -995,7 +1009,7 @@
   EXPECT_FALSE(TestParse(input));
 
   input = R"(
-      <overlayable>
+      <overlayable name="Name">
         <policy type="product">
           <item name="foo" />
         </policy>
@@ -1003,7 +1017,7 @@
   EXPECT_FALSE(TestParse(input));
 
   input = R"(
-      <overlayable>
+      <overlayable name="Name">
         <policy type="vendor">
           <item type="string" />
         </policy>
@@ -1013,7 +1027,7 @@
 
 TEST_F(ResourceParserTest, ParseOverlayableMultiplePolicy) {
   std::string input = R"(
-      <overlayable>
+      <overlayable name="Name">
         <policy type="vendor|product_services">
           <item type="string" name="foo" />
         </policy>
@@ -1026,39 +1040,59 @@
   auto search_result = table_.FindResource(test::ParseNameOrDie("string/foo"));
   ASSERT_TRUE(search_result);
   ASSERT_THAT(search_result.value().entry, NotNull());
-  ASSERT_TRUE(search_result.value().entry->overlayable);
-  Overlayable& overlayable = search_result.value().entry->overlayable.value();
-  EXPECT_THAT(overlayable.policies, Eq(Overlayable::Policy::kVendor
-                                       | Overlayable::Policy::kProductServices));
+  ASSERT_TRUE(search_result.value().entry->overlayable_item);
+  OverlayableItem result_overlayable_item = search_result.value().entry->overlayable_item.value();
+  EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name"));
+  EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kVendor
+                                                   | OverlayableItem::Policy::kProductServices));
 
   search_result = table_.FindResource(test::ParseNameOrDie("string/bar"));
   ASSERT_TRUE(search_result);
   ASSERT_THAT(search_result.value().entry, NotNull());
-  ASSERT_TRUE(search_result.value().entry->overlayable);
-  overlayable = search_result.value().entry->overlayable.value();
-  EXPECT_THAT(overlayable.policies, Eq(Overlayable::Policy::kProduct
-                                       | Overlayable::Policy::kSystem));
+  ASSERT_TRUE(search_result.value().entry->overlayable_item);
+  result_overlayable_item = search_result.value().entry->overlayable_item.value();
+  EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name"));
+  EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kProduct
+                                                   | OverlayableItem::Policy::kSystem));
 }
 
 TEST_F(ResourceParserTest, DuplicateOverlayableIsError) {
   std::string input = R"(
-      <overlayable>
+      <overlayable name="Name">
         <item type="string" name="foo" />
         <item type="string" name="foo" />
       </overlayable>)";
   EXPECT_FALSE(TestParse(input));
 
   input = R"(
-      <overlayable>
+      <overlayable name="Name">
         <item type="string" name="foo" />
       </overlayable>
-      <overlayable>
+      <overlayable name="Name">
         <item type="string" name="foo" />
       </overlayable>)";
   EXPECT_FALSE(TestParse(input));
 
   input = R"(
-      <overlayable>
+      <overlayable name="Name">
+        <item type="string" name="foo" />
+      </overlayable>
+      <overlayable name="Other">
+        <item type="string" name="foo" />
+      </overlayable>)";
+  EXPECT_FALSE(TestParse(input));
+
+  input = R"(
+      <overlayable name="Name" actor="overlay://my.actor.one">
+        <item type="string" name="foo" />
+      </overlayable>
+      <overlayable name="Other" actor="overlay://my.actor.two">
+        <item type="string" name="foo" />
+      </overlayable>)";
+  EXPECT_FALSE(TestParse(input));
+
+  input = R"(
+      <overlayable name="Name">
         <policy type="product">
           <item type="string" name="foo" />
           <item type="string" name="foo" />
@@ -1067,7 +1101,7 @@
   EXPECT_FALSE(TestParse(input));
 
   input = R"(
-      <overlayable>
+      <overlayable name="Name">
         <policy type="product">
           <item type="string" name="foo" />
         </policy>
@@ -1076,7 +1110,7 @@
   EXPECT_FALSE(TestParse(input));
 
   input = R"(
-      <overlayable>
+      <overlayable name="Name">
         <policy type="product">
           <item type="string" name="foo" />
         </policy>
@@ -1087,13 +1121,13 @@
   EXPECT_FALSE(TestParse(input));
 
   input = R"(
-      <overlayable>
+      <overlayable name="Name">
         <policy type="product">
           <item type="string" name="foo" />
         </policy>
       </overlayable>
 
-      <overlayable>
+      <overlayable name="Name">
         <policy type="product">
           <item type="string" name="foo" />
         </policy>
@@ -1103,7 +1137,7 @@
 
 TEST_F(ResourceParserTest, NestPolicyInOverlayableError) {
   std::string input = R"(
-      <overlayable>
+      <overlayable name="Name">
         <policy type="vendor|product">
           <policy type="product_services">
             <item type="string" name="foo" />
diff --git a/tools/aapt2/ResourceTable.cpp b/tools/aapt2/ResourceTable.cpp
index 54633ad..dbd0a0c 100644
--- a/tools/aapt2/ResourceTable.cpp
+++ b/tools/aapt2/ResourceTable.cpp
@@ -40,6 +40,8 @@
 
 namespace aapt {
 
+const char* Overlayable::kActorScheme = "overlay";
+
 static bool less_than_type_and_id(const std::unique_ptr<ResourceTableType>& lhs,
                                   const std::pair<ResourceType, Maybe<uint8_t>>& rhs) {
   return lhs->type < rhs.first || (lhs->type == rhs.first && rhs.second && lhs->id < rhs.second);
@@ -625,17 +627,18 @@
   return true;
 }
 
-bool ResourceTable::SetOverlayable(const ResourceNameRef& name, const Overlayable& overlayable,
+bool ResourceTable::SetOverlayable(const ResourceNameRef& name, const OverlayableItem& overlayable,
                                    IDiagnostics* diag) {
   return SetOverlayableImpl(name, overlayable, ResourceNameValidator, diag);
 }
 
 bool ResourceTable::SetOverlayableMangled(const ResourceNameRef& name,
-                                          const Overlayable& overlayable, IDiagnostics* diag) {
+                                          const OverlayableItem& overlayable, IDiagnostics* diag) {
   return SetOverlayableImpl(name, overlayable, SkipNameValidator, diag);
 }
 
-bool ResourceTable::SetOverlayableImpl(const ResourceNameRef& name, const Overlayable& overlayable,
+bool ResourceTable::SetOverlayableImpl(const ResourceNameRef& name,
+                                       const OverlayableItem& overlayable,
                                        NameValidator name_validator, IDiagnostics *diag) {
   CHECK(diag != nullptr);
 
@@ -647,14 +650,15 @@
   ResourceTableType* type = package->FindOrCreateType(name.type);
   ResourceEntry* entry = type->FindOrCreateEntry(name.entry);
 
-  if (entry->overlayable) {
+  if (entry->overlayable_item) {
     diag->Error(DiagMessage(overlayable.source)
-                    << "duplicate overlayable declaration for resource '" << name << "'");
-    diag->Error(DiagMessage(entry->overlayable.value().source) << "previous declaration here");
+                << "duplicate overlayable declaration for resource '" << name << "'");
+    diag->Error(DiagMessage(entry->overlayable_item.value().source)
+                << "previous declaration here");
     return false;
   }
 
-  entry->overlayable = overlayable;
+  entry->overlayable_item = overlayable;
   return true;
 }
 
@@ -690,7 +694,7 @@
         new_entry->id = entry->id;
         new_entry->visibility = entry->visibility;
         new_entry->allow_new = entry->allow_new;
-        new_entry->overlayable = entry->overlayable;
+        new_entry->overlayable_item = entry->overlayable_item;
 
         for (const auto& config_value : entry->values) {
           ResourceConfigValue* new_value =
diff --git a/tools/aapt2/ResourceTable.h b/tools/aapt2/ResourceTable.h
index e646f5b..eaf6a47 100644
--- a/tools/aapt2/ResourceTable.h
+++ b/tools/aapt2/ResourceTable.h
@@ -57,10 +57,27 @@
   std::string comment;
 };
 
-// Represents a declaration that a resource is overlayable at runtime.
 struct Overlayable {
+  Overlayable() = default;
+   Overlayable(const android::StringPiece& name, const android::StringPiece& actor)
+       : name(name.to_string()), actor(actor.to_string()) {}
+   Overlayable(const android::StringPiece& name, const android::StringPiece& actor,
+                    const Source& source)
+       : name(name.to_string()), actor(actor.to_string()), source(source ){}
+
+  static const char* kActorScheme;
+  std::string name;
+  std::string actor;
+  Source source;
+};
+
+// Represents a declaration that a resource is overlayable at runtime.
+struct OverlayableItem {
+  explicit OverlayableItem(const std::shared_ptr<Overlayable>& overlayable)
+      : overlayable(overlayable) {}
 
   // Represents the types overlays that are allowed to overlay the resource.
+  typedef uint32_t PolicyFlags;
   enum Policy : uint32_t {
     kNone = 0x00,
 
@@ -80,11 +97,10 @@
     kProductServices = 0x10
   };
 
-  typedef uint32_t PolicyFlags;
+  std::shared_ptr<Overlayable> overlayable;
   PolicyFlags policies = Policy::kNone;
-
-  Source source;
   std::string comment;
+  Source source;
 };
 
 class ResourceConfigValue {
@@ -121,7 +137,7 @@
   Maybe<AllowNew> allow_new;
 
   // The declarations of this resource as overlayable for RROs
-  Maybe<Overlayable> overlayable;
+  Maybe<OverlayableItem> overlayable_item;
 
   // The resource's values for each configuration.
   std::vector<std::unique_ptr<ResourceConfigValue>> values;
@@ -251,9 +267,9 @@
   bool SetVisibilityWithIdMangled(const ResourceNameRef& name, const Visibility& visibility,
                                   const ResourceId& res_id, IDiagnostics* diag);
 
-  bool SetOverlayable(const ResourceNameRef& name, const Overlayable& overlayable,
+  bool SetOverlayable(const ResourceNameRef& name, const OverlayableItem& overlayable,
                       IDiagnostics *diag);
-  bool SetOverlayableMangled(const ResourceNameRef& name, const Overlayable& overlayable,
+  bool SetOverlayableMangled(const ResourceNameRef& name, const OverlayableItem& overlayable,
                              IDiagnostics* diag);
 
   bool SetAllowNew(const ResourceNameRef& name, const AllowNew& allow_new, IDiagnostics* diag);
@@ -328,7 +344,7 @@
   bool SetAllowNewImpl(const ResourceNameRef& name, const AllowNew& allow_new,
                        NameValidator name_validator, IDiagnostics* diag);
 
-  bool SetOverlayableImpl(const ResourceNameRef &name, const Overlayable &overlayable,
+  bool SetOverlayableImpl(const ResourceNameRef &name, const OverlayableItem& overlayable,
                           NameValidator name_validator, IDiagnostics *diag);
 
   bool SetSymbolStateImpl(const ResourceNameRef& name, const ResourceId& res_id,
diff --git a/tools/aapt2/ResourceTable_test.cpp b/tools/aapt2/ResourceTable_test.cpp
index 31095c4..a733134 100644
--- a/tools/aapt2/ResourceTable_test.cpp
+++ b/tools/aapt2/ResourceTable_test.cpp
@@ -244,48 +244,90 @@
 
 TEST(ResourceTableTest, SetOverlayable) {
   ResourceTable table;
-  Overlayable overlayable{};
-  overlayable.policies |= Overlayable::Policy::kProduct;
-  overlayable.policies |= Overlayable::Policy::kProductServices;
-  overlayable.comment = "comment";
+  auto overlayable = std::make_shared<Overlayable>("Name", "overlay://theme",
+                                                   Source("res/values/overlayable.xml", 40));
+  OverlayableItem overlayable_item(overlayable);
+  overlayable_item.policies |= OverlayableItem::Policy::kProduct;
+  overlayable_item.policies |= OverlayableItem::Policy::kProductServices;
+  overlayable_item.comment = "comment";
+  overlayable_item.source = Source("res/values/overlayable.xml", 42);
 
   const ResourceName name = test::ParseNameOrDie("android:string/foo");
-  ASSERT_TRUE(table.SetOverlayable(name, overlayable, test::GetDiagnostics()));
+  ASSERT_TRUE(table.SetOverlayable(name, overlayable_item, test::GetDiagnostics()));
   Maybe<ResourceTable::SearchResult> search_result = table.FindResource(name);
 
   ASSERT_TRUE(search_result);
-  ASSERT_TRUE(search_result.value().entry->overlayable);
+  ASSERT_TRUE(search_result.value().entry->overlayable_item);
 
-  Overlayable& result_overlayable = search_result.value().entry->overlayable.value();
-  ASSERT_THAT(result_overlayable.comment, StrEq("comment"));
-  EXPECT_THAT(result_overlayable.policies, Eq(Overlayable::Policy::kProduct
-                                              | Overlayable::Policy::kProductServices));
+  OverlayableItem& result_overlayable_item = search_result.value().entry->overlayable_item.value();
+  EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name"));
+  EXPECT_THAT(result_overlayable_item.overlayable->actor, Eq("overlay://theme"));
+  EXPECT_THAT(result_overlayable_item.overlayable->source.path, Eq("res/values/overlayable.xml"));
+  EXPECT_THAT(result_overlayable_item.overlayable->source.line, 40);
+  EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kProduct
+                                                   | OverlayableItem::Policy::kProductServices));
+  ASSERT_THAT(result_overlayable_item.comment, StrEq("comment"));
+  EXPECT_THAT(result_overlayable_item.source.path, Eq("res/values/overlayable.xml"));
+  EXPECT_THAT(result_overlayable_item.source.line, 42);
 }
 
-TEST(ResourceTableTest, AddDuplicateOverlayableSamePolicyFail) {
+TEST(ResourceTableTest, SetMultipleOverlayableResources) {
+  ResourceTable table;
+
+  const ResourceName foo = test::ParseNameOrDie("android:string/foo");
+  auto group = std::make_shared<Overlayable>("Name", "overlay://theme");
+  OverlayableItem overlayable(group);
+  overlayable.policies = OverlayableItem::Policy::kProduct;
+  ASSERT_TRUE(table.SetOverlayable(foo, overlayable, test::GetDiagnostics()));
+
+  const ResourceName bar = test::ParseNameOrDie("android:string/bar");
+  OverlayableItem overlayable2(group);
+  overlayable2.policies = OverlayableItem::Policy::kProduct;
+  ASSERT_TRUE(table.SetOverlayable(bar, overlayable2, test::GetDiagnostics()));
+
+  const ResourceName baz = test::ParseNameOrDie("android:string/baz");
+  OverlayableItem overlayable3(group);
+  overlayable3.policies = OverlayableItem::Policy::kVendor;
+  ASSERT_TRUE(table.SetOverlayable(baz, overlayable3, test::GetDiagnostics()));
+}
+
+TEST(ResourceTableTest, SetOverlayableDifferentResourcesDifferentName) {
+  ResourceTable table;
+
+  const ResourceName foo = test::ParseNameOrDie("android:string/foo");
+  OverlayableItem overlayable_item(std::make_shared<Overlayable>("Name", "overlay://theme"));
+  overlayable_item.policies = OverlayableItem::Policy::kProduct;
+  ASSERT_TRUE(table.SetOverlayable(foo, overlayable_item, test::GetDiagnostics()));
+
+  const ResourceName bar = test::ParseNameOrDie("android:string/bar");
+  OverlayableItem overlayable_item2(std::make_shared<Overlayable>("Name2",  "overlay://theme"));
+  overlayable_item2.policies = OverlayableItem::Policy::kProduct;
+  ASSERT_TRUE(table.SetOverlayable(bar, overlayable_item2, test::GetDiagnostics()));
+}
+
+TEST(ResourceTableTest, SetOverlayableSameResourcesFail) {
   ResourceTable table;
   const ResourceName name = test::ParseNameOrDie("android:string/foo");
 
-  Overlayable overlayable{};
-  overlayable.policies = Overlayable::Policy::kProduct;
-  ASSERT_TRUE(table.SetOverlayable(name, overlayable, test::GetDiagnostics()));
+  auto overlayable = std::make_shared<Overlayable>("Name", "overlay://theme");
+  OverlayableItem overlayable_item(overlayable);
+  ASSERT_TRUE(table.SetOverlayable(name, overlayable_item, test::GetDiagnostics()));
 
-  Overlayable overlayable2{};
-  overlayable2.policies = Overlayable::Policy::kProduct;
-  ASSERT_FALSE(table.SetOverlayable(name, overlayable2, test::GetDiagnostics()));
+  OverlayableItem overlayable_item2(overlayable);
+  ASSERT_FALSE(table.SetOverlayable(name, overlayable_item2, test::GetDiagnostics()));
 }
 
-TEST(ResourceTableTest, AddDuplicateOverlayableDifferentPolicyFail) {
+TEST(ResourceTableTest,  SetOverlayableSameResourcesDifferentNameFail) {
   ResourceTable table;
   const ResourceName name = test::ParseNameOrDie("android:string/foo");
 
-  Overlayable overlayable{};
-  overlayable.policies = Overlayable::Policy::kProduct;
-  ASSERT_TRUE(table.SetOverlayable(name, overlayable, test::GetDiagnostics()));
+  auto overlayable = std::make_shared<Overlayable>("Name", "overlay://theme");
+  OverlayableItem overlayable_item(overlayable);
+  ASSERT_TRUE(table.SetOverlayable(name, overlayable_item, test::GetDiagnostics()));
 
-  Overlayable overlayable2{};
-  overlayable2.policies = Overlayable::Policy::kVendor;
-  ASSERT_FALSE(table.SetOverlayable(name, overlayable2, test::GetDiagnostics()));
+  auto overlayable2 = std::make_shared<Overlayable>("Other", "overlay://theme");
+  OverlayableItem overlayable_item2(overlayable2);
+  ASSERT_FALSE(table.SetOverlayable(name, overlayable_item2, test::GetDiagnostics()));
 }
 
 TEST(ResourceTableTest, AllowDuplictaeResourcesNames) {
diff --git a/tools/aapt2/Resources.proto b/tools/aapt2/Resources.proto
index 81a2c2e..da541be 100644
--- a/tools/aapt2/Resources.proto
+++ b/tools/aapt2/Resources.proto
@@ -49,6 +49,9 @@
 
   // Resource definitions corresponding to an Android package.
   repeated Package package = 2;
+
+  // The <overlayable> declarations within the resource table.
+  repeated Overlayable overlayable = 3;
 }
 
 // A package ID in the range [0x00, 0xff].
@@ -133,8 +136,20 @@
   string comment = 2;
 }
 
-// Represents a declaration that a resource is overayable at runtime.
+// Represents a set of overlayable resources.
 message Overlayable {
+  // The name of the <overlyabale>.
+  string name = 1;
+
+  // The location of the <overlyabale> declaration in the source.
+  Source source = 2;
+
+  // The component responsible for enabling and disabling overlays targeting this <overlayable>.
+  string actor = 3;
+}
+
+// Represents an overlayable <item> declaration within an <overlayable> tag.
+message OverlayableItem {
   enum Policy {
     PUBLIC = 0;
     SYSTEM = 1;
@@ -143,14 +158,18 @@
     PRODUCT_SERVICES = 4;
   }
 
-  // Where this declaration was defined in source.
+  // The location of the <item> declaration in source.
   Source source = 1;
 
   // Any comment associated with the declaration.
   string comment = 2;
 
-  // The policy defined in the overlayable declaration.
+  // The policy defined by the enclosing <policy> tag of this <item>.
   repeated Policy policy = 3;
+
+  // The index into overlayable list that points to the <overlayable> tag that contains
+  // this <item>.
+  uint32 overlayable_idx = 4;
 }
 
 // An entry ID in the range [0x0000, 0xffff].
@@ -180,7 +199,7 @@
   AllowNew allow_new = 4;
 
   // Whether this resource can be overlaid by a runtime resource overlay (RRO).
-  Overlayable overlayable = 5;
+  OverlayableItem overlayable_item = 5;
 
   // The set of values defined for this entry, each corresponding to a different
   // configuration/variant.
diff --git a/tools/aapt2/dump/DumpManifest.cpp b/tools/aapt2/dump/DumpManifest.cpp
index 11a4074..e17fb47 100644
--- a/tools/aapt2/dump/DumpManifest.cpp
+++ b/tools/aapt2/dump/DumpManifest.cpp
@@ -43,8 +43,10 @@
   PERMISSION_ATTR = 0x01010006,
   EXPORTED_ATTR = 0x01010010,
   GRANT_URI_PERMISSIONS_ATTR = 0x0101001b,
+  PRIORITY_ATTR = 0x0101001c,
   RESOURCE_ATTR = 0x01010025,
   DEBUGGABLE_ATTR = 0x0101000f,
+  TARGET_PACKAGE_ATTR = 0x01010021,
   VALUE_ATTR = 0x01010024,
   VERSION_CODE_ATTR = 0x0101021b,
   VERSION_NAME_ATTR = 0x0101021c,
@@ -77,8 +79,11 @@
   ISGAME_ATTR = 0x10103f4,
   VERSION_ATTR = 0x01010519,
   CERT_DIGEST_ATTR = 0x01010548,
-  REQUIRED_FEATURE_ATTR = 0x1010557,
-  REQUIRED_NOT_FEATURE_ATTR = 0x1010558,
+  REQUIRED_FEATURE_ATTR = 0x01010557,
+  REQUIRED_NOT_FEATURE_ATTR = 0x01010558,
+  IS_STATIC_ATTR = 0x0101055a,
+  REQUIRED_SYSTEM_PROPERTY_NAME_ATTR = 0x01010565,
+  REQUIRED_SYSTEM_PROPERTY_VALUE_ATTR = 0x01010566,
   COMPILE_SDK_VERSION_ATTR = 0x01010572,
   COMPILE_SDK_VERSION_CODENAME_ATTR = 0x01010573,
   VERSION_MAJOR_ATTR = 0x01010577,
@@ -1586,6 +1591,44 @@
   }
 };
 
+
+/** Represents <overlay> elements. **/
+class Overlay : public ManifestExtractor::Element {
+ public:
+  Overlay() = default;
+  const std::string* target_package = nullptr;
+  int priority;
+  bool is_static;
+  const std::string* required_property_name = nullptr;
+  const std::string* required_property_value = nullptr;
+
+  void Extract(xml::Element* element) override {
+    target_package = GetAttributeString(FindAttribute(element, TARGET_PACKAGE_ATTR));
+    priority = GetAttributeIntegerDefault(FindAttribute(element, PRIORITY_ATTR), 0);
+    is_static = GetAttributeIntegerDefault(FindAttribute(element, IS_STATIC_ATTR), false) != 0;
+    required_property_name = GetAttributeString(
+        FindAttribute(element, REQUIRED_SYSTEM_PROPERTY_NAME_ATTR));
+    required_property_value = GetAttributeString(
+        FindAttribute(element, REQUIRED_SYSTEM_PROPERTY_VALUE_ATTR));
+  }
+
+  void Print(text::Printer* printer) override {
+    printer->Print(StringPrintf("overlay:"));
+    if (target_package) {
+      printer->Print(StringPrintf(" targetPackage='%s'", target_package->c_str()));
+    }
+    printer->Print(StringPrintf(" priority='%d'", priority));
+    printer->Print(StringPrintf(" isStatic='%s'", is_static ? "true" : "false"));
+    if (required_property_name) {
+      printer->Print(StringPrintf(" requiredPropertyName='%s'", required_property_name->c_str()));
+    }
+    if (required_property_value) {
+      printer->Print(StringPrintf(" requiredPropertyValue='%s'", required_property_value->c_str()));
+    }
+    printer->Print("\n");
+  }
+};
+
 /** * Represents <package-verifier> elements. **/
 class PackageVerifier : public ManifestExtractor::Element {
  public:
@@ -2166,6 +2209,7 @@
     {"meta-data", std::is_base_of<MetaData, T>::value},
     {"manifest", std::is_base_of<Manifest, T>::value},
     {"original-package", std::is_base_of<OriginalPackage, T>::value},
+    {"overlay", std::is_base_of<Overlay, T>::value},
     {"package-verifier", std::is_base_of<PackageVerifier, T>::value},
     {"permission", std::is_base_of<Permission, T>::value},
     {"provider", std::is_base_of<Provider, T>::value},
@@ -2215,6 +2259,7 @@
     {"manifest", &CreateType<Manifest>},
     {"meta-data", &CreateType<MetaData>},
     {"original-package", &CreateType<OriginalPackage>},
+    {"overlay", &CreateType<Overlay>},
     {"package-verifier", &CreateType<PackageVerifier>},
     {"permission", &CreateType<Permission>},
     {"provider", &CreateType<Provider>},
diff --git a/tools/aapt2/format/binary/BinaryResourceParser.cpp b/tools/aapt2/format/binary/BinaryResourceParser.cpp
index 61ebd4e..c496ff0 100644
--- a/tools/aapt2/format/binary/BinaryResourceParser.cpp
+++ b/tools/aapt2/format/binary/BinaryResourceParser.cpp
@@ -434,6 +434,8 @@
     return false;
   }
 
+  auto overlayable = std::make_shared<Overlayable>();
+
   ResChunkPullParser parser(GetChunkData(chunk),
                             GetChunkDataLen(chunk));
   while (ResChunkPullParser::IsGoodEvent(parser.Next())) {
@@ -441,25 +443,25 @@
       const ResTable_overlayable_policy_header* policy_header =
           ConvertTo<ResTable_overlayable_policy_header>(parser.chunk());
 
-      Overlayable::PolicyFlags policies = Overlayable::Policy::kNone;
+      OverlayableItem::PolicyFlags policies = OverlayableItem::Policy::kNone;
       if (policy_header->policy_flags & ResTable_overlayable_policy_header::POLICY_PUBLIC) {
-        policies |= Overlayable::Policy::kPublic;
+        policies |= OverlayableItem::Policy::kPublic;
       }
       if (policy_header->policy_flags
           & ResTable_overlayable_policy_header::POLICY_SYSTEM_PARTITION) {
-        policies |= Overlayable::Policy::kSystem;
+        policies |= OverlayableItem::Policy::kSystem;
       }
       if (policy_header->policy_flags
           & ResTable_overlayable_policy_header::POLICY_VENDOR_PARTITION) {
-        policies |= Overlayable::Policy::kVendor;
+        policies |= OverlayableItem::Policy::kVendor;
       }
       if (policy_header->policy_flags
           & ResTable_overlayable_policy_header::POLICY_PRODUCT_PARTITION) {
-        policies |= Overlayable::Policy::kProduct;
+        policies |= OverlayableItem::Policy::kProduct;
       }
       if (policy_header->policy_flags
           & ResTable_overlayable_policy_header::POLICY_PRODUCT_SERVICES_PARTITION) {
-        policies |= Overlayable::Policy::kProductServices;
+        policies |= OverlayableItem::Policy::kProductServices;
       }
 
       const ResTable_ref* const ref_begin = reinterpret_cast<const ResTable_ref*>(
@@ -478,10 +480,10 @@
           return false;
         }
 
-        Overlayable overlayable{};
-        overlayable.source = source_.WithLine(0);
-        overlayable.policies = policies;
-        if (!table_->SetOverlayable(iter->second, overlayable, diag_)) {
+        OverlayableItem overlayable_item(overlayable);
+        overlayable_item.source = source_.WithLine(0);
+        overlayable_item.policies = policies;
+        if (!table_->SetOverlayable(iter->second, overlayable_item, diag_)) {
           return false;
         }
       }
diff --git a/tools/aapt2/format/binary/TableFlattener.cpp b/tools/aapt2/format/binary/TableFlattener.cpp
index 200e2d4..931d57b 100644
--- a/tools/aapt2/format/binary/TableFlattener.cpp
+++ b/tools/aapt2/format/binary/TableFlattener.cpp
@@ -429,29 +429,29 @@
       CHECK(bool(type->id)) << "type must have an ID set when flattening <overlayable>";
       for (auto& entry : type->entries) {
         CHECK(bool(type->id)) << "entry must have an ID set when flattening <overlayable>";
-        if (!entry->overlayable) {
+        if (!entry->overlayable_item) {
           continue;
         }
 
-        Overlayable overlayable = entry->overlayable.value();
-        uint32_t policy_flags = Overlayable::Policy::kNone;
-        if (overlayable.policies & Overlayable::Policy::kPublic) {
+        OverlayableItem& overlayable = entry->overlayable_item.value();
+        uint32_t policy_flags = OverlayableItem::Policy::kNone;
+        if (overlayable.policies & OverlayableItem::Policy::kPublic) {
           policy_flags |= ResTable_overlayable_policy_header::POLICY_PUBLIC;
         }
-        if (overlayable.policies & Overlayable::Policy::kSystem) {
+        if (overlayable.policies & OverlayableItem::Policy::kSystem) {
           policy_flags |= ResTable_overlayable_policy_header::POLICY_SYSTEM_PARTITION;
         }
-        if (overlayable.policies & Overlayable::Policy::kVendor) {
+        if (overlayable.policies & OverlayableItem::Policy::kVendor) {
           policy_flags |= ResTable_overlayable_policy_header::POLICY_VENDOR_PARTITION;
         }
-        if (overlayable.policies & Overlayable::Policy::kProduct) {
+        if (overlayable.policies & OverlayableItem::Policy::kProduct) {
           policy_flags |= ResTable_overlayable_policy_header::POLICY_PRODUCT_PARTITION;
         }
-        if (overlayable.policies & Overlayable::Policy::kProductServices) {
+        if (overlayable.policies & OverlayableItem::Policy::kProductServices) {
           policy_flags |= ResTable_overlayable_policy_header::POLICY_PRODUCT_SERVICES_PARTITION;
         }
 
-        if (overlayable.policies == Overlayable::Policy::kNone) {
+        if (overlayable.policies == OverlayableItem::Policy::kNone) {
           // Encode overlayable entries defined without a policy as publicly overlayable
           policy_flags |= ResTable_overlayable_policy_header::POLICY_PUBLIC;
         }
diff --git a/tools/aapt2/format/binary/TableFlattener_test.cpp b/tools/aapt2/format/binary/TableFlattener_test.cpp
index e99ab1f..a5fb6fd 100644
--- a/tools/aapt2/format/binary/TableFlattener_test.cpp
+++ b/tools/aapt2/format/binary/TableFlattener_test.cpp
@@ -628,17 +628,17 @@
 }
 
 TEST_F(TableFlattenerTest, FlattenOverlayable) {
-  Overlayable overlayable{};
-  overlayable.policies |= Overlayable::Policy::kProduct;
-  overlayable.policies |= Overlayable::Policy::kSystem;
-  overlayable.policies |= Overlayable::Policy::kVendor;
+  OverlayableItem overlayable_item(std::make_shared<Overlayable>("TestName", "overlay://theme"));
+  overlayable_item.policies |= OverlayableItem::Policy::kProduct;
+  overlayable_item.policies |= OverlayableItem::Policy::kSystem;
+  overlayable_item.policies |= OverlayableItem::Policy::kVendor;
 
   std::string name = "com.app.test:integer/overlayable";
   std::unique_ptr<ResourceTable> table =
       test::ResourceTableBuilder()
           .SetPackageId("com.app.test", 0x7f)
           .AddSimple(name, ResourceId(0x7f020000))
-          .SetOverlayable(name, overlayable)
+          .SetOverlayable(name, overlayable_item)
           .Build();
 
   ResourceTable output_table;
@@ -647,45 +647,46 @@
   auto search_result = output_table.FindResource(test::ParseNameOrDie(name));
   ASSERT_TRUE(search_result);
   ASSERT_THAT(search_result.value().entry, NotNull());
-  ASSERT_TRUE(search_result.value().entry->overlayable);
-  Overlayable& result_overlayable = search_result.value().entry->overlayable.value();
-  EXPECT_EQ(result_overlayable.policies, Overlayable::Policy::kSystem
-                                         | Overlayable::Policy::kVendor
-                                         | Overlayable::Policy::kProduct);
+  ASSERT_TRUE(search_result.value().entry->overlayable_item);
+  OverlayableItem& result_overlayable_item = search_result.value().entry->overlayable_item.value();
+  EXPECT_EQ(result_overlayable_item.policies, OverlayableItem::Policy::kSystem
+                                         | OverlayableItem::Policy::kVendor
+                                         | OverlayableItem::Policy::kProduct);
 }
 
 TEST_F(TableFlattenerTest, FlattenMultipleOverlayablePolicies) {
-  std::string name_zero = "com.app.test:integer/overlayable_zero";
-  Overlayable overlayable_zero{};
-  overlayable_zero.policies |= Overlayable::Policy::kProduct;
-  overlayable_zero.policies |= Overlayable::Policy::kSystem;
-  overlayable_zero.policies |= Overlayable::Policy::kProductServices;
+  auto overlayable = std::make_shared<Overlayable>("TestName", "overlay://theme");
+  std::string name_zero = "com.app.test:integer/overlayable_zero_item";
+  OverlayableItem overlayable_zero_item(overlayable);
+  overlayable_zero_item.policies |= OverlayableItem::Policy::kProduct;
+  overlayable_zero_item.policies |= OverlayableItem::Policy::kSystem;
+  overlayable_zero_item.policies |= OverlayableItem::Policy::kProductServices;
 
-  std::string name_one = "com.app.test:integer/overlayable_one";
-  Overlayable overlayable_one{};
-  overlayable_one.policies |= Overlayable::Policy::kPublic;
-  overlayable_one.policies |= Overlayable::Policy::kProductServices;
+  std::string name_one = "com.app.test:integer/overlayable_one_item";
+  OverlayableItem overlayable_one_item(overlayable);
+  overlayable_one_item.policies |= OverlayableItem::Policy::kPublic;
+  overlayable_one_item.policies |= OverlayableItem::Policy::kProductServices;
 
-  std::string name_two = "com.app.test:integer/overlayable_two";
-  Overlayable overlayable_two{};
-  overlayable_two.policies |= Overlayable::Policy::kProduct;
-  overlayable_two.policies |= Overlayable::Policy::kSystem;
-  overlayable_two.policies |= Overlayable::Policy::kVendor;
+  std::string name_two = "com.app.test:integer/overlayable_two_item";
+  OverlayableItem overlayable_two_item(overlayable);
+  overlayable_two_item.policies |= OverlayableItem::Policy::kProduct;
+  overlayable_two_item.policies |= OverlayableItem::Policy::kSystem;
+  overlayable_two_item.policies |= OverlayableItem::Policy::kVendor;
 
-  std::string name_three = "com.app.test:integer/overlayable_three";
-  Overlayable overlayable_three{};
+  std::string name_three = "com.app.test:integer/overlayable_three_item";
+  OverlayableItem overlayable_three_item(overlayable);
 
   std::unique_ptr<ResourceTable> table =
       test::ResourceTableBuilder()
           .SetPackageId("com.app.test", 0x7f)
           .AddSimple(name_zero, ResourceId(0x7f020000))
-          .SetOverlayable(name_zero, overlayable_zero)
+          .SetOverlayable(name_zero, overlayable_zero_item)
           .AddSimple(name_one, ResourceId(0x7f020001))
-          .SetOverlayable(name_one, overlayable_one)
+          .SetOverlayable(name_one, overlayable_one_item)
           .AddSimple(name_two, ResourceId(0x7f020002))
-          .SetOverlayable(name_two, overlayable_two)
+          .SetOverlayable(name_two, overlayable_two_item)
           .AddSimple(name_three, ResourceId(0x7f020003))
-          .SetOverlayable(name_three, overlayable_three)
+          .SetOverlayable(name_three, overlayable_three_item)
           .Build();
 
   ResourceTable output_table;
@@ -694,35 +695,35 @@
   auto search_result = output_table.FindResource(test::ParseNameOrDie(name_zero));
   ASSERT_TRUE(search_result);
   ASSERT_THAT(search_result.value().entry, NotNull());
-  ASSERT_TRUE(search_result.value().entry->overlayable);
-  Overlayable& result_overlayable = search_result.value().entry->overlayable.value();
-  EXPECT_EQ(result_overlayable.policies, Overlayable::Policy::kSystem
-                                         | Overlayable::Policy::kProduct
-                                         | Overlayable::Policy::kProductServices);
+  ASSERT_TRUE(search_result.value().entry->overlayable_item);
+  OverlayableItem& overlayable_item = search_result.value().entry->overlayable_item.value();
+  EXPECT_EQ(overlayable_item.policies, OverlayableItem::Policy::kSystem
+                                       | OverlayableItem::Policy::kProduct
+                                       | OverlayableItem::Policy::kProductServices);
 
   search_result = output_table.FindResource(test::ParseNameOrDie(name_one));
   ASSERT_TRUE(search_result);
   ASSERT_THAT(search_result.value().entry, NotNull());
-  ASSERT_TRUE(search_result.value().entry->overlayable);
-  result_overlayable = search_result.value().entry->overlayable.value();
-  EXPECT_EQ(result_overlayable.policies, Overlayable::Policy::kPublic
-                                         | Overlayable::Policy::kProductServices);
+  ASSERT_TRUE(search_result.value().entry->overlayable_item);
+  overlayable_item = search_result.value().entry->overlayable_item.value();
+  EXPECT_EQ(overlayable_item.policies, OverlayableItem::Policy::kPublic
+                                       | OverlayableItem::Policy::kProductServices);
 
   search_result = output_table.FindResource(test::ParseNameOrDie(name_two));
   ASSERT_TRUE(search_result);
   ASSERT_THAT(search_result.value().entry, NotNull());
-  ASSERT_TRUE(search_result.value().entry->overlayable);
-  result_overlayable = search_result.value().entry->overlayable.value();
-  EXPECT_EQ(result_overlayable.policies, Overlayable::Policy::kSystem
-                                         | Overlayable::Policy::kProduct
-                                         | Overlayable::Policy::kVendor);
+  ASSERT_TRUE(search_result.value().entry->overlayable_item);
+  overlayable_item = search_result.value().entry->overlayable_item.value();
+  EXPECT_EQ(overlayable_item.policies, OverlayableItem::Policy::kSystem
+                                       | OverlayableItem::Policy::kProduct
+                                       | OverlayableItem::Policy::kVendor);
 
   search_result = output_table.FindResource(test::ParseNameOrDie(name_three));
   ASSERT_TRUE(search_result);
   ASSERT_THAT(search_result.value().entry, NotNull());
-  ASSERT_TRUE(search_result.value().entry->overlayable);
-  result_overlayable = search_result.value().entry->overlayable.value();
-  EXPECT_EQ(result_overlayable.policies, Overlayable::Policy::kPublic);
+  ASSERT_TRUE(search_result.value().entry->overlayable_item);
+  overlayable_item = search_result.value().entry->overlayable_item.value();
+  EXPECT_EQ(overlayable_item.policies, OverlayableItem::Policy::kPublic);
 }
 
 }  // namespace aapt
diff --git a/tools/aapt2/format/proto/ProtoDeserialize.cpp b/tools/aapt2/format/proto/ProtoDeserialize.cpp
index cf2ab0f..6b5746d 100644
--- a/tools/aapt2/format/proto/ProtoDeserialize.cpp
+++ b/tools/aapt2/format/proto/ProtoDeserialize.cpp
@@ -373,9 +373,44 @@
   return Visibility::Level::kUndefined;
 }
 
+bool DeserializeOverlayableItemFromPb(const pb::OverlayableItem& pb_overlayable,
+                                      const android::ResStringPool& src_pool,
+                                      OverlayableItem* out_overlayable, std::string* out_error) {
+  for (const int policy : pb_overlayable.policy()) {
+    switch (policy) {
+      case pb::OverlayableItem::PUBLIC:
+        out_overlayable->policies |= OverlayableItem::Policy::kPublic;
+        break;
+      case pb::OverlayableItem::SYSTEM:
+        out_overlayable->policies |= OverlayableItem::Policy::kSystem;
+        break;
+      case pb::OverlayableItem::VENDOR:
+        out_overlayable->policies |= OverlayableItem::Policy::kVendor;
+        break;
+      case pb::OverlayableItem::PRODUCT:
+        out_overlayable->policies |= OverlayableItem::Policy::kProduct;
+        break;
+      case pb::OverlayableItem::PRODUCT_SERVICES:
+        out_overlayable->policies |= OverlayableItem::Policy::kProductServices;
+        break;
+      default:
+        *out_error = "unknown overlayable policy";
+        return false;
+    }
+  }
+
+  if (pb_overlayable.has_source()) {
+    DeserializeSourceFromPb(pb_overlayable.source(), src_pool, &out_overlayable->source);
+  }
+
+  out_overlayable->comment = pb_overlayable.comment();
+  return true;
+}
+
 static bool DeserializePackageFromPb(const pb::Package& pb_package, const ResStringPool& src_pool,
-                                     io::IFileCollection* files, ResourceTable* out_table,
-                                     std::string* out_error) {
+                                     io::IFileCollection* files,
+                                     const std::vector<std::shared_ptr<Overlayable>>& overlayables,
+                                     ResourceTable* out_table, std::string* out_error) {
   Maybe<uint8_t> id;
   if (pb_package.has_package_id()) {
     id = static_cast<uint8_t>(pb_package.package_id().id());
@@ -437,39 +472,22 @@
         entry->allow_new = std::move(allow_new);
       }
 
-      if (pb_entry.has_overlayable()) {
-        Overlayable overlayable{};
-
-        const pb::Overlayable& pb_overlayable = pb_entry.overlayable();
-        for (const int policy : pb_overlayable.policy()) {
-          switch (policy) {
-            case pb::Overlayable::PUBLIC:
-              overlayable.policies |= Overlayable::Policy::kPublic;
-              break;
-            case pb::Overlayable::SYSTEM:
-              overlayable.policies |= Overlayable::Policy::kSystem;
-              break;
-            case pb::Overlayable::VENDOR:
-              overlayable.policies |= Overlayable::Policy::kVendor;
-              break;
-            case pb::Overlayable::PRODUCT:
-              overlayable.policies |= Overlayable::Policy::kProduct;
-              break;
-            case pb::Overlayable::PRODUCT_SERVICES:
-              overlayable.policies |= Overlayable::Policy::kProductServices;
-              break;
-            default:
-              *out_error = "unknown overlayable policy";
-              return false;
-          }
+      if (pb_entry.has_overlayable_item()) {
+        // Find the overlayable to which this item belongs
+        pb::OverlayableItem pb_overlayable_item = pb_entry.overlayable_item();
+        if (pb_overlayable_item.overlayable_idx() >= overlayables.size()) {
+          *out_error = android::base::StringPrintf("invalid overlayable_idx value %d",
+                                                   pb_overlayable_item.overlayable_idx());
+          return false;
         }
 
-        if (pb_overlayable.has_source()) {
-          DeserializeSourceFromPb(pb_overlayable.source(), src_pool, &overlayable.source);
+        OverlayableItem overlayable_item(overlayables[pb_overlayable_item.overlayable_idx()]);
+        if (!DeserializeOverlayableItemFromPb(pb_overlayable_item, src_pool, &overlayable_item,
+                                              out_error)) {
+          return false;
         }
 
-        overlayable.comment = pb_overlayable.comment();
-        entry->overlayable = overlayable;
+        entry->overlayable_item = std::move(overlayable_item);
       }
 
       ResourceId resid(pb_package.package_id().id(), pb_type.type_id().id(),
@@ -522,8 +540,19 @@
     }
   }
 
+  // Deserialize the overlayable groups of the table
+  std::vector<std::shared_ptr<Overlayable>> overlayables;
+  for (const pb::Overlayable& pb_overlayable : pb_table.overlayable()) {
+    auto group = std::make_shared<Overlayable>(pb_overlayable.name(), pb_overlayable.actor());
+    if (pb_overlayable.has_source()) {
+      DeserializeSourceFromPb(pb_overlayable.source(), source_pool, &group->source);
+    }
+    overlayables.push_back(group);
+  }
+
   for (const pb::Package& pb_package : pb_table.package()) {
-    if (!DeserializePackageFromPb(pb_package, source_pool, files, out_table, out_error)) {
+    if (!DeserializePackageFromPb(pb_package, source_pool, files, overlayables, out_table,
+                                  out_error)) {
       return false;
     }
   }
diff --git a/tools/aapt2/format/proto/ProtoSerialize.cpp b/tools/aapt2/format/proto/ProtoSerialize.cpp
index 70bf868..76fbb46 100644
--- a/tools/aapt2/format/proto/ProtoSerialize.cpp
+++ b/tools/aapt2/format/proto/ProtoSerialize.cpp
@@ -272,9 +272,57 @@
   out_pb_config->set_sdk_version(config.sdkVersion);
 }
 
+static void SerializeOverlayableItemToPb(const OverlayableItem& overlayable_item,
+                                         std::vector<Overlayable*>& serialized_overlayables,
+                                         StringPool* source_pool, pb::Entry* pb_entry,
+                                         pb::ResourceTable* pb_table) {
+  // Retrieve the index of the overlayable in the list of groups that have already been serialized.
+  size_t i;
+  for (i = 0 ; i < serialized_overlayables.size(); i++) {
+    if (overlayable_item.overlayable.get() == serialized_overlayables[i]) {
+      break;
+    }
+  }
+
+  // Serialize the overlayable if it has not been serialized already.
+  if (i == serialized_overlayables.size()) {
+    serialized_overlayables.push_back(overlayable_item.overlayable.get());
+    pb::Overlayable* pb_overlayable = pb_table->add_overlayable();
+    pb_overlayable->set_name(overlayable_item.overlayable->name);
+    pb_overlayable->set_actor(overlayable_item.overlayable->actor);
+    SerializeSourceToPb(overlayable_item.overlayable->source, source_pool,
+                        pb_overlayable->mutable_source());
+  }
+
+  pb::OverlayableItem* pb_overlayable_item = pb_entry->mutable_overlayable_item();
+  pb_overlayable_item->set_overlayable_idx(i);
+
+  if (overlayable_item.policies & OverlayableItem::Policy::kPublic) {
+    pb_overlayable_item->add_policy(pb::OverlayableItem::PUBLIC);
+  }
+  if (overlayable_item.policies & OverlayableItem::Policy::kProduct) {
+    pb_overlayable_item->add_policy(pb::OverlayableItem::PRODUCT);
+  }
+  if (overlayable_item.policies & OverlayableItem::Policy::kProductServices) {
+    pb_overlayable_item->add_policy(pb::OverlayableItem::PRODUCT_SERVICES);
+  }
+  if (overlayable_item.policies & OverlayableItem::Policy::kSystem) {
+    pb_overlayable_item->add_policy(pb::OverlayableItem::SYSTEM);
+  }
+  if (overlayable_item.policies & OverlayableItem::Policy::kVendor) {
+    pb_overlayable_item->add_policy(pb::OverlayableItem::VENDOR);
+  }
+
+  SerializeSourceToPb(overlayable_item.source, source_pool,
+                      pb_overlayable_item->mutable_source());
+  pb_overlayable_item->set_comment(overlayable_item.comment);
+}
+
 void SerializeTableToPb(const ResourceTable& table, pb::ResourceTable* out_table,
                         IDiagnostics* diag) {
   StringPool source_pool;
+
+  std::vector<Overlayable*> overlayables;
   for (const std::unique_ptr<ResourceTablePackage>& package : table.packages) {
     pb::Package* pb_package = out_table->add_package();
     if (package->id) {
@@ -310,29 +358,9 @@
           pb_allow_new->set_comment(entry->allow_new.value().comment);
         }
 
-        if (entry->overlayable) {
-          pb::Overlayable* pb_overlayable = pb_entry->mutable_overlayable();
-
-          Overlayable overlayable = entry->overlayable.value();
-          if (overlayable.policies & Overlayable::Policy::kPublic) {
-            pb_overlayable->add_policy(pb::Overlayable::PUBLIC);
-          }
-          if (overlayable.policies & Overlayable::Policy::kProduct) {
-            pb_overlayable->add_policy(pb::Overlayable::PRODUCT);
-          }
-          if (overlayable.policies & Overlayable::Policy::kProductServices) {
-            pb_overlayable->add_policy(pb::Overlayable::PRODUCT_SERVICES);
-          }
-          if (overlayable.policies & Overlayable::Policy::kSystem) {
-            pb_overlayable->add_policy(pb::Overlayable::SYSTEM);
-          }
-          if (overlayable.policies & Overlayable::Policy::kVendor) {
-            pb_overlayable->add_policy(pb::Overlayable::VENDOR);
-          }
-
-          SerializeSourceToPb(overlayable.source, &source_pool,
-                              pb_overlayable->mutable_source());
-          pb_overlayable->set_comment(overlayable.comment);
+        if (entry->overlayable_item) {
+          SerializeOverlayableItemToPb(entry->overlayable_item.value(), overlayables, &source_pool,
+                                       pb_entry, out_table);
         }
 
         for (const std::unique_ptr<ResourceConfigValue>& config_value : entry->values) {
diff --git a/tools/aapt2/format/proto/ProtoSerialize_test.cpp b/tools/aapt2/format/proto/ProtoSerialize_test.cpp
index fb913f40..4a3c1b8 100644
--- a/tools/aapt2/format/proto/ProtoSerialize_test.cpp
+++ b/tools/aapt2/format/proto/ProtoSerialize_test.cpp
@@ -93,8 +93,11 @@
       util::make_unique<Reference>(expected_ref), context->GetDiagnostics()));
 
   // Make an overlayable resource.
+  OverlayableItem overlayable_item(std::make_shared<Overlayable>(
+      "OverlayableName", "overlay://theme", Source("res/values/overlayable.xml", 40)));
+  overlayable_item.source = Source("res/values/overlayable.xml", 42);
   ASSERT_TRUE(table->SetOverlayable(test::ParseNameOrDie("com.app.a:integer/overlayable"),
-                                    Overlayable{}, test::GetDiagnostics()));
+                                    overlayable_item, test::GetDiagnostics()));
 
   pb::ResourceTable pb_table;
   SerializeTableToPb(*table, &pb_table, context->GetDiagnostics());
@@ -160,9 +163,15 @@
       new_table.FindResource(test::ParseNameOrDie("com.app.a:integer/overlayable"));
   ASSERT_TRUE(search_result);
   ASSERT_THAT(search_result.value().entry, NotNull());
-  ASSERT_TRUE(search_result.value().entry->overlayable);
-  EXPECT_THAT(search_result.value().entry->overlayable.value().policies,
-              Eq(Overlayable::Policy::kNone));
+  ASSERT_TRUE(search_result.value().entry->overlayable_item);
+  OverlayableItem& result_overlayable_item = search_result.value().entry->overlayable_item.value();
+  EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("OverlayableName"));
+  EXPECT_THAT(result_overlayable_item.overlayable->actor, Eq("overlay://theme"));
+  EXPECT_THAT(result_overlayable_item.overlayable->source.path, Eq("res/values/overlayable.xml"));
+  EXPECT_THAT(result_overlayable_item.overlayable->source.line, Eq(40));
+  EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kNone));
+  EXPECT_THAT(result_overlayable_item.source.path, Eq("res/values/overlayable.xml"));
+  EXPECT_THAT(result_overlayable_item.source.line, Eq(42));
 }
 
 TEST(ProtoSerializeTest, SerializeAndDeserializeXml) {
@@ -503,26 +512,31 @@
 }
 
 TEST(ProtoSerializeTest, SerializeAndDeserializeOverlayable) {
-  Overlayable overlayable_foo{};
-  overlayable_foo.policies |= Overlayable::Policy::kSystem;
-  overlayable_foo.policies |= Overlayable::Policy::kProduct;
+  OverlayableItem overlayable_item_foo(std::make_shared<Overlayable>(
+      "CustomizableResources", "overlay://customization"));
+  overlayable_item_foo.policies |= OverlayableItem::Policy::kSystem;
+  overlayable_item_foo.policies |= OverlayableItem::Policy::kProduct;
 
-  Overlayable overlayable_bar{};
-  overlayable_bar.policies |= Overlayable::Policy::kProductServices;
-  overlayable_bar.policies |= Overlayable::Policy::kVendor;
+  OverlayableItem overlayable_item_bar(std::make_shared<Overlayable>(
+      "TaskBar", "overlay://theme"));
+  overlayable_item_bar.policies |= OverlayableItem::Policy::kProductServices;
+  overlayable_item_bar.policies |= OverlayableItem::Policy::kVendor;
 
-  Overlayable overlayable_baz{};
-  overlayable_baz.policies |= Overlayable::Policy::kPublic;
+  OverlayableItem overlayable_item_baz(std::make_shared<Overlayable>(
+      "FontPack", "overlay://theme"));
+  overlayable_item_baz.policies |= OverlayableItem::Policy::kPublic;
 
-  Overlayable overlayable_biz{};
+  OverlayableItem overlayable_item_biz(std::make_shared<Overlayable>(
+      "Other", "overlay://customization"));
+  overlayable_item_biz.comment ="comment";
 
   std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
   std::unique_ptr<ResourceTable> table =
       test::ResourceTableBuilder()
-          .SetOverlayable("com.app.a:bool/foo", overlayable_foo)
-          .SetOverlayable("com.app.a:bool/bar", overlayable_bar)
-          .SetOverlayable("com.app.a:bool/baz", overlayable_baz)
-          .SetOverlayable("com.app.a:bool/biz", overlayable_biz)
+          .SetOverlayable("com.app.a:bool/foo", overlayable_item_foo)
+          .SetOverlayable("com.app.a:bool/bar", overlayable_item_bar)
+          .SetOverlayable("com.app.a:bool/baz", overlayable_item_baz)
+          .SetOverlayable("com.app.a:bool/biz", overlayable_item_biz)
           .AddValue("com.app.a:bool/fiz", ResourceUtils::TryParseBool("true"))
           .Build();
 
@@ -538,33 +552,41 @@
   Maybe<ResourceTable::SearchResult> search_result =
       new_table.FindResource(test::ParseNameOrDie("com.app.a:bool/foo"));
   ASSERT_TRUE(search_result);
-  ASSERT_TRUE(search_result.value().entry->overlayable);
-  Overlayable result_overlayable = search_result.value().entry->overlayable.value();
-  EXPECT_THAT(result_overlayable.policies, Eq(Overlayable::Policy::kSystem
-                                              | Overlayable::Policy::kProduct));
+  ASSERT_TRUE(search_result.value().entry->overlayable_item);
+  OverlayableItem& overlayable_item = search_result.value().entry->overlayable_item.value();
+  EXPECT_THAT(overlayable_item.overlayable->name, Eq("CustomizableResources"));
+  EXPECT_THAT(overlayable_item.overlayable->actor, Eq("overlay://customization"));
+  EXPECT_THAT(overlayable_item.policies, Eq(OverlayableItem::Policy::kSystem
+                                              | OverlayableItem::Policy::kProduct));
 
   search_result = new_table.FindResource(test::ParseNameOrDie("com.app.a:bool/bar"));
   ASSERT_TRUE(search_result);
-  ASSERT_TRUE(search_result.value().entry->overlayable);
-  result_overlayable = search_result.value().entry->overlayable.value();
-  EXPECT_THAT(result_overlayable.policies, Eq(Overlayable::Policy::kProductServices
-                                              | Overlayable::Policy::kVendor));
+  ASSERT_TRUE(search_result.value().entry->overlayable_item);
+  overlayable_item = search_result.value().entry->overlayable_item.value();
+  EXPECT_THAT(overlayable_item.overlayable->name, Eq("TaskBar"));
+  EXPECT_THAT(overlayable_item.overlayable->actor, Eq("overlay://theme"));
+  EXPECT_THAT(overlayable_item.policies, Eq(OverlayableItem::Policy::kProductServices
+                                              | OverlayableItem::Policy::kVendor));
 
   search_result = new_table.FindResource(test::ParseNameOrDie("com.app.a:bool/baz"));
   ASSERT_TRUE(search_result);
-  ASSERT_TRUE(search_result.value().entry->overlayable);
-  result_overlayable = search_result.value().entry->overlayable.value();
-  EXPECT_THAT(result_overlayable.policies, Overlayable::Policy::kPublic);
+  ASSERT_TRUE(search_result.value().entry->overlayable_item);
+  overlayable_item = search_result.value().entry->overlayable_item.value();
+  EXPECT_THAT(overlayable_item.overlayable->name, Eq("FontPack"));
+  EXPECT_THAT(overlayable_item.overlayable->actor, Eq("overlay://theme"));
+  EXPECT_THAT(overlayable_item.policies, Eq(OverlayableItem::Policy::kPublic));
 
   search_result = new_table.FindResource(test::ParseNameOrDie("com.app.a:bool/biz"));
   ASSERT_TRUE(search_result);
-  ASSERT_TRUE(search_result.value().entry->overlayable);
-  result_overlayable = search_result.value().entry->overlayable.value();
-  EXPECT_THAT(result_overlayable.policies, Overlayable::Policy::kNone);
+  ASSERT_TRUE(search_result.value().entry->overlayable_item);
+  overlayable_item = search_result.value().entry->overlayable_item.value();
+  EXPECT_THAT(overlayable_item.overlayable->name, Eq("Other"));
+  EXPECT_THAT(overlayable_item.policies, Eq(OverlayableItem::Policy::kNone));
+  EXPECT_THAT(overlayable_item.comment, Eq("comment"));
 
   search_result = new_table.FindResource(test::ParseNameOrDie("com.app.a:bool/fiz"));
   ASSERT_TRUE(search_result);
-  ASSERT_FALSE(search_result.value().entry->overlayable);
+  ASSERT_FALSE(search_result.value().entry->overlayable_item);
 }
 
 }  // namespace aapt
diff --git a/tools/aapt2/link/ReferenceLinker.cpp b/tools/aapt2/link/ReferenceLinker.cpp
index 8cbc037..c2340ba 100644
--- a/tools/aapt2/link/ReferenceLinker.cpp
+++ b/tools/aapt2/link/ReferenceLinker.cpp
@@ -374,8 +374,8 @@
         }
 
         // Ensure that definitions for values declared as overlayable exist
-        if (entry->overlayable && entry->values.empty()) {
-          context->GetDiagnostics()->Error(DiagMessage(entry->overlayable.value().source)
+        if (entry->overlayable_item && entry->values.empty()) {
+          context->GetDiagnostics()->Error(DiagMessage(entry->overlayable_item.value().source)
                                            << "no definition for overlayable symbol '"
                                            << name << "'");
           error = true;
diff --git a/tools/aapt2/link/TableMerger.cpp b/tools/aapt2/link/TableMerger.cpp
index 22e1723..cc9fed5 100644
--- a/tools/aapt2/link/TableMerger.cpp
+++ b/tools/aapt2/link/TableMerger.cpp
@@ -134,18 +134,18 @@
     dst_entry->allow_new = std::move(src_entry->allow_new);
   }
 
-  if (src_entry->overlayable) {
-    if (dst_entry->overlayable) {
+  if (src_entry->overlayable_item) {
+    if (dst_entry->overlayable_item) {
       // Do not allow a resource with an overlayable declaration to have that overlayable
       // declaration redefined
-      context->GetDiagnostics()->Error(DiagMessage(src_entry->overlayable.value().source)
+      context->GetDiagnostics()->Error(DiagMessage(src_entry->overlayable_item.value().source)
                                        << "duplicate overlayable declaration for resource '"
                                        << src_entry->name << "'");
-      context->GetDiagnostics()->Error(DiagMessage(dst_entry->overlayable.value().source)
+      context->GetDiagnostics()->Error(DiagMessage(dst_entry->overlayable_item.value().source)
                                        << "previous declaration here");
       return false;
     } else {
-      dst_entry->overlayable = std::move(src_entry->overlayable);
+      dst_entry->overlayable_item = std::move(src_entry->overlayable_item);
     }
   }
 
diff --git a/tools/aapt2/link/TableMerger_test.cpp b/tools/aapt2/link/TableMerger_test.cpp
index 17b2a83..921d634 100644
--- a/tools/aapt2/link/TableMerger_test.cpp
+++ b/tools/aapt2/link/TableMerger_test.cpp
@@ -437,14 +437,16 @@
 }
 
 TEST_F(TableMergerTest, SetOverlayable) {
-  Overlayable overlayable{};
-  overlayable.policies |= Overlayable::Policy::kProduct;
-  overlayable.policies |= Overlayable::Policy::kVendor;
+  auto overlayable = std::make_shared<Overlayable>("CustomizableResources",
+                                                  "overlay://customization");
+  OverlayableItem overlayable_item(overlayable);
+  overlayable_item.policies |= OverlayableItem::Policy::kProduct;
+  overlayable_item.policies |= OverlayableItem::Policy::kVendor;
 
   std::unique_ptr<ResourceTable> table_a =
       test::ResourceTableBuilder()
           .SetPackageId("com.app.a", 0x7f)
-          .SetOverlayable("bool/foo", overlayable)
+          .SetOverlayable("bool/foo", overlayable_item)
           .Build();
 
   std::unique_ptr<ResourceTable> table_b =
@@ -463,26 +465,30 @@
   const ResourceName name = test::ParseNameOrDie("com.app.a:bool/foo");
   Maybe<ResourceTable::SearchResult> search_result = final_table.FindResource(name);
   ASSERT_TRUE(search_result);
-  ASSERT_TRUE(search_result.value().entry->overlayable);
-  Overlayable& result_overlayable = search_result.value().entry->overlayable.value();
-  EXPECT_THAT(result_overlayable.policies, Eq(Overlayable::Policy::kProduct
-                                              | Overlayable::Policy::kVendor));
+  ASSERT_TRUE(search_result.value().entry->overlayable_item);
+  OverlayableItem& result_overlayable_item = search_result.value().entry->overlayable_item.value();
+  EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("CustomizableResources"));
+  EXPECT_THAT(result_overlayable_item.overlayable->actor, Eq("overlay://customization"));
+  EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kProduct
+                                                   | OverlayableItem::Policy::kVendor));
 }
 
 TEST_F(TableMergerTest, SetOverlayableLater) {
+  auto overlayable = std::make_shared<Overlayable>("CustomizableResources",
+                                                  "overlay://customization");
   std::unique_ptr<ResourceTable> table_a =
       test::ResourceTableBuilder()
           .SetPackageId("com.app.a", 0x7f)
           .AddSimple("bool/foo")
           .Build();
 
-  Overlayable overlayable{};
-  overlayable.policies |= Overlayable::Policy::kPublic;
-  overlayable.policies |= Overlayable::Policy::kProductServices;
+  OverlayableItem overlayable_item(overlayable);
+  overlayable_item.policies |= OverlayableItem::Policy::kPublic;
+  overlayable_item.policies |= OverlayableItem::Policy::kProductServices;
   std::unique_ptr<ResourceTable> table_b =
       test::ResourceTableBuilder()
           .SetPackageId("com.app.a", 0x7f)
-          .SetOverlayable("bool/foo", overlayable)
+          .SetOverlayable("bool/foo", overlayable_item)
           .Build();
 
   ResourceTable final_table;
@@ -495,27 +501,33 @@
   const ResourceName name = test::ParseNameOrDie("com.app.a:bool/foo");
   Maybe<ResourceTable::SearchResult> search_result = final_table.FindResource(name);
   ASSERT_TRUE(search_result);
-  ASSERT_TRUE(search_result.value().entry->overlayable);
-  Overlayable& result_overlayable = search_result.value().entry->overlayable.value();
-  EXPECT_THAT(result_overlayable.policies, Eq(Overlayable::Policy::kPublic
-                                              | Overlayable::Policy::kProductServices));
+  ASSERT_TRUE(search_result.value().entry->overlayable_item);
+  OverlayableItem& result_overlayable_item = search_result.value().entry->overlayable_item.value();
+  EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("CustomizableResources"));
+  EXPECT_THAT(result_overlayable_item.overlayable->actor, Eq("overlay://customization"));
+  EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kPublic
+                                                   | OverlayableItem::Policy::kProductServices));
 }
 
-TEST_F(TableMergerTest, SetOverlayableSamePolicesFail) {
-  Overlayable overlayable_first{};
-  overlayable_first.policies |= Overlayable::Policy::kProduct;
+TEST_F(TableMergerTest, SameResourceDifferentNameFail) {
+  auto overlayable_first = std::make_shared<Overlayable>("CustomizableResources",
+                                                         "overlay://customization");
+  OverlayableItem overlayable_item_first(overlayable_first);
+  overlayable_item_first.policies |= OverlayableItem::Policy::kProduct;
   std::unique_ptr<ResourceTable> table_a =
       test::ResourceTableBuilder()
           .SetPackageId("com.app.a", 0x7f)
-          .SetOverlayable("bool/foo", overlayable_first)
+          .SetOverlayable("bool/foo", overlayable_item_first)
           .Build();
 
-  Overlayable overlayable_second{};
-  overlayable_second.policies |= Overlayable::Policy::kProduct;
+  auto overlayable_second = std::make_shared<Overlayable>("ThemeResources",
+                                                          "overlay://theme");
+  OverlayableItem overlayable_item_second(overlayable_second);
+  overlayable_item_second.policies |= OverlayableItem::Policy::kProduct;
   std::unique_ptr<ResourceTable> table_b =
       test::ResourceTableBuilder()
           .SetPackageId("com.app.a", 0x7f)
-          .SetOverlayable("bool/foo", overlayable_second)
+          .SetOverlayable("bool/foo", overlayable_item_second)
           .Build();
 
   ResourceTable final_table;
@@ -526,21 +538,24 @@
   ASSERT_FALSE(merger.Merge({}, table_b.get(), false /*overlay*/));
 }
 
-TEST_F(TableMergerTest, SetOverlayableDifferentPolicesFail) {
-  Overlayable overlayable_first{};
-  overlayable_first.policies |= Overlayable::Policy::kVendor;
+TEST_F(TableMergerTest, SameResourceSameNameFail) {
+  auto overlayable = std::make_shared<Overlayable>("CustomizableResources",
+                                                  "overlay://customization");
+
+  OverlayableItem overlayable_item_first(overlayable);
+  overlayable_item_first.policies |= OverlayableItem::Policy::kProduct;
   std::unique_ptr<ResourceTable> table_a =
       test::ResourceTableBuilder()
           .SetPackageId("com.app.a", 0x7f)
-          .SetOverlayable("bool/foo",overlayable_first)
+          .SetOverlayable("bool/foo", overlayable_item_first)
           .Build();
 
-  Overlayable overlayable_second{};
-  overlayable_second.policies |= Overlayable::Policy::kProduct;
+  OverlayableItem overlayable_item_second(overlayable);
+  overlayable_item_second.policies |= OverlayableItem::Policy::kSystem;
   std::unique_ptr<ResourceTable> table_b =
       test::ResourceTableBuilder()
           .SetPackageId("com.app.a", 0x7f)
-          .SetOverlayable("bool/foo", overlayable_second)
+          .SetOverlayable("bool/foo", overlayable_item_second)
           .Build();
 
   ResourceTable final_table;
diff --git a/tools/aapt2/split/TableSplitter.cpp b/tools/aapt2/split/TableSplitter.cpp
index 9c5b5d3..24cd5ba 100644
--- a/tools/aapt2/split/TableSplitter.cpp
+++ b/tools/aapt2/split/TableSplitter.cpp
@@ -248,7 +248,7 @@
             if (!split_entry->id) {
               split_entry->id = entry->id;
               split_entry->visibility = entry->visibility;
-              split_entry->overlayable = entry->overlayable;
+              split_entry->overlayable_item = entry->overlayable_item;
             }
 
             // Copy the selected values into the new Split Entry.
diff --git a/tools/aapt2/test/Builders.cpp b/tools/aapt2/test/Builders.cpp
index 884ec38..9a93f2a 100644
--- a/tools/aapt2/test/Builders.cpp
+++ b/tools/aapt2/test/Builders.cpp
@@ -136,7 +136,7 @@
 }
 
 ResourceTableBuilder& ResourceTableBuilder::SetOverlayable(const StringPiece& name,
-                                                           const Overlayable& overlayable) {
+                                                           const OverlayableItem& overlayable) {
 
   ResourceName res_name = ParseNameOrDie(name);
   CHECK(table_->SetOverlayable(res_name, overlayable, GetDiagnostics()));
diff --git a/tools/aapt2/test/Builders.h b/tools/aapt2/test/Builders.h
index a120484..c971a1b 100644
--- a/tools/aapt2/test/Builders.h
+++ b/tools/aapt2/test/Builders.h
@@ -74,7 +74,7 @@
   ResourceTableBuilder& SetSymbolState(const android::StringPiece& name, const ResourceId& id,
                                        Visibility::Level level, bool allow_new = false);
   ResourceTableBuilder& SetOverlayable(const android::StringPiece& name,
-                                       const Overlayable& overlayable);
+                                       const OverlayableItem& overlayable);
 
   StringPool* string_pool();
   std::unique_ptr<ResourceTable> Build();