Merge "Move the display targeted by a key event to top"
diff --git a/Android.bp b/Android.bp
index 980aa04..834c9b0 100644
--- a/Android.bp
+++ b/Android.bp
@@ -305,6 +305,7 @@
         "core/java/android/service/euicc/IUpdateSubscriptionNicknameCallback.aidl",
         "core/java/android/service/gatekeeper/IGateKeeperService.aidl",
         "core/java/android/service/contentcapture/IContentCaptureService.aidl",
+        "core/java/android/service/contentcapture/IContentCaptureServiceCallback.aidl",
         "core/java/android/service/notification/INotificationListener.aidl",
         "core/java/android/service/notification/IStatusBarNotificationHolder.aidl",
         "core/java/android/service/notification/IConditionListener.aidl",
@@ -476,6 +477,7 @@
         "media/java/android/media/IMediaRouterClient.aidl",
         "media/java/android/media/IMediaRouterService.aidl",
         "media/java/android/media/IMediaSession2.aidl",
+        "media/java/android/media/IMediaSession2Service.aidl",
         "media/java/android/media/IMediaScannerListener.aidl",
         "media/java/android/media/IMediaScannerService.aidl",
         "media/java/android/media/IPlaybackConfigDispatcher.aidl",
@@ -502,6 +504,7 @@
         "media/java/android/media/session/IOnMediaKeyListener.aidl",
         "media/java/android/media/session/IOnVolumeKeyLongPressListener.aidl",
         "media/java/android/media/session/ISession.aidl",
+        "media/java/android/media/session/ISession2TokensListener.aidl",
         "media/java/android/media/session/ISessionCallback.aidl",
         "media/java/android/media/session/ISessionController.aidl",
         "media/java/android/media/session/ISessionControllerCallback.aidl",
@@ -1066,6 +1069,7 @@
         "core/java/android/annotation/IntDef.java",
         "core/java/android/annotation/NonNull.java",
         "core/java/android/annotation/SystemApi.java",
+        "core/java/android/annotation/TestApi.java",
         "core/java/android/os/HwBinder.java",
         "core/java/android/os/HwBlob.java",
         "core/java/android/os/HwParcel.java",
@@ -1315,7 +1319,6 @@
         "ext",
         "framework",
         "voip-common",
-        "android.test.mock.impl",
     ],
     local_sourcepaths: frameworks_base_subdirs,
     installable: false,
diff --git a/api/current.txt b/api/current.txt
index 100052e..6316ee8 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -5245,8 +5245,8 @@
     method public android.app.Notification clone();
     method public int describeContents();
     method public boolean getAllowSystemGeneratedContextualActions();
-    method public android.app.PendingIntent getAppOverlayIntent();
     method public int getBadgeIconType();
+    method public android.app.Notification.BubbleMetadata getBubbleMetadata();
     method public java.lang.String getChannelId();
     method public java.lang.String getGroup();
     method public int getGroupAlertBehavior();
@@ -5459,6 +5459,25 @@
     method public android.app.Notification.BigTextStyle setSummaryText(java.lang.CharSequence);
   }
 
+  public static final class Notification.BubbleMetadata implements android.os.Parcelable {
+    method public int describeContents();
+    method public int getDesiredHeight();
+    method public android.graphics.drawable.Icon getIcon();
+    method public android.app.PendingIntent getIntent();
+    method public java.lang.CharSequence getTitle();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.app.Notification.BubbleMetadata> CREATOR;
+  }
+
+  public static class Notification.BubbleMetadata.Builder {
+    ctor public Notification.BubbleMetadata.Builder();
+    method public android.app.Notification.BubbleMetadata build();
+    method public android.app.Notification.BubbleMetadata.Builder setDesiredHeight(int);
+    method public android.app.Notification.BubbleMetadata.Builder setIcon(android.graphics.drawable.Icon);
+    method public android.app.Notification.BubbleMetadata.Builder setIntent(android.app.PendingIntent);
+    method public android.app.Notification.BubbleMetadata.Builder setTitle(java.lang.CharSequence);
+  }
+
   public static class Notification.Builder {
     ctor public Notification.Builder(android.content.Context, java.lang.String);
     ctor public deprecated Notification.Builder(android.content.Context);
@@ -5478,9 +5497,9 @@
     method public static android.app.Notification.Builder recoverBuilder(android.content.Context, android.app.Notification);
     method public android.app.Notification.Builder setActions(android.app.Notification.Action...);
     method public android.app.Notification.Builder setAllowSystemGeneratedContextualActions(boolean);
-    method public android.app.Notification.Builder setAppOverlayIntent(android.app.PendingIntent);
     method public android.app.Notification.Builder setAutoCancel(boolean);
     method public android.app.Notification.Builder setBadgeIconType(int);
+    method public android.app.Notification.Builder setBubbleMetadata(android.app.Notification.BubbleMetadata);
     method public android.app.Notification.Builder setCategory(java.lang.String);
     method public android.app.Notification.Builder setChannelId(java.lang.String);
     method public android.app.Notification.Builder setChronometerCountDown(boolean);
@@ -5695,8 +5714,8 @@
 
   public final class NotificationChannel implements android.os.Parcelable {
     ctor public NotificationChannel(java.lang.String, java.lang.CharSequence, int);
+    method public boolean canBubble();
     method public boolean canBypassDnd();
-    method public boolean canOverlayApps();
     method public boolean canShowBadge();
     method public int describeContents();
     method public void enableLights(boolean);
@@ -5712,7 +5731,7 @@
     method public android.net.Uri getSound();
     method public long[] getVibrationPattern();
     method public boolean hasUserSetImportance();
-    method public void setAllowAppOverlay(boolean);
+    method public void setAllowBubbles(boolean);
     method public void setBypassDnd(boolean);
     method public void setDescription(java.lang.String);
     method public void setGroup(java.lang.String);
@@ -5746,7 +5765,7 @@
 
   public class NotificationManager {
     method public java.lang.String addAutomaticZenRule(android.app.AutomaticZenRule);
-    method public boolean areAppOverlaysAllowed();
+    method public boolean areBubblesAllowed();
     method public boolean areNotificationsEnabled();
     method public boolean canNotifyAsPackage(java.lang.String);
     method public void cancel(int);
@@ -7663,6 +7682,7 @@
     field public static final int ACTIVITY_RESUMED = 1; // 0x1
     field public static final int ACTIVITY_STOPPED = 23; // 0x17
     field public static final int CONFIGURATION_CHANGE = 5; // 0x5
+    field public static final int DEVICE_SHUTDOWN = 26; // 0x1a
     field public static final int FOREGROUND_SERVICE_START = 19; // 0x13
     field public static final int FOREGROUND_SERVICE_STOP = 20; // 0x14
     field public static final int KEYGUARD_HIDDEN = 18; // 0x12
@@ -11191,6 +11211,7 @@
     method public void registerCallback(android.content.pm.LauncherApps.Callback);
     method public void registerCallback(android.content.pm.LauncherApps.Callback, android.os.Handler);
     method public android.content.pm.LauncherActivityInfo resolveActivity(android.content.Intent, android.os.UserHandle);
+    method public boolean shouldHideFromSuggestions(java.lang.String, android.os.UserHandle);
     method public void startAppDetailsActivity(android.content.ComponentName, android.os.UserHandle, android.graphics.Rect, android.os.Bundle);
     method public void startMainActivity(android.content.ComponentName, android.os.UserHandle, android.graphics.Rect, android.os.Bundle);
     method public void startShortcut(java.lang.String, java.lang.String, android.graphics.Rect, android.os.Bundle, android.os.UserHandle);
@@ -12304,6 +12325,7 @@
     method public java.lang.String getPositionDescription();
     method public int getResourceId(int, int);
     method public android.content.res.Resources getResources();
+    method public int getSourceStyleResourceId(int, int);
     method public java.lang.String getString(int);
     method public java.lang.CharSequence getText(int);
     method public java.lang.CharSequence[] getTextArray(int);
@@ -13736,6 +13758,7 @@
     method public void drawTextOnPath(java.lang.String, android.graphics.Path, float, float, android.graphics.Paint);
     method public void drawTextRun(char[], int, int, int, int, float, float, boolean, android.graphics.Paint);
     method public void drawTextRun(java.lang.CharSequence, int, int, int, int, float, float, boolean, android.graphics.Paint);
+    method public void drawTextRun(android.graphics.text.MeasuredText, int, int, int, int, float, float, boolean, android.graphics.Paint);
     method public void drawVertices(android.graphics.Canvas.VertexMode, int, float[], int, float[], int, int[], int, short[], int, int, android.graphics.Paint);
     method public void enableZ();
     method public boolean getClipBounds(android.graphics.Rect);
@@ -16436,9 +16459,9 @@
 
   public class BiometricManager {
     method public int canAuthenticate();
-    field public static final int BIOMETRIC_ERROR_NO_BIOMETRICS = 11; // 0xb
+    field public static final int BIOMETRIC_ERROR_HW_UNAVAILABLE = 1; // 0x1
+    field public static final int BIOMETRIC_ERROR_NONE_ENROLLED = 11; // 0xb
     field public static final int BIOMETRIC_ERROR_NO_HARDWARE = 12; // 0xc
-    field public static final int BIOMETRIC_ERROR_UNAVAILABLE = 1; // 0x1
     field public static final int BIOMETRIC_SUCCESS = 0; // 0x0
   }
 
@@ -16481,6 +16504,7 @@
     method public android.hardware.biometrics.BiometricPrompt build();
     method public android.hardware.biometrics.BiometricPrompt.Builder setDescription(java.lang.CharSequence);
     method public android.hardware.biometrics.BiometricPrompt.Builder setNegativeButton(java.lang.CharSequence, java.util.concurrent.Executor, android.content.DialogInterface.OnClickListener);
+    method public android.hardware.biometrics.BiometricPrompt.Builder setRequireConfirmation(boolean);
     method public android.hardware.biometrics.BiometricPrompt.Builder setSubtitle(java.lang.CharSequence);
     method public android.hardware.biometrics.BiometricPrompt.Builder setTitle(java.lang.CharSequence);
   }
@@ -22662,6 +22686,7 @@
     method public deprecated double getCarrierPhase();
     method public deprecated double getCarrierPhaseUncertainty();
     method public double getCn0DbHz();
+    method public int getCodeType();
     method public int getConstellationType();
     method public int getMultipathIndicator();
     method public double getPseudorangeRateMetersPerSecond();
@@ -22677,6 +22702,7 @@
     method public boolean hasCarrierFrequencyHz();
     method public deprecated boolean hasCarrierPhase();
     method public deprecated boolean hasCarrierPhaseUncertainty();
+    method public boolean hasCodeType();
     method public boolean hasSnrInDb();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final int ADR_STATE_CYCLE_SLIP = 4; // 0x4
@@ -22685,6 +22711,21 @@
     field public static final int ADR_STATE_RESET = 2; // 0x2
     field public static final int ADR_STATE_UNKNOWN = 0; // 0x0
     field public static final int ADR_STATE_VALID = 1; // 0x1
+    field public static final int CODE_TYPE_A = 0; // 0x0
+    field public static final int CODE_TYPE_B = 1; // 0x1
+    field public static final int CODE_TYPE_C = 2; // 0x2
+    field public static final int CODE_TYPE_CODELESS = 13; // 0xd
+    field public static final int CODE_TYPE_I = 3; // 0x3
+    field public static final int CODE_TYPE_L = 4; // 0x4
+    field public static final int CODE_TYPE_M = 5; // 0x5
+    field public static final int CODE_TYPE_P = 6; // 0x6
+    field public static final int CODE_TYPE_Q = 7; // 0x7
+    field public static final int CODE_TYPE_S = 8; // 0x8
+    field public static final int CODE_TYPE_UNKNOWN = -1; // 0xffffffff
+    field public static final int CODE_TYPE_W = 9; // 0x9
+    field public static final int CODE_TYPE_X = 10; // 0xa
+    field public static final int CODE_TYPE_Y = 11; // 0xb
+    field public static final int CODE_TYPE_Z = 12; // 0xc
     field public static final android.os.Parcelable.Creator<android.location.GnssMeasurement> CREATOR;
     field public static final int MULTIPATH_INDICATOR_DETECTED = 1; // 0x1
     field public static final int MULTIPATH_INDICATOR_NOT_DETECTED = 2; // 0x2
@@ -24175,8 +24216,11 @@
   public static final class MediaCodec.CryptoException extends java.lang.RuntimeException {
     ctor public MediaCodec.CryptoException(int, java.lang.String);
     method public int getErrorCode();
+    field public static final int ERROR_FRAME_TOO_LARGE = 8; // 0x8
     field public static final int ERROR_INSUFFICIENT_OUTPUT_PROTECTION = 4; // 0x4
+    field public static final int ERROR_INSUFFICIENT_SECURITY = 7; // 0x7
     field public static final int ERROR_KEY_EXPIRED = 2; // 0x2
+    field public static final int ERROR_LOST_STATE = 9; // 0x9
     field public static final int ERROR_NO_KEY = 1; // 0x1
     field public static final int ERROR_RESOURCE_BUSY = 3; // 0x3
     field public static final int ERROR_SESSION_NOT_OPENED = 5; // 0x5
@@ -24517,6 +24561,22 @@
     field public static final int REGULAR_CODECS = 0; // 0x0
   }
 
+  public class MediaController2 implements java.lang.AutoCloseable {
+    ctor public MediaController2(android.content.Context, android.media.Session2Token);
+    ctor public MediaController2(android.content.Context, android.media.Session2Token, java.util.concurrent.Executor, android.media.MediaController2.ControllerCallback);
+    method public void cancelSessionCommand(java.lang.Object);
+    method public void close();
+    method public java.lang.Object sendSessionCommand(android.media.Session2Command, android.os.Bundle);
+  }
+
+  public static abstract class MediaController2.ControllerCallback {
+    ctor public MediaController2.ControllerCallback();
+    method public void onCommandResult(android.media.MediaController2, java.lang.Object, android.media.Session2Command, android.media.Session2Command.Result);
+    method public void onConnected(android.media.MediaController2, android.media.Session2CommandGroup);
+    method public void onDisconnected(android.media.MediaController2);
+    method public android.media.Session2Command.Result onSessionCommand(android.media.MediaController2, android.media.Session2Command, android.os.Bundle);
+  }
+
   public final class MediaCrypto {
     ctor public MediaCrypto(java.util.UUID, byte[]) throws android.media.MediaCryptoException;
     method protected void finalize();
@@ -24624,6 +24684,7 @@
     method public void setOnEventListener(android.media.MediaDrm.OnEventListener);
     method public void setOnExpirationUpdateListener(android.media.MediaDrm.OnExpirationUpdateListener, android.os.Handler);
     method public void setOnKeyStatusChangeListener(android.media.MediaDrm.OnKeyStatusChangeListener, android.os.Handler);
+    method public void setOnSessionLostStateListener(android.media.MediaDrm.OnSessionLostStateListener, android.os.Handler);
     method public void setPropertyByteArray(java.lang.String, byte[]);
     method public void setPropertyString(java.lang.String, java.lang.String);
     field public static final deprecated int EVENT_KEY_EXPIRED = 3; // 0x3
@@ -24742,6 +24803,10 @@
     method public abstract void onKeyStatusChange(android.media.MediaDrm, byte[], java.util.List<android.media.MediaDrm.KeyStatus>, boolean);
   }
 
+  public static abstract interface MediaDrm.OnSessionLostStateListener {
+    method public abstract void onSessionLostState(android.media.MediaDrm, byte[]);
+  }
+
   public static final class MediaDrm.ProvisionRequest {
     method public byte[] getData();
     method public java.lang.String getDefaultUrl();
@@ -24750,6 +24815,12 @@
   public static abstract class MediaDrm.SecurityLevel implements java.lang.annotation.Annotation {
   }
 
+  public static final class MediaDrm.SessionException extends java.lang.RuntimeException {
+    ctor public MediaDrm.SessionException(int, java.lang.String);
+    method public int getErrorCode();
+    field public static final int ERROR_RESOURCE_CONTENTION = 1; // 0x1
+  }
+
   public class MediaDrmException extends java.lang.Exception {
     ctor public MediaDrmException(java.lang.String);
   }
@@ -25408,6 +25479,7 @@
     method public java.lang.Object setAuxEffectSendLevel(float);
     method public java.lang.Object setDataSource(android.media.DataSourceDesc);
     method public java.lang.Object setDisplay(android.view.SurfaceHolder);
+    method public void setDrmEventCallback(java.util.concurrent.Executor, android.media.MediaPlayer2.DrmEventCallback);
     method public java.lang.Object setNextDataSource(android.media.DataSourceDesc);
     method public java.lang.Object setNextDataSources(java.util.List<android.media.DataSourceDesc>);
     method public java.lang.Object setPlaybackParams(android.media.PlaybackParams);
@@ -25486,6 +25558,33 @@
     field public static final int SEEK_PREVIOUS_SYNC = 0; // 0x0
   }
 
+  public static class MediaPlayer2.DrmEventCallback {
+    ctor public MediaPlayer2.DrmEventCallback();
+    method public void onDrmConfig(android.media.MediaPlayer2, android.media.DataSourceDesc, android.media.MediaDrm);
+    method public android.media.MediaPlayer2.DrmPreparationInfo onDrmInfo(android.media.MediaPlayer2, android.media.DataSourceDesc, android.media.MediaPlayer2.DrmInfo);
+    method public byte[] onDrmKeyRequest(android.media.MediaPlayer2, android.media.DataSourceDesc, android.media.MediaDrm.KeyRequest);
+    method public void onDrmPrepared(android.media.MediaPlayer2, android.media.DataSourceDesc, int, byte[]);
+  }
+
+  public static final class MediaPlayer2.DrmInfo {
+    method public java.util.Map<java.util.UUID, byte[]> getPssh();
+    method public java.util.List<java.util.UUID> getSupportedSchemes();
+  }
+
+  public static final class MediaPlayer2.DrmPreparationInfo {
+  }
+
+  public static final class MediaPlayer2.DrmPreparationInfo.Builder {
+    ctor public MediaPlayer2.DrmPreparationInfo.Builder();
+    method public android.media.MediaPlayer2.DrmPreparationInfo build();
+    method public android.media.MediaPlayer2.DrmPreparationInfo.Builder setInitData(byte[]);
+    method public android.media.MediaPlayer2.DrmPreparationInfo.Builder setKeySetId(byte[]);
+    method public android.media.MediaPlayer2.DrmPreparationInfo.Builder setKeyType(int);
+    method public android.media.MediaPlayer2.DrmPreparationInfo.Builder setMimeType(java.lang.String);
+    method public android.media.MediaPlayer2.DrmPreparationInfo.Builder setOptionalParameters(java.util.Map<java.lang.String, java.lang.String>);
+    method public android.media.MediaPlayer2.DrmPreparationInfo.Builder setUuid(java.util.UUID);
+  }
+
   public static class MediaPlayer2.EventCallback {
     ctor public MediaPlayer2.EventCallback();
     method public void onCallCompleted(android.media.MediaPlayer2, android.media.DataSourceDesc, int, int);
@@ -25513,6 +25612,10 @@
     field public static final java.lang.String WIDTH = "android.media.mediaplayer.width";
   }
 
+  public static final class MediaPlayer2.NoDrmSchemeException extends android.media.MediaDrmException {
+    ctor public MediaPlayer2.NoDrmSchemeException(java.lang.String);
+  }
+
   public static class MediaPlayer2.TrackInfo {
     method public android.media.MediaFormat getFormat();
     method public java.lang.String getLanguage();
@@ -25604,6 +25707,7 @@
     field public static final int VOICE_CALL = 4; // 0x4
     field public static final int VOICE_COMMUNICATION = 7; // 0x7
     field public static final int VOICE_DOWNLINK = 3; // 0x3
+    field public static final int VOICE_PERFORMANCE = 10; // 0xa
     field public static final int VOICE_RECOGNITION = 6; // 0x6
     field public static final int VOICE_UPLINK = 2; // 0x2
   }
@@ -25805,6 +25909,37 @@
     method public abstract void onScanCompleted(java.lang.String, android.net.Uri);
   }
 
+  public class MediaSession2 implements java.lang.AutoCloseable {
+    method public void broadcastSessionCommand(android.media.Session2Command, android.os.Bundle);
+    method public void cancelSessionCommand(android.media.MediaSession2.ControllerInfo, java.lang.Object);
+    method public void close();
+    method public java.lang.String getSessionId();
+    method public android.media.Session2Token getSessionToken();
+    method public java.lang.Object sendSessionCommand(android.media.MediaSession2.ControllerInfo, android.media.Session2Command, android.os.Bundle);
+  }
+
+  public static final class MediaSession2.Builder {
+    ctor public MediaSession2.Builder(android.content.Context);
+    method public android.media.MediaSession2 build();
+    method public android.media.MediaSession2.Builder setId(java.lang.String);
+    method public android.media.MediaSession2.Builder setSessionActivity(android.app.PendingIntent);
+    method public android.media.MediaSession2.Builder setSessionCallback(java.util.concurrent.Executor, android.media.MediaSession2.SessionCallback);
+  }
+
+  public static final class MediaSession2.ControllerInfo {
+    method public java.lang.String getPackageName();
+    method public android.media.session.MediaSessionManager.RemoteUserInfo getRemoteUserInfo();
+    method public int getUid();
+  }
+
+  public static abstract class MediaSession2.SessionCallback {
+    ctor public MediaSession2.SessionCallback();
+    method public void onCommandResult(android.media.MediaSession2, android.media.MediaSession2.ControllerInfo, java.lang.Object, android.media.Session2Command, android.media.Session2Command.Result);
+    method public android.media.Session2CommandGroup onConnect(android.media.MediaSession2, android.media.MediaSession2.ControllerInfo);
+    method public void onDisconnected(android.media.MediaSession2, android.media.MediaSession2.ControllerInfo);
+    method public android.media.Session2Command.Result onSessionCommand(android.media.MediaSession2, android.media.MediaSession2.ControllerInfo, android.media.Session2Command, android.os.Bundle);
+  }
+
   public final class MediaSync {
     ctor public MediaSync();
     method public android.view.Surface createInputSurface();
@@ -26099,6 +26234,38 @@
     method public android.os.Bundle getResultData();
   }
 
+  public final class Session2CommandGroup implements android.os.Parcelable {
+    method public int describeContents();
+    method public java.util.Set<android.media.Session2Command> getCommands();
+    method public boolean hasCommand(android.media.Session2Command);
+    method public boolean hasCommand(int);
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.media.Session2CommandGroup> CREATOR;
+  }
+
+  public static final class Session2CommandGroup.Builder {
+    ctor public Session2CommandGroup.Builder();
+    ctor public Session2CommandGroup.Builder(android.media.Session2CommandGroup);
+    method public android.media.Session2CommandGroup.Builder addCommand(android.media.Session2Command);
+    method public android.media.Session2CommandGroup.Builder addCommand(int);
+    method public android.media.Session2CommandGroup build();
+    method public android.media.Session2CommandGroup.Builder removeCommand(android.media.Session2Command);
+    method public android.media.Session2CommandGroup.Builder removeCommand(int);
+  }
+
+  public final class Session2Token implements android.os.Parcelable {
+    ctor public Session2Token(android.content.Context, android.content.ComponentName);
+    method public int describeContents();
+    method public java.lang.String getPackageName();
+    method public java.lang.String getServiceName();
+    method public int getType();
+    method public int getUid();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.media.Session2Token> CREATOR;
+    field public static final int TYPE_SESSION = 0; // 0x0
+    field public static final int TYPE_SESSION_SERVICE = 1; // 0x1
+  }
+
   public class SoundPool {
     ctor public deprecated SoundPool(int, int, int);
     method public final void autoPause();
@@ -29630,8 +29797,10 @@
     method public java.lang.String getMacAddress();
     method public int getNetworkId();
     method public int getRssi();
+    method public int getRxLinkSpeedMbps();
     method public java.lang.String getSSID();
     method public android.net.wifi.SupplicantState getSupplicantState();
+    method public int getTxLinkSpeedMbps();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final java.lang.String FREQUENCY_UNITS = "MHz";
     field public static final java.lang.String LINK_SPEED_UNITS = "Mbps";
@@ -29922,12 +30091,16 @@
     method public android.net.NetworkSpecifier build();
     method public android.net.wifi.aware.WifiAwareManager.NetworkSpecifierBuilder setDiscoverySession(android.net.wifi.aware.DiscoverySession);
     method public android.net.wifi.aware.WifiAwareManager.NetworkSpecifierBuilder setPeerHandle(android.net.wifi.aware.PeerHandle);
+    method public android.net.wifi.aware.WifiAwareManager.NetworkSpecifierBuilder setPort(int);
     method public android.net.wifi.aware.WifiAwareManager.NetworkSpecifierBuilder setPskPassphrase(java.lang.String);
+    method public android.net.wifi.aware.WifiAwareManager.NetworkSpecifierBuilder setTransportProtocol(int);
   }
 
   public final class WifiAwareNetworkInfo implements android.os.Parcelable android.net.TransportInfo {
     method public int describeContents();
     method public java.net.Inet6Address getPeerIpv6Addr();
+    method public int getPort();
+    method public int getTransportProtocol();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.net.wifi.aware.WifiAwareNetworkInfo> CREATOR;
   }
@@ -41566,6 +41739,7 @@
     field public static final int SHOW_SOURCE_ACTIVITY = 16; // 0x10
     field public static final int SHOW_SOURCE_APPLICATION = 8; // 0x8
     field public static final int SHOW_SOURCE_ASSIST_GESTURE = 4; // 0x4
+    field public static final int SHOW_SOURCE_AUTOMOTIVE_SYSTEM_UI = 128; // 0x80
     field public static final int SHOW_SOURCE_NOTIFICATION = 64; // 0x40
     field public static final int SHOW_SOURCE_PUSH_TO_TALK = 32; // 0x20
     field public static final int SHOW_WITH_ASSIST = 1; // 0x1
@@ -43034,10 +43208,12 @@
     field public static final int CAPABILITY_SWAP_CONFERENCE = 8; // 0x8
     field public static final java.lang.String EVENT_CALL_MERGE_FAILED = "android.telecom.event.CALL_MERGE_FAILED";
     field public static final java.lang.String EVENT_CALL_PULL_FAILED = "android.telecom.event.CALL_PULL_FAILED";
+    field public static final java.lang.String EVENT_RTT_AUDIO_INDICATION_CHANGED = "android.telecom.event.RTT_AUDIO_INDICATION_CHANGED";
     field public static final java.lang.String EXTRA_ANSWERING_DROPS_FG_CALL = "android.telecom.extra.ANSWERING_DROPS_FG_CALL";
     field public static final java.lang.String EXTRA_ANSWERING_DROPS_FG_CALL_APP_NAME = "android.telecom.extra.ANSWERING_DROPS_FG_CALL_APP_NAME";
     field public static final java.lang.String EXTRA_CALL_SUBJECT = "android.telecom.extra.CALL_SUBJECT";
     field public static final java.lang.String EXTRA_CHILD_ADDRESS = "android.telecom.extra.CHILD_ADDRESS";
+    field public static final java.lang.String EXTRA_IS_RTT_AUDIO_PRESENT = "android.telecom.extra.IS_RTT_AUDIO_PRESENT";
     field public static final java.lang.String EXTRA_LAST_FORWARDED_NUMBER = "android.telecom.extra.LAST_FORWARDED_NUMBER";
     field public static final java.lang.String EXTRA_SIP_INVITE = "android.telecom.extra.SIP_INVITE";
     field public static final int PROPERTY_HAS_CDMA_VOICE_PRIVACY = 32; // 0x20
@@ -44313,6 +44489,7 @@
     method public int describeContents();
     method public int getCdmaDbm();
     method public int getCdmaEcio();
+    method public java.util.List<android.telephony.CellSignalStrength> getCellSignalStrengths();
     method public int getEvdoDbm();
     method public int getEvdoEcio();
     method public int getEvdoSnr();
@@ -44755,6 +44932,9 @@
   public static abstract class TelephonyManager.CellInfoCallback {
     ctor public TelephonyManager.CellInfoCallback();
     method public abstract void onCellInfo(java.util.List<android.telephony.CellInfo>);
+    method public void onError(int, java.lang.Throwable);
+    field public static final int ERROR_MODEM_ERROR = 2; // 0x2
+    field public static final int ERROR_TIMEOUT = 1; // 0x1
   }
 
   public static abstract class TelephonyManager.UssdResponseCallback {
@@ -44974,6 +45154,7 @@
   }
 
   public class EuiccManager {
+    method public android.telephony.euicc.EuiccManager createForCardId(int);
     method public void deleteSubscription(int, android.app.PendingIntent);
     method public void downloadSubscription(android.telephony.euicc.DownloadableSubscription, boolean, android.app.PendingIntent);
     method public java.lang.String getEid();
@@ -46976,7 +47157,7 @@
   public class Linkify {
     ctor public Linkify();
     method public static final boolean addLinks(android.text.Spannable, int);
-    method public static final boolean addLinks(android.text.Spannable, int, android.text.util.Linkify.UrlSpanFactory);
+    method public static final boolean addLinks(android.text.Spannable, int, java.util.function.Function<java.lang.String, android.text.style.URLSpan>);
     method public static final boolean addLinks(android.widget.TextView, int);
     method public static final void addLinks(android.widget.TextView, java.util.regex.Pattern, java.lang.String);
     method public static final void addLinks(android.widget.TextView, java.util.regex.Pattern, java.lang.String, android.text.util.Linkify.MatchFilter, android.text.util.Linkify.TransformFilter);
@@ -46984,7 +47165,7 @@
     method public static final boolean addLinks(android.text.Spannable, java.util.regex.Pattern, java.lang.String);
     method public static final boolean addLinks(android.text.Spannable, java.util.regex.Pattern, java.lang.String, android.text.util.Linkify.MatchFilter, android.text.util.Linkify.TransformFilter);
     method public static final boolean addLinks(android.text.Spannable, java.util.regex.Pattern, java.lang.String, java.lang.String[], android.text.util.Linkify.MatchFilter, android.text.util.Linkify.TransformFilter);
-    method public static final boolean addLinks(android.text.Spannable, java.util.regex.Pattern, java.lang.String, java.lang.String[], android.text.util.Linkify.MatchFilter, android.text.util.Linkify.TransformFilter, android.text.util.Linkify.UrlSpanFactory);
+    method public static final boolean addLinks(android.text.Spannable, java.util.regex.Pattern, java.lang.String, java.lang.String[], android.text.util.Linkify.MatchFilter, android.text.util.Linkify.TransformFilter, java.util.function.Function<java.lang.String, android.text.style.URLSpan>);
     field public static final int ALL = 15; // 0xf
     field public static final int EMAIL_ADDRESSES = 2; // 0x2
     field public static final deprecated int MAP_ADDRESSES = 8; // 0x8
@@ -47003,11 +47184,6 @@
     method public abstract java.lang.String transformUrl(java.util.regex.Matcher, java.lang.String);
   }
 
-  public static class Linkify.UrlSpanFactory {
-    ctor public Linkify.UrlSpanFactory();
-    method public android.text.style.URLSpan create(java.lang.String);
-  }
-
   public class Rfc822Token {
     ctor public Rfc822Token(java.lang.String, java.lang.String, java.lang.String);
     method public java.lang.String getAddress();
@@ -48080,6 +48256,7 @@
     field public int data;
     field public int density;
     field public int resourceId;
+    field public int sourceStyleResourceId;
     field public java.lang.CharSequence string;
     field public int type;
   }
@@ -52841,19 +53018,8 @@
     ctor public InspectionCompanion.UninitializedPropertyMapException();
   }
 
-  public final class IntEnumMapping {
-    method public java.lang.String nameOf(int);
-  }
-
-  public static final class IntEnumMapping.Builder {
-    ctor public IntEnumMapping.Builder();
-    method public android.view.inspector.IntEnumMapping.Builder addValue(java.lang.String, int);
-    method public android.view.inspector.IntEnumMapping build();
-    method public void clear();
-  }
-
   public final class IntFlagMapping {
-    method public java.lang.String[] namesOf(int);
+    method public java.util.Set<java.lang.String> get(int);
   }
 
   public static final class IntFlagMapping.Builder {
@@ -52861,7 +53027,6 @@
     method public android.view.inspector.IntFlagMapping.Builder addFlag(java.lang.String, int);
     method public android.view.inspector.IntFlagMapping.Builder addFlag(java.lang.String, int, int);
     method public android.view.inspector.IntFlagMapping build();
-    method public void clear();
   }
 
   public abstract interface PropertyMapper {
@@ -52873,7 +53038,7 @@
     method public abstract int mapFloat(java.lang.String, int);
     method public abstract int mapGravity(java.lang.String, int);
     method public abstract int mapInt(java.lang.String, int);
-    method public abstract int mapIntEnum(java.lang.String, int, android.view.inspector.IntEnumMapping);
+    method public abstract int mapIntEnum(java.lang.String, int, android.util.SparseArray<java.lang.String>);
     method public abstract int mapIntFlag(java.lang.String, int, android.view.inspector.IntFlagMapping);
     method public abstract int mapLong(java.lang.String, int);
     method public abstract int mapObject(java.lang.String, int);
@@ -52911,15 +53076,15 @@
 
 package android.view.textclassifier {
 
-  public final class ConversationActions implements android.os.Parcelable {
-    ctor public ConversationActions(java.util.List<android.view.textclassifier.ConversationActions.ConversationAction>, java.lang.String);
+  public final class ConversationAction implements android.os.Parcelable {
     method public int describeContents();
-    method public java.util.List<android.view.textclassifier.ConversationActions.ConversationAction> getConversationActions();
-    method public java.lang.String getId();
+    method public android.app.RemoteAction getAction();
+    method public float getConfidenceScore();
+    method public android.os.Bundle getExtras();
+    method public java.lang.CharSequence getTextReply();
+    method public java.lang.String getType();
     method public void writeToParcel(android.os.Parcel, int);
-    field public static final android.os.Parcelable.Creator<android.view.textclassifier.ConversationActions> CREATOR;
-    field public static final java.lang.String HINT_FOR_IN_APP = "in_app";
-    field public static final java.lang.String HINT_FOR_NOTIFICATION = "notification";
+    field public static final android.os.Parcelable.Creator<android.view.textclassifier.ConversationAction> CREATOR;
     field public static final java.lang.String TYPE_CALL_PHONE = "call_phone";
     field public static final java.lang.String TYPE_CREATE_REMINDER = "create_reminder";
     field public static final java.lang.String TYPE_OPEN_URL = "open_url";
@@ -52932,24 +53097,22 @@
     field public static final java.lang.String TYPE_VIEW_MAP = "view_map";
   }
 
-  public static final class ConversationActions.ConversationAction implements android.os.Parcelable {
-    method public int describeContents();
-    method public android.app.RemoteAction getAction();
-    method public float getConfidenceScore();
-    method public android.os.Bundle getExtras();
-    method public java.lang.CharSequence getTextReply();
-    method public java.lang.String getType();
-    method public void writeToParcel(android.os.Parcel, int);
-    field public static final android.os.Parcelable.Creator<android.view.textclassifier.ConversationActions.ConversationAction> CREATOR;
+  public static final class ConversationAction.Builder {
+    ctor public ConversationAction.Builder(java.lang.String);
+    method public android.view.textclassifier.ConversationAction build();
+    method public android.view.textclassifier.ConversationAction.Builder setAction(android.app.RemoteAction);
+    method public android.view.textclassifier.ConversationAction.Builder setConfidenceScore(float);
+    method public android.view.textclassifier.ConversationAction.Builder setExtras(android.os.Bundle);
+    method public android.view.textclassifier.ConversationAction.Builder setTextReply(java.lang.CharSequence);
   }
 
-  public static final class ConversationActions.ConversationAction.Builder {
-    ctor public ConversationActions.ConversationAction.Builder(java.lang.String);
-    method public android.view.textclassifier.ConversationActions.ConversationAction build();
-    method public android.view.textclassifier.ConversationActions.ConversationAction.Builder setAction(android.app.RemoteAction);
-    method public android.view.textclassifier.ConversationActions.ConversationAction.Builder setConfidenceScore(float);
-    method public android.view.textclassifier.ConversationActions.ConversationAction.Builder setExtras(android.os.Bundle);
-    method public android.view.textclassifier.ConversationActions.ConversationAction.Builder setTextReply(java.lang.CharSequence);
+  public final class ConversationActions implements android.os.Parcelable {
+    ctor public ConversationActions(java.util.List<android.view.textclassifier.ConversationAction>, java.lang.String);
+    method public int describeContents();
+    method public java.util.List<android.view.textclassifier.ConversationAction> getConversationActions();
+    method public java.lang.String getId();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.view.textclassifier.ConversationActions> CREATOR;
   }
 
   public static final class ConversationActions.Message implements android.os.Parcelable {
@@ -52979,9 +53142,11 @@
     method public java.lang.String getConversationId();
     method public java.util.List<java.lang.String> getHints();
     method public int getMaxSuggestions();
-    method public android.view.textclassifier.ConversationActions.TypeConfig getTypeConfig();
+    method public android.view.textclassifier.TextClassifier.EntityConfig getTypeConfig();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.view.textclassifier.ConversationActions.Request> CREATOR;
+    field public static final java.lang.String HINT_FOR_IN_APP = "in_app";
+    field public static final java.lang.String HINT_FOR_NOTIFICATION = "notification";
   }
 
   public static final class ConversationActions.Request.Builder {
@@ -52990,23 +53155,7 @@
     method public android.view.textclassifier.ConversationActions.Request.Builder setConversationId(java.lang.String);
     method public android.view.textclassifier.ConversationActions.Request.Builder setHints(java.util.List<java.lang.String>);
     method public android.view.textclassifier.ConversationActions.Request.Builder setMaxSuggestions(int);
-    method public android.view.textclassifier.ConversationActions.Request.Builder setTypeConfig(android.view.textclassifier.ConversationActions.TypeConfig);
-  }
-
-  public static final class ConversationActions.TypeConfig implements android.os.Parcelable {
-    method public int describeContents();
-    method public java.util.Collection<java.lang.String> resolveTypes(java.util.Collection<java.lang.String>);
-    method public boolean shouldIncludeTypesFromTextClassifier();
-    method public void writeToParcel(android.os.Parcel, int);
-    field public static final android.os.Parcelable.Creator<android.view.textclassifier.ConversationActions.TypeConfig> CREATOR;
-  }
-
-  public static final class ConversationActions.TypeConfig.Builder {
-    ctor public ConversationActions.TypeConfig.Builder();
-    method public android.view.textclassifier.ConversationActions.TypeConfig build();
-    method public android.view.textclassifier.ConversationActions.TypeConfig.Builder includeTypesFromTextClassifier(boolean);
-    method public android.view.textclassifier.ConversationActions.TypeConfig.Builder setExcludedTypes(java.util.Collection<java.lang.String>);
-    method public android.view.textclassifier.ConversationActions.TypeConfig.Builder setIncludedTypes(java.util.Collection<java.lang.String>);
+    method public android.view.textclassifier.ConversationActions.Request.Builder setTypeConfig(android.view.textclassifier.TextClassifier.EntityConfig);
   }
 
   public final class SelectionEvent implements android.os.Parcelable {
@@ -53180,16 +53329,26 @@
   }
 
   public static final class TextClassifier.EntityConfig implements android.os.Parcelable {
-    method public static android.view.textclassifier.TextClassifier.EntityConfig create(java.util.Collection<java.lang.String>, java.util.Collection<java.lang.String>, java.util.Collection<java.lang.String>);
-    method public static android.view.textclassifier.TextClassifier.EntityConfig createWithExplicitEntityList(java.util.Collection<java.lang.String>);
-    method public static android.view.textclassifier.TextClassifier.EntityConfig createWithHints(java.util.Collection<java.lang.String>);
+    method public static deprecated android.view.textclassifier.TextClassifier.EntityConfig create(java.util.Collection<java.lang.String>, java.util.Collection<java.lang.String>, java.util.Collection<java.lang.String>);
+    method public static deprecated android.view.textclassifier.TextClassifier.EntityConfig createWithExplicitEntityList(java.util.Collection<java.lang.String>);
+    method public static deprecated android.view.textclassifier.TextClassifier.EntityConfig createWithHints(java.util.Collection<java.lang.String>);
     method public int describeContents();
     method public java.util.Collection<java.lang.String> getHints();
     method public java.util.Collection<java.lang.String> resolveEntityListModifications(java.util.Collection<java.lang.String>);
+    method public boolean shouldIncludeTypesFromTextClassifier();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.view.textclassifier.TextClassifier.EntityConfig> CREATOR;
   }
 
+  public static final class TextClassifier.EntityConfig.Builder {
+    ctor public TextClassifier.EntityConfig.Builder();
+    method public android.view.textclassifier.TextClassifier.EntityConfig build();
+    method public android.view.textclassifier.TextClassifier.EntityConfig.Builder includeTypesFromTextClassifier(boolean);
+    method public android.view.textclassifier.TextClassifier.EntityConfig.Builder setExcludedTypes(java.util.Collection<java.lang.String>);
+    method public android.view.textclassifier.TextClassifier.EntityConfig.Builder setHints(java.util.Collection<java.lang.String>);
+    method public android.view.textclassifier.TextClassifier.EntityConfig.Builder setIncludedTypes(java.util.Collection<java.lang.String>);
+  }
+
   public final class TextClassifierEvent implements android.os.Parcelable {
     method public int describeContents();
     method public int[] getActionIndices();
diff --git a/api/system-current.txt b/api/system-current.txt
index a4b3978..acbc2a7 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -94,7 +94,6 @@
     field public static final java.lang.String MANAGE_CONTENT_CAPTURE = "android.permission.MANAGE_CONTENT_CAPTURE";
     field public static final java.lang.String MANAGE_CONTENT_SUGGESTIONS = "android.permission.MANAGE_CONTENT_SUGGESTIONS";
     field public static final java.lang.String MANAGE_DEBUGGING = "android.permission.MANAGE_DEBUGGING";
-    field public static final java.lang.String MANAGE_DEVICE_ADMINS = "android.permission.MANAGE_DEVICE_ADMINS";
     field public static final java.lang.String MANAGE_IPSEC_TUNNELS = "android.permission.MANAGE_IPSEC_TUNNELS";
     field public static final java.lang.String MANAGE_ROLE_HOLDERS = "android.permission.MANAGE_ROLE_HOLDERS";
     field public static final java.lang.String MANAGE_ROLLBACKS = "android.permission.MANAGE_ROLLBACKS";
@@ -151,6 +150,7 @@
     field public static final java.lang.String REGISTER_CALL_PROVIDER = "android.permission.REGISTER_CALL_PROVIDER";
     field public static final java.lang.String REGISTER_CONNECTION_MANAGER = "android.permission.REGISTER_CONNECTION_MANAGER";
     field public static final java.lang.String REGISTER_SIM_SUBSCRIPTION = "android.permission.REGISTER_SIM_SUBSCRIPTION";
+    field public static final java.lang.String REMOTE_DISPLAY_PROVIDER = "android.permission.REMOTE_DISPLAY_PROVIDER";
     field public static final java.lang.String REMOVE_DRM_CERTIFICATES = "android.permission.REMOVE_DRM_CERTIFICATES";
     field public static final java.lang.String REMOVE_TASKS = "android.permission.REMOVE_TASKS";
     field public static final java.lang.String RESET_PASSWORD = "android.permission.RESET_PASSWORD";
@@ -199,6 +199,7 @@
   }
 
   public static final class R.attr {
+    field public static final int inheritShowWhenLocked = 16844194; // 0x10105a2
     field public static final int isVrOnly = 16844152; // 0x1010578
     field public static final int requiredSystemPropertyName = 16844133; // 0x1010565
     field public static final int requiredSystemPropertyValue = 16844134; // 0x1010566
@@ -258,6 +259,7 @@
     method public boolean convertToTranslucent(android.app.Activity.TranslucentConversionListener, android.app.ActivityOptions);
     method public deprecated boolean isBackgroundVisibleBehind();
     method public deprecated void onBackgroundVisibleBehindChanged(boolean);
+    method public void setInheritShowWhenLocked(boolean);
   }
 
   public static abstract interface Activity.TranslucentConversionListener {
@@ -1045,7 +1047,6 @@
     method public void removeRoleHolderAsUser(java.lang.String, java.lang.String, android.os.UserHandle, java.util.concurrent.Executor, android.app.role.RoleManagerCallback);
     method public boolean removeRoleHolderFromController(java.lang.String, java.lang.String);
     method public void setRoleNamesFromController(java.util.List<java.lang.String>);
-    field public static final java.lang.String EXTRA_REQUEST_ROLE_NAME = "android.app.role.extra.REQUEST_ROLE_NAME";
   }
 
   public abstract interface RoleManagerCallback {
@@ -1252,6 +1253,7 @@
     field public static final java.lang.String ACTION_INSTANT_APP_RESOLVER_SETTINGS = "android.intent.action.INSTANT_APP_RESOLVER_SETTINGS";
     field public static final java.lang.String ACTION_INTENT_FILTER_NEEDS_VERIFICATION = "android.intent.action.INTENT_FILTER_NEEDS_VERIFICATION";
     field public static final java.lang.String ACTION_MANAGE_APP_PERMISSIONS = "android.intent.action.MANAGE_APP_PERMISSIONS";
+    field public static final java.lang.String ACTION_MANAGE_DEFAULT_APP = "android.intent.action.MANAGE_DEFAULT_APP";
     field public static final java.lang.String ACTION_MANAGE_PERMISSIONS = "android.intent.action.MANAGE_PERMISSIONS";
     field public static final java.lang.String ACTION_MANAGE_PERMISSION_APPS = "android.intent.action.MANAGE_PERMISSION_APPS";
     field public static final java.lang.String ACTION_MANAGE_SPECIAL_APP_ACCESSES = "android.intent.action.MANAGE_SPECIAL_APP_ACCESSES";
@@ -1282,11 +1284,12 @@
     field public static final java.lang.String EXTRA_LONG_VERSION_CODE = "android.intent.extra.LONG_VERSION_CODE";
     field public static final java.lang.String EXTRA_ORIGINATING_UID = "android.intent.extra.ORIGINATING_UID";
     field public static final java.lang.String EXTRA_PACKAGES = "android.intent.extra.PACKAGES";
-    field public static final java.lang.String EXTRA_PERMISSION_NAME = "android.intent.extra.PERMISSION_NAME";
     field public static final java.lang.String EXTRA_PERMISSION_GROUP_NAME = "android.intent.extra.PERMISSION_GROUP_NAME";
+    field public static final java.lang.String EXTRA_PERMISSION_NAME = "android.intent.extra.PERMISSION_NAME";
     field public static final java.lang.String EXTRA_REASON = "android.intent.extra.REASON";
     field public static final java.lang.String EXTRA_REMOTE_CALLBACK = "android.intent.extra.REMOTE_CALLBACK";
     field public static final java.lang.String EXTRA_RESULT_NEEDED = "android.intent.extra.RESULT_NEEDED";
+    field public static final java.lang.String EXTRA_ROLE_NAME = "android.intent.extra.ROLE_NAME";
     field public static final java.lang.String EXTRA_UNKNOWN_INSTANT_APP = "android.intent.extra.UNKNOWN_INSTANT_APP";
     field public static final java.lang.String EXTRA_VERIFICATION_BUNDLE = "android.intent.extra.VERIFICATION_BUNDLE";
     field public static final java.lang.String METADATA_SETUP_VERSION = "android.SETUP_VERSION";
@@ -1299,6 +1302,26 @@
 
 }
 
+package android.content.om {
+
+  public final class OverlayInfo implements android.os.Parcelable {
+    method public int describeContents();
+    method public boolean isEnabled();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.content.om.OverlayInfo> CREATOR;
+    field public final java.lang.String category;
+    field public final java.lang.String packageName;
+    field public final java.lang.String targetPackageName;
+    field public final int userId;
+  }
+
+  public class OverlayManager {
+    method public java.util.List<android.content.om.OverlayInfo> getOverlayInfosForTarget(java.lang.String, int);
+    method public boolean setEnabledExclusiveInCategory(java.lang.String, int);
+  }
+
+}
+
 package android.content.pm {
 
   public class ApplicationInfo extends android.content.pm.PackageItemInfo implements android.os.Parcelable {
@@ -1439,6 +1462,7 @@
     method public abstract void revokeRuntimePermission(java.lang.String, java.lang.String, android.os.UserHandle);
     method public void sendDeviceCustomizationReadyBroadcast();
     method public abstract boolean setDefaultBrowserPackageNameAsUser(java.lang.String, int);
+    method public java.lang.String[] setDistractingPackageRestrictions(java.lang.String[], int);
     method public void setHarmfulAppWarning(java.lang.String, java.lang.CharSequence);
     method public deprecated java.lang.String[] setPackagesSuspended(java.lang.String[], boolean, android.os.PersistableBundle, android.os.PersistableBundle, java.lang.String);
     method public java.lang.String[] setPackagesSuspended(java.lang.String[], boolean, android.os.PersistableBundle, android.os.PersistableBundle, android.content.pm.SuspendDialogInfo);
@@ -1506,6 +1530,9 @@
     field public static final int MATCH_ANY_USER = 4194304; // 0x400000
     field public static final int MATCH_FACTORY_ONLY = 2097152; // 0x200000
     field public static final int MATCH_INSTANT = 8388608; // 0x800000
+    field public static final int RESTRICTION_HIDE_FROM_SUGGESTIONS = 1; // 0x1
+    field public static final int RESTRICTION_HIDE_NOTIFICATIONS = 2; // 0x2
+    field public static final int RESTRICTION_NONE = 0; // 0x0
   }
 
   public static abstract class PackageManager.DexModuleRegisterCallback {
@@ -1513,6 +1540,9 @@
     method public abstract void onDexModuleRegistered(java.lang.String, boolean, java.lang.String);
   }
 
+  public static abstract class PackageManager.DistractionRestriction implements java.lang.annotation.Annotation {
+  }
+
   public static abstract interface PackageManager.OnPermissionsChangedListener {
     method public abstract void onPermissionsChanged(int);
   }
@@ -3271,6 +3301,7 @@
   }
 
   public final class MediaRecorder.AudioSource {
+    field public static final int ECHO_REFERENCE = 1997; // 0x7cd
     field public static final int HOTWORD = 1999; // 0x7cf
     field public static final int RADIO_TUNER = 1998; // 0x7ce
   }
@@ -3878,24 +3909,24 @@
 
 package android.net.wifi {
 
-  public abstract class DppStatusCallback {
-    ctor public DppStatusCallback();
+  public abstract class EasyConnectStatusCallback {
+    ctor public EasyConnectStatusCallback();
     method public abstract void onConfiguratorSuccess(int);
     method public abstract void onEnrolleeSuccess(int);
     method public abstract void onFailure(int);
     method public abstract void onProgress(int);
-    field public static final int DPP_EVENT_FAILURE = -7; // 0xfffffff9
-    field public static final int DPP_EVENT_FAILURE_AUTHENTICATION = -2; // 0xfffffffe
-    field public static final int DPP_EVENT_FAILURE_BUSY = -5; // 0xfffffffb
-    field public static final int DPP_EVENT_FAILURE_CONFIGURATION = -4; // 0xfffffffc
-    field public static final int DPP_EVENT_FAILURE_INVALID_NETWORK = -9; // 0xfffffff7
-    field public static final int DPP_EVENT_FAILURE_INVALID_URI = -1; // 0xffffffff
-    field public static final int DPP_EVENT_FAILURE_NOT_COMPATIBLE = -3; // 0xfffffffd
-    field public static final int DPP_EVENT_FAILURE_NOT_SUPPORTED = -8; // 0xfffffff8
-    field public static final int DPP_EVENT_FAILURE_TIMEOUT = -6; // 0xfffffffa
-    field public static final int DPP_EVENT_PROGRESS_AUTHENTICATION_SUCCESS = 0; // 0x0
-    field public static final int DPP_EVENT_PROGRESS_RESPONSE_PENDING = 1; // 0x1
-    field public static final int DPP_EVENT_SUCCESS_CONFIGURATION_SENT = 0; // 0x0
+    field public static final int EASY_CONNECT_EVENT_FAILURE = -7; // 0xfffffff9
+    field public static final int EASY_CONNECT_EVENT_FAILURE_AUTHENTICATION = -2; // 0xfffffffe
+    field public static final int EASY_CONNECT_EVENT_FAILURE_BUSY = -5; // 0xfffffffb
+    field public static final int EASY_CONNECT_EVENT_FAILURE_CONFIGURATION = -4; // 0xfffffffc
+    field public static final int EASY_CONNECT_EVENT_FAILURE_INVALID_NETWORK = -9; // 0xfffffff7
+    field public static final int EASY_CONNECT_EVENT_FAILURE_INVALID_URI = -1; // 0xffffffff
+    field public static final int EASY_CONNECT_EVENT_FAILURE_NOT_COMPATIBLE = -3; // 0xfffffffd
+    field public static final int EASY_CONNECT_EVENT_FAILURE_NOT_SUPPORTED = -8; // 0xfffffff8
+    field public static final int EASY_CONNECT_EVENT_FAILURE_TIMEOUT = -6; // 0xfffffffa
+    field public static final int EASY_CONNECT_EVENT_PROGRESS_AUTHENTICATION_SUCCESS = 0; // 0x0
+    field public static final int EASY_CONNECT_EVENT_PROGRESS_RESPONSE_PENDING = 1; // 0x1
+    field public static final int EASY_CONNECT_EVENT_SUCCESS_CONFIGURATION_SENT = 0; // 0x0
   }
 
   public deprecated class RttManager {
@@ -4126,10 +4157,10 @@
     method public void save(android.net.wifi.WifiConfiguration, android.net.wifi.WifiManager.ActionListener);
     method public void setDeviceMobilityState(int);
     method public boolean setWifiApConfiguration(android.net.wifi.WifiConfiguration);
-    method public void startDppAsConfiguratorInitiator(java.lang.String, int, int, android.os.Handler, android.net.wifi.DppStatusCallback);
-    method public void startDppAsEnrolleeInitiator(java.lang.String, android.os.Handler, android.net.wifi.DppStatusCallback);
+    method public void startEasyConnectAsConfiguratorInitiator(java.lang.String, int, int, android.os.Handler, android.net.wifi.EasyConnectStatusCallback);
+    method public void startEasyConnectAsEnrolleeInitiator(java.lang.String, android.os.Handler, android.net.wifi.EasyConnectStatusCallback);
     method public boolean startScan(android.os.WorkSource);
-    method public void stopDppSession();
+    method public void stopEasyConnectSession();
     method public void unregisterNetworkRequestMatchCallback(android.net.wifi.WifiManager.NetworkRequestMatchCallback);
     field public static final int CHANGE_REASON_ADDED = 0; // 0x0
     field public static final int CHANGE_REASON_CONFIG_CHANGE = 2; // 0x2
@@ -4139,8 +4170,8 @@
     field public static final int DEVICE_MOBILITY_STATE_LOW_MVMT = 2; // 0x2
     field public static final int DEVICE_MOBILITY_STATE_STATIONARY = 3; // 0x3
     field public static final int DEVICE_MOBILITY_STATE_UNKNOWN = 0; // 0x0
-    field public static final int DPP_NETWORK_ROLE_AP = 1; // 0x1
-    field public static final int DPP_NETWORK_ROLE_STA = 0; // 0x0
+    field public static final int EASY_CONNECT_NETWORK_ROLE_AP = 1; // 0x1
+    field public static final int EASY_CONNECT_NETWORK_ROLE_STA = 0; // 0x0
     field public static final java.lang.String EXTRA_CHANGE_REASON = "changeReason";
     field public static final java.lang.String EXTRA_MULTIPLE_NETWORKS_CHANGED = "multipleChanges";
     field public static final java.lang.String EXTRA_PREVIOUS_WIFI_AP_STATE = "previous_wifi_state";
@@ -4415,6 +4446,10 @@
     method public abstract java.lang.Object onTransactStarted(android.os.IBinder, int);
   }
 
+  public static class Build.VERSION {
+    field public static final java.lang.String PREVIEW_SDK_FINGERPRINT;
+  }
+
   public final class ConfigUpdate {
     field public static final java.lang.String ACTION_UPDATE_CARRIER_ID_DB = "android.os.action.UPDATE_CARRIER_ID_DB";
     field public static final java.lang.String ACTION_UPDATE_CARRIER_PROVISIONING_URLS = "android.intent.action.UPDATE_CARRIER_PROVISIONING_URLS";
@@ -4833,6 +4868,8 @@
     method public final android.os.IBinder onBind(android.content.Intent);
     method public abstract int onCountPermissionApps(java.util.List<java.lang.String>, boolean, boolean);
     method public abstract java.util.List<android.permission.RuntimePermissionPresentationInfo> onGetAppPermissions(java.lang.String);
+    method public abstract void onGetRuntimePermissionsBackup(android.os.UserHandle, java.io.OutputStream);
+    method public abstract java.util.List<android.permission.RuntimePermissionUsageInfo> onPermissionUsageResult(boolean, long);
     method public abstract void onRevokeRuntimePermission(java.lang.String, java.lang.String);
     method public abstract java.util.Map<java.lang.String, java.util.List<java.lang.String>> onRevokeRuntimePermissions(java.util.Map<java.lang.String, java.util.List<java.lang.String>>, boolean, int, java.lang.String);
     field public static final java.lang.String SERVICE_INTERFACE = "android.permission.PermissionControllerService";
@@ -4858,6 +4895,15 @@
     field public static final android.os.Parcelable.Creator<android.permission.RuntimePermissionPresentationInfo> CREATOR;
   }
 
+  public final class RuntimePermissionUsageInfo implements android.os.Parcelable {
+    ctor public RuntimePermissionUsageInfo(java.lang.CharSequence, int);
+    method public int describeContents();
+    method public int getAppAccessCount();
+    method public java.lang.CharSequence getName();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.permission.RuntimePermissionUsageInfo> CREATOR;
+  }
+
 }
 
 package android.permissionpresenterservice {
@@ -4979,6 +5025,7 @@
     method public static void removeOnPropertyChangedListener(android.provider.DeviceConfig.OnPropertyChangedListener);
     method public static void resetToDefaults(int, java.lang.String);
     method public static boolean setProperty(java.lang.String, java.lang.String, java.lang.String, boolean);
+    field public static final java.lang.String NAMESPACE_INPUT_NATIVE_BOOT = "input_native_boot";
   }
 
   public static abstract interface DeviceConfig.OnPropertyChangedListener {
@@ -5662,7 +5709,7 @@
     ctor public NotificationAssistantService();
     method public final void adjustNotification(android.service.notification.Adjustment);
     method public final void adjustNotifications(java.util.List<android.service.notification.Adjustment>);
-    method public void onActionClicked(java.lang.String, android.app.Notification.Action, int);
+    method public void onActionInvoked(java.lang.String, android.app.Notification.Action, int);
     method public final android.os.IBinder onBind(android.content.Intent);
     method public void onNotificationDirectReplied(java.lang.String);
     method public android.service.notification.Adjustment onNotificationEnqueued(android.service.notification.StatusBarNotification);
@@ -6179,6 +6226,40 @@
     field public static final int WWAN = 1; // 0x1
   }
 
+  public class CallAttributes implements android.os.Parcelable {
+    ctor public CallAttributes(android.telephony.PreciseCallState, int, android.telephony.CallQuality);
+    method public int describeContents();
+    method public android.telephony.CallQuality getCallQuality();
+    method public int getNetworkType();
+    method public android.telephony.PreciseCallState getPreciseCallState();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.telephony.CallAttributes> CREATOR;
+  }
+
+  public final class CallQuality implements android.os.Parcelable {
+    ctor public CallQuality(int, int, int, int, int, int, int, int, int, int, int);
+    method public int describeContents();
+    method public int getAverageRelativeJitter();
+    method public int getAverageRoundTripTime();
+    method public int getCallDuration();
+    method public int getCodecType();
+    method public int getDownlinkCallQualityLevel();
+    method public int getMaxRelativeJitter();
+    method public int getNumRtpPacketsNotReceived();
+    method public int getNumRtpPacketsReceived();
+    method public int getNumRtpPacketsTransmitted();
+    method public int getNumRtpPacketsTransmittedLost();
+    method public int getUplinkCallQualityLevel();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final int CALL_QUALITY_BAD = 4; // 0x4
+    field public static final int CALL_QUALITY_EXCELLENT = 0; // 0x0
+    field public static final int CALL_QUALITY_FAIR = 2; // 0x2
+    field public static final int CALL_QUALITY_GOOD = 1; // 0x1
+    field public static final int CALL_QUALITY_NOT_AVAILABLE = 5; // 0x5
+    field public static final int CALL_QUALITY_POOR = 3; // 0x3
+    field public static final android.os.Parcelable.Creator<android.telephony.CallQuality> CREATOR;
+  }
+
   public class CarrierConfigManager {
     method public static android.os.PersistableBundle getDefaultConfig();
     method public void overrideConfig(int, android.os.PersistableBundle);
@@ -6444,12 +6525,14 @@
   }
 
   public class PhoneStateListener {
+    method public void onCallAttributesChanged(android.telephony.CallAttributes);
     method public void onCallDisconnectCauseChanged(int, int);
     method public void onPreciseCallStateChanged(android.telephony.PreciseCallState);
     method public void onPreciseDataConnectionStateChanged(android.telephony.PreciseDataConnectionState);
     method public void onRadioPowerStateChanged(int);
     method public void onSrvccStateChanged(int);
     method public void onVoiceActivationStateChanged(int);
+    field public static final int LISTEN_CALL_ATTRIBUTES_CHANGED = 67108864; // 0x4000000
     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_PRECISE_DATA_CONNECTION_STATE = 4096; // 0x1000
@@ -7186,6 +7269,7 @@
     method public void callSessionResumeFailed(android.telephony.ims.ImsReasonInfo);
     method public void callSessionResumeReceived(android.telephony.ims.ImsCallProfile);
     method public void callSessionResumed(android.telephony.ims.ImsCallProfile);
+    method public void callSessionRttAudioIndicatorChanged(android.telephony.ims.ImsStreamMediaProfile);
     method public void callSessionRttMessageReceived(java.lang.String);
     method public void callSessionRttModifyRequestReceived(android.telephony.ims.ImsCallProfile);
     method public void callSessionRttModifyResponseReceived(int);
@@ -7593,10 +7677,12 @@
     method public int describeContents();
     method public int getAudioDirection();
     method public int getAudioQuality();
+    method public boolean getRttAudioSpeech();
     method public int getRttMode();
     method public int getVideoDirection();
     method public int getVideoQuality();
     method public boolean isRttCall();
+    method public void setRttAudioSpeech(boolean);
     method public void setRttMode(int);
     method public void writeToParcel(android.os.Parcel, int);
     field public static final int AUDIO_QUALITY_AMR = 1; // 0x1
diff --git a/api/system-removed.txt b/api/system-removed.txt
index 4beb699..5a7ec8b 100644
--- a/api/system-removed.txt
+++ b/api/system-removed.txt
@@ -1,3 +1,11 @@
+package android {
+
+  public static final class Manifest.permission {
+    field public static final java.lang.String MANAGE_DEVICE_ADMINS = "android.permission.MANAGE_DEVICE_ADMINS";
+  }
+
+}
+
 package android.app {
 
   public class Notification implements android.os.Parcelable {
diff --git a/api/test-current.txt b/api/test-current.txt
index 575875d..6ddb341 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -28,6 +28,7 @@
 
   public class Activity extends android.view.ContextThemeWrapper implements android.content.ComponentCallbacks2 android.view.KeyEvent.Callback android.view.LayoutInflater.Factory2 android.view.View.OnCreateContextMenuListener android.view.Window.Callback {
     method public void onMovedToDisplay(int, android.content.res.Configuration);
+    method public void setInheritShowWhenLocked(boolean);
   }
 
   public class ActivityManager {
@@ -219,6 +220,11 @@
     method public abstract void onOpActiveChanged(int, int, java.lang.String, boolean);
   }
 
+  public final class NotificationChannel implements android.os.Parcelable {
+    method public boolean isImportanceLockedByOEM();
+    method public void setImportanceLockedByOEM(boolean);
+  }
+
   public final class NotificationChannelGroup implements android.os.Parcelable {
     method public int getUserLockedFields();
     method public void lockFields(int);
@@ -415,6 +421,7 @@
 
   public class PermissionInfo extends android.content.pm.PackageItemInfo implements android.os.Parcelable {
     field public static final int PROTECTION_FLAG_DOCUMENTER = 262144; // 0x40000
+    field public static final int PROTECTION_FLAG_OEM = 16384; // 0x4000
     field public static final int PROTECTION_FLAG_SYSTEM_TEXT_CLASSIFIER = 65536; // 0x10000
     field public static final int PROTECTION_FLAG_VENDOR_PRIVILEGED = 32768; // 0x8000
     field public static final int PROTECTION_FLAG_WELLBEING = 131072; // 0x20000
@@ -494,10 +501,19 @@
 
 package android.graphics {
 
+  public final class Bitmap implements android.os.Parcelable {
+    method public void eraseColor(long);
+  }
+
   public final class ImageDecoder implements java.lang.AutoCloseable {
     method public static android.graphics.ImageDecoder.Source createSource(android.content.res.Resources, java.io.InputStream, int);
   }
 
+  public class Paint {
+    method public void setColor(long);
+    method public void setShadowLayer(float, float, float, long);
+  }
+
 }
 
 package android.graphics.drawable {
@@ -617,6 +633,7 @@
     method public void resetCarrierFrequencyHz();
     method public deprecated void resetCarrierPhase();
     method public deprecated void resetCarrierPhaseUncertainty();
+    method public void resetCodeType();
     method public void resetSnrInDb();
     method public void set(android.location.GnssMeasurement);
     method public void setAccumulatedDeltaRangeMeters(double);
@@ -628,6 +645,7 @@
     method public deprecated void setCarrierPhase(double);
     method public deprecated void setCarrierPhaseUncertainty(double);
     method public void setCn0DbHz(double);
+    method public void setCodeType(int);
     method public void setConstellationType(int);
     method public void setMultipathIndicator(int);
     method public void setPseudorangeRateMetersPerSecond(double);
@@ -784,6 +802,134 @@
     method public static java.io.File getStorageDirectory();
   }
 
+  public abstract class HwBinder implements android.os.IHwBinder {
+    ctor public HwBinder();
+    method public static final void configureRpcThreadpool(long, boolean);
+    method public static void enableInstrumentation();
+    method public static final android.os.IHwBinder getService(java.lang.String, java.lang.String) throws java.util.NoSuchElementException, android.os.RemoteException;
+    method public static final android.os.IHwBinder getService(java.lang.String, java.lang.String, boolean) throws java.util.NoSuchElementException, android.os.RemoteException;
+    method public static final void joinRpcThreadpool();
+    method public abstract void onTransact(int, android.os.HwParcel, android.os.HwParcel, int) throws android.os.RemoteException;
+    method public final void registerService(java.lang.String) throws android.os.RemoteException;
+    method public final void transact(int, android.os.HwParcel, android.os.HwParcel, int) throws android.os.RemoteException;
+  }
+
+  public class HwBlob {
+    ctor public HwBlob(int);
+    method public final void copyToBoolArray(long, boolean[], int);
+    method public final void copyToDoubleArray(long, double[], int);
+    method public final void copyToFloatArray(long, float[], int);
+    method public final void copyToInt16Array(long, short[], int);
+    method public final void copyToInt32Array(long, int[], int);
+    method public final void copyToInt64Array(long, long[], int);
+    method public final void copyToInt8Array(long, byte[], int);
+    method public final boolean getBool(long);
+    method public final double getDouble(long);
+    method public final float getFloat(long);
+    method public final short getInt16(long);
+    method public final int getInt32(long);
+    method public final long getInt64(long);
+    method public final byte getInt8(long);
+    method public final java.lang.String getString(long);
+    method public final long handle();
+    method public final void putBlob(long, android.os.HwBlob);
+    method public final void putBool(long, boolean);
+    method public final void putBoolArray(long, boolean[]);
+    method public final void putDouble(long, double);
+    method public final void putDoubleArray(long, double[]);
+    method public final void putFloat(long, float);
+    method public final void putFloatArray(long, float[]);
+    method public final void putInt16(long, short);
+    method public final void putInt16Array(long, short[]);
+    method public final void putInt32(long, int);
+    method public final void putInt32Array(long, int[]);
+    method public final void putInt64(long, long);
+    method public final void putInt64Array(long, long[]);
+    method public final void putInt8(long, byte);
+    method public final void putInt8Array(long, byte[]);
+    method public final void putNativeHandle(long, android.os.NativeHandle);
+    method public final void putString(long, java.lang.String);
+    method public static java.lang.Boolean[] wrapArray(boolean[]);
+    method public static java.lang.Long[] wrapArray(long[]);
+    method public static java.lang.Byte[] wrapArray(byte[]);
+    method public static java.lang.Short[] wrapArray(short[]);
+    method public static java.lang.Integer[] wrapArray(int[]);
+    method public static java.lang.Float[] wrapArray(float[]);
+    method public static java.lang.Double[] wrapArray(double[]);
+  }
+
+  public class HwParcel {
+    ctor public HwParcel();
+    method public final void enforceInterface(java.lang.String);
+    method public final boolean readBool();
+    method public final java.util.ArrayList<java.lang.Boolean> readBoolVector();
+    method public final android.os.HwBlob readBuffer(long);
+    method public final double readDouble();
+    method public final java.util.ArrayList<java.lang.Double> readDoubleVector();
+    method public final android.os.HwBlob readEmbeddedBuffer(long, long, long, boolean);
+    method public final android.os.NativeHandle readEmbeddedNativeHandle(long, long);
+    method public final float readFloat();
+    method public final java.util.ArrayList<java.lang.Float> readFloatVector();
+    method public final short readInt16();
+    method public final java.util.ArrayList<java.lang.Short> readInt16Vector();
+    method public final int readInt32();
+    method public final java.util.ArrayList<java.lang.Integer> readInt32Vector();
+    method public final long readInt64();
+    method public final java.util.ArrayList<java.lang.Long> readInt64Vector();
+    method public final byte readInt8();
+    method public final java.util.ArrayList<java.lang.Byte> readInt8Vector();
+    method public final android.os.NativeHandle readNativeHandle();
+    method public final java.util.ArrayList<android.os.NativeHandle> readNativeHandleVector();
+    method public final java.lang.String readString();
+    method public final java.util.ArrayList<java.lang.String> readStringVector();
+    method public final android.os.IHwBinder readStrongBinder();
+    method public final void release();
+    method public final void releaseTemporaryStorage();
+    method public final void send();
+    method public final void verifySuccess();
+    method public final void writeBool(boolean);
+    method public final void writeBoolVector(java.util.ArrayList<java.lang.Boolean>);
+    method public final void writeBuffer(android.os.HwBlob);
+    method public final void writeDouble(double);
+    method public final void writeDoubleVector(java.util.ArrayList<java.lang.Double>);
+    method public final void writeFloat(float);
+    method public final void writeFloatVector(java.util.ArrayList<java.lang.Float>);
+    method public final void writeInt16(short);
+    method public final void writeInt16Vector(java.util.ArrayList<java.lang.Short>);
+    method public final void writeInt32(int);
+    method public final void writeInt32Vector(java.util.ArrayList<java.lang.Integer>);
+    method public final void writeInt64(long);
+    method public final void writeInt64Vector(java.util.ArrayList<java.lang.Long>);
+    method public final void writeInt8(byte);
+    method public final void writeInt8Vector(java.util.ArrayList<java.lang.Byte>);
+    method public final void writeInterfaceToken(java.lang.String);
+    method public final void writeNativeHandle(android.os.NativeHandle);
+    method public final void writeNativeHandleVector(java.util.ArrayList<android.os.NativeHandle>);
+    method public final void writeStatus(int);
+    method public final void writeString(java.lang.String);
+    method public final void writeStringVector(java.util.ArrayList<java.lang.String>);
+    method public final void writeStrongBinder(android.os.IHwBinder);
+    field public static final int STATUS_SUCCESS = 0; // 0x0
+  }
+
+  public static abstract class HwParcel.Status implements java.lang.annotation.Annotation {
+  }
+
+  public abstract interface IHwBinder {
+    method public abstract boolean linkToDeath(android.os.IHwBinder.DeathRecipient, long);
+    method public abstract android.os.IHwInterface queryLocalInterface(java.lang.String);
+    method public abstract void transact(int, android.os.HwParcel, android.os.HwParcel, int) throws android.os.RemoteException;
+    method public abstract boolean unlinkToDeath(android.os.IHwBinder.DeathRecipient);
+  }
+
+  public static abstract interface IHwBinder.DeathRecipient {
+    method public abstract void serviceDied(long);
+  }
+
+  public abstract interface IHwInterface {
+    method public abstract android.os.IHwBinder asBinder();
+  }
+
   public class IncidentManager {
     method public void reportIncident(android.os.IncidentReportArgs);
   }
@@ -809,6 +955,18 @@
     method public void removeSyncBarrier(int);
   }
 
+  public final class NativeHandle implements java.io.Closeable {
+    ctor public NativeHandle();
+    ctor public NativeHandle(java.io.FileDescriptor, boolean);
+    ctor public NativeHandle(java.io.FileDescriptor[], int[], boolean);
+    method public void close() throws java.io.IOException;
+    method public android.os.NativeHandle dup() throws java.io.IOException;
+    method public java.io.FileDescriptor getFileDescriptor();
+    method public java.io.FileDescriptor[] getFileDescriptors();
+    method public int[] getInts();
+    method public boolean hasSingleFileDescriptor();
+  }
+
   public final class PowerManager {
     method public int getPowerSaveMode();
     method public boolean setDynamicPowerSavings(boolean, int);
@@ -819,6 +977,11 @@
 
   public class Process {
     method public static final int getThreadScheduler(int) throws java.lang.IllegalArgumentException;
+    field public static final int FIRST_APP_ZYGOTE_ISOLATED_UID = 90000; // 0x15f90
+    field public static final int FIRST_ISOLATED_UID = 99000; // 0x182b8
+    field public static final int LAST_APP_ZYGOTE_ISOLATED_UID = 98999; // 0x182b7
+    field public static final int LAST_ISOLATED_UID = 99999; // 0x1869f
+    field public static final int NUM_UIDS_PER_APP_ZYGOTE = 100; // 0x64
   }
 
   public final class RemoteCallback implements android.os.Parcelable {
@@ -1246,6 +1409,71 @@
 
 }
 
+package android.service.autofill.augmented {
+
+  public abstract class AugmentedAutofillService extends android.app.Service {
+    ctor public AugmentedAutofillService();
+    method protected final void dump(java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
+    method protected void dump(java.io.PrintWriter, java.lang.String[]);
+    method public void onFillRequest(android.service.autofill.augmented.FillRequest, android.os.CancellationSignal, android.service.autofill.augmented.FillController, android.service.autofill.augmented.FillCallback);
+    field public static final java.lang.String SERVICE_INTERFACE = "android.service.autofill.augmented.AugmentedAutofillService";
+  }
+
+  public final class FillCallback {
+    method public void onSuccess(android.service.autofill.augmented.FillResponse);
+  }
+
+  public final class FillController {
+    method public void autofill(java.util.List<android.util.Pair<android.view.autofill.AutofillId, android.view.autofill.AutofillValue>>);
+  }
+
+  public final class FillRequest {
+    method public android.content.ComponentName getActivityComponent();
+    method public android.view.autofill.AutofillId getFocusedId();
+    method public android.view.autofill.AutofillValue getFocusedValue();
+    method public android.service.autofill.augmented.PresentationParams getPresentationParams();
+    method public int getTaskId();
+  }
+
+  public final class FillResponse implements android.os.Parcelable {
+    method public int describeContents();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.service.autofill.augmented.FillResponse> CREATOR;
+  }
+
+  public static final class FillResponse.Builder {
+    ctor public FillResponse.Builder();
+    method public android.service.autofill.augmented.FillResponse build();
+    method public android.service.autofill.augmented.FillResponse.Builder setFillWindow(android.service.autofill.augmented.FillWindow);
+    method public android.service.autofill.augmented.FillResponse.Builder setIgnoredIds(java.util.List<android.view.autofill.AutofillId>);
+  }
+
+  public final class FillWindow implements java.lang.AutoCloseable {
+    ctor public FillWindow();
+    method public void destroy();
+    method public boolean update(android.service.autofill.augmented.PresentationParams.Area, android.view.View, long);
+    field public static final long FLAG_METADATA_ADDRESS = 1L; // 0x1L
+  }
+
+  public abstract class PresentationParams {
+    method public int getFlags();
+    method public android.service.autofill.augmented.PresentationParams.Area getFullArea();
+    method public android.service.autofill.augmented.PresentationParams.Area getSuggestionArea();
+    field public static final int FLAG_HINT_GRAVITY_BOTTOM = 2; // 0x2
+    field public static final int FLAG_HINT_GRAVITY_LEFT = 4; // 0x4
+    field public static final int FLAG_HINT_GRAVITY_RIGHT = 8; // 0x8
+    field public static final int FLAG_HINT_GRAVITY_TOP = 1; // 0x1
+    field public static final int FLAG_HOST_IME = 16; // 0x10
+    field public static final int FLAG_HOST_SYSTEM = 32; // 0x20
+  }
+
+  public static abstract class PresentationParams.Area {
+    method public android.graphics.Rect getBounds();
+    method public android.service.autofill.augmented.PresentationParams.Area getSubArea(android.graphics.Rect);
+  }
+
+}
+
 package android.service.notification {
 
   public final class Adjustment implements android.os.Parcelable {
@@ -1275,7 +1503,7 @@
     ctor public NotificationAssistantService();
     method public final void adjustNotification(android.service.notification.Adjustment);
     method public final void adjustNotifications(java.util.List<android.service.notification.Adjustment>);
-    method public void onActionClicked(java.lang.String, android.app.Notification.Action, int);
+    method public void onActionInvoked(java.lang.String, android.app.Notification.Action, int);
     method public final android.os.IBinder onBind(android.content.Intent);
     method public void onNotificationDirectReplied(java.lang.String);
     method public android.service.notification.Adjustment onNotificationEnqueued(android.service.notification.StatusBarNotification);
@@ -1847,6 +2075,10 @@
     ctor public AutofillId(android.view.autofill.AutofillId, int);
   }
 
+  public final class AutofillManager {
+    field public static final int MAX_TEMP_AUGMENTED_SERVICE_DURATION_MS = 120000; // 0x1d4c0
+  }
+
 }
 
 package android.view.inputmethod {
diff --git a/cmds/idmap2/libidmap2/RawPrintVisitor.cpp b/cmds/idmap2/libidmap2/RawPrintVisitor.cpp
index ec2decf..b78e942 100644
--- a/cmds/idmap2/libidmap2/RawPrintVisitor.cpp
+++ b/cmds/idmap2/libidmap2/RawPrintVisitor.cpp
@@ -89,6 +89,7 @@
   }
 }
 
+// NOLINTNEXTLINE(cert-dcl50-cpp)
 void RawPrintVisitor::print(uint16_t value, const char* fmt, ...) {
   va_list ap;
   va_start(ap, fmt);
@@ -101,6 +102,7 @@
   offset_ += sizeof(uint16_t);
 }
 
+// NOLINTNEXTLINE(cert-dcl50-cpp)
 void RawPrintVisitor::print(uint32_t value, const char* fmt, ...) {
   va_list ap;
   va_start(ap, fmt);
@@ -113,6 +115,7 @@
   offset_ += sizeof(uint32_t);
 }
 
+// NOLINTNEXTLINE(cert-dcl50-cpp)
 void RawPrintVisitor::print(const std::string& value, const char* fmt, ...) {
   va_list ap;
   va_start(ap, fmt);
diff --git a/cmds/screencap/screencap.cpp b/cmds/screencap/screencap.cpp
index 8fa2980..3d74f8b 100644
--- a/cmds/screencap/screencap.cpp
+++ b/cmds/screencap/screencap.cpp
@@ -81,8 +81,7 @@
         case ui::Dataspace::V0_SRGB:
             return SkColorSpace::MakeSRGB();
         case ui::Dataspace::DISPLAY_P3:
-            return SkColorSpace::MakeRGB(
-                    SkColorSpace::kSRGB_RenderTargetGamma, SkColorSpace::kDCIP3_D65_Gamut);
+            return SkColorSpace::MakeRGB(SkNamedTransferFn::kSRGB, SkNamedGamut::kDCIP3);
         default:
             return nullptr;
     }
diff --git a/cmds/statsd/src/anomaly/subscriber_util.cpp b/cmds/statsd/src/anomaly/subscriber_util.cpp
index 9d37cdb..ad5eae3 100644
--- a/cmds/statsd/src/anomaly/subscriber_util.cpp
+++ b/cmds/statsd/src/anomaly/subscriber_util.cpp
@@ -57,7 +57,7 @@
                 break;
             case Subscription::SubscriberInformationCase::kPerfettoDetails:
                 if (!CollectPerfettoTraceAndUploadToDropbox(subscription.perfetto_details(),
-                                                            rule_id, configKey)) {
+                                                            subscription.id(), rule_id, configKey)) {
                     ALOGW("Failed to generate perfetto traces.");
                 }
                 break;
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index f347867..7e2df27 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -25,7 +25,11 @@
 import "frameworks/base/core/proto/android/app/settings_enums.proto";
 import "frameworks/base/core/proto/android/app/job/enums.proto";
 import "frameworks/base/core/proto/android/bluetooth/enums.proto";
+import "frameworks/base/core/proto/android/bluetooth/hci/enums.proto";
+import "frameworks/base/core/proto/android/bluetooth/hfp/enums.proto";
+import "frameworks/base/core/proto/android/net/networkcapabilities.proto";
 import "frameworks/base/core/proto/android/os/enums.proto";
+import "frameworks/base/core/proto/android/server/connectivity/data_stall_event.proto";
 import "frameworks/base/core/proto/android/server/enums.proto";
 import "frameworks/base/core/proto/android/server/location/enums.proto";
 import "frameworks/base/core/proto/android/service/procstats_enum.proto";
@@ -172,11 +176,18 @@
         WifiEnabledStateChanged wifi_enabled_state_changed = 113;
         WifiRunningStateChanged wifi_running_state_changed = 114;
         AppCompacted app_compacted = 115;
-        NetworkDnsEventReported network_dns_event_Reported = 116;
+        NetworkDnsEventReported network_dns_event_reported = 116;
         DocsUIPickerLaunchedFromReported docs_ui_picker_launched_from_reported = 117;
         DocsUIPickResultReported docs_ui_pick_result_reported = 118;
         DocsUISearchModeReported docs_ui_search_mode_reported = 119;
         DocsUISearchTypeReported docs_ui_search_type_reported = 120;
+        DataStallEvent data_stall_event = 121;
+        RescuePartyResetReported rescue_party_reset_reported = 122;
+        SignedConfigReported signed_config_reported = 123;
+        GnssNiEventReported gnss_ni_event_reported = 124;
+        BluetoothLinkLayerConnectionEvent bluetooth_link_layer_connection_event = 125;
+        BluetoothAclConnectionStateChanged bluetooth_acl_connection_state_changed = 126;
+        BluetoothScoConnectionStateChanged bluetooth_sco_connection_state_changed = 127;
     }
 
     // Pulled events will start at field 10000.
@@ -1292,10 +1303,12 @@
 }
 
 /**
- * Logs when a Bluetooth device connects and disconnects.
+ * Logs when profiles on a Bluetooth device connects and disconnects.
  *
  * Logged from:
- *    packages/apps/Bluetooth/src/com/android/bluetooth/btservice/AdapterProperties.java
+ *    packages/apps/Bluetooth/src/com/android/bluetooth/btservice/RemoteDevices.java
+ *
+ * Next Tag: 5
  */
 message BluetoothConnectionStateChanged {
     // The state of the connection.
@@ -1304,13 +1317,152 @@
     // An identifier that can be used to match connect and disconnect events.
     // Currently is last two bytes of a hash of a device level ID and
     // the mac address of the bluetooth device that is connected.
-    optional int32 obfuscated_id = 2;
+    // Deprecated: use obfuscated_id instead, this one is always 0 for Q+
+    optional int32 OBSOLETE_obfuscated_id = 2 [deprecated = true];
     // The profile that is connected. Eg. GATT, A2DP, HEADSET.
     // From android.bluetooth.BluetoothAdapter.java
+    // Default: 0 when not used
     optional int32 bt_profile = 3;
+    // An identifier that can be used to match events for this device.
+    // Currently, this is a salted hash of the MAC address of this Bluetooth device.
+    // Salt: Randomly generated 256 bit value
+    // Hash algorithm: HMAC-SHA256
+    // Size: 32 byte
+    // Default: null or empty if the device identifier is not known
+    optional bytes obfuscated_id = 4 [(android.os.statsd.log_mode) = MODE_BYTES];
 }
 
 /**
+ * Logs when a Bluetooth device connects and disconnects over ACL
+ *
+ * Logged from:
+ *    packages/apps/Bluetooth/src/com/android/bluetooth/btservice/AdapterProperties.java
+ *
+ * Next Tag: 3
+ */
+message BluetoothAclConnectionStateChanged {
+    // An identifier that can be used to match events for this device.
+    // Currently, this is a salted hash of the MAC address of this Bluetooth device.
+    // Salt: Randomly generated 256 bit value
+    // Hash algorithm: HMAC-SHA256
+    // Size: 32 byte
+    // Default: null or empty if the device identifier is not known
+    optional bytes obfuscated_id = 1 [(android.os.statsd.log_mode) = MODE_BYTES];
+    // The state of the connection.
+    // Eg: CONNECTING, CONNECTED, DISCONNECTING, DISCONNECTED.
+    optional android.bluetooth.ConnectionStateEnum state = 2;
+}
+
+/**
+ * Logs when a Bluetooth device connects and disconnects over SCO
+ *
+ * Logged from:
+ *    packages/apps/Bluetooth/src/com/android/bluetooth/hfp/HeadsetStateMachine.java
+ *    packages/apps/Bluetooth/src/com/android/bluetooth/hfp/HeadsetClientStateMachine.java
+ *
+ * Next Tag: 4
+ */
+message BluetoothScoConnectionStateChanged {
+    // An identifier that can be used to match events for this device.
+    // Currently, this is a salted hash of the MAC address of this Bluetooth device.
+    // Salt: Randomly generated 256 bit value
+    // Hash algorithm: HMAC-SHA256
+    // Size: 32 byte
+    // Default: null or empty if the device identifier is not known
+    optional bytes obfuscated_id = 1 [(android.os.statsd.log_mode) = MODE_BYTES];
+    // The state of the connection.
+    // Eg: CONNECTING, CONNECTED, DISCONNECTING, DISCONNECTED.
+    optional android.bluetooth.ConnectionStateEnum state = 2;
+    // Codec used for this SCO connection
+    // Default: UNKNOWN
+    optional android.bluetooth.hfp.ScoCodec codec = 3;
+}
+
+// Logs when there is an event affecting Bluetooth device's link layer connection.
+// - This event is triggered when there is a related HCI command or event
+// - Users of this metrics can deduce Bluetooth device's connection state from these events
+// - HCI commands are logged before the command is sent, after receiving command status, and after
+//   receiving command complete
+// - HCI events are logged when they arrive
+//
+// Low level log from system/bt
+//
+// Bluetooth classic commands:
+// - CMD_CREATE_CONNECTION
+// - CMD_DISCONNECT
+// - CMD_CREATE_CONNECTION_CANCEL
+// - CMD_ACCEPT_CONNECTION_REQUEST
+// - CMD_REJECT_CONNECTION_REQUEST
+// - CMD_SETUP_ESCO_CONNECTION
+// - CMD_ACCEPT_ESCO_CONNECTION
+// - CMD_REJECT_ESCO_CONNECTION
+// - CMD_ENH_SETUP_ESCO_CONNECTION
+// - CMD_ENH_ACCEPT_ESCO_CONNECTION
+//
+// Bluetooth low energy commands:
+// - CMD_BLE_CREATE_LL_CONN [Only logged on error or when initiator filter policy is 0x00]
+// - CMD_BLE_CREATE_CONN_CANCEL [Only logged when there is an error]
+// - CMD_BLE_EXTENDED_CREATE_CONNECTION [Only logged on error or when initiator filter policy is 0x00]
+// - CMD_BLE_CLEAR_WHITE_LIST
+// - CMD_BLE_ADD_WHITE_LIST
+// - CMD_BLE_REMOVE_WHITE_LIST
+//
+// Bluetooth classic events:
+// - EVT_CONNECTION_COMP
+// - EVT_CONNECTION_REQUEST
+// - EVT_DISCONNECTION_COMP
+// - EVT_ESCO_CONNECTION_COMP
+// - EVT_ESCO_CONNECTION_CHANGED
+//
+// Bluetooth low energy meta events:
+// - BLE_EVT_CONN_COMPLETE_EVT
+// - BLE_EVT_ENHANCED_CONN_COMPLETE_EVT
+//
+// Next tag: 10
+message BluetoothLinkLayerConnectionEvent {
+    // An identifier that can be used to match events for this device.
+    // Currently, this is a salted hash of the MAC address of this Bluetooth device.
+    // Salt: Randomly generated 256 bit value
+    // Hash algorithm: HMAC-SHA256
+    // Size: 32 byte
+    // Default: null or empty if the device identifier is not known
+    optional bytes obfuscated_id = 1 [(android.os.statsd.log_mode) = MODE_BYTES];
+    // Connection handle of this connection if available
+    // Range: 0x0000 - 0x0EFF (12 bits)
+    // Default: 0xFFFF if the handle is unknown
+    optional int32 connection_handle = 2;
+    // Direction of the link
+    // Default: DIRECTION_UNKNOWN
+    optional android.bluetooth.DirectionEnum direction = 3;
+    // Type of this link
+    // Default: LINK_TYPE_UNKNOWN
+    optional android.bluetooth.LinkTypeEnum type = 4;
+
+    // Reason metadata for this link layer connection event, rules for interpretation:
+    // 1. If hci_cmd is set and valid, hci_event can be either EVT_COMMAND_STATUS or
+    //    EVT_COMMAND_COMPLETE, ignore hci_ble_event in this case
+    // 2. If hci_event is set to EVT_BLE_META, look at hci_ble_event; otherwise, if hci_event is
+    //    set and valid, ignore hci_ble_event
+
+    // HCI command associated with this event
+    // Default: CMD_UNKNOWN
+    optional android.bluetooth.hci.CommandEnum hci_cmd = 5;
+    // HCI event associated with this event
+    // Default: EVT_UNKNOWN
+    optional android.bluetooth.hci.EventEnum hci_event = 6;
+    // HCI BLE meta event associated with this event
+    // Default: BLE_EVT_UNKNOWN
+    optional android.bluetooth.hci.BleMetaEventEnum hci_ble_event = 7;
+    // HCI command status code if this is triggerred by hci_cmd
+    // Default: STATUS_UNKNOWN
+    optional android.bluetooth.hci.StatusEnum cmd_status = 8;
+    // HCI reason code associated with this event
+    // Default: STATUS_UNKNOWN
+    optional android.bluetooth.hci.StatusEnum reason_code = 9;
+}
+
+
+/**
  * Logs when something is plugged into or removed from the USB-C connector.
  *
  * Logged from:
@@ -3798,7 +3950,7 @@
     //bionic/libc/include/netdb.h
     //system/netd/resolv/include/netd_resolv/resolv.h
     enum ReturnCode {
-        EAI_NOERR = 0;
+        EAI_NO_ERROR = 0;
         EAI_ADDRFAMILY = 1;
         EAI_AGAIN = 2;
         EAI_BADFLAGS = 3;
@@ -3821,3 +3973,139 @@
     // The latency period(in microseconds) it took for this DNS lookup to complete.
     optional int32 latency_micros = 3;
 }
+
+/**
+ * Logs when a data stall event occurs.
+ *
+ * Log from:
+ *     frameworks/base/services/core/java/com/android/server/connectivity/NetworkMonitor.java
+ */
+message DataStallEvent {
+    // Data stall evaluation type.
+    // See frameworks/base/services/core/java/com/android/server/connectivity/NetworkMonitor.java
+    // Refer to the definition of DATA_STALL_EVALUATION_TYPE_*.
+    optional int32 evaluation_type = 1;
+    // See definition in data_stall_event.proto.
+    optional com.android.server.connectivity.ProbeResult validation_result = 2;
+    // See definition in data_stall_event.proto.
+    optional android.net.NetworkCapabilitiesProto.Transport network_type = 3;
+    // See definition in data_stall_event.proto.
+    optional com.android.server.connectivity.WifiData wifi_info = 4 [(log_mode) = MODE_BYTES];
+    // See definition in data_stall_event.proto.
+    optional com.android.server.connectivity.CellularData cell_info = 5 [(log_mode) = MODE_BYTES];
+    // See definition in data_stall_event.proto.
+    optional com.android.server.connectivity.DnsEvent dns_event = 6 [(log_mode) = MODE_BYTES];
+}
+
+/*
+ * Logs when RescueParty resets some set of experiment flags.
+ *
+ * Logged from:
+ *     frameworks/base/services/core/java/com/android/server/RescueParty.java
+ */
+message RescuePartyResetReported {
+    // The rescue level of this reset. A value of 0 indicates missing or unknown level information.
+    optional int32 rescue_level = 1;
+}
+
+/**
+ * Logs when signed config is received from an APK, and if that config was applied successfully.
+ * Logged from:
+ *   frameworks/base/services/core/java/com/android/server/signedconfig/SignedConfigService.java
+ */
+message SignedConfigReported {
+    enum Type {
+        UNKNOWN_TYPE = 0;
+        GLOBAL_SETTINGS = 1;
+    }
+    optional Type type = 1;
+
+    // The final status of the signed config received.
+    enum Status {
+        UNKNOWN_STATUS = 0;
+        APPLIED = 1;
+        BASE64_FAILURE_CONFIG = 2;
+        BASE64_FAILURE_SIGNATURE = 3;
+        SECURITY_EXCEPTION = 4;
+        INVALID_CONFIG = 5;
+        OLD_CONFIG = 6;
+        SIGNATURE_CHECK_FAILED = 7;
+        NOT_APPLICABLE = 8;
+        SIGNATURE_CHECK_FAILED_PROD_KEY_ABSENT = 9;
+    }
+    optional Status status = 2;
+
+    // The version of the signed config processed.
+    optional int32 version = 3;
+
+    // The package name that the config was extracted from.
+    optional string from_package = 4;
+
+    enum Key {
+        NO_KEY = 0;
+        DEBUG = 1;
+        PRODUCTION = 2;
+    }
+    // Which key was used to verify the config.
+    optional Key verified_with = 5;
+}
+
+/*
+ * Logs GNSS Network-Initiated (NI) location events.
+ *
+ * Logged from:
+ *   frameworks/base/services/core/java/com/android/server/location/GnssLocationProvider.java
+ */
+message GnssNiEventReported {
+    // The type of GnssNiEvent.
+    enum EventType {
+        UNKNOWN = 0;
+        NI_REQUEST = 1;
+        NI_RESPONSE = 2;
+    }
+    optional EventType event_type = 1;
+
+    // An ID generated by HAL to associate NI notifications and UI responses.
+    optional int32 notification_id = 2;
+
+    // A type which distinguishes different categories of NI request, such as VOICE, UMTS_SUPL etc.
+    optional int32 ni_type = 3;
+
+    // NI requires notification.
+    optional bool need_notify = 4;
+
+    // NI requires verification.
+    optional bool need_verify = 5;
+
+    // NI requires privacy override, no notification/minimal trace.
+    optional bool privacy_override = 6;
+
+    // Timeout period to wait for user response. Set to 0 for no timeout limit. Specified in
+    // seconds.
+    optional int32 timeout = 7;
+
+    // Default response when timeout.
+    optional int32 default_response = 8;
+
+    // String representing the requester of the network inititated location request.
+    optional string requestor_id = 9;
+
+    // Notification message text string representing the service(for eg. SUPL-service) who sent the
+    // network initiated location request.
+    optional string text = 10;
+
+    // requestorId decoding scheme.
+    optional int32 requestor_id_encoding = 11;
+
+    // Notification message text decoding scheme.
+    optional int32 text_encoding = 12;
+
+    // True if SUPL ES is enabled.
+    optional bool is_supl_es_enabled = 13;
+
+    // True if GNSS location is enabled.
+    optional bool is_location_enabled = 14;
+
+    // GNSS NI responses which define the response in NI structures.
+    optional int32 user_response = 15;
+}
diff --git a/cmds/statsd/src/external/Perfetto.cpp b/cmds/statsd/src/external/Perfetto.cpp
index 42cc543..0c4c330 100644
--- a/cmds/statsd/src/external/Perfetto.cpp
+++ b/cmds/statsd/src/external/Perfetto.cpp
@@ -39,6 +39,7 @@
 namespace statsd {
 
 bool CollectPerfettoTraceAndUploadToDropbox(const PerfettoDetails& config,
+                                            int64_t subscription_id,
                                             int64_t alert_id,
                                             const ConfigKey& configKey) {
     VLOG("Starting trace collection through perfetto");
@@ -48,9 +49,11 @@
         return false;
     }
 
-    char alertId[20];
-    char configId[20];
-    char configUid[20];
+    char subscriptionId[25];
+    char alertId[25];
+    char configId[25];
+    char configUid[25];
+    snprintf(subscriptionId, sizeof(subscriptionId), "%" PRId64, subscription_id);
     snprintf(alertId, sizeof(alertId), "%" PRId64, alert_id);
     snprintf(configId, sizeof(configId), "%" PRId64, configKey.GetId());
     snprintf(configUid, sizeof(configUid), "%d", configKey.GetUid());
@@ -94,7 +97,7 @@
 
         execl("/system/bin/perfetto", "perfetto", "--background", "--config", "-", "--dropbox",
               kDropboxTag, "--alert-id", alertId, "--config-id", configId, "--config-uid",
-              configUid, nullptr);
+              configUid, "--subscription-id", subscriptionId, nullptr);
 
         // execl() doesn't return in case of success, if we get here something
         // failed.
diff --git a/cmds/statsd/src/external/Perfetto.h b/cmds/statsd/src/external/Perfetto.h
index 1e7f728..ab2c195 100644
--- a/cmds/statsd/src/external/Perfetto.h
+++ b/cmds/statsd/src/external/Perfetto.h
@@ -32,6 +32,7 @@
 // This method returns immediately after passing the config and does NOT wait
 // for the full duration of the trace.
 bool CollectPerfettoTraceAndUploadToDropbox(const PerfettoDetails& config,
+                                            int64_t subscription_id,
                                             int64_t alert_id,
                                             const ConfigKey& configKey);
 
diff --git a/config/hiddenapi-greylist.txt b/config/hiddenapi-greylist.txt
index 4d3c5d1..5392a3c 100644
--- a/config/hiddenapi-greylist.txt
+++ b/config/hiddenapi-greylist.txt
@@ -1492,7 +1492,6 @@
 Landroid/test/AndroidTestCase;->getTestContext()Landroid/content/Context;
 Landroid/test/AndroidTestCase;->setTestContext(Landroid/content/Context;)V
 Landroid/test/InstrumentationTestCase;->runMethod(Ljava/lang/reflect/Method;I)V
-Landroid/test/RepetitiveTest;->numIterations()I
 Landroid/util/Singleton;-><init>()V
 Landroid/util/XmlPullAttributes;-><init>(Lorg/xmlpull/v1/XmlPullParser;)V
 Landroid/util/XmlPullAttributes;->mParser:Lorg/xmlpull/v1/XmlPullParser;
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index c89848e..d374f1c 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -17,6 +17,7 @@
 package android.app;
 
 import static android.Manifest.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS;
+import static android.os.Process.myUid;
 
 import static java.lang.Character.MIN_VALUE;
 
@@ -1023,7 +1024,9 @@
      *
      * @return The content capture manager
      */
-    @NonNull private ContentCaptureManager getContentCaptureManager() {
+    @Nullable private ContentCaptureManager getContentCaptureManager() {
+        // ContextCapture disabled for system apps
+        if (!UserHandle.isApp(myUid())) return null;
         if (mContentCaptureManager == null) {
             mContentCaptureManager = getSystemService(ContentCaptureManager.class);
         }
@@ -1046,9 +1049,8 @@
 
     private void notifyContentCaptureManagerIfNeeded(@ContentCaptureNotificationType int type) {
         final ContentCaptureManager cm = getContentCaptureManager();
-        if (cm == null) {
-            return;
-        }
+        if (cm == null) return;
+
         switch (type) {
             case CONTENT_CAPTURE_START:
                 //TODO(b/111276913): decide whether the InteractionSessionId should be
@@ -8289,6 +8291,33 @@
     }
 
     /**
+     * Specifies whether this {@link Activity} should be shown on top of the lock screen whenever
+     * the lockscreen is up and this activity has another activity behind it with the showWhenLock
+     * attribute set. That is, this activity is only visible on the lock screen if there is another
+     * activity with the showWhenLock attribute visible at the same time on the lock screen. A use
+     * case for this is permission dialogs, that should only be visible on the lock screen if their
+     * requesting activity is also visible. This value can be set as a manifest attribute using
+     * android.R.attr#inheritShowWhenLocked.
+     *
+     * @param inheritShowWhenLocked {@code true} to show the {@link Activity} on top of the lock
+     *                              screen when this activity has another activity behind it with
+     *                              the showWhenLock attribute set; {@code false} otherwise.
+     * @see #setShowWhenLocked(boolean)
+     * See android.R.attr#inheritShowWhenLocked
+     * @hide
+     */
+    @SystemApi
+    @TestApi
+    public void setInheritShowWhenLocked(boolean inheritShowWhenLocked) {
+        try {
+            ActivityTaskManager.getService().setInheritShowWhenLocked(
+                    mToken, inheritShowWhenLocked);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Failed to call setInheritShowWhenLocked", e);
+        }
+    }
+
+    /**
      * Specifies whether the screen should be turned on when the {@link Activity} is resumed.
      * Normally an activity will be transitioned to the stopped state if it is started while the
      * screen if off, but with this flag set the activity will cause the screen to turn on if the
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index d423260..e0b8d78 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -3705,11 +3705,16 @@
      * Returns whether switching to provided user was successful.
      *
      * @param user the user to switch to.
+     *
+     * @throws IllegalArgumentException if the user is null.
      * @hide
      */
     @SystemApi
     @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
-    public boolean switchUser(UserHandle user) {
+    public boolean switchUser(@NonNull UserHandle user) {
+        if (user == null) {
+            throw new IllegalArgumentException("UserHandle cannot be null.");
+        }
         return switchUser(user.getIdentifier());
     }
 
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index c90e404..5cac048 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -317,4 +317,13 @@
 
     /** Returns true if the given uid is the app in the foreground. */
     public abstract boolean isAppForeground(int uid);
+
+    /** Remove pending backup for the given userId. */
+    public abstract void clearPendingBackup(int userId);
+
+    /**
+     * When power button is very long pressed, call this interface to do some pre-shutdown work
+     * like persisting database etc.
+     */
+    public abstract void prepareForPossibleShutdown();
 }
diff --git a/core/java/android/app/ActivityView.java b/core/java/android/app/ActivityView.java
index 57132a7..ab8f234 100644
--- a/core/java/android/app/ActivityView.java
+++ b/core/java/android/app/ActivityView.java
@@ -16,6 +16,10 @@
 
 package android.app;
 
+import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_DESTROY_CONTENT_ON_REMOVAL;
+import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY;
+import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC;
+
 import android.annotation.NonNull;
 import android.annotation.UnsupportedAppUsage;
 import android.app.ActivityManager.StackInfo;
@@ -32,7 +36,6 @@
 import android.view.IWindowManager;
 import android.view.InputDevice;
 import android.view.MotionEvent;
-import android.view.Surface;
 import android.view.SurfaceControl;
 import android.view.SurfaceHolder;
 import android.view.SurfaceSession;
@@ -82,7 +85,6 @@
     private boolean mOpened; // Protected by mGuard.
 
     private final SurfaceControl.Transaction mTmpTransaction = new SurfaceControl.Transaction();
-    private Surface mTmpSurface = new Surface();
 
     /** The ActivityView is only allowed to contain one task. */
     private final boolean mSingleTaskInstance;
@@ -319,20 +321,20 @@
     private class SurfaceCallback implements SurfaceHolder.Callback {
         @Override
         public void surfaceCreated(SurfaceHolder surfaceHolder) {
-            mTmpSurface = new Surface();
             if (mVirtualDisplay == null) {
                 initVirtualDisplay(new SurfaceSession());
                 if (mVirtualDisplay != null && mActivityViewCallback != null) {
                     mActivityViewCallback.onActivityViewReady(ActivityView.this);
                 }
             } else {
-                // TODO (b/119209373): DisplayManager determines if a VirtualDisplay is on by
-                // whether it has a surface. Setting a fake surface here so DisplayManager will
-                // consider this display on.
-                mVirtualDisplay.setSurface(mTmpSurface);
                 mTmpTransaction.reparent(mRootSurfaceControl,
                         mSurfaceView.getSurfaceControl().getHandle()).apply();
             }
+
+            if (mVirtualDisplay != null) {
+                mVirtualDisplay.setDisplayState(true);
+            }
+
             updateLocation();
         }
 
@@ -346,10 +348,8 @@
 
         @Override
         public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
-            mTmpSurface.release();
-            mTmpSurface = null;
             if (mVirtualDisplay != null) {
-                mVirtualDisplay.setSurface(null);
+                mVirtualDisplay.setDisplayState(false);
             }
             cleanTapExcludeRegion();
         }
@@ -370,15 +370,11 @@
         final int height = mSurfaceView.getHeight();
         final DisplayManager displayManager = mContext.getSystemService(DisplayManager.class);
 
-        // TODO (b/119209373): DisplayManager determines if a VirtualDisplay is on by
-        // whether it has a surface. Setting a fake surface here so DisplayManager will consider
-        // this display on.
         mVirtualDisplay = displayManager.createVirtualDisplay(
-                DISPLAY_NAME + "@" + System.identityHashCode(this),
-                width, height, getBaseDisplayDensity(), mTmpSurface,
-                DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC
-                        | DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY
-                        | DisplayManager.VIRTUAL_DISPLAY_FLAG_DESTROY_CONTENT_ON_REMOVAL);
+                DISPLAY_NAME + "@" + System.identityHashCode(this), width, height,
+                getBaseDisplayDensity(), null,
+                VIRTUAL_DISPLAY_FLAG_PUBLIC | VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY
+                        | VIRTUAL_DISPLAY_FLAG_DESTROY_CONTENT_ON_REMOVAL);
         if (mVirtualDisplay == null) {
             Log.e(TAG, "Failed to initialize ActivityView");
             return;
@@ -443,11 +439,6 @@
             displayReleased = false;
         }
 
-        if (mTmpSurface != null) {
-            mTmpSurface.release();
-            mTmpSurface = null;
-        }
-
         if (displayReleased && mActivityViewCallback != null) {
             mActivityViewCallback.onActivityViewDestroyed(this);
         }
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 94983e1..9bcb36f 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -2276,6 +2276,16 @@
     }
 
     @Override
+    public String[] setDistractingPackageRestrictions(String[] packages, int distractionFlags) {
+        try {
+            return mPM.setDistractingPackageRestrictionsAsUser(packages, distractionFlags,
+                    mContext.getUserId());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    @Override
     public String[] setPackagesSuspended(String[] packageNames, boolean suspended,
             PersistableBundle appExtras, PersistableBundle launcherExtras,
             String dialogMessage) {
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index 347973e..fb65da1 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -317,7 +317,6 @@
      */
     void requestWifiBugReport(in String shareTitle, in String shareDescription);
 
-    void clearPendingBackup();
     Intent getIntentForIntentSender(in IIntentSender sender);
     // This is not public because you need to be very careful in how you
     // manage your activity to make sure it is always the uid you expect.
diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl
index dd87dc3..cc6c999 100644
--- a/core/java/android/app/IActivityTaskManager.aidl
+++ b/core/java/android/app/IActivityTaskManager.aidl
@@ -419,6 +419,7 @@
     void updateLockTaskFeatures(int userId, int flags);
 
     void setShowWhenLocked(in IBinder token, boolean showWhenLocked);
+    void setInheritShowWhenLocked(in IBinder token, boolean setInheritShownWhenLocked);
     void setTurnScreenOn(in IBinder token, boolean turnScreenOn);
 
     /**
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index 163be8e..199c133 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -65,9 +65,9 @@
     boolean areNotificationsEnabled(String pkg);
     int getPackageImportance(String pkg);
 
-    void setAppOverlaysAllowed(String pkg, int uid, boolean allowed);
-    boolean areAppOverlaysAllowed(String pkg);
-    boolean areAppOverlaysAllowedForPackage(String pkg, int uid);
+    void setBubblesAllowed(String pkg, int uid, boolean allowed);
+    boolean areBubblesAllowed(String pkg);
+    boolean areBubblesAllowedForPackage(String pkg, int uid);
 
     void createNotificationChannelGroups(String pkg, in ParceledListSlice channelGroupList);
     void createNotificationChannels(String pkg, in ParceledListSlice channelsList);
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index b657a91..72819cb 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -1276,7 +1276,7 @@
     private String mShortcutId;
     private CharSequence mSettingsText;
 
-    private PendingIntent mAppOverlayIntent;
+    private BubbleMetadata mBubbleMetadata;
 
     /** @hide */
     @IntDef(prefix = { "GROUP_ALERT_" }, value = {
@@ -2278,7 +2278,7 @@
 
         mGroupAlertBehavior = parcel.readInt();
         if (parcel.readInt() != 0) {
-            mAppOverlayIntent = PendingIntent.CREATOR.createFromParcel(parcel);
+            mBubbleMetadata = BubbleMetadata.CREATOR.createFromParcel(parcel);
         }
 
         mAllowSystemGeneratedContextualActions = parcel.readBoolean();
@@ -2396,7 +2396,7 @@
         that.mBadgeIcon = this.mBadgeIcon;
         that.mSettingsText = this.mSettingsText;
         that.mGroupAlertBehavior = this.mGroupAlertBehavior;
-        that.mAppOverlayIntent = this.mAppOverlayIntent;
+        that.mBubbleMetadata = this.mBubbleMetadata;
         that.mAllowSystemGeneratedContextualActions = this.mAllowSystemGeneratedContextualActions;
 
         if (!heavy) {
@@ -2719,9 +2719,9 @@
 
         parcel.writeInt(mGroupAlertBehavior);
 
-        if (mAppOverlayIntent != null) {
+        if (mBubbleMetadata != null) {
             parcel.writeInt(1);
-            mAppOverlayIntent.writeToParcel(parcel, 0);
+            mBubbleMetadata.writeToParcel(parcel, 0);
         } else {
             parcel.writeInt(0);
         }
@@ -3141,11 +3141,11 @@
     }
 
     /**
-     * Returns the intent that will be used to display app content in a floating window over the
-     * existing foreground activity.
+     * Returns the bubble metadata that will be used to display app content in a floating window
+     * over the existing foreground activity.
      */
-    public PendingIntent getAppOverlayIntent() {
-        return mAppOverlayIntent;
+    public BubbleMetadata getBubbleMetadata() {
+        return mBubbleMetadata;
     }
 
     /**
@@ -3508,19 +3508,18 @@
         }
 
         /**
-         * Sets the intent that will be used to display app content in a floating window
-         * over the existing foreground activity.
+         * Sets the {@link BubbleMetadata} that will be used to display app content in a floating
+         * window over the existing foreground activity.
          *
-         * <p>This intent will be ignored unless this notification is posted to a channel that
-         * allows {@link NotificationChannel#canOverlayApps() app overlays}.</p>
+         * <p>This data will be ignored unless the notification is posted to a channel that
+         * allows {@link NotificationChannel#canBubble() bubbles}.</p>
          *
-         * <p>Notifications with a valid and allowed app overlay intent will be displayed as
-         * floating windows outside of the notification shade on unlocked devices. When a user
-         * interacts with one of these windows, this app overlay intent will be invoked and
-         * displayed.</p>
+         * <b>Notifications with a valid and allowed bubble metadata will display in collapsed state
+         * outside of the notification shade on unlocked devices. When a user interacts with the
+         * collapsed state, the bubble intent will be invoked and displayed.</b>
          */
-        public Builder setAppOverlayIntent(PendingIntent intent) {
-            mN.mAppOverlayIntent = intent;
+        public Builder setBubbleMetadata(BubbleMetadata data) {
+            mN.mBubbleMetadata = data;
             return this;
         }
 
@@ -8422,6 +8421,186 @@
         }
     }
 
+    /**
+     * Encapsulates the information needed to display a notification as a bubble.
+     *
+     * <p>A bubble is used to display app content in a floating window over the existing
+     * foreground activity. A bubble has a collapsed state represented by an icon,
+     * {@link BubbleMetadata.Builder#setIcon(Icon)} and an expanded state which is populated
+     * via {@link BubbleMetadata.Builder#setIntent(PendingIntent)}.</p>
+     *
+     * <b>Notifications with a valid and allowed bubble will display in collapsed state
+     * outside of the notification shade on unlocked devices. When a user interacts with the
+     * collapsed bubble, the bubble intent will be invoked and displayed.</b>
+     *
+     * @see Notification.Builder#setBubbleMetadata(BubbleMetadata)
+     */
+    public static final class BubbleMetadata implements Parcelable {
+
+        private PendingIntent mPendingIntent;
+        private CharSequence mTitle;
+        private Icon mIcon;
+        private int mDesiredHeight;
+
+        private BubbleMetadata(PendingIntent intent, CharSequence title, Icon icon, int height) {
+            mPendingIntent = intent;
+            mTitle = title;
+            mIcon = icon;
+            mDesiredHeight = height;
+        }
+
+        private BubbleMetadata(Parcel in) {
+            mPendingIntent = PendingIntent.CREATOR.createFromParcel(in);
+            mTitle = in.readCharSequence();
+            mIcon = Icon.CREATOR.createFromParcel(in);
+            mDesiredHeight = in.readInt();
+        }
+
+        /**
+         * @return the pending intent used to populate the floating window for this bubble.
+         */
+        public PendingIntent getIntent() {
+            return mPendingIntent;
+        }
+
+        /**
+         * @return the title that will appear along with the app content defined by
+         * {@link #getIntent()} for this bubble.
+         */
+        public CharSequence getTitle() {
+            return mTitle;
+        }
+
+        /**
+         * @return the icon that will be displayed for this bubble when it is collapsed.
+         */
+        public Icon getIcon() {
+            return mIcon;
+        }
+
+        /**
+         * @return the ideal height for the floating window that app content defined by
+         * {@link #getIntent()} for this bubble.
+         */
+        public int getDesiredHeight() {
+            return mDesiredHeight;
+        }
+
+        public static final Parcelable.Creator<BubbleMetadata> CREATOR =
+                new Parcelable.Creator<BubbleMetadata>() {
+
+                    @Override
+                    public BubbleMetadata createFromParcel(Parcel source) {
+                        return new BubbleMetadata(source);
+                    }
+
+                    @Override
+                    public BubbleMetadata[] newArray(int size) {
+                        return new BubbleMetadata[size];
+                    }
+                };
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        @Override
+        public void writeToParcel(Parcel out, int flags) {
+            mPendingIntent.writeToParcel(out, 0);
+            out.writeCharSequence(mTitle);
+            mIcon.writeToParcel(out, 0);
+            out.writeInt(mDesiredHeight);
+        }
+
+        /**
+         * Builder to construct a {@link BubbleMetadata} object.
+         */
+        public static class Builder {
+
+            private PendingIntent mPendingIntent;
+            private CharSequence mTitle;
+            private Icon mIcon;
+            private int mDesiredHeight;
+
+            /**
+             * Constructs a new builder object.
+             */
+            public Builder() {
+            }
+
+            /**
+             * Sets the intent that will be used when the bubble is expanded. This will display the
+             * app content in a floating window over the existing foreground activity.
+             */
+            public BubbleMetadata.Builder setIntent(PendingIntent intent) {
+                if (intent == null) {
+                    throw new IllegalArgumentException("Bubble requires non-null pending intent");
+                }
+                mPendingIntent = intent;
+                return this;
+            }
+
+            /**
+             * Sets the title that will appear along with the app content for this bubble.
+             *
+             * <p>A title is required and should expect to fit on a single line and make sense when
+             * shown with the content defined by {@link #setIntent(PendingIntent)}.</p>
+             */
+            public BubbleMetadata.Builder setTitle(CharSequence title) {
+                if (TextUtils.isEmpty(title)) {
+                    throw new IllegalArgumentException("Bubbles require non-null or empty title");
+                }
+                mTitle = title;
+                return this;
+            }
+
+            /**
+             * Sets the icon that will represent the bubble when it is collapsed.
+             *
+             * <p>An icon is required and should be representative of the content within the bubble.
+             * If your app produces multiple bubbles, the image should be unique for each of them.
+             * </p>
+             */
+            public BubbleMetadata.Builder setIcon(Icon icon) {
+                if (icon == null) {
+                    throw new IllegalArgumentException("Bubbles require non-null icon");
+                }
+                mIcon = icon;
+                return this;
+            }
+
+            /**
+             * Sets the desired height for the app content defined by
+             * {@link #setIntent(PendingIntent)}, this height may not be respected if there is not
+             * enough space on the screen or if the provided height is too small to be useful.
+             */
+            public BubbleMetadata.Builder setDesiredHeight(int height) {
+                mDesiredHeight = Math.max(height, 0);
+                return this;
+            }
+
+            /**
+             * Creates the {@link BubbleMetadata} defined by this builder.
+             * <p>Will throw {@link IllegalStateException} if required fields have not been set
+             * on this builder.</p>
+             */
+            public BubbleMetadata build() {
+                if (mPendingIntent == null) {
+                    throw new IllegalStateException("Must supply pending intent to bubble");
+                }
+                if (TextUtils.isEmpty(mTitle)) {
+                    throw new IllegalStateException("Must supply a title for the bubble");
+                }
+                if (mIcon == null) {
+                    throw new IllegalStateException("Must supply an icon for the bubble");
+                }
+                return new BubbleMetadata(mPendingIntent, mTitle, mIcon, mDesiredHeight);
+            }
+        }
+    }
+
+
     // When adding a new Style subclass here, don't forget to update
     // Builder.getNotificationStyleClass.
 
diff --git a/core/java/android/app/NotificationChannel.java b/core/java/android/app/NotificationChannel.java
index 41ceaaf..e95d62f 100644
--- a/core/java/android/app/NotificationChannel.java
+++ b/core/java/android/app/NotificationChannel.java
@@ -19,6 +19,7 @@
 
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
+import android.annotation.TestApi;
 import android.annotation.UnsupportedAppUsage;
 import android.app.NotificationManager.Importance;
 import android.content.ContentResolver;
@@ -84,7 +85,7 @@
     private static final String ATT_FG_SERVICE_SHOWN = "fgservice";
     private static final String ATT_GROUP = "group";
     private static final String ATT_BLOCKABLE_SYSTEM = "blockable_system";
-    private static final String ATT_ALLOW_APP_OVERLAY = "app_overlay";
+    private static final String ATT_ALLOW_BUBBLE = "allow_bubble";
     private static final String DELIMITER = ",";
 
     /**
@@ -120,7 +121,7 @@
     /**
      * @hide
      */
-    public static final int USER_LOCKED_ALLOW_APP_OVERLAY = 0x00000100;
+    public static final int USER_LOCKED_ALLOW_BUBBLE = 0x00000100;
 
     /**
      * @hide
@@ -133,7 +134,7 @@
             USER_LOCKED_VIBRATION,
             USER_LOCKED_SOUND,
             USER_LOCKED_SHOW_BADGE,
-            USER_LOCKED_ALLOW_APP_OVERLAY
+            USER_LOCKED_ALLOW_BUBBLE
     };
 
     private static final int DEFAULT_LIGHT_COLOR = 0;
@@ -143,7 +144,7 @@
             NotificationManager.IMPORTANCE_UNSPECIFIED;
     private static final boolean DEFAULT_DELETED = false;
     private static final boolean DEFAULT_SHOW_BADGE = true;
-    private static final boolean DEFAULT_ALLOW_APP_OVERLAY = true;
+    private static final boolean DEFAULT_ALLOW_BUBBLE = true;
 
     @UnsupportedAppUsage
     private final String mId;
@@ -167,7 +168,8 @@
     private AudioAttributes mAudioAttributes = Notification.AUDIO_ATTRIBUTES_DEFAULT;
     // If this is a blockable system notification channel.
     private boolean mBlockableSystem = false;
-    private boolean mAllowAppOverlay = DEFAULT_ALLOW_APP_OVERLAY;
+    private boolean mAllowBubbles = DEFAULT_ALLOW_BUBBLE;
+    private boolean mImportanceLockedByOEM;
 
     /**
      * Creates a notification channel.
@@ -229,7 +231,8 @@
         mAudioAttributes = in.readInt() > 0 ? AudioAttributes.CREATOR.createFromParcel(in) : null;
         mLightColor = in.readInt();
         mBlockableSystem = in.readBoolean();
-        mAllowAppOverlay = in.readBoolean();
+        mAllowBubbles = in.readBoolean();
+        mImportanceLockedByOEM = in.readBoolean();
     }
 
     @Override
@@ -282,7 +285,8 @@
         }
         dest.writeInt(mLightColor);
         dest.writeBoolean(mBlockableSystem);
-        dest.writeBoolean(mAllowAppOverlay);
+        dest.writeBoolean(mAllowBubbles);
+        dest.writeBoolean(mImportanceLockedByOEM);
     }
 
     /**
@@ -476,7 +480,7 @@
 
     /**
      * Sets whether notifications posted to this channel can appear outside of the notification
-     * shade, floating over other apps' content.
+     * shade, floating over other apps' content as a bubble.
      *
      * <p>This value will be ignored for channels that aren't allowed to pop on screen (that is,
      * channels whose {@link #getImportance() importance} is <
@@ -484,10 +488,10 @@
      *
      * <p>Only modifiable before the channel is submitted to
      *      * {@link NotificationManager#createNotificationChannel(NotificationChannel)}.</p>
-     * @see Notification#getAppOverlayIntent()
+     * @see Notification#getBubbleMetadata()
      */
-    public void setAllowAppOverlay(boolean allowAppOverlay) {
-        mAllowAppOverlay = allowAppOverlay;
+    public void setAllowBubbles(boolean allowBubbles) {
+        mAllowBubbles = allowBubbles;
     }
 
     /**
@@ -606,16 +610,16 @@
      * Returns whether notifications posted to this channel can display outside of the notification
      * shade, in a floating window on top of other apps.
      */
-    public boolean canOverlayApps() {
-        return isAppOverlayAllowed() && getImportance() >= IMPORTANCE_HIGH;
+    public boolean canBubble() {
+        return isBubbleAllowed() && getImportance() >= IMPORTANCE_HIGH;
     }
 
     /**
-     * Like {@link #canOverlayApps()}, but only checks the permission, not the importance.
+     * Like {@link #canBubble()}, but only checks the permission, not the importance.
      * @hide
      */
-    public boolean isAppOverlayAllowed() {
-        return mAllowAppOverlay;
+    public boolean isBubbleAllowed() {
+        return mAllowBubbles;
     }
 
     /**
@@ -649,6 +653,22 @@
     }
 
     /**
+     * @hide
+     */
+    @TestApi
+    public void setImportanceLockedByOEM(boolean locked) {
+        mImportanceLockedByOEM = locked;
+    }
+
+    /**
+     * @hide
+     */
+    @TestApi
+    public boolean isImportanceLockedByOEM() {
+        return mImportanceLockedByOEM;
+    }
+
+    /**
      * Returns whether the user has chosen the importance of this channel, either to affirm the
      * initial selection from the app, or changed it to be higher or lower.
      * @see #getImportance()
@@ -699,7 +719,7 @@
         lockFields(safeInt(parser, ATT_USER_LOCKED, 0));
         setFgServiceShown(safeBool(parser, ATT_FG_SERVICE_SHOWN, false));
         setBlockableSystem(safeBool(parser, ATT_BLOCKABLE_SYSTEM, false));
-        setAllowAppOverlay(safeBool(parser, ATT_ALLOW_APP_OVERLAY, DEFAULT_ALLOW_APP_OVERLAY));
+        setAllowBubbles(safeBool(parser, ATT_ALLOW_BUBBLE, DEFAULT_ALLOW_BUBBLE));
     }
 
     @Nullable
@@ -818,8 +838,8 @@
         if (isBlockableSystem()) {
             out.attribute(null, ATT_BLOCKABLE_SYSTEM, Boolean.toString(isBlockableSystem()));
         }
-        if (canOverlayApps() != DEFAULT_ALLOW_APP_OVERLAY) {
-            out.attribute(null, ATT_ALLOW_APP_OVERLAY, Boolean.toString(canOverlayApps()));
+        if (canBubble() != DEFAULT_ALLOW_BUBBLE) {
+            out.attribute(null, ATT_ALLOW_BUBBLE, Boolean.toString(canBubble()));
         }
 
         out.endTag(null, TAG_CHANNEL);
@@ -863,7 +883,7 @@
         record.put(ATT_DELETED, Boolean.toString(isDeleted()));
         record.put(ATT_GROUP, getGroup());
         record.put(ATT_BLOCKABLE_SYSTEM, isBlockableSystem());
-        record.put(ATT_ALLOW_APP_OVERLAY, canOverlayApps());
+        record.put(ATT_ALLOW_BUBBLE, canBubble());
         return record;
     }
 
@@ -952,25 +972,26 @@
         if (this == o) return true;
         if (o == null || getClass() != o.getClass()) return false;
         NotificationChannel that = (NotificationChannel) o;
-        return getImportance() == that.getImportance() &&
-                mBypassDnd == that.mBypassDnd &&
-                getLockscreenVisibility() == that.getLockscreenVisibility() &&
-                mLights == that.mLights &&
-                getLightColor() == that.getLightColor() &&
-                getUserLockedFields() == that.getUserLockedFields() &&
-                isFgServiceShown() == that.isFgServiceShown() &&
-                mVibrationEnabled == that.mVibrationEnabled &&
-                mShowBadge == that.mShowBadge &&
-                isDeleted() == that.isDeleted() &&
-                isBlockableSystem() == that.isBlockableSystem() &&
-                mAllowAppOverlay == that.mAllowAppOverlay &&
-                Objects.equals(getId(), that.getId()) &&
-                Objects.equals(getName(), that.getName()) &&
-                Objects.equals(mDesc, that.mDesc) &&
-                Objects.equals(getSound(), that.getSound()) &&
-                Arrays.equals(mVibration, that.mVibration) &&
-                Objects.equals(getGroup(), that.getGroup()) &&
-                Objects.equals(getAudioAttributes(), that.getAudioAttributes());
+        return getImportance() == that.getImportance()
+                && mBypassDnd == that.mBypassDnd
+                && getLockscreenVisibility() == that.getLockscreenVisibility()
+                && mLights == that.mLights
+                && getLightColor() == that.getLightColor()
+                && getUserLockedFields() == that.getUserLockedFields()
+                && isFgServiceShown() == that.isFgServiceShown()
+                && mVibrationEnabled == that.mVibrationEnabled
+                && mShowBadge == that.mShowBadge
+                && isDeleted() == that.isDeleted()
+                && isBlockableSystem() == that.isBlockableSystem()
+                && mAllowBubbles == that.mAllowBubbles
+                && Objects.equals(getId(), that.getId())
+                && Objects.equals(getName(), that.getName())
+                && Objects.equals(mDesc, that.mDesc)
+                && Objects.equals(getSound(), that.getSound())
+                && Arrays.equals(mVibration, that.mVibration)
+                && Objects.equals(getGroup(), that.getGroup())
+                && Objects.equals(getAudioAttributes(), that.getAudioAttributes())
+                && mImportanceLockedByOEM == that.mImportanceLockedByOEM;
     }
 
     @Override
@@ -979,7 +1000,8 @@
                 getLockscreenVisibility(), getSound(), mLights, getLightColor(),
                 getUserLockedFields(),
                 isFgServiceShown(), mVibrationEnabled, mShowBadge, isDeleted(), getGroup(),
-                getAudioAttributes(), isBlockableSystem(), mAllowAppOverlay);
+                getAudioAttributes(), isBlockableSystem(), mAllowBubbles,
+                mImportanceLockedByOEM);
         result = 31 * result + Arrays.hashCode(mVibration);
         return result;
     }
@@ -1006,7 +1028,8 @@
                 + ", mGroup='" + mGroup + '\''
                 + ", mAudioAttributes=" + mAudioAttributes
                 + ", mBlockableSystem=" + mBlockableSystem
-                + ", mAllowAppOverlay=" + mAllowAppOverlay
+                + ", mAllowBubbles=" + mAllowBubbles
+                + ", mImportanceLockedByOEM=" + mImportanceLockedByOEM
                 + '}';
         pw.println(prefix + output);
     }
@@ -1032,7 +1055,8 @@
                 + ", mGroup='" + mGroup + '\''
                 + ", mAudioAttributes=" + mAudioAttributes
                 + ", mBlockableSystem=" + mBlockableSystem
-                + ", mAllowAppOverlay=" + mAllowAppOverlay
+                + ", mAllowBubbles=" + mAllowBubbles
+                + ", mImportanceLockedByOEM=" + mImportanceLockedByOEM
                 + '}';
     }
 
@@ -1066,7 +1090,7 @@
             mAudioAttributes.writeToProto(proto, NotificationChannelProto.AUDIO_ATTRIBUTES);
         }
         proto.write(NotificationChannelProto.IS_BLOCKABLE_SYSTEM, mBlockableSystem);
-        proto.write(NotificationChannelProto.ALLOW_APP_OVERLAY, mAllowAppOverlay);
+        proto.write(NotificationChannelProto.ALLOW_APP_OVERLAY, mAllowBubbles);
 
         proto.end(token);
     }
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index aad3253..43614fe 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -1080,14 +1080,14 @@
      * notification shade, floating over other apps' content.
      *
      * <p>This value will be ignored for notifications that are posted to channels that do not
-     * allow app overlays ({@link NotificationChannel#canOverlayApps()}.
+     * allow bubbles ({@link NotificationChannel#canBubble()}.
      *
-     * @see Notification#getAppOverlayIntent()
+     * @see Notification#getBubbleMetadata()
      */
-    public boolean areAppOverlaysAllowed() {
+    public boolean areBubblesAllowed() {
         INotificationManager service = getService();
         try {
-            return service.areAppOverlaysAllowed(mContext.getPackageName());
+            return service.areBubblesAllowed(mContext.getPackageName());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index d827f6c..3adafd72 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -45,6 +45,8 @@
 import android.content.Context;
 import android.content.IRestrictionsManager;
 import android.content.RestrictionsManager;
+import android.content.om.IOverlayManager;
+import android.content.om.OverlayManager;
 import android.content.pm.CrossProfileApps;
 import android.content.pm.ICrossProfileApps;
 import android.content.pm.IShortcutService;
@@ -1053,6 +1055,14 @@
                 return new ShortcutManager(ctx, IShortcutService.Stub.asInterface(b));
             }});
 
+        registerService(Context.OVERLAY_SERVICE, OverlayManager.class,
+                new CachedServiceFetcher<OverlayManager>() {
+            @Override
+            public OverlayManager createService(ContextImpl ctx) throws ServiceNotFoundException {
+                IBinder b = ServiceManager.getServiceOrThrow(Context.OVERLAY_SERVICE);
+                return new OverlayManager(ctx, IOverlayManager.Stub.asInterface(b));
+            }});
+
         registerService(Context.NETWORK_WATCHLIST_SERVICE, NetworkWatchlistManager.class,
                 new CachedServiceFetcher<NetworkWatchlistManager>() {
                     @Override
diff --git a/core/java/android/app/role/RoleManager.java b/core/java/android/app/role/RoleManager.java
index 27581fc..a6abe0b 100644
--- a/core/java/android/app/role/RoleManager.java
+++ b/core/java/android/app/role/RoleManager.java
@@ -179,16 +179,6 @@
     public static final String ACTION_REQUEST_ROLE = "android.app.role.action.REQUEST_ROLE";
 
     /**
-     * The name of the requested role.
-     * <p>
-     * <strong>Type:</strong> String
-     *
-     * @hide
-     */
-    @SystemApi
-    public static final String EXTRA_REQUEST_ROLE_NAME = "android.app.role.extra.REQUEST_ROLE_NAME";
-
-    /**
      * The permission required to manage records of role holders in {@link RoleManager} directly.
      *
      * @hide
@@ -236,7 +226,7 @@
         Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
         Intent intent = new Intent(ACTION_REQUEST_ROLE);
         intent.setPackage(mContext.getPackageManager().getPermissionControllerPackageName());
-        intent.putExtra(EXTRA_REQUEST_ROLE_NAME, roleName);
+        intent.putExtra(Intent.EXTRA_ROLE_NAME, roleName);
         return intent;
     }
 
diff --git a/core/java/android/app/usage/EventList.java b/core/java/android/app/usage/EventList.java
index a79ad2f..aaae57e5 100644
--- a/core/java/android/app/usage/EventList.java
+++ b/core/java/android/app/usage/EventList.java
@@ -103,21 +103,4 @@
         }
         return result;
     }
-
-    /**
-     * Remove events of certain type on or after a timestamp.
-     * @param type The type of event to remove.
-     * @param timeStamp the timeStamp on or after which to remove the event.
-     */
-    public void removeOnOrAfter(int type, long timeStamp) {
-        for (int i = mEvents.size() - 1; i >= 0; i--) {
-            UsageEvents.Event event = mEvents.get(i);
-            if (event.mTimeStamp < timeStamp) {
-                break;
-            }
-            if (event.mEventType == type) {
-                mEvents.remove(i);
-            }
-        }
-    }
 }
diff --git a/core/java/android/app/usage/UsageEvents.java b/core/java/android/app/usage/UsageEvents.java
index d7a5328..2c5fe04 100644
--- a/core/java/android/app/usage/UsageEvents.java
+++ b/core/java/android/app/usage/UsageEvents.java
@@ -245,10 +245,18 @@
         public static final int FLUSH_TO_DISK = 25;
 
         /**
+         * An event type denoting that the device underwent a shutdown process.
+         * A DEVICE_SHUTDOWN event should be treated as if all started activities and foreground
+         * services are now stopped and no explicit {@link #ACTIVITY_STOPPED} and
+         * {@link #FOREGROUND_SERVICE_STOP} events will be generated for them.
+         */
+        public static final int DEVICE_SHUTDOWN = 26;
+
+        /**
          * Keep in sync with the greatest event type value.
          * @hide
          */
-        public static final int MAX_EVENT_TYPE = 25;
+        public static final int MAX_EVENT_TYPE = 26;
 
         /** @hide */
         public static final int FLAG_IS_PACKAGE_INSTANT_APP = 1 << 0;
diff --git a/core/java/android/app/usage/UsageStats.java b/core/java/android/app/usage/UsageStats.java
index 308180b..94a2a3e 100644
--- a/core/java/android/app/usage/UsageStats.java
+++ b/core/java/android/app/usage/UsageStats.java
@@ -21,6 +21,7 @@
 import static android.app.usage.UsageEvents.Event.ACTIVITY_RESUMED;
 import static android.app.usage.UsageEvents.Event.ACTIVITY_STOPPED;
 import static android.app.usage.UsageEvents.Event.CONTINUING_FOREGROUND_SERVICE;
+import static android.app.usage.UsageEvents.Event.DEVICE_SHUTDOWN;
 import static android.app.usage.UsageEvents.Event.END_OF_DAY;
 import static android.app.usage.UsageEvents.Event.FLUSH_TO_DISK;
 import static android.app.usage.UsageEvents.Event.FOREGROUND_SERVICE_START;
@@ -119,12 +120,9 @@
     public int mLastEvent;
 
     /**
-     * If an activity is visible(onStart(), onPause() states) or in foreground (onResume() state),
-     * it has one entry in this map. When an activity becomes invisible (onStop() or onDestroy()),
-     * it is removed from this map.
      * Key is instanceId of the activity (ActivityRecode appToken hashCode)..
-     * Value is this activity's last event, one of ACTIVITY_RESUMED or
-     * ACTIVITY_PAUSED.
+     * Value is this activity's last event, one of ACTIVITY_RESUMED, ACTIVITY_PAUSED or
+     * ACTIVITY_STOPPED.
      * {@hide}
      */
     public SparseIntArray mActivities = new SparseIntArray();
@@ -560,6 +558,7 @@
                 mLastTimeForegroundServiceUsed = timeStamp;
                 mForegroundServices.put(className, eventType);
                 break;
+            case DEVICE_SHUTDOWN:
             case FLUSH_TO_DISK:
                 // update usage of all active activities/services.
                 if (hasForegroundActivity()) {
diff --git a/core/java/android/app/usage/UsageStatsManagerInternal.java b/core/java/android/app/usage/UsageStatsManagerInternal.java
index 2edad35..cc3ab00 100644
--- a/core/java/android/app/usage/UsageStatsManagerInternal.java
+++ b/core/java/android/app/usage/UsageStatsManagerInternal.java
@@ -98,6 +98,12 @@
     public abstract void prepareShutdown();
 
     /**
+     * When the device power button is long pressed for 3.5 seconds, prepareForPossibleShutdown()
+     * is called.
+     */
+    public abstract void prepareForPossibleShutdown();
+
+    /**
      * Returns true if the app has not been used for a certain amount of time. How much time?
      * Could be hours, could be days, who knows?
      *
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index e37126b..d27cce5 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -1833,6 +1833,37 @@
             "android.intent.action.REVIEW_PERMISSIONS";
 
     /**
+     * Activity action: Launch UI to manage a default app.
+     * <p>
+     * Input: {@link #EXTRA_ROLE_NAME} specifies the role of the default app which will be managed
+     * by the launched UI.
+     * </p>
+     * <p>
+     * Output: Nothing.
+     * </p>
+     *
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS)
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    @SystemApi
+    public static final String ACTION_MANAGE_DEFAULT_APP =
+            "android.intent.action.MANAGE_DEFAULT_APP";
+
+    /**
+     * Intent extra: A role name.
+     * <p>
+     * Type: String
+     * </p>
+     *
+     * @see android.app.role.RoleManager
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final String EXTRA_ROLE_NAME = "android.intent.extra.ROLE_NAME";
+
+    /**
      * Activity action: Launch UI to manage special app accesses.
      * <p>
      * Input: Nothing.
@@ -2411,6 +2442,25 @@
     public static final String ACTION_PACKAGES_UNSUSPENDED = "android.intent.action.PACKAGES_UNSUSPENDED";
 
     /**
+     * Broadcast Action: Distracting packages have been changed.
+     * <p>Includes the following extras:
+     * <ul>
+     * <li> {@link #EXTRA_CHANGED_PACKAGE_LIST} is the set of packages which have been changed.
+     * <li> {@link #EXTRA_CHANGED_UID_LIST} is the set of uids which have been changed.
+     * <li> {@link #EXTRA_DISTRACTION_RESTRICTIONS} the new restrictions set on these packages.
+     * </ul>
+     *
+     * <p class="note">This is a protected intent that can only be sent
+     * by the system. It is only sent to registered receivers.
+     *
+     * @see PackageManager#setDistractingPackageRestrictions(String[], int)
+     * @hide
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_DISTRACTING_PACKAGES_CHANGED =
+            "android.intent.action.DISTRACTING_PACKAGES_CHANGED";
+
+    /**
      * Broadcast Action: Sent to a package that has been suspended by the system. This is sent
      * whenever a package is put into a suspended state or any of its app extras change while in the
      * suspended state.
@@ -5120,6 +5170,17 @@
             "android.intent.extra.changed_uid_list";
 
     /**
+     * An integer denoting a bitwise combination of restrictions set on distracting packages via
+     * {@link PackageManager#setDistractingPackageRestrictions(String[], int)}
+     *
+     * @hide
+     * @see PackageManager.DistractionRestriction
+     * @see PackageManager#setDistractingPackageRestrictions(String[], int)
+     */
+    public static final String EXTRA_DISTRACTION_RESTRICTIONS =
+            "android.intent.extra.distraction_restrictions";
+
+    /**
      * @hide
      * Magic extra system code can use when binding, to give a label for
      * who it is that has bound to a service.  This is an integer giving
diff --git a/core/java/android/content/om/OverlayInfo.java b/core/java/android/content/om/OverlayInfo.java
index dd55003..1989f06 100644
--- a/core/java/android/content/om/OverlayInfo.java
+++ b/core/java/android/content/om/OverlayInfo.java
@@ -18,8 +18,7 @@
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
-import android.annotation.UnsupportedAppUsage;
-import android.os.Build;
+import android.annotation.SystemApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -32,8 +31,10 @@
  *
  * @hide
  */
+@SystemApi
 public final class OverlayInfo implements Parcelable {
 
+    /** @hide */
     @IntDef(prefix = "STATE_", value = {
             STATE_UNKNOWN,
             STATE_MISSING_TARGET,
@@ -44,6 +45,7 @@
             STATE_TARGET_UPGRADING,
             STATE_OVERLAY_UPGRADING,
     })
+    /** @hide */
     @Retention(RetentionPolicy.SOURCE)
     public @interface State {}
 
@@ -52,17 +54,23 @@
      * objects exposed outside the {@link
      * com.android.server.om.OverlayManagerService} should never have this
      * state.
+     *
+     * @hide
      */
     public static final int STATE_UNKNOWN = -1;
 
     /**
      * The target package of the overlay is not installed. The overlay cannot be enabled.
+     *
+     * @hide
      */
     public static final int STATE_MISSING_TARGET = 0;
 
     /**
      * Creation of idmap file failed (e.g. no matching resources). The overlay
      * cannot be enabled.
+     *
+     * @hide
      */
     public static final int STATE_NO_IDMAP = 1;
 
@@ -70,6 +78,7 @@
      * The overlay is currently disabled. It can be enabled.
      *
      * @see IOverlayManager#setEnabled
+     * @hide
      */
     public static final int STATE_DISABLED = 2;
 
@@ -77,18 +86,21 @@
      * The overlay is currently enabled. It can be disabled.
      *
      * @see IOverlayManager#setEnabled
+     * @hide
      */
     public static final int STATE_ENABLED = 3;
 
     /**
      * The target package is currently being upgraded; the state will change
      * once the package installation has finished.
+     * @hide
      */
     public static final int STATE_TARGET_UPGRADING = 4;
 
     /**
      * The overlay package is currently being upgraded; the state will change
      * once the package installation has finished.
+     * @hide
      */
     public static final int STATE_OVERLAY_UPGRADING = 5;
 
@@ -96,6 +108,7 @@
      * The overlay package is currently enabled because it is marked as
      * 'static'. It cannot be disabled but will change state if for instance
      * its target is uninstalled.
+     * @hide
      */
     public static final int STATE_ENABLED_STATIC = 6;
 
@@ -103,40 +116,52 @@
      * Overlay category: theme.
      * <p>
      * Change how Android (including the status bar, dialogs, ...) looks.
+     *
+     * @hide
      */
     public static final String CATEGORY_THEME = "android.theme";
 
     /**
      * Package name of the overlay package
+     *
+     * @hide
      */
-    @UnsupportedAppUsage
+    @SystemApi
     public final String packageName;
 
     /**
      * Package name of the target package
+     *
+     * @hide
      */
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
+    @SystemApi
     public final String targetPackageName;
 
     /**
      * Category of the overlay package
+     *
+     * @hide
      */
+    @SystemApi
     public final String category;
 
     /**
      * Full path to the base APK for this overlay package
+     * @hide
      */
     public final String baseCodePath;
 
     /**
      * The state of this OverlayInfo as defined by the STATE_* constants in this class.
+     * @hide
      */
-    @UnsupportedAppUsage
     public final @State int state;
 
     /**
      * User handle for which this overlay applies
+     * @hide
      */
+    @SystemApi
     public final int userId;
 
     /**
@@ -161,12 +186,15 @@
      *
      * @param source the source OverlayInfo to base the new instance on
      * @param state the new state for the source OverlayInfo
+     *
+     * @hide
      */
     public OverlayInfo(@NonNull OverlayInfo source, @State int state) {
         this(source.packageName, source.targetPackageName, source.category, source.baseCodePath,
                 state, source.userId, source.priority, source.isStatic);
     }
 
+    /** @hide */
     public OverlayInfo(@NonNull String packageName, @NonNull String targetPackageName,
             @NonNull String category, @NonNull String baseCodePath, int state, int userId,
             int priority, boolean isStatic) {
@@ -181,6 +209,7 @@
         ensureValidState();
     }
 
+    /** @hide */
     public OverlayInfo(Parcel source) {
         packageName = source.readString();
         targetPackageName = source.readString();
@@ -255,8 +284,9 @@
      * Disabled overlay packages are installed but are currently not in use.
      *
      * @return true if the overlay is enabled, else false.
+     * @hide
      */
-    @UnsupportedAppUsage
+    @SystemApi
     public boolean isEnabled() {
         switch (state) {
             case STATE_ENABLED:
@@ -272,6 +302,7 @@
      * debugging purposes.
      *
      * @return a human readable String representing the state.
+     * @hide
      */
     public static String stateToString(@State int state) {
         switch (state) {
diff --git a/core/java/android/content/om/OverlayManager.java b/core/java/android/content/om/OverlayManager.java
new file mode 100644
index 0000000..7a2220bf
--- /dev/null
+++ b/core/java/android/content/om/OverlayManager.java
@@ -0,0 +1,98 @@
+/*
+ * 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.content.om;
+
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.annotation.SystemService;
+import android.content.Context;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+
+import java.util.List;
+
+/**
+ * Updates OverlayManager state; gets information about installed overlay packages.
+ * @hide
+ */
+@SystemApi
+@SystemService(Context.OVERLAY_SERVICE)
+public class OverlayManager {
+
+    private final IOverlayManager mService;
+    private final Context mContext;
+
+    /**
+     * Creates a new instance.
+     *
+     * @param context The current context in which to operate.
+     * @param service The backing system service.
+     *
+     * @hide
+     */
+    public OverlayManager(Context context, IOverlayManager service) {
+        mContext = context;
+        mService = service;
+    }
+
+    /** @hide */
+    public OverlayManager(Context context) {
+        this(context, IOverlayManager.Stub.asInterface(
+            ServiceManager.getService(Context.OVERLAY_SERVICE)));
+    }
+    /**
+     * Request that an overlay package is enabled and any other overlay packages with the same
+     * target package and category are disabled.
+     *
+     * @param packageName the name of the overlay package to enable.
+     * @param userId The user for which to change the overlay.
+     * @return true if the system successfully registered the request, false otherwise.
+     *
+     * @hide
+     */
+    @SystemApi
+    public boolean setEnabledExclusiveInCategory(@Nullable final String packageName,
+            int userId) {
+        try {
+            return mService.setEnabledExclusiveInCategory(packageName, userId);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Returns information about all overlays for the given target package for
+     * the specified user. The returned list is ordered according to the
+     * overlay priority with the highest priority at the end of the list.
+     *
+     * @param targetPackageName The name of the target package.
+     * @param userId The user to get the OverlayInfos for.
+     * @return A list of OverlayInfo objects; if no overlays exist for the
+     *         requested package, an empty list is returned.
+     *
+     * @hide
+     */
+    @SystemApi
+    public List<OverlayInfo> getOverlayInfosForTarget(@Nullable final String targetPackageName,
+            int userId) {
+        try {
+            return mService.getOverlayInfosForTarget(targetPackageName, userId);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+}
diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java
index 0edb36c..3cfbe0c 100644
--- a/core/java/android/content/pm/ActivityInfo.java
+++ b/core/java/android/content/pm/ActivityInfo.java
@@ -505,6 +505,22 @@
      */
     public int flags;
 
+    /**
+     * Bit in {@link #privateFlags} indicating if the activity should be shown when locked in case
+     * an activity behind this can also be shown when locked.
+     * See android.R.attr#inheritShowWhenLocked
+     * @hide
+     */
+    public static final int FLAG_INHERIT_SHOW_WHEN_LOCKED = 0x1;
+
+    /**
+     * Options that have been set in the activity declaration in the manifest.
+     * These include:
+     * {@link #FLAG_INHERIT_SHOW_WHEN_LOCKED}.
+     * @hide
+     */
+    public int privateFlags;
+
     /** @hide */
     @IntDef(prefix = { "SCREEN_ORIENTATION_" }, value = {
             SCREEN_ORIENTATION_UNSET,
@@ -975,6 +991,7 @@
         taskAffinity = orig.taskAffinity;
         targetActivity = orig.targetActivity;
         flags = orig.flags;
+        privateFlags = orig.privateFlags;
         screenOrientation = orig.screenOrientation;
         configChanges = orig.configChanges;
         softInputMode = orig.softInputMode;
@@ -1122,9 +1139,10 @@
                     + " targetActivity=" + targetActivity
                     + " persistableMode=" + persistableModeToString());
         }
-        if (launchMode != 0 || flags != 0 || theme != 0) {
+        if (launchMode != 0 || flags != 0 || privateFlags != 0 || theme != 0) {
             pw.println(prefix + "launchMode=" + launchMode
                     + " flags=0x" + Integer.toHexString(flags)
+                    + " privateFlags=0x" + Integer.toHexString(privateFlags)
                     + " theme=0x" + Integer.toHexString(theme));
         }
         if (screenOrientation != SCREEN_ORIENTATION_UNSPECIFIED
@@ -1178,6 +1196,7 @@
         dest.writeString(targetActivity);
         dest.writeString(launchToken);
         dest.writeInt(flags);
+        dest.writeInt(privateFlags);
         dest.writeInt(screenOrientation);
         dest.writeInt(configChanges);
         dest.writeInt(softInputMode);
@@ -1307,6 +1326,7 @@
         targetActivity = source.readString();
         launchToken = source.readString();
         flags = source.readInt();
+        privateFlags = source.readInt();
         screenOrientation = source.readInt();
         configChanges = source.readInt();
         softInputMode = source.readInt();
diff --git a/core/java/android/content/pm/ILauncherApps.aidl b/core/java/android/content/pm/ILauncherApps.aidl
index ba7710b..db2b6fd 100644
--- a/core/java/android/content/pm/ILauncherApps.aidl
+++ b/core/java/android/content/pm/ILauncherApps.aidl
@@ -69,6 +69,7 @@
             int userId);
 
     boolean hasShortcutHostPermission(String callingPackage);
+    boolean shouldHideFromSuggestions(String packageName, in UserHandle user);
 
     ParceledListSlice getShortcutConfigActivities(
             String callingPackage, String packageName, in UserHandle user);
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index 64a4479b..d5c3b26 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -273,6 +273,9 @@
 
     void clearCrossProfileIntentFilters(int sourceUserId, String ownerPackage);
 
+    String[] setDistractingPackageRestrictionsAsUser(in String[] packageNames, int restrictionFlags,
+            int userId);
+
     String[] setPackagesSuspendedAsUser(in String[] packageNames, boolean suspended,
             in PersistableBundle appExtras, in PersistableBundle launcherExtras,
             in SuspendDialogInfo dialogInfo, String callingPackage, int userId);
diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java
index 44e652f..983ea9f 100644
--- a/core/java/android/content/pm/LauncherApps.java
+++ b/core/java/android/content/pm/LauncherApps.java
@@ -697,6 +697,26 @@
     }
 
     /**
+     * Returns whether a package should be hidden from suggestions to the user. Currently, this
+     * could be done because the package was marked as distracting to the user via
+     * {@code PackageManager.setDistractingPackageRestrictions(String[], int)}.
+     *
+     * @param packageName The package for which to check.
+     * @param user the {@link UserHandle} of the profile.
+     * @return
+     */
+    public boolean shouldHideFromSuggestions(@NonNull String packageName,
+            @NonNull UserHandle user) {
+        Preconditions.checkNotNull(packageName, "packageName");
+        Preconditions.checkNotNull(user, "user");
+        try {
+            return mService.shouldHideFromSuggestions(packageName, user);
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Returns {@link ApplicationInfo} about an application installed for a specific user profile.
      *
      * @param packageName The package name of the application
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 2aeb68d..b845673 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -5555,26 +5555,24 @@
     }
 
     /**
-     * @deprecated This function no longer does anything; it was an old
-     * approach to managing preferred activities, which has been superseded
-     * by (and conflicts with) the modern activity-based preferences.
+     * @deprecated This function no longer does anything. It is the platform's
+     * responsibility to assign preferred activities and this cannot be modified
+     * directly. To determine the activities resolved by the platform, use
+     * {@link #resolveActivity} or {@link #queryIntentActivities}.
      */
     @Deprecated
     public abstract void addPackageToPreferred(String packageName);
 
     /**
-     * @deprecated This function no longer does anything; it was an old
-     * approach to managing preferred activities, which has been superseded
-     * by (and conflicts with) the modern activity-based preferences.
+     * @deprecated This function no longer does anything. It is the platform's
+     * responsibility to assign preferred activities and this cannot be modified
+     * directly. To determine the activities resolved by the platform, use
+     * {@link #resolveActivity} or {@link #queryIntentActivities}.
      */
     @Deprecated
     public abstract void removePackageFromPreferred(String packageName);
 
     /**
-     * @deprecated This function no longer does anything; it was an old
-     * approach to managing preferred activities, which has been superseded
-     * by (and conflicts with) the modern activity-based preferences.
-     *
      * Retrieve the list of all currently configured preferred packages. The
      * first package on the list is the most preferred, the last is the least
      * preferred.
@@ -5582,15 +5580,16 @@
      * @param flags Additional option flags to modify the data returned.
      * @return A List of PackageInfo objects, one for each preferred
      *         application, in order of preference.
+     *
+     * @deprecated This function no longer does anything. It is the platform's
+     * responsibility to assign preferred activities and this cannot be modified
+     * directly. To determine the activities resolved by the platform, use
+     * {@link #resolveActivity} or {@link #queryIntentActivities}.
      */
     @Deprecated
     public abstract List<PackageInfo> getPreferredPackages(@PackageInfoFlags int flags);
 
     /**
-     * @deprecated This is a protected API that should not have been available
-     * to third party applications.  It is the platform's responsibility for
-     * assigning preferred activities and this cannot be directly modified.
-     *
      * Add a new preferred activity mapping to the system.  This will be used
      * to automatically select the given activity component when
      * {@link Context#startActivity(Intent) Context.startActivity()} finds
@@ -5604,20 +5603,26 @@
      * this preference was made.
      * @param activity The component name of the activity that is to be
      * preferred.
+     *
+     * @deprecated This function no longer does anything. It is the platform's
+     * responsibility to assign preferred activities and this cannot be modified
+     * directly. To determine the activities resolved by the platform, use
+     * {@link #resolveActivity} or {@link #queryIntentActivities}.
      */
     @Deprecated
     public abstract void addPreferredActivity(IntentFilter filter, int match,
             ComponentName[] set, ComponentName activity);
 
     /**
-     * @deprecated This is a protected API that should not have been available
-     * to third party applications.  It is the platform's responsibility for
-     * assigning preferred activities and this cannot be directly modified.
-     *
      * Same as {@link #addPreferredActivity(IntentFilter, int,
             ComponentName[], ComponentName)}, but with a specific userId to apply the preference
             to.
      * @hide
+     *
+     * @deprecated This function no longer does anything. It is the platform's
+     * responsibility to assign preferred activities and this cannot be modified
+     * directly. To determine the activities resolved by the platform, use
+     * {@link #resolveActivity} or {@link #queryIntentActivities}.
      */
     @Deprecated
     @UnsupportedAppUsage
@@ -5627,10 +5632,6 @@
     }
 
     /**
-     * @deprecated This is a protected API that should not have been available
-     * to third party applications.  It is the platform's responsibility for
-     * assigning preferred activities and this cannot be directly modified.
-     *
      * Replaces an existing preferred activity mapping to the system, and if that were not present
      * adds a new preferred activity.  This will be used
      * to automatically select the given activity component when
@@ -5645,7 +5646,13 @@
      * this preference was made.
      * @param activity The component name of the activity that is to be
      * preferred.
+     *
      * @hide
+     *
+     * @deprecated This function no longer does anything. It is the platform's
+     * responsibility to assign preferred activities and this cannot be modified
+     * directly. To determine the activities resolved by the platform, use
+     * {@link #resolveActivity} or {@link #queryIntentActivities}.
      */
     @Deprecated
     @UnsupportedAppUsage
@@ -5653,10 +5660,6 @@
             ComponentName[] set, ComponentName activity);
 
     /**
-     * @deprecated This is a protected API that should not have been available
-     * to third party applications.  It is the platform's responsibility for
-     * assigning preferred activities and this cannot be directly modified.
-     *
      * Replaces an existing preferred activity mapping to the system, and if that were not present
      * adds a new preferred activity.  This will be used to automatically select the given activity
      * component when {@link Context#startActivity(Intent) Context.startActivity()} finds multiple
@@ -5671,6 +5674,11 @@
      * @param activity The component name of the activity that is to be preferred.
      *
      * @hide
+     *
+     * @deprecated This function no longer does anything. It is the platform's
+     * responsibility to assign preferred activities and this cannot be modified
+     * directly. To determine the activities resolved by the platform, use
+     * {@link #resolveActivity} or {@link #queryIntentActivities}.
      */
     @Deprecated
     @SystemApi
@@ -5681,6 +5689,11 @@
 
     /**
      * @hide
+     *
+     * @deprecated This function no longer does anything. It is the platform's
+     * responsibility to assign preferred activities and this cannot be modified
+     * directly. To determine the activities resolved by the platform, use
+     * {@link #resolveActivity} or {@link #queryIntentActivities}.
      */
     @Deprecated
     @UnsupportedAppUsage
@@ -5690,10 +5703,6 @@
     }
 
     /**
-     * @deprecated This function no longer does anything; it was an old
-     * approach to managing preferred activities, which has been superseded
-     * by (and conflicts with) the modern activity-based preferences.
-     *
      * Remove all preferred activity mappings, previously added with
      * {@link #addPreferredActivity}, from the
      * system whose activities are implemented in the given package name.
@@ -5701,15 +5710,16 @@
      *
      * @param packageName The name of the package whose preferred activity
      * mappings are to be removed.
+     *
+     * @deprecated This function no longer does anything. It is the platform's
+     * responsibility to assign preferred activities and this cannot be modified
+     * directly. To determine the activities resolved by the platform, use
+     * {@link #resolveActivity} or {@link #queryIntentActivities}.
      */
     @Deprecated
     public abstract void clearPackagePreferredActivities(String packageName);
 
     /**
-     * @deprecated This function no longer does anything; it was an old
-     * approach to managing preferred activities, which has been superseded
-     * by (and conflicts with) the modern activity-based preferences.
-     *
      * Retrieve all preferred activities, previously added with
      * {@link #addPreferredActivity}, that are
      * currently registered with the system.
@@ -5725,6 +5735,11 @@
      * @return Returns the total number of registered preferred activities
      * (the number of distinct IntentFilter records, not the number of unique
      * activity components) that were found.
+     *
+     * @deprecated This function no longer does anything. It is the platform's
+     * responsibility to assign preferred activities and this cannot be modified
+     * directly. To determine the activities resolved by the platform, use
+     * {@link #resolveActivity} or {@link #queryIntentActivities}.
      */
     @Deprecated
     public abstract int getPreferredActivities(@NonNull List<IntentFilter> outFilters,
@@ -5889,6 +5904,74 @@
     public abstract boolean isSignedByExactly(String packageName, KeySet ks);
 
     /**
+     * Flag to denote no restrictions. This should be used to clear any restrictions that may have
+     * been previously set for the package.
+     * @see PackageManager.DistractionRestriction
+     * @hide
+     */
+    @SystemApi
+    public static final int RESTRICTION_NONE = 0x0;
+
+    /**
+     * Flag to denote that a package should be hidden from any suggestions to the user.
+     * @see PackageManager.DistractionRestriction
+     * @hide
+     */
+    @SystemApi
+    public static final int RESTRICTION_HIDE_FROM_SUGGESTIONS = 0x00000001;
+
+    /**
+     * Flag to denote that a package's notifications should be hidden.
+     * @see PackageManager.DistractionRestriction
+     * @hide
+     */
+    @SystemApi
+    public static final int RESTRICTION_HIDE_NOTIFICATIONS = 0x00000002;
+
+    /**
+     * Restriction flags to set on a package that is considered as distracting to the user.
+     * These should help the user to restrict their usage of these apps.
+     *
+     * @see #setDistractingPackageRestrictions(String[], int)
+     * @hide
+     */
+    @SystemApi
+    @IntDef(flag = true, prefix = {"RESTRICTION_"}, value = {
+            RESTRICTION_NONE,
+            RESTRICTION_HIDE_FROM_SUGGESTIONS,
+            RESTRICTION_HIDE_NOTIFICATIONS
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface DistractionRestriction {}
+
+    /**
+     * Mark or unmark the given packages as distracting to the user.
+     * These packages can have certain restrictions set that should discourage the user to launch
+     * them often. For example, notifications from such an app can be hidden, or the app can be
+     * removed from launcher suggestions, so the user is able to restrict their use of these apps.
+     *
+     * <p>The caller must hold {@link android.Manifest.permission#SUSPEND_APPS} to use this API.
+     *
+     * @param packages Packages to mark as distracting.
+     * @param restrictionFlags Any combination of {@link DistractionRestriction restrictions} to
+     *                         impose on the given packages. {@link #RESTRICTION_NONE} can be used
+     *                         to clear any existing restrictions.
+     * @return A list of packages that could not have the {@code restrictionFlags} set. The system
+     * may prevent restricting critical packages to preserve normal device function.
+     *
+     * @see DistractionRestriction
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.SUSPEND_APPS)
+    @NonNull
+    public String[] setDistractingPackageRestrictions(@NonNull String[] packages,
+            @DistractionRestriction int restrictionFlags) {
+        throw new UnsupportedOperationException(
+                "setDistractingPackageRestrictions not implemented");
+    }
+
+    /**
      * Puts the package in a suspended state, where attempts at starting activities are denied.
      *
      * <p>It doesn't remove the data or the actual package file. The application's notifications
@@ -5911,8 +5994,7 @@
      * {@link PersistableBundle} objects to be shared with the apps being suspended and the
      * launcher to support customization that they might need to handle the suspended state.
      *
-     * <p>The caller must hold {@link Manifest.permission#SUSPEND_APPS} or
-     * {@link Manifest.permission#MANAGE_USERS} to use this api.</p>
+     * <p>The caller must hold {@link Manifest.permission#SUSPEND_APPS} to use this API.
      *
      * @param packageNames The names of the packages to set the suspended status.
      * @param suspended If set to {@code true}, the packages will be suspended, if set to
@@ -5955,7 +6037,7 @@
      * <p>When the user tries to launch a suspended app, a system dialog alerting them that the app
      * is suspended will be shown instead.
      * The caller can optionally customize the dialog by passing a {@link SuspendDialogInfo} object
-     * to this api. This dialog will have a button that starts the
+     * to this API. This dialog will have a button that starts the
      * {@link Intent#ACTION_SHOW_SUSPENDED_APP_DETAILS} intent if the suspending app declares an
      * activity which handles this action.
      *
@@ -5966,7 +6048,7 @@
      * {@link PersistableBundle} objects to be shared with the apps being suspended and the
      * launcher to support customization that they might need to handle the suspended state.
      *
-     * <p>The caller must hold {@link Manifest.permission#SUSPEND_APPS} to use this api.
+     * <p>The caller must hold {@link Manifest.permission#SUSPEND_APPS} to use this API.
      *
      * @param packageNames The names of the packages to set the suspended status.
      * @param suspended If set to {@code true}, the packages will be suspended, if set to
@@ -6005,7 +6087,7 @@
      * #setPackagesSuspended(String[], boolean, PersistableBundle, PersistableBundle,
      * SuspendDialogInfo) setPackagesSuspended}. The platform prevents suspending certain critical
      * packages to keep the device in a functioning state, e.g. the default dialer.
-     * Apps need to hold {@link Manifest.permission#SUSPEND_APPS SUSPEND_APPS} to call this api.
+     * Apps need to hold {@link Manifest.permission#SUSPEND_APPS SUSPEND_APPS} to call this API.
      *
      * <p>
      * Note that this set of critical packages can change with time, so even though a package name
diff --git a/core/java/android/content/pm/PackageManagerInternal.java b/core/java/android/content/pm/PackageManagerInternal.java
index 83979e9..45b5dca 100644
--- a/core/java/android/content/pm/PackageManagerInternal.java
+++ b/core/java/android/content/pm/PackageManagerInternal.java
@@ -137,6 +137,12 @@
     public abstract void setLocationPackagesProvider(PackagesProvider provider);
 
     /**
+     * Set the location extra packages provider.
+     * @param provider The packages provider.
+     */
+    public abstract  void setLocationExtraPackagesProvider(PackagesProvider provider);
+
+    /**
      * Sets the voice interaction packages provider.
      * @param provider The packages provider.
      */
@@ -284,6 +290,17 @@
     public abstract SuspendDialogInfo getSuspendedDialogInfo(String suspendedPackage, int userId);
 
     /**
+     * Gets any distraction flags set via
+     * {@link PackageManager#setDistractingPackageRestrictions(String[], int)}
+     *
+     * @param packageName
+     * @param userId
+     * @return A bitwise OR of any of the {@link PackageManager.DistractionRestriction}
+     */
+    public abstract @PackageManager.DistractionRestriction int getDistractingPackageRestrictions(
+            String packageName, int userId);
+
+    /**
      * Do a straight uid lookup for the given package/application in the given user.
      * @see PackageManager#getPackageUidAsUser(String, int, int)
      * @return The app's uid, or < 0 if the package was not found in that user
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index c984651..dbf3574 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -2656,6 +2656,23 @@
     }
 
     /**
+     * Matches a given {@code targetCode} against a set of release codeNames. Target codes can
+     * either be of the form {@code [codename]}" (e.g {@code "Q"}) or of the form
+     * {@code [codename].[fingerprint]} (e.g {@code "Q.cafebc561"}).
+     */
+    private static boolean matchTargetCode(@NonNull String[] codeNames,
+            @NonNull String targetCode) {
+        final String targetCodeName;
+        final int targetCodeIdx = targetCode.indexOf('.');
+        if (targetCodeIdx == -1) {
+            targetCodeName = targetCode;
+        } else {
+            targetCodeName = targetCode.substring(0, targetCodeIdx);
+        }
+        return ArrayUtils.contains(codeNames, targetCodeName);
+    }
+
+    /**
      * Computes the targetSdkVersion to use at runtime. If the package is not
      * compatible with this platform, populates {@code outError[0]} with an
      * error message.
@@ -2698,7 +2715,7 @@
 
         // If it's a pre-release SDK and the codename matches this platform, it
         // definitely targets this SDK.
-        if (ArrayUtils.contains(platformSdkCodenames, targetCode) || forceCurrentDev) {
+        if (matchTargetCode(platformSdkCodenames, targetCode) || forceCurrentDev) {
             return Build.VERSION_CODES.CUR_DEVELOPMENT;
         }
 
@@ -2831,7 +2848,7 @@
 
         // If it's a pre-release SDK and the codename matches this platform, we
         // definitely meet the minimum SDK requirement.
-        if (ArrayUtils.contains(platformSdkCodenames, minCode)) {
+        if (matchTargetCode(platformSdkCodenames, minCode)) {
             return Build.VERSION_CODES.CUR_DEVELOPMENT;
         }
 
@@ -3760,9 +3777,11 @@
             ai.flags |= ApplicationInfo.FLAG_MULTIARCH;
         }
 
+        final boolean extractNativeLibsDefault =
+                owner.applicationInfo.targetSdkVersion < Build.VERSION_CODES.Q;
         if (sa.getBoolean(
                 com.android.internal.R.styleable.AndroidManifestApplication_extractNativeLibs,
-                true)) {
+                extractNativeLibsDefault)) {
             ai.flags |= ApplicationInfo.FLAG_EXTRACT_NATIVE_LIBS;
         }
 
@@ -4646,6 +4665,9 @@
                 a.info.flags |= ActivityInfo.FLAG_TURN_SCREEN_ON;
             }
 
+            if (sa.getBoolean(R.styleable.AndroidManifestActivity_inheritShowWhenLocked, false)) {
+                a.info.privateFlags |= ActivityInfo.FLAG_INHERIT_SHOW_WHEN_LOCKED;
+            }
         } else {
             a.info.launchMode = ActivityInfo.LAUNCH_MULTIPLE;
             a.info.configChanges = 0;
@@ -5025,6 +5047,7 @@
         info.targetActivity = targetActivity;
         info.configChanges = target.info.configChanges;
         info.flags = target.info.flags;
+        info.privateFlags = target.info.privateFlags;
         info.icon = target.info.icon;
         info.logo = target.info.logo;
         info.banner = target.info.banner;
diff --git a/core/java/android/content/pm/PackageUserState.java b/core/java/android/content/pm/PackageUserState.java
index 74dd08f..249b691 100644
--- a/core/java/android/content/pm/PackageUserState.java
+++ b/core/java/android/content/pm/PackageUserState.java
@@ -54,6 +54,7 @@
     public boolean stopped;
     public boolean notLaunched;
     public boolean hidden; // Is the app restricted by owner / admin
+    public int distractionFlags;
     public boolean suspended;
     public String suspendingPackage;
     public SuspendDialogInfo dialogInfo;
@@ -92,6 +93,7 @@
         stopped = o.stopped;
         notLaunched = o.notLaunched;
         hidden = o.hidden;
+        distractionFlags = o.distractionFlags;
         suspended = o.suspended;
         suspendingPackage = o.suspendingPackage;
         dialogInfo = o.dialogInfo;
@@ -222,6 +224,9 @@
         if (hidden != oldState.hidden) {
             return false;
         }
+        if (distractionFlags != oldState.distractionFlags) {
+            return false;
+        }
         if (suspended != oldState.suspended) {
             return false;
         }
diff --git a/core/java/android/content/pm/PermissionInfo.java b/core/java/android/content/pm/PermissionInfo.java
index bb8c92d..a3395ac 100644
--- a/core/java/android/content/pm/PermissionInfo.java
+++ b/core/java/android/content/pm/PermissionInfo.java
@@ -158,6 +158,7 @@
      * @hide
      */
     @SystemApi
+    @TestApi
     public static final int PROTECTION_FLAG_OEM = 0x4000;
 
     /**
diff --git a/core/java/android/content/res/AssetManager.java b/core/java/android/content/res/AssetManager.java
index f1a4db2..9e0a9ba 100644
--- a/core/java/android/content/res/AssetManager.java
+++ b/core/java/android/content/res/AssetManager.java
@@ -732,6 +732,38 @@
         }
     }
 
+    /**
+     * Enable resource resolution logging to track the steps taken to resolve the last resource
+     * entry retrieved. Stores the configuration and package names for each step.
+     *
+     * Default disabled.
+     *
+     * @param enabled Boolean indicating whether to enable or disable logging.
+     *
+     * @hide
+     */
+    public void setResourceResolutionLoggingEnabled(boolean enabled) {
+        synchronized (this) {
+            ensureValidLocked();
+            nativeSetResourceResolutionLoggingEnabled(mObject, enabled);
+        }
+    }
+
+    /**
+     * Retrieve the last resource resolution path logged.
+     *
+     * @return Formatted string containing last resource ID/name and steps taken to resolve final
+     * entry, including configuration and package names.
+     *
+     * @hide
+     */
+    public @Nullable String getLastResourceResolution() {
+        synchronized (this) {
+            ensureValidLocked();
+            return nativeGetLastResourceResolution(mObject);
+        }
+    }
+
     CharSequence getPooledStringForCookie(int cookie, int id) {
         // Cookies map to ApkAssets starting at 1.
         return getApkAssets()[cookie - 1].getStringFromPool(id);
@@ -1383,6 +1415,8 @@
     private static native @Nullable String nativeGetResourceEntryName(long ptr, @AnyRes int resid);
     private static native @Nullable String[] nativeGetLocales(long ptr, boolean excludeSystem);
     private static native @Nullable Configuration[] nativeGetSizeConfigurations(long ptr);
+    private static native void nativeSetResourceResolutionLoggingEnabled(long ptr, boolean enabled);
+    private static native @Nullable String nativeGetLastResourceResolution(long ptr);
 
     // Style attribute retrieval native methods.
     private static native void nativeApplyStyle(long ptr, long themePtr, @AttrRes int defStyleAttr,
diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java
index 365ceac..c4b315e 100644
--- a/core/java/android/content/res/Resources.java
+++ b/core/java/android/content/res/Resources.java
@@ -2010,22 +2010,36 @@
     public String getResourceTypeName(@AnyRes int resid) throws NotFoundException {
         return mResourcesImpl.getResourceTypeName(resid);
     }
-    
+
     /**
      * Return the entry name for a given resource identifier.
-     * 
+     *
      * @param resid The resource identifier whose entry name is to be
      * retrieved.
-     * 
+     *
      * @return A string holding the entry name of the resource.
-     * 
+     *
      * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
-     * 
+     *
      * @see #getResourceName
      */
     public String getResourceEntryName(@AnyRes int resid) throws NotFoundException {
         return mResourcesImpl.getResourceEntryName(resid);
     }
+
+    /**
+     * Return formatted log of the last retrieved resource's resolution path.
+     *
+     * @return A string holding a formatted log of the steps taken to resolve the last resource.
+     *
+     * @throws NotFoundException Throws NotFoundException if there hasn't been a resource
+     * resolved yet.
+     *
+     * @hide
+     */
+    public String getLastResourceResolution() throws NotFoundException {
+        return mResourcesImpl.getLastResourceResolution();
+    }
     
     /**
      * Parse a series of {@link android.R.styleable#Extra &lt;extra&gt;} tags from
diff --git a/core/java/android/content/res/ResourcesImpl.java b/core/java/android/content/res/ResourcesImpl.java
index 2ad4f62..77796d9 100644
--- a/core/java/android/content/res/ResourcesImpl.java
+++ b/core/java/android/content/res/ResourcesImpl.java
@@ -299,6 +299,13 @@
     }
 
     @NonNull
+    String getLastResourceResolution() throws NotFoundException {
+        String str = mAssets.getLastResourceResolution();
+        if (str != null) return str;
+        throw new NotFoundException("Associated AssetManager hasn't resolved a resource");
+    }
+
+    @NonNull
     CharSequence getQuantityText(@PluralsRes int id, int quantity) throws NotFoundException {
         PluralRules rule = getPluralRule();
         CharSequence res = mAssets.getResourceBagText(id,
diff --git a/core/java/android/content/res/TypedArray.java b/core/java/android/content/res/TypedArray.java
index 508626b..d53834c 100644
--- a/core/java/android/content/res/TypedArray.java
+++ b/core/java/android/content/res/TypedArray.java
@@ -19,6 +19,7 @@
 import android.annotation.AnyRes;
 import android.annotation.ColorInt;
 import android.annotation.Nullable;
+import android.annotation.StyleRes;
 import android.annotation.StyleableRes;
 import android.annotation.UnsupportedAppUsage;
 import android.content.pm.ActivityInfo;
@@ -63,13 +64,15 @@
     }
 
     // STYLE_ prefixed constants are offsets within the typed data array.
-    static final int STYLE_NUM_ENTRIES = 6;
+    // Keep this in sync with libs/androidfw/include/androidfw/AttributeResolution.h
+    static final int STYLE_NUM_ENTRIES = 7;
     static final int STYLE_TYPE = 0;
     static final int STYLE_DATA = 1;
     static final int STYLE_ASSET_COOKIE = 2;
     static final int STYLE_RESOURCE_ID = 3;
     static final int STYLE_CHANGING_CONFIGURATIONS = 4;
     static final int STYLE_DENSITY = 5;
+    static final int SYTLE_SOURCE_STYLE_RESOURCE_ID = 6;
 
     @UnsupportedAppUsage
     private final Resources mResources;
@@ -1098,6 +1101,31 @@
     }
 
     /**
+     * Returns the resource ID of the style against which the specified attribute was resolved,
+     * otherwise returns defValue.
+     *
+     * @param index Index of attribute whose source style to retrieve.
+     * @param defValue Value to return if the attribute is not defined or
+     *                 not a resource.
+     *
+     * @return Attribute source style resource ID or defValue if it was not resolved in any style.
+     * @throws RuntimeException if the TypedArray has already been recycled.
+     */
+    @StyleRes
+    public int getSourceStyleResourceId(@StyleableRes int index, @StyleRes int defValue) {
+        if (mRecycled) {
+            throw new RuntimeException("Cannot make calls to a recycled instance!");
+        }
+
+        index *= STYLE_NUM_ENTRIES;
+        final int resid = mData[index + SYTLE_SOURCE_STYLE_RESOURCE_ID];
+        if (resid != 0) {
+            return resid;
+        }
+        return defValue;
+    }
+
+    /**
      * Determines whether there is an attribute at <var>index</var>.
      * <p>
      * <strong>Note:</strong> If the attribute was set to {@code @empty} or
@@ -1309,6 +1337,7 @@
                 data[index + STYLE_CHANGING_CONFIGURATIONS]);
         outValue.density = data[index + STYLE_DENSITY];
         outValue.string = (type == TypedValue.TYPE_STRING) ? loadStringValueAt(index) : null;
+        outValue.sourceStyleResourceId = data[index + SYTLE_SOURCE_STYLE_RESOURCE_ID];
         return true;
     }
 
diff --git a/core/java/android/hardware/biometrics/BiometricManager.java b/core/java/android/hardware/biometrics/BiometricManager.java
index eb3414d..8aac1bf 100644
--- a/core/java/android/hardware/biometrics/BiometricManager.java
+++ b/core/java/android/hardware/biometrics/BiometricManager.java
@@ -42,13 +42,13 @@
     /**
      * The hardware is unavailable. Try again later.
      */
-    public static final int BIOMETRIC_ERROR_UNAVAILABLE =
+    public static final int BIOMETRIC_ERROR_HW_UNAVAILABLE =
             BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE;
 
     /**
      * The user does not have any biometrics enrolled.
      */
-    public static final int BIOMETRIC_ERROR_NO_BIOMETRICS =
+    public static final int BIOMETRIC_ERROR_NONE_ENROLLED =
             BiometricConstants.BIOMETRIC_ERROR_NO_BIOMETRICS;
 
     /**
@@ -58,8 +58,8 @@
             BiometricConstants.BIOMETRIC_ERROR_HW_NOT_PRESENT;
 
     @IntDef({BIOMETRIC_SUCCESS,
-            BIOMETRIC_ERROR_UNAVAILABLE,
-            BIOMETRIC_ERROR_NO_BIOMETRICS,
+            BIOMETRIC_ERROR_HW_UNAVAILABLE,
+            BIOMETRIC_ERROR_NONE_ENROLLED,
             BIOMETRIC_ERROR_NO_HARDWARE})
     @interface BiometricError {}
 
@@ -95,8 +95,8 @@
      * Determine if biometrics can be used. In other words, determine if {@link BiometricPrompt}
      * can be expected to be shown (hardware available, templates enrolled, user-enabled).
      *
-     * @return Returns {@link #BIOMETRIC_ERROR_NO_BIOMETRICS} if the user does not have any
-     *     enrolled, or {@link #BIOMETRIC_ERROR_UNAVAILABLE} if none are currently
+     * @return Returns {@link #BIOMETRIC_ERROR_NONE_ENROLLED} if the user does not have any
+     *     enrolled, or {@link #BIOMETRIC_ERROR_HW_UNAVAILABLE} if none are currently
      *     supported/enabled. Returns {@link #BIOMETRIC_SUCCESS} if a biometric can currently be
      *     used (enrolled and available).
      */
@@ -113,7 +113,7 @@
                 return BIOMETRIC_ERROR_NO_HARDWARE;
             } else {
                 Slog.w(TAG, "hasEnrolledBiometrics(): Service not connected");
-                return BIOMETRIC_ERROR_UNAVAILABLE;
+                return BIOMETRIC_ERROR_HW_UNAVAILABLE;
             }
         }
     }
diff --git a/core/java/android/hardware/biometrics/BiometricPrompt.java b/core/java/android/hardware/biometrics/BiometricPrompt.java
index f652f85..c69b68e4 100644
--- a/core/java/android/hardware/biometrics/BiometricPrompt.java
+++ b/core/java/android/hardware/biometrics/BiometricPrompt.java
@@ -235,7 +235,6 @@
          * requiring confirmation.
          *
          * @param requireConfirmation
-         * @hide
          */
         public Builder setRequireConfirmation(boolean requireConfirmation) {
             mBundle.putBoolean(KEY_REQUIRE_CONFIRMATION, requireConfirmation);
diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java
index cda8498..7e45441 100644
--- a/core/java/android/hardware/display/DisplayManagerGlobal.java
+++ b/core/java/android/hardware/display/DisplayManagerGlobal.java
@@ -436,6 +436,7 @@
     public void setVirtualDisplaySurface(IVirtualDisplayCallback token, Surface surface) {
         try {
             mDm.setVirtualDisplaySurface(token, surface);
+            setVirtualDisplayState(token, surface != null);
         } catch (RemoteException ex) {
             throw ex.rethrowFromSystemServer();
         }
@@ -458,6 +459,14 @@
         }
     }
 
+    void setVirtualDisplayState(IVirtualDisplayCallback token, boolean isOn) {
+        try {
+            mDm.setVirtualDisplayState(token, isOn);
+        } catch (RemoteException ex) {
+            throw ex.rethrowFromSystemServer();
+        }
+    }
+
     /**
      * Gets the stable device display size, in pixels.
      */
diff --git a/core/java/android/hardware/display/IDisplayManager.aidl b/core/java/android/hardware/display/IDisplayManager.aidl
index 2d81cdf..aae8afb 100644
--- a/core/java/android/hardware/display/IDisplayManager.aidl
+++ b/core/java/android/hardware/display/IDisplayManager.aidl
@@ -82,6 +82,9 @@
     // No permissions required but must be same Uid as the creator.
     void releaseVirtualDisplay(in IVirtualDisplayCallback token);
 
+    // No permissions required but must be same Uid as the creator.
+    void setVirtualDisplayState(in IVirtualDisplayCallback token, boolean isOn);
+
     // Get a stable metric for the device's display size. No permissions required.
     Point getStableDisplaySize();
 
diff --git a/core/java/android/hardware/display/VirtualDisplay.java b/core/java/android/hardware/display/VirtualDisplay.java
index d354666..bf62c95 100644
--- a/core/java/android/hardware/display/VirtualDisplay.java
+++ b/core/java/android/hardware/display/VirtualDisplay.java
@@ -104,6 +104,18 @@
         }
     }
 
+    /**
+     * Sets the on/off state for a virtual display.
+     *
+     * @param isOn Whether the display should be on or off.
+     * @hide
+     */
+    public void setDisplayState(boolean isOn) {
+        if (mToken != null) {
+            mGlobal.setVirtualDisplayState(mToken, isOn);
+        }
+    }
+
     @Override
     public String toString() {
         return "VirtualDisplay{display=" + mDisplay + ", token=" + mToken
diff --git a/core/java/android/hardware/hdmi/HdmiAudioSystemClient.java b/core/java/android/hardware/hdmi/HdmiAudioSystemClient.java
index d382eb9..bdd5ab6 100644
--- a/core/java/android/hardware/hdmi/HdmiAudioSystemClient.java
+++ b/core/java/android/hardware/hdmi/HdmiAudioSystemClient.java
@@ -15,10 +15,12 @@
  */
 package android.hardware.hdmi;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.RemoteException;
+import android.util.Log;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.annotations.VisibleForTesting.Visibility;
@@ -56,6 +58,21 @@
         mHandler = handler == null ? new Handler(Looper.getMainLooper()) : handler;
     }
 
+    /**
+     * Callback interface used to get the set System Audio Mode result.
+     *
+     * @hide
+     */
+    // TODO(b/110094868): unhide and add @SystemApi for Q
+    public interface SetSystemAudioModeCallback {
+        /**
+         * Called when the input was changed.
+         *
+         * @param result the result of the set System Audio Mode
+         */
+        void onComplete(int result);
+    }
+
     /** @hide */
     // TODO(b/110094868): unhide and add @SystemApi for Q
     @Override
@@ -117,4 +134,34 @@
             mPendingReportAudioStatus = true;
         }
     }
+
+    /**
+     * Set System Audio Mode on/off with audio system device.
+     *
+     * @param state true to set System Audio Mode on. False to set off.
+     * @param callback callback offer the setting result.
+     *
+     * @hide
+     */
+    // TODO(b/110094868): unhide and add @SystemApi for Q
+    public void setSystemAudioMode(boolean state, @NonNull SetSystemAudioModeCallback callback) {
+        // TODO(amyjojo): implement this when needed.
+    }
+
+    /**
+     * When device is switching to an audio only source, this method is called to broadcast
+     * a setSystemAudioMode on message to the HDMI CEC system without querying Active Source or
+     * TV supporting System Audio Control or not. This is to get volume control passthrough
+     * from STB even if TV does not support it.
+     *
+     * @hide
+     */
+    // TODO(b/110094868): unhide and add @SystemApi for Q
+    public void setSystemAudioModeOnForAudioOnlySource() {
+        try {
+            mService.setSystemAudioModeOnForAudioOnlySource();
+        } catch (RemoteException e) {
+            Log.d(TAG, "Failed to set System Audio Mode on for Audio Only source");
+        }
+    }
 }
diff --git a/core/java/android/hardware/hdmi/HdmiControlManager.java b/core/java/android/hardware/hdmi/HdmiControlManager.java
index b520d2c..a98b31a 100644
--- a/core/java/android/hardware/hdmi/HdmiControlManager.java
+++ b/core/java/android/hardware/hdmi/HdmiControlManager.java
@@ -16,6 +16,8 @@
 
 package android.hardware.hdmi;
 
+import static com.android.internal.os.RoSystemProperties.PROPERTY_HDMI_IS_DEVICE_HDMI_CEC_SWITCH;
+
 import android.annotation.Nullable;
 import android.annotation.RequiresFeature;
 import android.annotation.RequiresPermission;
@@ -27,9 +29,12 @@
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.os.RemoteException;
+import android.os.SystemProperties;
 import android.util.ArrayMap;
 import android.util.Log;
 
+import java.util.List;
+
 /**
  * The {@link HdmiControlManager} class is used to send HDMI control messages
  * to attached CEC devices.
@@ -50,6 +55,10 @@
 
     @Nullable private final IHdmiControlService mService;
 
+    private static final int INVALID_PHYSICAL_ADDRESS = 0xFFFF;
+
+    private int mPhysicalAddress = INVALID_PHYSICAL_ADDRESS;
+
     /**
      * Broadcast Action: Display OSD message.
      * <p>Send when the service has a message to display on screen for events
@@ -264,6 +273,10 @@
     private final boolean mHasTvDevice;
     // True if we have a logical device of type audio system hosted in the system.
     private final boolean mHasAudioSystemDevice;
+    // True if we have a logical device of type audio system hosted in the system.
+    private final boolean mHasSwitchDevice;
+    // True if it's a switch device.
+    private final boolean mIsSwitchDevice;
 
     /**
      * {@hide} - hide this constructor because it has a parameter of type IHdmiControlService,
@@ -283,6 +296,9 @@
         mHasTvDevice = hasDeviceType(types, HdmiDeviceInfo.DEVICE_TV);
         mHasPlaybackDevice = hasDeviceType(types, HdmiDeviceInfo.DEVICE_PLAYBACK);
         mHasAudioSystemDevice = hasDeviceType(types, HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM);
+        mHasSwitchDevice = hasDeviceType(types, HdmiDeviceInfo.DEVICE_PURE_CEC_SWITCH);
+        mIsSwitchDevice = SystemProperties.getBoolean(
+            PROPERTY_HDMI_IS_DEVICE_HDMI_CEC_SWITCH, false);
     }
 
     private static boolean hasDeviceType(int[] types, int type) {
@@ -319,6 +335,9 @@
                 return mHasPlaybackDevice ? new HdmiPlaybackClient(mService) : null;
             case HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM:
                 return mHasAudioSystemDevice ? new HdmiAudioSystemClient(mService) : null;
+            case HdmiDeviceInfo.DEVICE_PURE_CEC_SWITCH:
+                return (mHasSwitchDevice || mIsSwitchDevice)
+                    ? new HdmiSwitchClient(mService) : null;
             default:
                 return null;
         }
@@ -373,6 +392,90 @@
     }
 
     /**
+     * Gets an object that represents an HDMI-CEC logical device of type switch on the system.
+     *
+     * <p>Used to send HDMI control messages to other devices like TV through HDMI bus. It is also
+     * possible to communicate with other logical devices hosted in the same system if the system is
+     * configured to host more than one type of HDMI-CEC logical devices.
+     *
+     * @return {@link HdmiSwitchClient} instance. {@code null} on failure.
+     *
+     * TODO(b/110094868): unhide for Q
+     * @hide
+     */
+    @Nullable
+    @SuppressLint("Doclava125")
+    public HdmiSwitchClient getSwitchClient() {
+        return (HdmiSwitchClient) getClient(HdmiDeviceInfo.DEVICE_PURE_CEC_SWITCH);
+    }
+
+    /**
+     * Get a snapshot of the real-time status of the remote devices.
+     *
+     * @return a list of {@link HdmiDeviceInfo} of the devices connected to the current device.
+     *
+     * TODO(b/110094868): unhide for Q
+     * @hide
+     */
+    public List<HdmiDeviceInfo> getConnectedDevicesList() {
+        try {
+            return mService.getDeviceList();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Power off the target device.
+     *
+     * @param deviceInfo HdmiDeviceInfo of the device to be powered off
+     *
+     * TODO(b/110094868): unhide for Q
+     * @hide
+     */
+    public void powerOffRemoteDevice(HdmiDeviceInfo deviceInfo) {
+        try {
+            mService.powerOffRemoteDevice(
+                    deviceInfo.getLogicalAddress(), deviceInfo.getDevicePowerStatus());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Power on the target device.
+     *
+     * @param deviceInfo HdmiDeviceInfo of the device to be powered on
+     *
+     * TODO(b/110094868): unhide for Q
+     * @hide
+     */
+    public void powerOnRemoteDevice(HdmiDeviceInfo deviceInfo) {
+        try {
+            mService.powerOnRemoteDevice(
+                    deviceInfo.getLogicalAddress(), deviceInfo.getDevicePowerStatus());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Ask the target device to be the new Active Source.
+     *
+     * @param deviceInfo HdmiDeviceInfo of the target device
+     *
+     * TODO(b/110094868): unhide for Q
+     * @hide
+     */
+    public void askRemoteDeviceToBecomeActiveSource(HdmiDeviceInfo deviceInfo) {
+        try {
+            mService.askRemoteDeviceToBecomeActiveSource(deviceInfo.getPhysicalAddress());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Controls standby mode of the system. It will also try to turn on/off the connected devices if
      * necessary.
      *
@@ -401,6 +504,46 @@
     }
 
     /**
+     * Get the physical address of the device.
+     *
+     * @hide
+     */
+    public int getPhysicalAddress() {
+        if (mPhysicalAddress != INVALID_PHYSICAL_ADDRESS) {
+            return mPhysicalAddress;
+        }
+        try {
+            mPhysicalAddress = mService.getPhysicalAddress();
+            return mPhysicalAddress;
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Check if the target device is connected to the current device. The
+     * API also returns true if the current device is the target.
+     *
+     * @param targetDevice {@link HdmiDeviceInfo} of the target device.
+     * @return true if {@code device} is directly or indirectly connected to the
+     *
+     * TODO(b/110094868): unhide for Q
+     * @hide
+     */
+    public boolean isTargetDeviceConnected(HdmiDeviceInfo targetDevice) {
+        mPhysicalAddress = getPhysicalAddress();
+        if (mPhysicalAddress == INVALID_PHYSICAL_ADDRESS) {
+            return false;
+        }
+        int targetPhysicalAddress = targetDevice.getPhysicalAddress();
+        if (targetPhysicalAddress == INVALID_PHYSICAL_ADDRESS) {
+            return false;
+        }
+        return HdmiUtils.getLocalPortFromPhysicalAddress(targetPhysicalAddress, mPhysicalAddress)
+            != HdmiUtils.TARGET_NOT_UNDER_LOCAL_DEVICE;
+    }
+
+    /**
      * Listener used to get hotplug event from HDMI port.
      */
     public interface HotplugEventListener {
diff --git a/core/java/android/hardware/hdmi/HdmiSwitchClient.java b/core/java/android/hardware/hdmi/HdmiSwitchClient.java
new file mode 100644
index 0000000..1ac2973
--- /dev/null
+++ b/core/java/android/hardware/hdmi/HdmiSwitchClient.java
@@ -0,0 +1,137 @@
+/*
+ * 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.hdmi;
+
+import android.annotation.NonNull;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * HdmiSwitchClient represents HDMI-CEC logical device of type Switch in the Android system which
+ * acts as switch.
+ *
+ * <p>HdmiSwitchClient has a CEC device type of HdmiDeviceInfo.DEVICE_PURE_CEC_SWITCH,
+ * but it is used by all Android TV devices that have multiple HDMI inputs,
+ * even if it is not a "pure" swicth and has another device type like TV or Player.
+ *
+ * @hide
+ * TODO(b/110094868): unhide and add @SystemApi for Q
+ */
+public class HdmiSwitchClient extends HdmiClient {
+
+    private static final String TAG = "HdmiSwitchClient";
+
+    /* package */ HdmiSwitchClient(IHdmiControlService service) {
+        super(service);
+    }
+
+    private static IHdmiControlCallback getCallbackWrapper(final SelectCallback callback) {
+        return new IHdmiControlCallback.Stub() {
+            @Override
+            public void onComplete(int result) {
+                callback.onComplete(result);
+            }
+        };
+    }
+
+    /** @hide */
+    // TODO(b/110094868): unhide for Q
+    @Override
+    public int getDeviceType() {
+        return HdmiDeviceInfo.DEVICE_PURE_CEC_SWITCH;
+    }
+
+    /**
+     * Selects a CEC logical device to be a new active source.
+     *
+     * @param logicalAddress logical address of the device to select
+     * @param callback callback to get the result with
+     * @throws {@link IllegalArgumentException} if the {@code callback} is null
+     *
+     * @hide
+     * TODO(b/110094868): unhide and add @SystemApi for Q
+     */
+    public void deviceSelect(int logicalAddress, @NonNull SelectCallback callback) {
+        if (callback == null) {
+            throw new IllegalArgumentException("callback must not be null.");
+        }
+        try {
+            mService.deviceSelect(logicalAddress, getCallbackWrapper(callback));
+        } catch (RemoteException e) {
+            Log.e(TAG, "failed to select device: ", e);
+        }
+    }
+
+    /**
+     * Selects a HDMI port to be a new route path.
+     *
+     * @param portId HDMI port to select
+     * @param callback callback to get the result with
+     * @throws {@link IllegalArgumentException} if the {@code callback} is null
+     *
+     * @hide
+     * TODO(b/110094868): unhide and add @SystemApi for Q
+     */
+    public void portSelect(int portId, @NonNull SelectCallback callback) {
+        if (callback == null) {
+            throw new IllegalArgumentException("Callback must not be null");
+        }
+        try {
+            mService.portSelect(portId, getCallbackWrapper(callback));
+        } catch (RemoteException e) {
+            Log.e(TAG, "failed to select port: ", e);
+        }
+    }
+
+    /**
+     * Returns all the CEC devices connected to the device.
+     *
+     * <p>This only applies to device with multiple HDMI inputs
+     *
+     * @return list of {@link HdmiDeviceInfo} for connected CEC devices. Empty list is returned if
+     * there is none.
+     *
+     * @hide
+     * TODO(b/110094868): unhide and add @SystemApi for Q
+     */
+    public List<HdmiDeviceInfo> getDeviceList() {
+        try {
+            return mService.getDeviceList();
+        } catch (RemoteException e) {
+            Log.e("TAG", "Failed to call getDeviceList():", e);
+            return Collections.<HdmiDeviceInfo>emptyList();
+        }
+    }
+
+    /**
+     * Callback interface used to get the result of {@link #deviceSelect} or {@link #portSelect}.
+     *
+     * @hide
+     * TODO(b/110094868): unhide and add @SystemApi for Q
+     */
+    public interface SelectCallback {
+
+        /**
+         * Called when the operation is finished.
+         *
+         * @param result the result value of {@link #deviceSelect} or {@link #portSelect}.
+         */
+        void onComplete(int result);
+    }
+}
diff --git a/core/java/android/hardware/hdmi/HdmiUtils.java b/core/java/android/hardware/hdmi/HdmiUtils.java
new file mode 100644
index 0000000..3081738
--- /dev/null
+++ b/core/java/android/hardware/hdmi/HdmiUtils.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2014 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.hdmi;
+
+/**
+ * Various utilities to handle HDMI CEC messages.
+ *
+ * TODO(b/110094868): unhide for Q
+ * @hide
+ */
+public class HdmiUtils {
+
+    /**
+     * Return value of {@link #getLocalPortFromPhysicalAddress(int, int)}
+     */
+    static final int TARGET_NOT_UNDER_LOCAL_DEVICE = -1;
+    static final int TARGET_SAME_PHYSICAL_ADDRESS = 0;
+
+    private HdmiUtils() { /* cannot be instantiated */ }
+
+    /**
+     * Method to parse target physical address to the port number on the current device.
+     *
+     * <p>This check assumes target address is valid.
+     *
+     * @param targetPhysicalAddress is the physical address of the target device
+     * @param myPhysicalAddress is the physical address of the current device
+     * @return
+     * If the target device is under the current device, return the port number of current device
+     * that the target device is connected to. This also applies to the devices that are indirectly
+     * connected to the current device.
+     *
+     * <p>If the target device has the same physical address as the current device, return
+     * {@link #TARGET_SAME_PHYSICAL_ADDRESS}.
+     *
+     * <p>If the target device is not under the current device, return
+     * {@link #TARGET_NOT_UNDER_LOCAL_DEVICE}.
+     */
+    public static int getLocalPortFromPhysicalAddress(
+            int targetPhysicalAddress, int myPhysicalAddress) {
+        if (myPhysicalAddress == targetPhysicalAddress) {
+            return TARGET_SAME_PHYSICAL_ADDRESS;
+        }
+
+        int mask = 0xF000;
+        int finalMask = 0xF000;
+        int maskedAddress = myPhysicalAddress;
+
+        while (maskedAddress != 0) {
+            maskedAddress = myPhysicalAddress & mask;
+            finalMask |= mask;
+            mask >>= 4;
+        }
+
+        int portAddress = targetPhysicalAddress & finalMask;
+        if ((portAddress & (finalMask << 4)) != myPhysicalAddress) {
+            return TARGET_NOT_UNDER_LOCAL_DEVICE;
+        }
+
+        mask <<= 4;
+        int port = portAddress & mask;
+        while ((port >> 4) != 0) {
+            port >>= 4;
+        }
+        return port;
+    }
+}
diff --git a/core/java/android/hardware/hdmi/IHdmiControlService.aidl b/core/java/android/hardware/hdmi/IHdmiControlService.aidl
index 2b8d00b..1cd9920 100644
--- a/core/java/android/hardware/hdmi/IHdmiControlService.aidl
+++ b/core/java/android/hardware/hdmi/IHdmiControlService.aidl
@@ -50,6 +50,7 @@
     List<HdmiPortInfo> getPortInfo();
     boolean canChangeSystemAudioMode();
     boolean getSystemAudioMode();
+    int getPhysicalAddress();
     void setSystemAudioMode(boolean enabled, IHdmiControlCallback callback);
     void addSystemAudioModeChangeListener(IHdmiSystemAudioModeChangeListener listener);
     void removeSystemAudioModeChangeListener(IHdmiSystemAudioModeChangeListener listener);
@@ -60,6 +61,9 @@
     void setInputChangeListener(IHdmiInputChangeListener listener);
     List<HdmiDeviceInfo> getInputDevices();
     List<HdmiDeviceInfo> getDeviceList();
+    void powerOffRemoteDevice(int logicalAddress, int powerStatus);
+    void powerOnRemoteDevice(int logicalAddress, int powerStatus);
+    void askRemoteDeviceToBecomeActiveSource(int physicalAddress);
     void sendVendorCommand(int deviceType, int targetAddress, in byte[] params,
             boolean hasVendorId);
     void addVendorCommandListener(IHdmiVendorCommandListener listener, int deviceType);
@@ -73,4 +77,5 @@
     void addHdmiMhlVendorCommandListener(IHdmiMhlVendorCommandListener listener);
     void setStandbyMode(boolean isStandbyModeOn);
     void reportAudioStatus(int deviceType, int volume, int maxVolume, boolean isMute);
+    void setSystemAudioModeOnForAudioOnlySource();
 }
diff --git a/core/java/android/net/LinkProperties.java b/core/java/android/net/LinkProperties.java
index 4a466f3..c2963fd 100644
--- a/core/java/android/net/LinkProperties.java
+++ b/core/java/android/net/LinkProperties.java
@@ -66,6 +66,7 @@
     private int mMtu;
     // in the format "rmem_min,rmem_def,rmem_max,wmem_min,wmem_def,wmem_max"
     private String mTcpBufferSizes;
+    private IpPrefix mNat64Prefix;
 
     private static final int MIN_MTU    = 68;
     private static final int MIN_MTU_V6 = 1280;
@@ -190,6 +191,7 @@
             }
             setMtu(source.mMtu);
             mTcpBufferSizes = source.mTcpBufferSizes;
+            mNat64Prefix = source.mNat64Prefix;
         }
     }
 
@@ -760,6 +762,32 @@
     }
 
     /**
+     * Returns the NAT64 prefix in use on this link, if any.
+     *
+     * @return the NAT64 prefix.
+     * @hide
+     */
+    public @Nullable IpPrefix getNat64Prefix() {
+        return mNat64Prefix;
+    }
+
+    /**
+     * Sets the NAT64 prefix in use on this link.
+     *
+     * Currently, only 96-bit prefixes (i.e., where the 32-bit IPv4 address is at the end of the
+     * 128-bit IPv6 address) are supported.
+     *
+     * @param prefix the NAT64 prefix.
+     * @hide
+     */
+    public void setNat64Prefix(IpPrefix prefix) {
+        if (prefix != null && prefix.getPrefixLength() != 96) {
+            throw new IllegalArgumentException("Only 96-bit prefixes are supported: " + prefix);
+        }
+        mNat64Prefix = prefix;  // IpPrefix objects are immutable.
+    }
+
+    /**
      * Adds a stacked link.
      *
      * If there is already a stacked link with the same interface name as link,
@@ -831,6 +859,7 @@
         mStackedLinks.clear();
         mMtu = 0;
         mTcpBufferSizes = null;
+        mNat64Prefix = null;
     }
 
     /**
@@ -908,6 +937,11 @@
             resultJoiner.add(mHttpProxy.toString());
         }
 
+        if (mNat64Prefix != null) {
+            resultJoiner.add("Nat64Prefix:");
+            resultJoiner.add(mNat64Prefix.toString());
+        }
+
         final Collection<LinkProperties> stackedLinksValues = mStackedLinks.values();
         if (!stackedLinksValues.isEmpty()) {
             final StringJoiner stackedLinksJoiner = new StringJoiner(",", "Stacked: [", "]");
@@ -1295,6 +1329,17 @@
     }
 
     /**
+     * Compares this {@code LinkProperties} NAT64 prefix against the target.
+     *
+     * @param target LinkProperties to compare.
+     * @return {@code true} if both are identical, {@code false} otherwise.
+     * @hide
+     */
+    public boolean isIdenticalNat64Prefix(LinkProperties target) {
+        return Objects.equals(mNat64Prefix, target.mNat64Prefix);
+    }
+
+    /**
      * Compares this {@code LinkProperties} instance against the target
      * LinkProperties in {@code obj}. Two LinkPropertieses are equal if
      * all their fields are equal in values.
@@ -1330,7 +1375,8 @@
                 && isIdenticalHttpProxy(target)
                 && isIdenticalStackedLinks(target)
                 && isIdenticalMtu(target)
-                && isIdenticalTcpBufferSizes(target);
+                && isIdenticalTcpBufferSizes(target)
+                && isIdenticalNat64Prefix(target);
     }
 
     /**
@@ -1443,7 +1489,8 @@
                 + ((null == mTcpBufferSizes) ? 0 : mTcpBufferSizes.hashCode())
                 + (mUsePrivateDns ? 57 : 0)
                 + mPcscfs.size() * 67
-                + ((null == mPrivateDnsServerName) ? 0 : mPrivateDnsServerName.hashCode());
+                + ((null == mPrivateDnsServerName) ? 0 : mPrivateDnsServerName.hashCode())
+                + Objects.hash(mNat64Prefix);
     }
 
     /**
@@ -1484,6 +1531,8 @@
         } else {
             dest.writeByte((byte)0);
         }
+        dest.writeParcelable(mNat64Prefix, 0);
+
         ArrayList<LinkProperties> stackedLinks = new ArrayList<>(mStackedLinks.values());
         dest.writeList(stackedLinks);
     }
@@ -1535,6 +1584,7 @@
                 if (in.readByte() == 1) {
                     netProp.setHttpProxy(in.readParcelable(null));
                 }
+                netProp.setNat64Prefix(in.readParcelable(null));
                 ArrayList<LinkProperties> stackedLinks = new ArrayList<LinkProperties>();
                 in.readList(stackedLinks, LinkProperties.class.getClassLoader());
                 for (LinkProperties stackedLink: stackedLinks) {
diff --git a/core/java/android/net/ipmemorystore/NetworkAttributes.java b/core/java/android/net/ipmemorystore/NetworkAttributes.java
index d7e5b27..b932d21 100644
--- a/core/java/android/net/ipmemorystore/NetworkAttributes.java
+++ b/core/java/android/net/ipmemorystore/NetworkAttributes.java
@@ -28,6 +28,7 @@
 import java.util.Collections;
 import java.util.List;
 import java.util.Objects;
+import java.util.StringJoiner;
 
 /**
  * A POD object to represent attributes of a single L2 network entry.
@@ -207,4 +208,52 @@
     public int hashCode() {
         return Objects.hash(assignedV4Address, groupHint, dnsAddresses, mtu);
     }
+
+    /** Pretty print */
+    @Override
+    public String toString() {
+        final StringJoiner resultJoiner = new StringJoiner(" ", "{", "}");
+        final ArrayList<String> nullFields = new ArrayList<>();
+
+        if (null != assignedV4Address) {
+            resultJoiner.add("assignedV4Addr :");
+            resultJoiner.add(assignedV4Address.toString());
+        } else {
+            nullFields.add("assignedV4Addr");
+        }
+
+        if (null != groupHint) {
+            resultJoiner.add("groupHint :");
+            resultJoiner.add(groupHint);
+        } else {
+            nullFields.add("groupHint");
+        }
+
+        if (null != dnsAddresses) {
+            resultJoiner.add("dnsAddr : [");
+            for (final InetAddress addr : dnsAddresses) {
+                resultJoiner.add(addr.getHostAddress());
+            }
+            resultJoiner.add("]");
+        } else {
+            nullFields.add("dnsAddr");
+        }
+
+        if (null != mtu) {
+            resultJoiner.add("mtu :");
+            resultJoiner.add(mtu.toString());
+        } else {
+            nullFields.add("mtu");
+        }
+
+        if (!nullFields.isEmpty()) {
+            resultJoiner.add("; Null fields : [");
+            for (final String field : nullFields) {
+                resultJoiner.add(field);
+            }
+            resultJoiner.add("]");
+        }
+
+        return resultJoiner.toString();
+    }
 }
diff --git a/core/java/android/net/ipmemorystore/SameL3NetworkResponse.java b/core/java/android/net/ipmemorystore/SameL3NetworkResponse.java
index 0cb37e9..d040dcc 100644
--- a/core/java/android/net/ipmemorystore/SameL3NetworkResponse.java
+++ b/core/java/android/net/ipmemorystore/SameL3NetworkResponse.java
@@ -128,4 +128,19 @@
     public int hashCode() {
         return Objects.hash(l2Key1, l2Key2, confidence);
     }
+
+    @Override
+    /** Pretty print */
+    public String toString() {
+        switch (getNetworkSameness()) {
+            case NETWORK_SAME:
+                return "\"" + l2Key1 + "\" same L3 network as \"" + l2Key2 + "\"";
+            case NETWORK_DIFFERENT:
+                return "\"" + l2Key1 + "\" different L3 network from \"" + l2Key2 + "\"";
+            case NETWORK_NEVER_CONNECTED:
+                return "\"" + l2Key1 + "\" can't be tested against \"" + l2Key2 + "\"";
+            default:
+                return "Buggy sameness value ? \"" + l2Key1 + "\", \"" + l2Key2 + "\"";
+        }
+    }
 }
diff --git a/core/java/android/net/ipmemorystore/Status.java b/core/java/android/net/ipmemorystore/Status.java
index 5b016ec..95e5042 100644
--- a/core/java/android/net/ipmemorystore/Status.java
+++ b/core/java/android/net/ipmemorystore/Status.java
@@ -26,6 +26,8 @@
 public class Status {
     public static final int SUCCESS = 0;
 
+    public static final int ERROR_DATABASE_CANNOT_BE_OPENED = -1;
+
     public final int resultCode;
 
     public Status(final int resultCode) {
@@ -47,4 +49,14 @@
     public boolean isSuccess() {
         return SUCCESS == resultCode;
     }
+
+    /** Pretty print */
+    @Override
+    public String toString() {
+        switch (resultCode) {
+            case SUCCESS: return "SUCCESS";
+            case ERROR_DATABASE_CANNOT_BE_OPENED: return "DATABASE CANNOT BE OPENED";
+            default: return "Unknown value ?!";
+        }
+    }
 }
diff --git a/core/java/android/net/ipmemorystore/Utils.java b/core/java/android/net/ipmemorystore/Utils.java
new file mode 100644
index 0000000..73d8c83
--- /dev/null
+++ b/core/java/android/net/ipmemorystore/Utils.java
@@ -0,0 +1,44 @@
+/*
+ * 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.ipmemorystore;
+
+import android.annotation.NonNull;
+
+/** {@hide} */
+public class Utils {
+    /** Pretty print */
+    public static String blobToString(final Blob blob) {
+        final StringBuilder sb = new StringBuilder("Blob : [");
+        if (blob.data.length <= 24) {
+            appendByteArray(sb, blob.data, 0, blob.data.length);
+        } else {
+            appendByteArray(sb, blob.data, 0, 16);
+            sb.append("...");
+            appendByteArray(sb, blob.data, blob.data.length - 8, blob.data.length);
+        }
+        sb.append("]");
+        return sb.toString();
+    }
+
+    // Adds the hex representation of the array between the specified indices (inclusive, exclusive)
+    private static void appendByteArray(@NonNull final StringBuilder sb, @NonNull final byte[] ar,
+            final int from, final int to) {
+        for (int i = from; i < to; ++i) {
+            sb.append(String.format("%02X", ar[i]));
+        }
+    }
+}
diff --git a/core/java/android/os/BugreportManager.java b/core/java/android/os/BugreportManager.java
index 1343d24..cbb3909 100644
--- a/core/java/android/os/BugreportManager.java
+++ b/core/java/android/os/BugreportManager.java
@@ -114,8 +114,6 @@
         }
     }
 
-
-    // TODO(b/111441001) Connect up with BugreportListener methods.
     private final class DumpstateListener extends IDumpstateListener.Stub
             implements DeathRecipient {
         private final BugreportListener mListener;
@@ -130,19 +128,36 @@
         }
 
         @Override
+        public void onProgress(int progress) throws RemoteException {
+            mListener.onProgress(progress);
+        }
+
+        @Override
+        public void onError(int errorCode) throws RemoteException {
+            mListener.onError(errorCode);
+        }
+
+        @Override
+        public void onFinished(long durationMs, String title, String description)
+                throws RemoteException {
+            mListener.onFinished(durationMs, title, description);
+        }
+
+        // Old methods; should go away
+        @Override
         public void onProgressUpdated(int progress) throws RemoteException {
-            // TODO(b/111441001): implement
+            // TODO(b/111441001): remove from interface
         }
 
         @Override
         public void onMaxProgressUpdated(int maxProgress) throws RemoteException {
-            // TODO(b/111441001): implement
+            // TODO(b/111441001): remove from interface
         }
 
         @Override
         public void onSectionComplete(String title, int status, int size, int durationMs)
                 throws RemoteException {
-            // TODO(b/111441001): implement
+            // TODO(b/111441001): remove from interface
         }
     }
 }
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index 9fea873..2d61a4e 100755
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -289,6 +289,26 @@
                 "ro.build.version.preview_sdk", 0);
 
         /**
+         * The SDK fingerprint for a given prerelease SDK. This value will always be
+         * {@code REL} on production platform builds/devices.
+         *
+         * <p>When this value is not {@code REL}, it contains a string fingerprint of the API
+         * surface exposed by the preview SDK. Preview platforms with different API surfaces
+         * will have different {@code PREVIEW_SDK_FINGERPRINT}.
+         *
+         * <p>This attribute is intended for use by installers for finer grained targeting of
+         * packages. Applications targeting preview APIs should not use this field and should
+         * instead use {@code PREVIEW_SDK_INT} or use reflection or other runtime checks to
+         * detect the presence of an API or guard themselves against unexpected runtime
+         * behavior.
+         *
+         * @hide
+         */
+        @SystemApi
+        public static final String PREVIEW_SDK_FINGERPRINT = SystemProperties.get(
+                "ro.build.version.preview_sdk_fingerprint", "REL");
+
+        /**
          * The current development codename, or the string "REL" if this is
          * a release build.
          */
diff --git a/core/java/android/os/GraphicsEnvironment.java b/core/java/android/os/GraphicsEnvironment.java
index b0b8f49..e4622db 100644
--- a/core/java/android/os/GraphicsEnvironment.java
+++ b/core/java/android/os/GraphicsEnvironment.java
@@ -480,21 +480,36 @@
             return;
         }
 
-        if (getGlobalSettingsString(coreSettings, Settings.Global.GUP_DEV_OPT_OUT_APPS)
-                        .contains(ai.packageName)) {
+        // GUP_DEV_ALL_APPS
+        // 0: Default (Invalid values fallback to default as well)
+        // 1: All apps use Game Update Package
+        // 2: All apps use system graphics driver
+        int gupDevAllApps = coreSettings.getInt(Settings.Global.GUP_DEV_ALL_APPS, 0);
+        if (gupDevAllApps == 2) {
             if (DEBUG) {
-                Log.w(TAG, ai.packageName + " opts out from GUP.");
+                Log.w(TAG, "GUP is turned off on this device");
             }
             return;
         }
 
-        if (!getGlobalSettingsString(coreSettings, Settings.Global.GUP_DEV_OPT_IN_APPS)
-                        .contains(ai.packageName)
-                && !onWhitelist(context, driverPackageName, ai.packageName)) {
-            if (DEBUG) {
-                Log.w(TAG, ai.packageName + " is not on the whitelist.");
+        if (gupDevAllApps != 1) {
+            // GUP_DEV_OPT_OUT_APPS has higher priority than GUP_DEV_OPT_IN_APPS
+            if (getGlobalSettingsString(coreSettings, Settings.Global.GUP_DEV_OPT_OUT_APPS)
+                            .contains(ai.packageName)) {
+                if (DEBUG) {
+                    Log.w(TAG, ai.packageName + " opts out from GUP.");
+                }
+                return;
             }
-            return;
+
+            if (!getGlobalSettingsString(coreSettings, Settings.Global.GUP_DEV_OPT_IN_APPS)
+                            .contains(ai.packageName)
+                    && !onWhitelist(context, driverPackageName, ai.packageName)) {
+                if (DEBUG) {
+                    Log.w(TAG, ai.packageName + " is not on the whitelist.");
+                }
+                return;
+            }
         }
 
         ApplicationInfo driverInfo;
diff --git a/core/java/android/os/HwBinder.java b/core/java/android/os/HwBinder.java
index 3de3494..9e3e83e 100644
--- a/core/java/android/os/HwBinder.java
+++ b/core/java/android/os/HwBinder.java
@@ -17,6 +17,7 @@
 package android.os;
 
 import android.annotation.SystemApi;
+import android.annotation.TestApi;
 
 import libcore.util.NativeAllocationRegistry;
 
@@ -24,6 +25,7 @@
 
 /** @hide */
 @SystemApi
+@TestApi
 public abstract class HwBinder implements IHwBinder {
     private static final String TAG = "HwBinder";
 
diff --git a/core/java/android/os/HwBlob.java b/core/java/android/os/HwBlob.java
index 6a5bb1c..0ec63b5 100644
--- a/core/java/android/os/HwBlob.java
+++ b/core/java/android/os/HwBlob.java
@@ -18,6 +18,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
+import android.annotation.TestApi;
 
 import libcore.util.NativeAllocationRegistry;
 
@@ -28,6 +29,7 @@
  * @hide
  */
 @SystemApi
+@TestApi
 public class HwBlob {
     private static final String TAG = "HwBlob";
 
diff --git a/core/java/android/os/HwParcel.java b/core/java/android/os/HwParcel.java
index 7a51db2..7919a00 100644
--- a/core/java/android/os/HwParcel.java
+++ b/core/java/android/os/HwParcel.java
@@ -18,6 +18,7 @@
 
 import android.annotation.IntDef;
 import android.annotation.SystemApi;
+import android.annotation.TestApi;
 
 import libcore.util.NativeAllocationRegistry;
 
@@ -28,6 +29,7 @@
 
 /** @hide */
 @SystemApi
+@TestApi
 public class HwParcel {
     private static final String TAG = "HwParcel";
 
diff --git a/core/java/android/os/IHwBinder.java b/core/java/android/os/IHwBinder.java
index 249eb3a..46fa6ef 100644
--- a/core/java/android/os/IHwBinder.java
+++ b/core/java/android/os/IHwBinder.java
@@ -17,9 +17,11 @@
 package android.os;
 
 import android.annotation.SystemApi;
+import android.annotation.TestApi;
 
 /** @hide */
 @SystemApi
+@TestApi
 public interface IHwBinder {
     /**
      * Process a hwbinder transaction.
diff --git a/core/java/android/os/IHwInterface.java b/core/java/android/os/IHwInterface.java
index f9edd5b..0a5a715 100644
--- a/core/java/android/os/IHwInterface.java
+++ b/core/java/android/os/IHwInterface.java
@@ -17,8 +17,11 @@
 package android.os;
 
 import android.annotation.SystemApi;
+import android.annotation.TestApi;
+
 /** @hide */
 @SystemApi
+@TestApi
 public interface IHwInterface {
     /**
      * @return the binder object that corresponds to this interface.
diff --git a/core/java/android/os/INetworkManagementService.aidl b/core/java/android/os/INetworkManagementService.aidl
index fdd7488..8ced722 100644
--- a/core/java/android/os/INetworkManagementService.aidl
+++ b/core/java/android/os/INetworkManagementService.aidl
@@ -356,16 +356,6 @@
     void removeVpnUidRanges(int netId, in UidRange[] ranges);
 
     /**
-     * Start the clatd (464xlat) service on the given interface.
-     */
-    void startClatd(String interfaceName);
-
-    /**
-     * Stop the clatd (464xlat) service on the given interface.
-     */
-    void stopClatd(String interfaceName);
-
-    /**
      * Start listening for mobile activity state changes.
      */
     void registerNetworkActivityListener(INetworkActivityListener listener);
diff --git a/core/java/android/os/NativeHandle.java b/core/java/android/os/NativeHandle.java
index f7ffc37..f13bf5f 100644
--- a/core/java/android/os/NativeHandle.java
+++ b/core/java/android/os/NativeHandle.java
@@ -20,6 +20,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
+import android.annotation.TestApi;
 import android.system.ErrnoException;
 import android.system.Os;
 
@@ -32,6 +33,7 @@
  * @hide
  */
 @SystemApi
+@TestApi
 public final class NativeHandle implements Closeable {
     // whether this object owns mFds
     private boolean mOwn = false;
diff --git a/core/java/android/os/ParcelableException.aidl b/core/java/android/os/ParcelableException.aidl
new file mode 100644
index 0000000..d214922
--- /dev/null
+++ b/core/java/android/os/ParcelableException.aidl
@@ -0,0 +1,18 @@
+/* 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.os;
+
+parcelable ParcelableException;
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index ee56e3d..760fef7 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -208,30 +208,35 @@
      * First uid used for fully isolated sandboxed processes spawned from an app zygote
      * @hide
      */
+    @TestApi
     public static final int FIRST_APP_ZYGOTE_ISOLATED_UID = 90000;
 
     /**
      * Number of UIDs we allocate per application zygote
      * @hide
      */
+    @TestApi
     public static final int NUM_UIDS_PER_APP_ZYGOTE = 100;
 
     /**
      * Last uid used for fully isolated sandboxed processes spawned from an app zygote
      * @hide
      */
+    @TestApi
     public static final int LAST_APP_ZYGOTE_ISOLATED_UID = 98999;
 
     /**
      * First uid used for fully isolated sandboxed processes (with no permissions of their own)
      * @hide
      */
+    @TestApi
     public static final int FIRST_ISOLATED_UID = 99000;
 
     /**
      * Last uid used for fully isolated sandboxed processes (with no permissions of their own)
      * @hide
      */
+    @TestApi
     public static final int LAST_ISOLATED_UID = 99999;
 
     /**
diff --git a/core/java/android/permission/IPermissionController.aidl b/core/java/android/permission/IPermissionController.aidl
index 0e18b44..249b622 100644
--- a/core/java/android/permission/IPermissionController.aidl
+++ b/core/java/android/permission/IPermissionController.aidl
@@ -18,6 +18,8 @@
 
 import android.os.RemoteCallback;
 import android.os.Bundle;
+import android.os.ParcelFileDescriptor;
+import android.os.UserHandle;
 
 /**
  * Interface for system apps to communication with the permission controller.
@@ -27,8 +29,10 @@
 oneway interface IPermissionController {
     void revokeRuntimePermissions(in Bundle request, boolean doDryRun, int reason,
             String callerPackageName, in RemoteCallback callback);
+    void getRuntimePermissionBackup(in UserHandle user, in ParcelFileDescriptor pipe);
     void getAppPermissions(String packageName, in RemoteCallback callback);
     void revokeRuntimePermission(String packageName, String permissionName);
     void countPermissionApps(in List<String> permissionNames, boolean countOnlyGranted,
             boolean countSystem, in RemoteCallback callback);
+    void getPermissionUsages(boolean countSystem, long numMillis, in RemoteCallback callback);
 }
diff --git a/core/java/android/permission/PermissionControllerManager.java b/core/java/android/permission/PermissionControllerManager.java
index d7332ae..bfcca7c 100644
--- a/core/java/android/permission/PermissionControllerManager.java
+++ b/core/java/android/permission/PermissionControllerManager.java
@@ -18,6 +18,7 @@
 
 import static android.permission.PermissionControllerService.SERVICE_INTERFACE;
 
+import static com.android.internal.util.Preconditions.checkArgumentNonnegative;
 import static com.android.internal.util.Preconditions.checkCollectionElementsNotNull;
 import static com.android.internal.util.Preconditions.checkNotNull;
 
@@ -35,10 +36,12 @@
 import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
+import android.os.AsyncTask;
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
+import android.os.ParcelFileDescriptor;
 import android.os.RemoteCallback;
 import android.os.RemoteException;
 import android.os.UserHandle;
@@ -50,6 +53,11 @@
 import com.android.internal.infra.AbstractRemoteService;
 import com.android.internal.util.Preconditions;
 
+import libcore.io.IoUtils;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
@@ -57,6 +65,7 @@
 import java.util.List;
 import java.util.Map;
 import java.util.concurrent.Executor;
+import java.util.function.Consumer;
 
 /**
  * Interface for communicating with the permission controller.
@@ -117,6 +126,20 @@
     }
 
     /**
+     * Callback for delivering the result of {@link #getRuntimePermissionBackup}.
+     *
+     * @hide
+     */
+    public interface OnGetRuntimePermissionBackupCallback {
+        /**
+         * The result for {@link #getRuntimePermissionBackup}.
+         *
+         * @param backup The backup file
+         */
+        void onGetRuntimePermissionsBackup(@NonNull byte[] backup);
+    }
+
+    /**
      * Callback for delivering the result of {@link #getAppPermissions}.
      *
      * @hide
@@ -146,6 +169,20 @@
         void onCountPermissionApps(int numApps);
     }
 
+    /**
+     * Callback for delivering the result of {@link #getPermissionUsages}.
+     *
+     * @hide
+     */
+    public interface OnPermissionUsageResultCallback {
+        /**
+         * The result for {@link #getPermissionUsages}.
+         *
+         * @param users The users list.
+         */
+        void onPermissionUsageResult(@NonNull List<RuntimePermissionUsageInfo> users);
+    }
+
     private final @NonNull Context mContext;
 
     /**
@@ -204,6 +241,26 @@
     }
 
     /**
+     * Create a backup of the runtime permissions.
+     *
+     * @param user The user to be backed up
+     * @param executor Executor on which to invoke the callback
+     * @param callback Callback to receive the result
+     *
+     * @hide
+     */
+    @RequiresPermission(Manifest.permission.GET_RUNTIME_PERMISSIONS)
+    public void getRuntimePermissionBackup(@NonNull UserHandle user,
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull OnGetRuntimePermissionBackupCallback callback) {
+        checkNotNull(executor);
+        checkNotNull(callback);
+
+        sRemoteService.scheduleRequest(new PendingGetRuntimePermissionBackup(sRemoteService,
+                user, executor, callback));
+    }
+
+    /**
      * Gets the runtime permissions for an app.
      *
      * @param packageName The package for which to query.
@@ -264,6 +321,28 @@
     }
 
     /**
+     * Count how many apps have used permissions.
+     *
+     * @param countSystem Also count system apps
+     * @param numMillis The number of milliseconds in the past to check for uses
+     * @param executor Executor on which to invoke the callback
+     * @param callback Callback to receive the result
+     *
+     * @hide
+     */
+    @RequiresPermission(Manifest.permission.GET_RUNTIME_PERMISSIONS)
+    public void getPermissionUsages(boolean countSystem, long numMillis,
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull OnPermissionUsageResultCallback callback) {
+        checkArgumentNonnegative(numMillis);
+        checkNotNull(executor);
+        checkNotNull(callback);
+
+        sRemoteService.scheduleRequest(new PendingGetPermissionUsagesRequest(sRemoteService,
+                countSystem, numMillis, executor, callback));
+    }
+
+    /**
      * A connection to the remote service
      */
     static final class RemoteService extends
@@ -318,6 +397,89 @@
     }
 
     /**
+     * Task to read a large amount of data from a remote service.
+     */
+    private static class FileReaderTask<Callback extends Consumer<byte[]>>
+            extends AsyncTask<Void, Void, byte[]> {
+        private ParcelFileDescriptor mLocalPipe;
+        private ParcelFileDescriptor mRemotePipe;
+
+        private final @NonNull Callback mCallback;
+
+        FileReaderTask(@NonNull Callback callback) {
+            mCallback = callback;
+        }
+
+        @Override
+        protected void onPreExecute() {
+            ParcelFileDescriptor[] pipe;
+            try {
+                pipe = ParcelFileDescriptor.createPipe();
+            } catch (IOException e) {
+                Log.e(TAG, "Could not create pipe needed to get runtime permission backup", e);
+                return;
+            }
+
+            mLocalPipe = pipe[0];
+            mRemotePipe = pipe[1];
+        }
+
+        /**
+         * Get the file descriptor the remote service should write the data to.
+         *
+         * <p>Needs to be closed <u>locally</u> before the FileReader can finish.
+         *
+         * @return The file the data should be written to
+         */
+        ParcelFileDescriptor getRemotePipe() {
+            return mRemotePipe;
+        }
+
+        @Override
+        protected byte[] doInBackground(Void... ignored) {
+            ByteArrayOutputStream combinedBuffer = new ByteArrayOutputStream();
+
+            try (InputStream in = new ParcelFileDescriptor.AutoCloseInputStream(mLocalPipe)) {
+                byte[] buffer = new byte[16 * 1024];
+
+                while (!isCancelled()) {
+                    int numRead = in.read(buffer);
+                    if (numRead == -1) {
+                        break;
+                    }
+
+                    combinedBuffer.write(buffer, 0, numRead);
+                }
+            } catch (IOException | NullPointerException e) {
+                Log.e(TAG, "Error reading runtime permission backup", e);
+                combinedBuffer.reset();
+            }
+
+            return combinedBuffer.toByteArray();
+        }
+
+        /**
+         * Interrupt the reading of the data.
+         *
+         * <p>Needs to be called when canceling this task as it might be hung.
+         */
+        void interruptRead() {
+            IoUtils.closeQuietly(mLocalPipe);
+        }
+
+        @Override
+        protected void onCancelled() {
+            onPostExecute(new byte[]{});
+        }
+
+        @Override
+        protected void onPostExecute(byte[] backup) {
+            IoUtils.closeQuietly(mLocalPipe);
+            mCallback.accept(backup);
+        }
+    }
+
+    /**
      * Request for {@link #revokeRuntimePermissions}
      */
     private static final class PendingRevokeRuntimePermissionRequest extends
@@ -326,6 +488,7 @@
         private final boolean mDoDryRun;
         private final int mReason;
         private final @NonNull String mCallingPackage;
+        private final @NonNull Executor mExecutor;
         private final @NonNull OnRevokeRuntimePermissionsCallback mCallback;
 
         private final @NonNull RemoteCallback mRemoteCallback;
@@ -341,6 +504,7 @@
             mDoDryRun = doDryRun;
             mReason = reason;
             mCallingPackage = callingPackage;
+            mExecutor = executor;
             mCallback = callback;
 
             mRemoteCallback = new RemoteCallback(result -> executor.execute(() -> {
@@ -375,7 +539,13 @@
 
         @Override
         protected void onTimeout(RemoteService remoteService) {
-            mCallback.onRevokeRuntimePermissions(Collections.emptyMap());
+            long token = Binder.clearCallingIdentity();
+            try {
+                mExecutor.execute(
+                        () -> mCallback.onRevokeRuntimePermissions(Collections.emptyMap()));
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
         }
 
         @Override
@@ -396,6 +566,68 @@
     }
 
     /**
+     * Request for {@link #getRuntimePermissionBackup}
+     */
+    private static final class PendingGetRuntimePermissionBackup extends
+            AbstractRemoteService.PendingRequest<RemoteService, IPermissionController>
+            implements Consumer<byte[]> {
+        private final @NonNull FileReaderTask<PendingGetRuntimePermissionBackup> mBackupReader;
+        private final @NonNull Executor mExecutor;
+        private final @NonNull OnGetRuntimePermissionBackupCallback mCallback;
+        private final @NonNull UserHandle mUser;
+
+        private PendingGetRuntimePermissionBackup(@NonNull RemoteService service,
+                @NonNull UserHandle user, @NonNull @CallbackExecutor Executor executor,
+                @NonNull OnGetRuntimePermissionBackupCallback callback) {
+            super(service);
+
+            mUser = user;
+            mExecutor = executor;
+            mCallback = callback;
+
+            mBackupReader = new FileReaderTask<>(this);
+        }
+
+        @Override
+        protected void onTimeout(RemoteService remoteService) {
+            mBackupReader.cancel(true);
+            mBackupReader.interruptRead();
+        }
+
+        @Override
+        public void run() {
+            mBackupReader.execute();
+
+            ParcelFileDescriptor remotePipe = mBackupReader.getRemotePipe();
+            try {
+                getService().getServiceInterface().getRuntimePermissionBackup(mUser, remotePipe);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Error getting runtime permission backup", e);
+            } finally {
+                // Remote pipe end is duped by binder call. Local copy is not needed anymore
+                IoUtils.closeQuietly(remotePipe);
+            }
+        }
+
+        /**
+         * Called when the {@link #mBackupReader} finished reading the file.
+         *
+         * @param backup The data read
+         */
+        @Override
+        public void accept(byte[] backup) {
+            long token = Binder.clearCallingIdentity();
+            try {
+                mExecutor.execute(() -> mCallback.onGetRuntimePermissionsBackup(backup));
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+
+            finish();
+        }
+    }
+
+    /**
      * Request for {@link #getAppPermissions}
      */
     private static final class PendingGetAppPermissionRequest extends
@@ -521,4 +753,61 @@
             }
         }
     }
+
+    /**
+     * Request for {@link #getPermissionUsages}
+     */
+    private static final class PendingGetPermissionUsagesRequest extends
+            AbstractRemoteService.PendingRequest<RemoteService, IPermissionController> {
+        private final @NonNull OnPermissionUsageResultCallback mCallback;
+        private final boolean mCountSystem;
+        private final long mNumMillis;
+
+        private final @NonNull RemoteCallback mRemoteCallback;
+
+        private PendingGetPermissionUsagesRequest(@NonNull RemoteService service,
+                boolean countSystem, long numMillis, @NonNull @CallbackExecutor Executor executor,
+                @NonNull OnPermissionUsageResultCallback callback) {
+            super(service);
+
+            mCountSystem = countSystem;
+            mNumMillis = numMillis;
+            mCallback = callback;
+
+            mRemoteCallback = new RemoteCallback(result -> executor.execute(() -> {
+                long token = Binder.clearCallingIdentity();
+                try {
+                    final List<RuntimePermissionUsageInfo> reportedUsers;
+                    List<RuntimePermissionUsageInfo> users = null;
+                    if (result != null) {
+                        users = result.getParcelableArrayList(KEY_RESULT);
+                    } else {
+                        users = Collections.emptyList();
+                    }
+                    reportedUsers = users;
+
+                    callback.onPermissionUsageResult(reportedUsers);
+                } finally {
+                    Binder.restoreCallingIdentity(token);
+
+                    finish();
+                }
+            }), null);
+        }
+
+        @Override
+        protected void onTimeout(RemoteService remoteService) {
+            mCallback.onPermissionUsageResult(Collections.emptyList());
+        }
+
+        @Override
+        public void run() {
+            try {
+                getService().getServiceInterface().getPermissionUsages(mCountSystem, mNumMillis,
+                        mRemoteCallback);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Error counting permission users", e);
+            }
+        }
+    }
 }
diff --git a/core/java/android/permission/PermissionControllerService.java b/core/java/android/permission/PermissionControllerService.java
index f621737..10e8c8d 100644
--- a/core/java/android/permission/PermissionControllerService.java
+++ b/core/java/android/permission/PermissionControllerService.java
@@ -17,6 +17,7 @@
 package android.permission;
 
 import static com.android.internal.util.Preconditions.checkArgument;
+import static com.android.internal.util.Preconditions.checkArgumentNonnegative;
 import static com.android.internal.util.Preconditions.checkCollectionElementsNotNull;
 import static com.android.internal.util.Preconditions.checkNotNull;
 import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
@@ -32,11 +33,16 @@
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
+import android.os.ParcelFileDescriptor;
 import android.os.RemoteCallback;
+import android.os.UserHandle;
 import android.util.ArrayMap;
+import android.util.Log;
 
 import com.android.internal.util.Preconditions;
 
+import java.io.IOException;
+import java.io.OutputStream;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
@@ -50,6 +56,7 @@
  */
 @SystemApi
 public abstract class PermissionControllerService extends Service {
+    private static final String LOG_TAG = PermissionControllerService.class.getSimpleName();
 
     /**
      * The {@link Intent} action that must be declared as handled by a service
@@ -82,6 +89,15 @@
             @PermissionControllerManager.Reason int reason, @NonNull String callerPackageName);
 
     /**
+     * Create a backup of the runtime permissions.
+     *
+     * @param user The user to back up
+     * @param out The stream to write the backup to
+     */
+    public abstract void onGetRuntimePermissionsBackup(@NonNull UserHandle user,
+            @NonNull OutputStream out);
+
+    /**
      * Gets the runtime permissions for an app.
      *
      * @param packageName The package for which to query.
@@ -112,6 +128,17 @@
     public abstract int onCountPermissionApps(@NonNull List<String> permissionNames,
             boolean countOnlyGranted, boolean countSystem);
 
+    /**
+     * Count how many apps have used permissions.
+     *
+     * @param countSystem Also count system apps
+     * @param numMillis The number of milliseconds in the past to check for uses
+     *
+     * @return descriptions of the users of permissions
+     */
+    public abstract @NonNull List<RuntimePermissionUsageInfo>
+            onPermissionUsageResult(boolean countSystem, long numMillis);
+
     @Override
     public final IBinder onBind(Intent intent) {
         return new IPermissionController.Stub() {
@@ -151,6 +178,18 @@
             }
 
             @Override
+            public void getRuntimePermissionBackup(UserHandle user, ParcelFileDescriptor pipe) {
+                checkNotNull(user);
+                checkNotNull(pipe);
+
+                enforceCallingPermission(Manifest.permission.GET_RUNTIME_PERMISSIONS, null);
+
+                mHandler.sendMessage(obtainMessage(
+                        PermissionControllerService::getRuntimePermissionsBackup,
+                        PermissionControllerService.this, user, pipe));
+            }
+
+            @Override
             public void getAppPermissions(String packageName, RemoteCallback callback) {
                 checkNotNull(packageName, "packageName");
                 checkNotNull(callback, "callback");
@@ -187,6 +226,20 @@
                                 PermissionControllerService.this, permissionNames, countOnlyGranted,
                                 countSystem, callback));
             }
+
+            @Override
+            public void getPermissionUsages(boolean countSystem, long numMillis,
+                    RemoteCallback callback) {
+                checkArgumentNonnegative(numMillis);
+                checkNotNull(callback, "callback");
+
+                enforceCallingPermission(Manifest.permission.GET_RUNTIME_PERMISSIONS, null);
+
+                mHandler.sendMessage(
+                        obtainMessage(PermissionControllerService::getPermissionUsages,
+                                PermissionControllerService.this, countSystem, numMillis,
+                                callback));
+            }
         };
     }
 
@@ -211,6 +264,15 @@
         callback.sendResult(result);
     }
 
+    private void getRuntimePermissionsBackup(@NonNull UserHandle user,
+            @NonNull ParcelFileDescriptor outFile) {
+        try (OutputStream out = new ParcelFileDescriptor.AutoCloseOutputStream(outFile)) {
+            onGetRuntimePermissionsBackup(user, out);
+        } catch (IOException e) {
+            Log.e(LOG_TAG, "Could not open pipe to write backup tp", e);
+        }
+    }
+
     private void getAppPermissions(@NonNull String packageName, @NonNull RemoteCallback callback) {
         List<RuntimePermissionPresentationInfo> permissions = onGetAppPermissions(packageName);
         if (permissions != null && !permissions.isEmpty()) {
@@ -230,4 +292,17 @@
         result.putInt(PermissionControllerManager.KEY_RESULT, numApps);
         callback.sendResult(result);
     }
+
+    private void getPermissionUsages(boolean countSystem, long numMillis,
+            @NonNull RemoteCallback callback) {
+        List<RuntimePermissionUsageInfo> users =
+                onPermissionUsageResult(countSystem, numMillis);
+        if (users != null && !users.isEmpty()) {
+            Bundle result = new Bundle();
+            result.putParcelableList(PermissionControllerManager.KEY_RESULT, users);
+            callback.sendResult(result);
+        } else {
+            callback.sendResult(null);
+        }
+    }
 }
diff --git a/core/java/android/permission/RuntimePermissionUsageInfo.aidl b/core/java/android/permission/RuntimePermissionUsageInfo.aidl
new file mode 100644
index 0000000..88820dd
--- /dev/null
+++ b/core/java/android/permission/RuntimePermissionUsageInfo.aidl
@@ -0,0 +1,19 @@
+/**
+ * Copyright (c) 2019, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.permission;
+
+parcelable RuntimePermissionUsageInfo;
\ No newline at end of file
diff --git a/core/java/android/permission/RuntimePermissionUsageInfo.java b/core/java/android/permission/RuntimePermissionUsageInfo.java
new file mode 100644
index 0000000..af1a1be
--- /dev/null
+++ b/core/java/android/permission/RuntimePermissionUsageInfo.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.permission;
+
+import static com.android.internal.util.Preconditions.checkArgumentNonnegative;
+import static com.android.internal.util.Preconditions.checkNotNull;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * This class contains information about how a runtime permission
+ * is used. A single runtime permission presented to the user may
+ * correspond to multiple platform defined permissions, e.g. the
+ * location permission may control both the coarse and fine platform
+ * permissions.
+ *
+ * @hide
+ */
+@SystemApi
+public final class RuntimePermissionUsageInfo implements Parcelable {
+    private final @NonNull CharSequence mName;
+    private final int mNumUsers;
+
+    /**
+     * Creates a new instance.
+     *
+     * @param name The permission group name.
+     * @param numUsers The number of apps that have used this permission.
+     */
+    public RuntimePermissionUsageInfo(@NonNull CharSequence name, int numUsers) {
+        checkNotNull(name);
+        checkArgumentNonnegative(numUsers);
+
+        mName = name;
+        mNumUsers = numUsers;
+    }
+
+    private RuntimePermissionUsageInfo(Parcel parcel) {
+        this(parcel.readCharSequence(), parcel.readInt());
+    }
+
+    /**
+     * @return The number of apps that accessed this permission
+     */
+    public int getAppAccessCount() {
+        return mNumUsers;
+    }
+
+    /**
+     * Gets the permission group name.
+     *
+     * @return The name.
+     */
+    public @NonNull CharSequence getName() {
+        return mName;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel parcel, int flags) {
+        parcel.writeCharSequence(mName);
+        parcel.writeInt(mNumUsers);
+    }
+
+    public static final Creator<RuntimePermissionUsageInfo> CREATOR =
+            new Creator<RuntimePermissionUsageInfo>() {
+        public RuntimePermissionUsageInfo createFromParcel(Parcel source) {
+            return new RuntimePermissionUsageInfo(source);
+        }
+
+        public RuntimePermissionUsageInfo[] newArray(int size) {
+            return new RuntimePermissionUsageInfo[size];
+        }
+    };
+}
diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java
index 4e207ed..c795895 100644
--- a/core/java/android/provider/DeviceConfig.java
+++ b/core/java/android/provider/DeviceConfig.java
@@ -48,6 +48,15 @@
      */
     public static final Uri CONTENT_URI = Uri.parse("content://" + Settings.AUTHORITY + "/config");
 
+    /**
+     * Namespace for all input-related features that are used at the native level.
+     * These features are applied at reboot.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final String NAMESPACE_INPUT_NATIVE_BOOT = "input_native_boot";
+
     private static final Object sLock = new Object();
     @GuardedBy("sLock")
     private static Map<OnPropertyChangedListener, Pair<String, Executor>> sListeners =
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 574ccaf..757c03e 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -1190,7 +1190,8 @@
     /**
      * Activity Action: Show Do Not Disturb access settings.
      * <p>
-     * Users can grant and deny access to Do Not Disturb configuration from here.
+     * Users can grant and deny access to Do Not Disturb configuration from here. Managed
+     * profiles cannot grant Do Not Disturb access.
      * See {@link android.app.NotificationManager#isNotificationPolicyAccessGranted()} for more
      * details.
      * <p>
@@ -7390,6 +7391,14 @@
         private static final Validator DOZE_DOUBLE_TAP_GESTURE_VALIDATOR = BOOLEAN_VALIDATOR;
 
         /**
+         * Whether the device should respond to the SLPI tap gesture.
+         * @hide
+         */
+        public static final String DOZE_TAP_SCREEN_GESTURE = "doze_tap_gesture";
+
+        private static final Validator DOZE_TAP_SCREEN_GESTURE_VALIDATOR = BOOLEAN_VALIDATOR;
+
+        /**
          * Gesture that wakes up the lock screen.
          * @hide
          */
@@ -7406,6 +7415,22 @@
         private static final Validator DOZE_WAKE_SCREEN_GESTURE_VALIDATOR = BOOLEAN_VALIDATOR;
 
         /**
+         * Gesture that skips media.
+         * @hide
+         */
+        public static final String SKIP_GESTURE = "skip_gesture";
+
+        private static final Validator SKIP_GESTURE_VALIDATOR = BOOLEAN_VALIDATOR;
+
+        /**
+         * Gesture that silences sound (alarms, notification, calls).
+         * @hide
+         */
+        public static final String SILENCE_GESTURE = "silence_gesture";
+
+        private static final Validator SILENCE_GESTURE_VALIDATOR = BOOLEAN_VALIDATOR;
+
+        /**
          * The current night mode that has been selected by the user.  Owned
          * and controlled by UiModeManagerService.  Constants are as per
          * UiModeManager.
@@ -7707,6 +7732,23 @@
         public static final String TV_INPUT_CUSTOM_LABELS = "tv_input_custom_labels";
 
         /**
+         * Whether TV app uses non-system inputs.
+         *
+         * <p>
+         * The value is boolean (1 or 0), where 1 means non-system TV inputs are allowed,
+         * and 0 means non-system TV inputs are not allowed.
+         *
+         * <p>
+         * Devices such as sound bars may have changed the system property allow_third_party_inputs
+         * to false so the TV Application only uses HDMI and other built in inputs. This setting
+         * allows user to override the default and have the TV Application use third party TV inputs
+         * available on play store.
+         *
+         * @hide
+         */
+        public static final String TV_APP_USES_NON_SYSTEM_INPUTS = "tv_app_uses_non_system_inputs";
+
+        /**
          * Whether automatic routing of system audio to USB audio peripheral is disabled.
          * The value is boolean (1 or 0), where 1 means automatic routing is disabled,
          * and 0 means automatic routing is enabled.
@@ -8444,6 +8486,7 @@
             DOZE_ALWAYS_ON,
             DOZE_PICK_UP_GESTURE,
             DOZE_DOUBLE_TAP_GESTURE,
+            DOZE_TAP_SCREEN_GESTURE,
             DOZE_WAKE_LOCK_SCREEN_GESTURE,
             DOZE_WAKE_SCREEN_GESTURE,
             NFC_PAYMENT_DEFAULT_COMPONENT,
@@ -8482,6 +8525,8 @@
             NOTIFICATION_NEW_INTERRUPTION_MODEL,
             TRUST_AGENTS_EXTEND_UNLOCK,
             LOCK_SCREEN_WHEN_TRUST_LOST,
+            SKIP_GESTURE,
+            SILENCE_GESTURE,
         };
 
         /**
@@ -8598,6 +8643,7 @@
             VALIDATORS.put(DOZE_ALWAYS_ON, DOZE_ALWAYS_ON_VALIDATOR);
             VALIDATORS.put(DOZE_PICK_UP_GESTURE, DOZE_PICK_UP_GESTURE_VALIDATOR);
             VALIDATORS.put(DOZE_DOUBLE_TAP_GESTURE, DOZE_DOUBLE_TAP_GESTURE_VALIDATOR);
+            VALIDATORS.put(DOZE_TAP_SCREEN_GESTURE, DOZE_TAP_SCREEN_GESTURE_VALIDATOR);
             VALIDATORS.put(DOZE_WAKE_LOCK_SCREEN_GESTURE, DOZE_WAKE_LOCK_SCREEN_GESTURE_VALIDATOR);
             VALIDATORS.put(DOZE_WAKE_SCREEN_GESTURE, DOZE_WAKE_SCREEN_GESTURE_VALIDATOR);
             VALIDATORS.put(NFC_PAYMENT_DEFAULT_COMPONENT, NFC_PAYMENT_DEFAULT_COMPONENT_VALIDATOR);
@@ -8649,6 +8695,8 @@
             VALIDATORS.put(TRUST_AGENTS_EXTEND_UNLOCK, TRUST_AGENTS_EXTEND_UNLOCK_VALIDATOR);
             VALIDATORS.put(LOCK_SCREEN_CUSTOM_CLOCK_FACE, LOCK_SCREEN_CUSTOM_CLOCK_FACE_VALIDATOR);
             VALIDATORS.put(LOCK_SCREEN_WHEN_TRUST_LOST, LOCK_SCREEN_WHEN_TRUST_LOST_VALIDATOR);
+            VALIDATORS.put(SKIP_GESTURE, SKIP_GESTURE_VALIDATOR);
+            VALIDATORS.put(SILENCE_GESTURE, SILENCE_GESTURE_VALIDATOR);
         }
 
         /**
@@ -8680,10 +8728,10 @@
             CLONE_TO_MANAGED_PROFILE.add(LOCATION_CHANGER);
             CLONE_TO_MANAGED_PROFILE.add(LOCATION_MODE);
             CLONE_TO_MANAGED_PROFILE.add(LOCATION_PROVIDERS_ALLOWED);
-            CLONE_TO_MANAGED_PROFILE.add(SELECTED_INPUT_METHOD_SUBTYPE);
             if (!InputMethodSystemProperty.PER_PROFILE_IME_ENABLED) {
                 CLONE_TO_MANAGED_PROFILE.add(DEFAULT_INPUT_METHOD);
                 CLONE_TO_MANAGED_PROFILE.add(ENABLED_INPUT_METHODS);
+                CLONE_TO_MANAGED_PROFILE.add(SELECTED_INPUT_METHOD_SUBTYPE);
                 CLONE_TO_MANAGED_PROFILE.add(SELECTED_SPELL_CHECKER);
                 CLONE_TO_MANAGED_PROFILE.add(SELECTED_SPELL_CHECKER_SUBTYPE);
             }
@@ -9360,6 +9408,15 @@
                 "hdmi_system_audio_control_enabled";
 
         /**
+         * Whether HDMI Routing Control feature is enabled. If enabled, the switch device will
+         * route to the correct input source on receiving Routing Control related messages. If
+         * disabled, you can only switch the input via controls on this device.
+         * @hide
+         */
+        public static final String HDMI_CEC_SWITCH_ENABLED =
+                "hdmi_cec_switch_enabled";
+
+        /**
          * Whether TV will automatically turn on upon reception of the CEC command
          * &lt;Text View On&gt; or &lt;Image View On&gt;. (0 = false, 1 = true)
          *
@@ -11061,6 +11118,31 @@
         /** {@hide} */
         public static final String
                 BLUETOOTH_HEARING_AID_PRIORITY_PREFIX = "bluetooth_hearing_aid_priority_";
+        /**
+         * Enable/disable radio bug detection
+         *
+         * {@hide}
+         */
+        public static final String
+                ENABLE_RADIO_BUG_DETECTION = "enable_radio_bug_detection";
+
+        /**
+         * Count threshold of RIL wakelock timeout for radio bug detection
+         *
+         * {@hide}
+         */
+        public static final String
+                RADIO_BUG_WAKELOCK_TIMEOUT_COUNT_THRESHOLD =
+                "radio_bug_wakelock_timeout_count_threshold";
+
+        /**
+         * Count threshold of RIL system error for radio bug detection
+         *
+         * {@hide}
+         */
+        public static final String
+                RADIO_BUG_SYSTEM_ERROR_COUNT_THRESHOLD =
+                "radio_bug_system_error_count_threshold";
 
         /**
          * Activity manager specific settings.
@@ -11551,6 +11633,7 @@
          * battery_level_collection_delay_ms (long)
          * max_history_files (int)
          * max_history_buffer_kb (int)
+         * battery_charged_delay_ms (int)
          * </pre>
          *
          * <p>
@@ -11936,22 +12019,33 @@
                 "angle_gl_driver_selection_values";
 
         /**
-         * List of Apps selected to use Game Update Packages.
+         * Game Update Package global preference for all Apps.
+         * 0 = Default
+         * 1 = All Apps use Game Update Package
+         * 2 = All Apps use system graphics driver
+         * @hide
+         */
+        public static final String GUP_DEV_ALL_APPS = "gup_dev_all_apps";
+
+        /**
+         * List of Apps selected to use Game Update Package.
+         * i.e. <pkg1>,<pkg2>,...,<pkgN>
          * @hide
          */
         public static final String GUP_DEV_OPT_IN_APPS = "gup_dev_opt_in_apps";
 
         /**
-         * List of Apps selected not to use Game Update Packages.
+         * List of Apps selected not to use Game Update Package.
+         * i.e. <pkg1>,<pkg2>,...,<pkgN>
          * @hide
          */
         public static final String GUP_DEV_OPT_OUT_APPS = "gup_dev_opt_out_apps";
 
         /**
-         * Apps on the black list that are forbidden to useGame Update Package.
+         * Apps on the blacklist that are forbidden to use Game Update Package.
          * @hide
          */
-        public static final String GUP_BLACK_LIST = "gup_black_list";
+        public static final String GUP_BLACKLIST = "gup_blacklist";
 
         /**
          * Ordered GPU debug layer list for Vulkan
@@ -13772,6 +13866,7 @@
          * requires_targeting_p            (boolean)
          * max_squeeze_remeasure_attempts  (int)
          * edit_choices_before_sending     (boolean)
+         * show_in_heads_up                (boolean)
          * </pre>
          * @see com.android.systemui.statusbar.policy.SmartReplyConstants
          * @hide
diff --git a/core/java/android/service/autofill/augmented/AugmentedAutofillService.java b/core/java/android/service/autofill/augmented/AugmentedAutofillService.java
index a9f4034..aaba85b 100644
--- a/core/java/android/service/autofill/augmented/AugmentedAutofillService.java
+++ b/core/java/android/service/autofill/augmented/AugmentedAutofillService.java
@@ -22,6 +22,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
+import android.annotation.TestApi;
 import android.app.Service;
 import android.content.ComponentName;
 import android.content.Intent;
@@ -41,6 +42,7 @@
 import android.view.autofill.AutofillId;
 import android.view.autofill.AutofillValue;
 import android.view.autofill.IAugmentedAutofillManagerClient;
+import android.view.autofill.IAutofillWindowPresenter;
 
 import com.android.internal.annotations.GuardedBy;
 
@@ -58,6 +60,9 @@
  * @hide
  */
 @SystemApi
+@TestApi
+// TODO(b/122654591): @TestApi is needed because CtsAutoFillServiceTestCases hosts the service
+// in the same package as the test, and that module is compiled with SDK=test_current
 public abstract class AugmentedAutofillService extends Service {
 
     private static final String TAG = AugmentedAutofillService.class.getSimpleName();
@@ -91,10 +96,10 @@
         }
 
         @Override
-        public void onDestroyFillWindowRequest(int sessionId) {
+        public void onDestroyAllFillWindowsRequest() {
             mHandler.sendMessage(
-                    obtainMessage(AugmentedAutofillService::handleOnDestroyFillWindowRequest,
-                            AugmentedAutofillService.this, sessionId));
+                    obtainMessage(AugmentedAutofillService::handleOnDestroyAllFillWindowsRequest,
+                            AugmentedAutofillService.this));
         }
     };
 
@@ -181,18 +186,21 @@
                 new FillCallback(proxy));
     }
 
-    private void handleOnDestroyFillWindowRequest(@NonNull int sessionId) {
-        AutofillProxy proxy = null;
+    private void handleOnDestroyAllFillWindowsRequest() {
         if (mAutofillProxies != null) {
-            proxy = mAutofillProxies.get(sessionId);
+            final int size = mAutofillProxies.size();
+            for (int i = 0; i < size; i++) {
+                final int sessionId = mAutofillProxies.keyAt(i);
+                final AutofillProxy proxy = mAutofillProxies.valueAt(i);
+                if (proxy == null) {
+                    // TODO(b/111330312): this might be fine, in which case we should logv it
+                    Log.w(TAG, "No proxy for session " + sessionId);
+                    return;
+                }
+                proxy.destroy();
+            }
+            mAutofillProxies.clear();
         }
-        if (proxy == null) {
-            // TODO(b/111330312): this might be fine, in which case we should logv it
-            Log.w(TAG, "No proxy for session " + sessionId);
-            return;
-        }
-        proxy.destroy();
-        mAutofillProxies.remove(sessionId);
     }
 
     private void handleOnUnbind() {
@@ -268,7 +276,6 @@
          *
          * <p>Used to make sure the SmartSuggestionsParams is updated when a new fields is focused.
          */
-        // TODO(b/111330312): might not be needed when using IME
         @GuardedBy("mLock")
         private AutofillId mLastShownId;
 
@@ -347,6 +354,16 @@
             }
         }
 
+        public void requestShowFillUi(int width, int height, Rect anchorBounds,
+                IAutofillWindowPresenter presenter) throws RemoteException {
+            mClient.requestShowFillUi(mSessionId, mFocusedId, width, height, anchorBounds,
+                    presenter);
+        }
+
+        public void requestHideFillUi() throws RemoteException {
+            mClient.requestHideFillUi(mSessionId, mFocusedId);
+        }
+
         private void update(@NonNull AutofillId focusedId, @NonNull AutofillValue focusedValue) {
             synchronized (mLock) {
                 // TODO(b/111330312): should we close the popupwindow if the focused id changed?
diff --git a/core/java/android/service/autofill/augmented/FillCallback.java b/core/java/android/service/autofill/augmented/FillCallback.java
index 620ec59..bfb4aad 100644
--- a/core/java/android/service/autofill/augmented/FillCallback.java
+++ b/core/java/android/service/autofill/augmented/FillCallback.java
@@ -20,6 +20,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
+import android.annotation.TestApi;
 import android.service.autofill.augmented.AugmentedAutofillService.AutofillProxy;
 import android.util.Log;
 
@@ -29,6 +30,9 @@
  * @hide
  */
 @SystemApi
+@TestApi
+//TODO(b/122654591): @TestApi is needed because CtsAutoFillServiceTestCases hosts the service
+//in the same package as the test, and that module is compiled with SDK=test_current
 public final class FillCallback {
 
     private static final String TAG = FillCallback.class.getSimpleName();
diff --git a/core/java/android/service/autofill/augmented/FillController.java b/core/java/android/service/autofill/augmented/FillController.java
index e65cf47..d7bc893 100644
--- a/core/java/android/service/autofill/augmented/FillController.java
+++ b/core/java/android/service/autofill/augmented/FillController.java
@@ -19,6 +19,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
+import android.annotation.TestApi;
 import android.os.RemoteException;
 import android.service.autofill.augmented.AugmentedAutofillService.AutofillProxy;
 import android.util.Log;
@@ -36,6 +37,9 @@
  * @hide
  */
 @SystemApi
+@TestApi
+//TODO(b/122654591): @TestApi is needed because CtsAutoFillServiceTestCases hosts the service
+//in the same package as the test, and that module is compiled with SDK=test_current
 public final class FillController {
     private static final String TAG = "FillController";
 
diff --git a/core/java/android/service/autofill/augmented/FillRequest.java b/core/java/android/service/autofill/augmented/FillRequest.java
index 57d2cc8..dad5067 100644
--- a/core/java/android/service/autofill/augmented/FillRequest.java
+++ b/core/java/android/service/autofill/augmented/FillRequest.java
@@ -18,6 +18,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
+import android.annotation.TestApi;
 import android.content.ComponentName;
 import android.service.autofill.augmented.AugmentedAutofillService.AutofillProxy;
 import android.view.autofill.AutofillId;
@@ -29,6 +30,9 @@
  */
 @SystemApi
 // TODO(b/111330312): pass a requestId and/or sessionId
+@TestApi
+// TODO(b/122654591): @TestApi is needed because CtsAutoFillServiceTestCases hosts the service
+// in the same package as the test, and that module is compiled with SDK=test_current
 public final class FillRequest {
 
     final AutofillProxy mProxy;
diff --git a/core/java/android/service/autofill/augmented/FillResponse.java b/core/java/android/service/autofill/augmented/FillResponse.java
index 1ecfab4..5285132 100644
--- a/core/java/android/service/autofill/augmented/FillResponse.java
+++ b/core/java/android/service/autofill/augmented/FillResponse.java
@@ -18,6 +18,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
+import android.annotation.TestApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.view.autofill.AutofillId;
@@ -30,6 +31,9 @@
  * @hide
  */
 @SystemApi
+@TestApi
+//TODO(b/122654591): @TestApi is needed because CtsAutoFillServiceTestCases hosts the service
+//in the same package as the test, and that module is compiled with SDK=test_current
 public final class FillResponse implements Parcelable {
 
     private final FillWindow mFillWindow;
@@ -50,6 +54,9 @@
      * @hide
      */
     @SystemApi
+    @TestApi
+    //TODO(b/122654591): @TestApi is needed because CtsAutoFillServiceTestCases hosts the service
+    //in the same package as the test, and that module is compiled with SDK=test_current
     public static final class Builder {
 
         private FillWindow mFillWindow;
diff --git a/core/java/android/service/autofill/augmented/FillWindow.java b/core/java/android/service/autofill/augmented/FillWindow.java
index bad7ddd..51b0f01 100644
--- a/core/java/android/service/autofill/augmented/FillWindow.java
+++ b/core/java/android/service/autofill/augmented/FillWindow.java
@@ -16,21 +16,25 @@
 package android.service.autofill.augmented;
 
 import static android.service.autofill.augmented.AugmentedAutofillService.DEBUG;
+import static android.service.autofill.augmented.AugmentedAutofillService.VERBOSE;
+
+import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
 
 import android.annotation.LongDef;
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
-import android.app.Dialog;
+import android.annotation.TestApi;
 import android.graphics.Rect;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.RemoteException;
 import android.service.autofill.augmented.AugmentedAutofillService.AutofillProxy;
 import android.service.autofill.augmented.PresentationParams.Area;
 import android.util.Log;
-import android.view.Gravity;
 import android.view.MotionEvent;
 import android.view.View;
-import android.view.ViewGroup;
-import android.view.Window;
 import android.view.WindowManager;
+import android.view.autofill.IAutofillWindowPresenter;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.util.Preconditions;
@@ -61,13 +65,16 @@
  * @hide
  */
 @SystemApi
+@TestApi
+//TODO(b/122654591): @TestApi is needed because CtsAutoFillServiceTestCases hosts the service
+//in the same package as the test, and that module is compiled with SDK=test_current
 public final class FillWindow implements AutoCloseable {
     private static final String TAG = "FillWindow";
 
     /** Indicates the data being shown is a physical address */
     public static final long FLAG_METADATA_ADDRESS = 0x1;
 
-    // TODO(b/111330312): add moar flags
+    // TODO(b/111330312): add more flags
 
     /** @hide */
     @LongDef(prefix = { "FLAG" }, value = {
@@ -79,8 +86,17 @@
     private final Object mLock = new Object();
     private final CloseGuard mCloseGuard = CloseGuard.get();
 
+    private final @NonNull Handler mUiThreadHandler = new Handler(Looper.getMainLooper());
+    private final @NonNull FillWindowPresenter mFillWindowPresenter = new FillWindowPresenter();
+
     @GuardedBy("mLock")
-    private Dialog mDialog;
+    private WindowManager mWm;
+    @GuardedBy("mLock")
+    private View mFillView;
+    @GuardedBy("mLock")
+    private boolean mShowing;
+    @GuardedBy("mLock")
+    private Rect mBounds;
 
     @GuardedBy("mLock")
     private boolean mDestroyed;
@@ -136,51 +152,28 @@
             // window instead of destroying. In fact, it might be better to allocate a full window
             // initially, which is transparent (and let touches get through) everywhere but in the
             // rect boundaries.
-            destroy();
 
             // TODO(b/111330312): make sure all touch events are handled, window is always closed,
             // etc.
 
-            mDialog = new Dialog(rootView.getContext()) {
-                @Override
-                public boolean onTouchEvent(MotionEvent event) {
-                    if (event.getAction() == MotionEvent.ACTION_OUTSIDE) {
-                        FillWindow.this.destroy();
+            mWm = rootView.getContext().getSystemService(WindowManager.class);
+            mFillView = rootView;
+            // Listen to the touch outside to destroy the window when typing is detected.
+            mFillView.setOnTouchListener(
+                    (view, motionEvent) -> {
+                        if (motionEvent.getAction() == MotionEvent.ACTION_OUTSIDE) {
+                            if (VERBOSE) Log.v(TAG, "Outside touch detected, hiding the window");
+                            hide();
+                        }
+                        return false;
                     }
-                    return false;
-                }
-            };
-            mCloseGuard.open("destroy");
-            final Window window = mDialog.getWindow();
-            window.setType(WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY);
-            // Makes sure touch outside the dialog is received by the window behind the dialog.
-            window.addFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL);
-            // Makes sure the touch outside the dialog is received by the dialog to dismiss it.
-            window.addFlags(WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH);
-            // Makes sure keyboard shows up.
-            window.addFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);
-
-            final int height = rect.bottom - rect.top;
-            final int width = rect.right - rect.left;
-            final WindowManager.LayoutParams windowParams = window.getAttributes();
-            windowParams.gravity = Gravity.TOP | Gravity.LEFT;
-            windowParams.y = rect.top + height;
-            windowParams.height = height;
-            windowParams.x = rect.left;
-            windowParams.width = width;
-
-            window.setAttributes(windowParams);
-            window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
-            window.setBackgroundDrawableResource(android.R.color.transparent);
-
-            mDialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
-            final ViewGroup.LayoutParams diagParams = new ViewGroup.LayoutParams(width, height);
-            mDialog.setContentView(rootView, diagParams);
-
+            );
+            mShowing = false;
+            mBounds = new Rect(area.getBounds());
             if (DEBUG) {
                 Log.d(TAG, "Created FillWindow: params= " + smartSuggestion + " view=" + rootView);
             }
-
+            mDestroyed = false;
             mProxy.setFillWindow(this);
             return true;
         }
@@ -190,36 +183,87 @@
     void show() {
         // TODO(b/111330312): check if updated first / throw exception
         if (DEBUG) Log.d(TAG, "show()");
-
         synchronized (mLock) {
             checkNotDestroyedLocked();
-            if (mDialog == null) {
+            if (mWm == null || mFillView == null) {
                 throw new IllegalStateException("update() not called yet, or already destroyed()");
             }
-
-            mDialog.show();
             if (mProxy != null) {
+                try {
+                    mProxy.requestShowFillUi(mBounds.right - mBounds.left,
+                            mBounds.bottom - mBounds.top,
+                            /*anchorBounds=*/ null, mFillWindowPresenter);
+                } catch (RemoteException e) {
+                    Log.w(TAG, "Error requesting to show fill window", e);
+                }
                 mProxy.report(AutofillProxy.REPORT_EVENT_UI_SHOWN);
             }
         }
     }
 
     /**
+     * Hides the window.
+     *
+     * <p>The window is not destroyed and can be shown again
+     */
+    private void hide() {
+        if (DEBUG) Log.d(TAG, "hide()");
+        synchronized (mLock) {
+            checkNotDestroyedLocked();
+            if (mWm == null || mFillView == null) {
+                throw new IllegalStateException("update() not called yet, or already destroyed()");
+            }
+            if (mProxy != null && mShowing) {
+                try {
+                    mProxy.requestHideFillUi();
+                } catch (RemoteException e) {
+                    Log.w(TAG, "Error requesting to hide fill window", e);
+                }
+            }
+        }
+    }
+
+    private void handleShow(WindowManager.LayoutParams p) {
+        if (DEBUG) Log.d(TAG, "handleShow()");
+        synchronized (mLock) {
+            if (mWm != null && mFillView != null) {
+                p.flags |= WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;
+                if (!mShowing) {
+                    mWm.addView(mFillView, p);
+                    mShowing = true;
+                } else {
+                    mWm.updateViewLayout(mFillView, p);
+                }
+            }
+        }
+    }
+
+    private void handleHide() {
+        if (DEBUG) Log.d(TAG, "handleHide()");
+        synchronized (mLock) {
+            if (mWm != null && mFillView != null && mShowing) {
+                mWm.removeView(mFillView);
+                mShowing = false;
+            }
+        }
+    }
+
+    /**
      * Destroys the window.
      *
      * <p>Once destroyed, this window cannot be used anymore
      */
     public void destroy() {
-        if (DEBUG) Log.d(TAG, "destroy(): mDestroyed=" + mDestroyed + " mDialog=" + mDialog);
-
-        synchronized (this) {
-            if (mDestroyed || mDialog == null) return;
-
-            mDialog.dismiss();
-            mDialog = null;
-            if (mProxy != null) {
-                mProxy.report(AutofillProxy.REPORT_EVENT_UI_DESTROYED);
-            }
+        if (DEBUG) {
+            Log.d(TAG,
+                    "destroy(): mDestroyed=" + mDestroyed + " mShowing=" + mShowing + " mFillView="
+                            + mFillView);
+        }
+        synchronized (mLock) {
+            if (mDestroyed) return;
+            hide();
+            mProxy.report(AutofillProxy.REPORT_EVENT_UI_DESTROYED);
+            mDestroyed = true;
             mCloseGuard.close();
         }
     }
@@ -246,11 +290,15 @@
     public void dump(@NonNull String prefix, @NonNull PrintWriter pw) {
         synchronized (this) {
             pw.print(prefix); pw.print("destroyed: "); pw.println(mDestroyed);
-            if (mDialog != null) {
-                pw.print(prefix); pw.print("dialog: ");
-                pw.println(mDialog.isShowing() ? "shown" : "hidden");
-                pw.print(prefix); pw.print("window: ");
-                pw.println(mDialog.getWindow().getAttributes());
+            if (mFillView != null) {
+                pw.print(prefix); pw.print("fill window: ");
+                pw.println(mShowing ? "shown" : "hidden");
+                pw.print(prefix); pw.print("fill view: ");
+                pw.println(mFillView);
+                pw.print(prefix); pw.print("mBounds: ");
+                pw.println(mBounds);
+                pw.print(prefix); pw.print("mWm: ");
+                pw.println(mWm);
             }
         }
     }
@@ -260,4 +308,19 @@
     public void close() throws Exception {
         destroy();
     }
+
+    private final class FillWindowPresenter extends IAutofillWindowPresenter.Stub {
+        @Override
+        public void show(WindowManager.LayoutParams p, Rect transitionEpicenter,
+                boolean fitsSystemWindows, int layoutDirection) {
+            if (DEBUG) Log.d(TAG, "FillWindowPresenter.show()");
+            mUiThreadHandler.sendMessage(obtainMessage(FillWindow::handleShow, FillWindow.this, p));
+        }
+
+        @Override
+        public void hide(Rect transitionEpicenter) {
+            if (DEBUG) Log.d(TAG, "FillWindowPresenter.hide()");
+            mUiThreadHandler.sendMessage(obtainMessage(FillWindow::handleHide, FillWindow.this));
+        }
+    }
 }
diff --git a/core/java/android/service/autofill/augmented/IAugmentedAutofillService.aidl b/core/java/android/service/autofill/augmented/IAugmentedAutofillService.aidl
index b3ac2da..fb6912a 100644
--- a/core/java/android/service/autofill/augmented/IAugmentedAutofillService.aidl
+++ b/core/java/android/service/autofill/augmented/IAugmentedAutofillService.aidl
@@ -36,5 +36,5 @@
                        in ComponentName activityComponent, in AutofillId focusedId,
                        in AutofillValue focusedValue, long requestTime, in IFillCallback callback);
 
-    void onDestroyFillWindowRequest(int sessionId);
+    void onDestroyAllFillWindowsRequest();
 }
diff --git a/core/java/android/service/autofill/augmented/PresentationParams.java b/core/java/android/service/autofill/augmented/PresentationParams.java
index 0124ecc..b60064e 100644
--- a/core/java/android/service/autofill/augmented/PresentationParams.java
+++ b/core/java/android/service/autofill/augmented/PresentationParams.java
@@ -19,6 +19,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
+import android.annotation.TestApi;
 import android.graphics.Rect;
 import android.service.autofill.augmented.AugmentedAutofillService.AutofillProxy;
 import android.util.DebugUtils;
@@ -48,6 +49,9 @@
  * @hide
  */
 @SystemApi
+@TestApi
+//TODO(b/122654591): @TestApi is needed because CtsAutoFillServiceTestCases hosts the service
+//in the same package as the test, and that module is compiled with SDK=test_current
 public abstract class PresentationParams {
 
     /**
@@ -147,8 +151,11 @@
      * Area associated with a {@link PresentationParams Smart Suggestions} provider.
      *
      * @hide
-     * */
+     */
     @SystemApi
+    @TestApi
+    //TODO(b/122654591): @TestApi is needed because CtsAutoFillServiceTestCases hosts the service
+    //in the same package as the test, and that module is compiled with SDK=test_current
     public abstract static class Area {
 
         /** @hide */
diff --git a/core/java/android/service/contentcapture/ContentCaptureService.java b/core/java/android/service/contentcapture/ContentCaptureService.java
index e5e028d..302e1a6 100644
--- a/core/java/android/service/contentcapture/ContentCaptureService.java
+++ b/core/java/android/service/contentcapture/ContentCaptureService.java
@@ -48,6 +48,7 @@
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
+import java.util.Collections;
 import java.util.List;
 import java.util.Set;
 
@@ -77,6 +78,7 @@
             "android.service.contentcapture.ContentCaptureService";
 
     private Handler mHandler;
+    private IContentCaptureServiceCallback mCallback;
 
     /**
      * Binder that receives calls from the system server.
@@ -84,9 +86,15 @@
     private final IContentCaptureService mServerInterface = new IContentCaptureService.Stub() {
 
         @Override
-        public void onConnectedStateChanged(boolean state) {
-            mHandler.sendMessage(obtainMessage(ContentCaptureService::handleOnConnectedStateChanged,
-                    ContentCaptureService.this, state));
+        public void onConnected(IBinder callback) {
+            mHandler.sendMessage(obtainMessage(ContentCaptureService::handleOnConnected,
+                    ContentCaptureService.this, callback));
+        }
+
+        @Override
+        public void onDisconnected() {
+            mHandler.sendMessage(obtainMessage(ContentCaptureService::handleOnDisconnected,
+                    ContentCaptureService.this));
         }
 
         @Override
@@ -166,7 +174,16 @@
      */
     public final void setContentCaptureWhitelist(@Nullable List<String> packages,
             @Nullable List<ComponentName> activities) {
-        //TODO(b/122595322): implement
+        final IContentCaptureServiceCallback callback = mCallback;
+        if (callback == null) {
+            Log.w(TAG, "setContentCaptureWhitelist(): no server callback");
+            return;
+        }
+        try {
+            callback.setContentCaptureWhitelist(packages, activities);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
     }
 
     /**
@@ -177,7 +194,16 @@
      */
     public final void setActivityContentCaptureEnabled(@NonNull ComponentName activity,
             boolean enabled) {
-        //TODO(b/122595322): implement
+        final IContentCaptureServiceCallback callback = mCallback;
+        if (callback == null) {
+            Log.w(TAG, "setActivityContentCaptureEnabled(): no server callback");
+            return;
+        }
+        try {
+            callback.setActivityContentCaptureEnabled(activity, enabled);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
     }
 
     /**
@@ -188,7 +214,16 @@
      */
     public final void setPackageContentCaptureEnabled(@NonNull String packageName,
             boolean enabled) {
-        //TODO(b/122595322): implement
+        final IContentCaptureServiceCallback callback = mCallback;
+        if (callback == null) {
+            Log.w(TAG, "setPackageContentCaptureEnabled(): no server callback");
+            return;
+        }
+        try {
+            callback.setPackageContentCaptureEnabled(packageName, enabled);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
     }
 
     /**
@@ -197,7 +232,12 @@
      */
     @NonNull
     public final Set<ComponentName> getContentCaptureDisabledActivities() {
-        //TODO(b/122595322): implement
+        final IContentCaptureServiceCallback callback = mCallback;
+        if (callback == null) {
+            Log.w(TAG, "getContentCaptureDisabledActivities(): no server callback");
+            return Collections.emptySet();
+        }
+        //TODO(b/122595322): implement (using SyncResultReceiver)
         return null;
     }
 
@@ -207,7 +247,12 @@
      */
     @NonNull
     public final Set<String> getContentCaptureDisabledPackages() {
-        //TODO(b/122595322): implement
+        final IContentCaptureServiceCallback callback = mCallback;
+        if (callback == null) {
+            Log.w(TAG, "getContentCaptureDisabledPackages(): no server callback");
+            return Collections.emptySet();
+        }
+        //TODO(b/122595322): implement (using SyncResultReceiver)
         return null;
     }
 
@@ -307,12 +352,14 @@
         }
     }
 
-    private void handleOnConnectedStateChanged(boolean state) {
-        if (state) {
-            onConnected();
-        } else {
-            onDisconnected();
-        }
+    private void handleOnConnected(@NonNull IBinder callback) {
+        mCallback = IContentCaptureServiceCallback.Stub.asInterface(callback);
+        onConnected();
+    }
+
+    private void handleOnDisconnected() {
+        onDisconnected();
+        mCallback = null;
     }
 
     //TODO(b/111276913): consider caching the InteractionSessionId for the lifetime of the session,
@@ -323,15 +370,21 @@
         mSessionUids.put(sessionId, uid);
         onCreateContentCaptureSession(context, new ContentCaptureSessionId(sessionId));
 
-        final int flags = context.getFlags();
-        if ((flags & ContentCaptureContext.FLAG_DISABLED_BY_FLAG_SECURE) != 0) {
-            setClientState(clientReceiver, ContentCaptureSession.STATE_DISABLED_BY_FLAG_SECURE,
-                    mClientInterface.asBinder());
-            return;
+        final int clientFlags = context.getFlags();
+        int stateFlags = 0;
+        if ((clientFlags & ContentCaptureContext.FLAG_DISABLED_BY_FLAG_SECURE) != 0) {
+            stateFlags |= ContentCaptureSession.STATE_FLAG_SECURE;
         }
+        if ((clientFlags & ContentCaptureContext.FLAG_DISABLED_BY_APP) != 0) {
+            stateFlags |= ContentCaptureSession.STATE_BY_APP;
+        }
+        if (stateFlags == 0) {
+            stateFlags = ContentCaptureSession.STATE_ACTIVE;
+        } else {
+            stateFlags |= ContentCaptureSession.STATE_DISABLED;
 
-        setClientState(clientReceiver, ContentCaptureSession.STATE_ACTIVE,
-                mClientInterface.asBinder());
+        }
+        setClientState(clientReceiver, stateFlags, mClientInterface.asBinder());
     }
 
     private void handleSendEvents(int uid,
diff --git a/core/java/android/service/contentcapture/IContentCaptureService.aidl b/core/java/android/service/contentcapture/IContentCaptureService.aidl
index 1b4cccf..a8dd213 100644
--- a/core/java/android/service/contentcapture/IContentCaptureService.aidl
+++ b/core/java/android/service/contentcapture/IContentCaptureService.aidl
@@ -16,6 +16,7 @@
 
 package android.service.contentcapture;
 
+import android.os.IBinder;
 import android.service.contentcapture.SnapshotData;
 import android.view.contentcapture.ContentCaptureContext;
 
@@ -29,7 +30,8 @@
  * @hide
  */
 oneway interface IContentCaptureService {
-    void onConnectedStateChanged(boolean state);
+    void onConnected(IBinder callback);
+    void onDisconnected();
     void onSessionStarted(in ContentCaptureContext context, String sessionId, int uid,
                           in IResultReceiver clientReceiver);
     void onSessionFinished(String sessionId);
diff --git a/core/java/android/service/contentcapture/IContentCaptureServiceCallback.aidl b/core/java/android/service/contentcapture/IContentCaptureServiceCallback.aidl
new file mode 100644
index 0000000..e84bd6f
--- /dev/null
+++ b/core/java/android/service/contentcapture/IContentCaptureServiceCallback.aidl
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.contentcapture;
+
+import android.content.ComponentName;
+import com.android.internal.os.IResultReceiver;
+
+import java.util.List;
+
+/**
+ * Interface from the Content Capture service to the system.
+ *
+ * @hide
+ */
+oneway interface IContentCaptureServiceCallback {
+    void setContentCaptureWhitelist(in List<String> packages, in List<ComponentName> activities);
+    void setActivityContentCaptureEnabled(in ComponentName activity, boolean enabled);
+    void setPackageContentCaptureEnabled(in String packageName, boolean enabled);
+    void getContentCaptureDisabledActivities(in IResultReceiver receiver);
+    void getContentCaptureDisabledPackages(in IResultReceiver receiver);
+}
diff --git a/core/java/android/service/notification/NotificationAssistantService.java b/core/java/android/service/notification/NotificationAssistantService.java
index fd2b0fa..63fd563 100644
--- a/core/java/android/service/notification/NotificationAssistantService.java
+++ b/core/java/android/service/notification/NotificationAssistantService.java
@@ -203,7 +203,7 @@
      * @param action the action that is just clicked
      * @param source the source that provided the action, e.g. SOURCE_FROM_APP
      */
-    public void onActionClicked(@NonNull String key, @NonNull Notification.Action action,
+    public void onActionInvoked(@NonNull String key, @NonNull Notification.Action action,
             @Source int source) {
     }
 
@@ -338,7 +338,7 @@
             args.arg1 = key;
             args.arg2 = action;
             args.argi2 = source;
-            mHandler.obtainMessage(MyHandler.MSG_ON_ACTION_CLICKED, args).sendToTarget();
+            mHandler.obtainMessage(MyHandler.MSG_ON_ACTION_INVOKED, args).sendToTarget();
         }
     }
 
@@ -349,7 +349,7 @@
         public static final int MSG_ON_NOTIFICATION_EXPANSION_CHANGED = 4;
         public static final int MSG_ON_NOTIFICATION_DIRECT_REPLY_SENT = 5;
         public static final int MSG_ON_SUGGESTED_REPLY_SENT = 6;
-        public static final int MSG_ON_ACTION_CLICKED = 7;
+        public static final int MSG_ON_ACTION_INVOKED = 7;
 
         public MyHandler(Looper looper) {
             super(looper, null, false);
@@ -419,13 +419,13 @@
                     onSuggestedReplySent(key, reply, source);
                     break;
                 }
-                case MSG_ON_ACTION_CLICKED: {
+                case MSG_ON_ACTION_INVOKED: {
                     SomeArgs args = (SomeArgs) msg.obj;
                     String key = (String) args.arg1;
                     Notification.Action action = (Notification.Action) args.arg2;
                     int source = args.argi2;
                     args.recycle();
-                    onActionClicked(key, action, source);
+                    onActionInvoked(key, action, source);
                     break;
                 }
             }
diff --git a/core/java/android/service/notification/StatusBarNotification.java b/core/java/android/service/notification/StatusBarNotification.java
index ad10cc9..3a644d4 100644
--- a/core/java/android/service/notification/StatusBarNotification.java
+++ b/core/java/android/service/notification/StatusBarNotification.java
@@ -22,16 +22,21 @@
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
+import android.metrics.LogMaker;
 import android.os.Build;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.UserHandle;
 
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+
 /**
  * Class encapsulating a Notification. Sent by the NotificationManagerService to clients including
  * the status bar and any {@link android.service.notification.NotificationListenerService}s.
  */
 public class StatusBarNotification implements Parcelable {
+    static final int MAX_LOG_TAG_LENGTH = 36;
+
     @UnsupportedAppUsage
     private final String pkg;
     @UnsupportedAppUsage
@@ -56,6 +61,9 @@
 
     private Context mContext; // used for inflation & icon expansion
 
+    // Contains the basic logging data of the notification.
+    private LogMaker mLogMaker;
+
     /** @hide */
     public StatusBarNotification(String pkg, String opPkg, int id,
             String tag, int uid, int initialPid, Notification notification, UserHandle user,
@@ -381,4 +389,51 @@
         }
         return mContext;
     }
+
+    /**
+     * Returns a LogMaker that contains all basic information of the notification.
+     * @hide
+     */
+    public LogMaker getLogMaker() {
+        if (mLogMaker == null) {
+            // Initialize fields that only change on update (so a new record).
+            mLogMaker = new LogMaker(MetricsEvent.VIEW_UNKNOWN)
+                .setPackageName(getPackageName())
+                .addTaggedData(MetricsEvent.NOTIFICATION_ID, getId())
+                .addTaggedData(MetricsEvent.NOTIFICATION_TAG, getTag())
+                .addTaggedData(MetricsEvent.FIELD_NOTIFICATION_CHANNEL_ID, getChannelIdLogTag());
+        }
+        // Reset fields that can change between updates, or are used by multiple logs.
+        return mLogMaker
+            .clearCategory()
+            .clearType()
+            .clearSubtype()
+            .addTaggedData(MetricsEvent.FIELD_NOTIFICATION_GROUP_ID, getGroupLogTag())
+            .addTaggedData(MetricsEvent.FIELD_NOTIFICATION_GROUP_SUMMARY,
+                getNotification().isGroupSummary() ? 1 : 0);
+    }
+
+    private String getGroupLogTag() {
+        return shortenTag(getGroup());
+    }
+
+    private String getChannelIdLogTag() {
+        if (notification.getChannelId() == null) {
+            return null;
+        }
+        return shortenTag(notification.getChannelId());
+    }
+
+    // Make logTag with max size MAX_LOG_TAG_LENGTH.
+    // For shorter or equal tags, returns the tag.
+    // For longer tags, truncate the tag and append a hash of the full tag to
+    // fill the maximum size.
+    private String shortenTag(String logTag) {
+        if (logTag == null || logTag.length() <= MAX_LOG_TAG_LENGTH) {
+            return logTag;
+        }
+        String hash = Integer.toHexString(logTag.hashCode());
+        return logTag.substring(0, MAX_LOG_TAG_LENGTH - hash.length() - 1) + "-"
+            + hash;
+    }
 }
diff --git a/core/java/android/service/voice/VoiceInteractionSession.java b/core/java/android/service/voice/VoiceInteractionSession.java
index 163e3d5..6f27447 100644
--- a/core/java/android/service/voice/VoiceInteractionSession.java
+++ b/core/java/android/service/voice/VoiceInteractionSession.java
@@ -121,6 +121,12 @@
      */
     public static final int SHOW_SOURCE_NOTIFICATION = 1 << 6;
 
+    /**
+     * Flag for use with {@link #onShow}: indicates that the voice interaction service was invoked
+     * from an Android automotive system Ui.
+     */
+    public static final int SHOW_SOURCE_AUTOMOTIVE_SYSTEM_UI = 1 << 7;
+
     final Context mContext;
     final HandlerCaller mHandlerCaller;
 
diff --git a/core/java/android/text/TextUtils.java b/core/java/android/text/TextUtils.java
index 85b6b88..44353b1 100644
--- a/core/java/android/text/TextUtils.java
+++ b/core/java/android/text/TextUtils.java
@@ -33,8 +33,7 @@
 import android.icu.util.ULocale;
 import android.os.Parcel;
 import android.os.Parcelable;
-import android.os.SystemProperties;
-import android.provider.Settings;
+import android.sysprop.DisplayProperties;
 import android.text.style.AbsoluteSizeSpan;
 import android.text.style.AccessibilityClickableSpan;
 import android.text.style.AccessibilityURLSpan;
@@ -2059,7 +2058,7 @@
         return ((locale != null && !locale.equals(Locale.ROOT)
                         && ULocale.forLocale(locale).isRightToLeft())
                 // If forcing into RTL layout mode, return RTL as default
-                || SystemProperties.getBoolean(Settings.Global.DEVELOPMENT_FORCE_RTL, false))
+                || DisplayProperties.debug_force_rtl().orElse(false))
             ? View.LAYOUT_DIRECTION_RTL
             : View.LAYOUT_DIRECTION_LTR;
     }
diff --git a/core/java/android/text/util/Linkify.java b/core/java/android/text/util/Linkify.java
index eef7ea2..50e7ec3 100644
--- a/core/java/android/text/util/Linkify.java
+++ b/core/java/android/text/util/Linkify.java
@@ -37,7 +37,6 @@
 import com.android.i18n.phonenumbers.PhoneNumberMatch;
 import com.android.i18n.phonenumbers.PhoneNumberUtil;
 import com.android.i18n.phonenumbers.PhoneNumberUtil.Leniency;
-import com.android.internal.annotations.GuardedBy;
 
 import libcore.util.EmptyArray;
 
@@ -49,6 +48,7 @@
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.Locale;
+import java.util.function.Function;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
@@ -69,7 +69,6 @@
  *
  * @see MatchFilter
  * @see TransformFilter
- * @see UrlSpanFactory
  */
 
 public class Linkify {
@@ -228,44 +227,6 @@
     }
 
     /**
-     * Factory class to create {@link URLSpan}s. While adding spans to a {@link Spannable},
-     * {@link Linkify} will call {@link UrlSpanFactory#create(String)} function to create a
-     * {@link URLSpan}.
-     *
-     * @see #addLinks(Spannable, int, UrlSpanFactory)
-     * @see #addLinks(Spannable, Pattern, String, String[], MatchFilter, TransformFilter,
-     * UrlSpanFactory)
-     */
-    public static class UrlSpanFactory {
-        private static final Object sInstanceLock = new Object();
-
-        @GuardedBy("sInstanceLock")
-        private static volatile UrlSpanFactory sInstance = null;
-
-        private static synchronized UrlSpanFactory getInstance() {
-            if (sInstance == null) {
-                synchronized (sInstanceLock) {
-                    if (sInstance == null) {
-                        sInstance = new UrlSpanFactory();
-                    }
-                }
-            }
-            return sInstance;
-        }
-
-        /**
-         * Factory function that will called by {@link Linkify} in order to create a
-         * {@link URLSpan}.
-         *
-         * @param url URL found
-         * @return a URLSpan instance
-         */
-        public URLSpan create(final String url) {
-            return new URLSpan(url);
-        }
-    }
-
-    /**
      *  Scans the text of the provided Spannable and turns all occurrences
      *  of the link types indicated in the mask into clickable links.
      *  If the mask is nonzero, it also removes any existing URLSpans
@@ -277,7 +238,7 @@
      *
      *  @return True if at least one link is found and applied.
      *
-     * @see #addLinks(Spannable, int, UrlSpanFactory)
+     * @see #addLinks(Spannable, int, Function)
      */
     public static final boolean addLinks(@NonNull Spannable text, @LinkifyMask int mask) {
         return addLinks(text, mask, null, null);
@@ -292,11 +253,11 @@
      *
      *  @param text Spannable whose text is to be marked-up with links
      *  @param mask mask to define which kinds of links will be searched
-     *  @param urlSpanFactory factory class used to create {@link URLSpan}s
+     *  @param urlSpanFactory function used to create {@link URLSpan}s
      *  @return True if at least one link is found and applied.
      */
     public static final boolean addLinks(@NonNull Spannable text, @LinkifyMask int mask,
-            @Nullable UrlSpanFactory urlSpanFactory) {
+            @Nullable Function<String, URLSpan> urlSpanFactory) {
         return addLinks(text, mask, null, urlSpanFactory);
     }
 
@@ -309,11 +270,11 @@
      * @param text Spannable whose text is to be marked-up with links
      * @param mask mask to define which kinds of links will be searched
      * @param context Context to be used while identifying phone numbers
-     * @param urlSpanFactory factory class used to create {@link URLSpan}s
+     * @param urlSpanFactory function used to create {@link URLSpan}s
      * @return true if at least one link is found and applied.
      */
     private static boolean addLinks(@NonNull Spannable text, @LinkifyMask int mask,
-            @Nullable Context context, @Nullable UrlSpanFactory urlSpanFactory) {
+            @Nullable Context context, @Nullable Function<String, URLSpan> urlSpanFactory) {
         if (text != null && containsUnsupportedCharacters(text.toString())) {
             android.util.EventLog.writeEvent(0x534e4554, "116321860", -1, "");
             return false;
@@ -398,7 +359,7 @@
      *
      *  @return True if at least one link is found and applied.
      *
-     *  @see #addLinks(Spannable, int, UrlSpanFactory)
+     *  @see #addLinks(Spannable, int, Function)
      */
     public static final boolean addLinks(@NonNull TextView text, @LinkifyMask int mask) {
         if (mask == 0) {
@@ -512,8 +473,7 @@
      *  @param pattern      Regex pattern to be used for finding links
      *  @param scheme       URL scheme string (eg <code>http://</code>) to be
      *                      prepended to the links that do not start with this scheme.
-     * @see #addLinks(Spannable, Pattern, String, String[], MatchFilter, TransformFilter,
-     * UrlSpanFactory)
+     * @see #addLinks(Spannable, Pattern, String, String[], MatchFilter, TransformFilter, Function)
      */
     public static final boolean addLinks(@NonNull Spannable text, @NonNull Pattern pattern,
             @Nullable String scheme) {
@@ -534,8 +494,7 @@
      * @param transformFilter Filter to allow the client code to update the link found.
      *
      * @return True if at least one link is found and applied.
-     * @see #addLinks(Spannable, Pattern, String, String[], MatchFilter, TransformFilter,
-     * UrlSpanFactory)
+     * @see #addLinks(Spannable, Pattern, String, String[], MatchFilter, TransformFilter, Function)
      */
     public static final boolean addLinks(@NonNull Spannable spannable, @NonNull Pattern pattern,
             @Nullable String scheme, @Nullable MatchFilter matchFilter,
@@ -560,8 +519,7 @@
      *
      * @return True if at least one link is found and applied.
      *
-     * @see #addLinks(Spannable, Pattern, String, String[], MatchFilter, TransformFilter,
-     * UrlSpanFactory)
+     * @see #addLinks(Spannable, Pattern, String, String[], MatchFilter, TransformFilter, Function)
      */
     public static final boolean addLinks(@NonNull Spannable spannable, @NonNull Pattern pattern,
             @Nullable String defaultScheme, @Nullable String[] schemes,
@@ -584,14 +542,14 @@
      * @param matchFilter     the filter that is used to allow the client code additional control
      *                        over which pattern matches are to be converted into links.
      * @param transformFilter filter to allow the client code to update the link found.
-     * @param urlSpanFactory  factory class used to create {@link URLSpan}s
+     * @param urlSpanFactory  function used to create {@link URLSpan}s
      *
      * @return True if at least one link is found and applied.
      */
     public static final boolean addLinks(@NonNull Spannable spannable, @NonNull Pattern pattern,
             @Nullable String defaultScheme, @Nullable String[] schemes,
             @Nullable MatchFilter matchFilter, @Nullable TransformFilter transformFilter,
-            @Nullable UrlSpanFactory urlSpanFactory) {
+            @Nullable Function<String, URLSpan> urlSpanFactory) {
         if (spannable != null && containsUnsupportedCharacters(spannable.toString())) {
             android.util.EventLog.writeEvent(0x534e4554, "116321860", -1, "");
             return false;
@@ -634,11 +592,11 @@
     }
 
     private static void applyLink(String url, int start, int end, Spannable text,
-            @Nullable UrlSpanFactory urlSpanFactory) {
+            @Nullable Function<String, URLSpan> urlSpanFactory) {
         if (urlSpanFactory == null) {
-            urlSpanFactory = UrlSpanFactory.getInstance();
+            urlSpanFactory = DEFAULT_SPAN_FACTORY;
         }
-        final URLSpan span = urlSpanFactory.create(url);
+        final URLSpan span = urlSpanFactory.apply(url);
         text.setSpan(span, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
     }
 
@@ -805,6 +763,13 @@
             i++;
         }
     }
+
+    /**
+     * Default factory function to create {@link URLSpan}s. While adding spans to a
+     * {@link Spannable}, {@link Linkify} will call this function to create a {@link URLSpan}.
+     */
+    private static final Function<String, URLSpan> DEFAULT_SPAN_FACTORY =
+            (String string) -> new URLSpan(string);
 }
 
 class LinkSpec {
diff --git a/core/java/android/util/TypedValue.java b/core/java/android/util/TypedValue.java
index ea4464d..99106be 100644
--- a/core/java/android/util/TypedValue.java
+++ b/core/java/android/util/TypedValue.java
@@ -216,6 +216,12 @@
      * */
     public int density;
 
+    /**
+     * If the Value came from a style resource, this holds the corresponding style resource id
+     * against which the attribute was resolved.
+     */
+    public int sourceStyleResourceId;
+
     /* ------------------------------------------------------------ */
 
     /** Return the data for this value as a float.  Only use for values
diff --git a/core/java/android/view/Choreographer.java b/core/java/android/view/Choreographer.java
index 96ef8ba..ccd0fc1 100644
--- a/core/java/android/view/Choreographer.java
+++ b/core/java/android/view/Choreographer.java
@@ -22,6 +22,7 @@
 import android.annotation.TestApi;
 import android.annotation.UnsupportedAppUsage;
 import android.graphics.FrameInfo;
+import android.graphics.Insets;
 import android.hardware.display.DisplayManagerGlobal;
 import android.os.Build;
 import android.os.Handler;
@@ -199,7 +200,7 @@
      * @hide
      */
     private static final String[] CALLBACK_TRACE_TITLES = {
-            "input", "animation", "traversal", "commit"
+            "input", "animation", "insets_animation", "traversal", "commit"
     };
 
     /**
@@ -209,18 +210,33 @@
     public static final int CALLBACK_INPUT = 0;
 
     /**
-     * Callback type: Animation callback.  Runs before traversals.
+     * Callback type: Animation callback.  Runs before {@link #CALLBACK_INSETS_ANIMATION}.
      * @hide
      */
     @TestApi
     public static final int CALLBACK_ANIMATION = 1;
 
     /**
+     * Callback type: Animation callback to handle inset updates. This is separate from
+     * {@link #CALLBACK_ANIMATION} as we need to "gather" all inset animation updates via
+     * {@link WindowInsetsAnimationController#changeInsets} for multiple ongoing animations but then
+     * update the whole view system with a single callback to {@link View#dispatchWindowInsetsAnimationProgress}
+     * that contains all the combined updated insets.
+     * <p>
+     * Both input and animation may change insets, so we need to run this after these callbacks, but
+     * before traversals.
+     * <p>
+     * Runs before traversals.
+     * @hide
+     */
+    public static final int CALLBACK_INSETS_ANIMATION = 2;
+
+    /**
      * Callback type: Traversal callback.  Handles layout and draw.  Runs
      * after all other asynchronous messages have been handled.
      * @hide
      */
-    public static final int CALLBACK_TRAVERSAL = 2;
+    public static final int CALLBACK_TRAVERSAL = 3;
 
     /**
      * Callback type: Commit callback.  Handles post-draw operations for the frame.
@@ -232,7 +248,7 @@
      * to the view hierarchy state) actually took effect.
      * @hide
      */
-    public static final int CALLBACK_COMMIT = 3;
+    public static final int CALLBACK_COMMIT = 4;
 
     private static final int CALLBACK_LAST = CALLBACK_COMMIT;
 
@@ -704,6 +720,7 @@
 
             mFrameInfo.markAnimationsStart();
             doCallbacks(Choreographer.CALLBACK_ANIMATION, frameTimeNanos);
+            doCallbacks(Choreographer.CALLBACK_INSETS_ANIMATION, frameTimeNanos);
 
             mFrameInfo.markPerformTraversalsStart();
             doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos);
diff --git a/core/java/android/view/InsetsAnimationControlImpl.java b/core/java/android/view/InsetsAnimationControlImpl.java
index 7b9f78e..ce71b07 100644
--- a/core/java/android/view/InsetsAnimationControlImpl.java
+++ b/core/java/android/view/InsetsAnimationControlImpl.java
@@ -45,7 +45,9 @@
  * @hide
  */
 @VisibleForTesting
-public class InsetsAnimationControlImpl implements WindowInsetsAnimationController {
+public class InsetsAnimationControlImpl implements WindowInsetsAnimationController  {
+
+    private final Rect mTmpFrame = new Rect();
 
     private final WindowInsetsAnimationControlListener mListener;
     private final SparseArray<InsetsSourceConsumer> mConsumers;
@@ -61,19 +63,23 @@
     private final InsetsState mInitialInsetsState;
     private final @InsetType int mTypes;
     private final Supplier<SyncRtSurfaceTransactionApplier> mTransactionApplierSupplier;
-
+    private final InsetsController mController;
+    private final WindowInsetsAnimationListener.InsetsAnimation mAnimation;
     private Insets mCurrentInsets;
+    private Insets mPendingInsets;
 
     @VisibleForTesting
     public InsetsAnimationControlImpl(SparseArray<InsetsSourceConsumer> consumers, Rect frame,
             InsetsState state, WindowInsetsAnimationControlListener listener,
             @InsetType int types,
-            Supplier<SyncRtSurfaceTransactionApplier> transactionApplierSupplier) {
+            Supplier<SyncRtSurfaceTransactionApplier> transactionApplierSupplier,
+            InsetsController controller) {
         mConsumers = consumers;
         mListener = listener;
         mTypes = types;
         mTransactionApplierSupplier = transactionApplierSupplier;
-        mInitialInsetsState = new InsetsState(state);
+        mController = controller;
+        mInitialInsetsState = new InsetsState(state, true /* copySources */);
         mCurrentInsets = getInsetsFromState(mInitialInsetsState, frame, null /* typeSideMap */);
         mHiddenInsets = calculateInsets(mInitialInsetsState, frame, consumers, false /* shown */,
                 null /* typeSideMap */);
@@ -83,6 +89,10 @@
 
         // TODO: Check for controllability first and wait for IME if needed.
         listener.onReady(this, types);
+
+        mAnimation = new WindowInsetsAnimationListener.InsetsAnimation(mTypes, mHiddenInsets,
+                mShownInsets);
+        mController.dispatchAnimationStarted(mAnimation);
     }
 
     @Override
@@ -108,29 +118,35 @@
 
     @Override
     public void changeInsets(Insets insets) {
-        insets = sanitize(insets);
-        final Insets offset = Insets.subtract(mShownInsets, insets);
+        mPendingInsets = sanitize(insets);
+        mController.scheduleApplyChangeInsets();
+    }
+
+    void applyChangeInsets(InsetsState state) {
+        final Insets offset = Insets.subtract(mShownInsets, mPendingInsets);
         ArrayList<SurfaceParams> params = new ArrayList<>();
         if (offset.left != 0) {
-            updateLeashesForSide(INSET_SIDE_LEFT, offset.left, params);
+            updateLeashesForSide(INSET_SIDE_LEFT, offset.left, params, state);
         }
         if (offset.top != 0) {
-            updateLeashesForSide(INSET_SIDE_TOP, offset.top, params);
+            updateLeashesForSide(INSET_SIDE_TOP, offset.top, params, state);
         }
         if (offset.right != 0) {
-            updateLeashesForSide(INSET_SIDE_RIGHT, offset.right, params);
+            updateLeashesForSide(INSET_SIDE_RIGHT, offset.right, params, state);
         }
         if (offset.bottom != 0) {
-            updateLeashesForSide(INSET_SIDE_BOTTOM, offset.bottom, params);
+            updateLeashesForSide(INSET_SIDE_BOTTOM, offset.bottom, params, state);
         }
         SyncRtSurfaceTransactionApplier applier = mTransactionApplierSupplier.get();
         applier.scheduleApply(params.toArray(new SurfaceParams[params.size()]));
-        mCurrentInsets = insets;
+        mCurrentInsets = mPendingInsets;
     }
 
     @Override
     public void finish(int shownTypes) {
         // TODO
+
+        mController.dispatchAnimationFinished(mAnimation);
     }
 
     private Insets calculateInsets(InsetsState state, Rect frame,
@@ -146,7 +162,7 @@
             @Nullable @InsetSide SparseIntArray typeSideMap) {
         return state.calculateInsets(frame, false /* isScreenRound */,
                 false /* alwaysConsumerNavBar */, null /* displayCutout */, typeSideMap)
-                .getSystemWindowInsets();
+                .getInsets(mTypes);
     }
 
     private Insets sanitize(Insets insets) {
@@ -154,7 +170,7 @@
     }
 
     private void updateLeashesForSide(@InsetSide int side, int inset,
-            ArrayList<SurfaceParams> surfaceParams) {
+            ArrayList<SurfaceParams> surfaceParams, InsetsState state) {
         ArraySet<InsetsSourceConsumer> items = mSideSourceMap.get(side);
         // TODO: Implement behavior when inset spans over multiple types
         for (int i = items.size() - 1; i >= 0; i--) {
@@ -162,24 +178,32 @@
             final InsetsSource source = mInitialInsetsState.getSource(consumer.getType());
             final SurfaceControl leash = consumer.getControl().getLeash();
             mTmpMatrix.setTranslate(source.getFrame().left, source.getFrame().top);
-            addTranslationToMatrix(side, inset, mTmpMatrix);
+
+            mTmpFrame.set(source.getFrame());
+            addTranslationToMatrix(side, inset, mTmpMatrix, mTmpFrame);
+
+            state.getSource(source.getType()).setFrame(mTmpFrame);
             surfaceParams.add(new SurfaceParams(leash, 1f, mTmpMatrix, null, 0, 0f));
         }
     }
 
-    private void addTranslationToMatrix(@InsetSide int side, int inset, Matrix m) {
+    private void addTranslationToMatrix(@InsetSide int side, int inset, Matrix m, Rect frame) {
         switch (side) {
             case INSET_SIDE_LEFT:
                 m.postTranslate(-inset, 0);
+                frame.offset(-inset, 0);
                 break;
             case INSET_SIDE_TOP:
                 m.postTranslate(0, -inset);
+                frame.offset(0, -inset);
                 break;
             case INSET_SIDE_RIGHT:
                 m.postTranslate(inset, 0);
+                frame.offset(inset, 0);
                 break;
             case INSET_SIDE_BOTTOM:
                 m.postTranslate(0, inset);
+                frame.offset(0, inset);
                 break;
         }
     }
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index 01af37e..c2ade76 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -18,6 +18,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.graphics.Insets;
 import android.graphics.Rect;
 import android.os.RemoteException;
 import android.util.ArraySet;
@@ -49,9 +50,29 @@
 
     private final SparseArray<InsetsSourceControl> mTmpControlArray = new SparseArray<>();
     private final ArrayList<InsetsAnimationControlImpl> mAnimationControls = new ArrayList<>();
+    private WindowInsets mLastInsets;
+
+    private boolean mAnimCallbackScheduled;
+
+    private final Runnable mAnimCallback;
 
     public InsetsController(ViewRootImpl viewRoot) {
         mViewRoot = viewRoot;
+        mAnimCallback = () -> {
+            mAnimCallbackScheduled = false;
+            if (mAnimationControls.isEmpty()) {
+                return;
+            }
+
+            InsetsState state = new InsetsState(mState, true /* copySources */);
+            for (int i = mAnimationControls.size() - 1; i >= 0; i--) {
+                mAnimationControls.get(i).applyChangeInsets(state);
+            }
+            WindowInsets insets = state.calculateInsets(mFrame, mLastInsets.isRound(),
+                    mLastInsets.shouldAlwaysConsumeNavBar(), mLastInsets.getDisplayCutout(),
+                    null /* typeSideMap */);
+            mViewRoot.mView.dispatchWindowInsetsAnimationProgress(insets);
+        };
     }
 
     void onFrameChanged(Rect frame) {
@@ -82,8 +103,9 @@
     @VisibleForTesting
     public WindowInsets calculateInsets(boolean isScreenRound,
             boolean alwaysConsumeNavBar, DisplayCutout cutout) {
-        return mState.calculateInsets(mFrame, isScreenRound, alwaysConsumeNavBar, cutout,
+        mLastInsets = mState.calculateInsets(mFrame, isScreenRound, alwaysConsumeNavBar, cutout,
                 null /* typeSideMap */);
+        return mLastInsets;
     }
 
     /**
@@ -148,7 +170,7 @@
         }
         final InsetsAnimationControlImpl controller = new InsetsAnimationControlImpl(consumers,
                 mFrame, mState, listener, types,
-                () -> new SyncRtSurfaceTransactionApplier(mViewRoot.mView));
+                () -> new SyncRtSurfaceTransactionApplier(mViewRoot.mView), this);
         mAnimationControls.add(controller);
     }
 
@@ -200,4 +222,20 @@
         pw.println(prefix); pw.println("InsetsController:");
         mState.dump(prefix + "  ", pw);
     }
+
+    void dispatchAnimationStarted(WindowInsetsAnimationListener.InsetsAnimation animation) {
+        mViewRoot.mView.dispatchWindowInsetsAnimationStarted(animation);
+    }
+
+    void dispatchAnimationFinished(WindowInsetsAnimationListener.InsetsAnimation animation) {
+        mViewRoot.mView.dispatchWindowInsetsAnimationFinished(animation);
+    }
+
+    void scheduleApplyChangeInsets() {
+        if (!mAnimCallbackScheduled) {
+            mViewRoot.mChoreographer.postCallback(Choreographer.CALLBACK_INSETS_ANIMATION,
+                    mAnimCallback, null /* token*/);
+            mAnimCallbackScheduled = true;
+        }
+    }
 }
diff --git a/core/java/android/view/InsetsState.java b/core/java/android/view/InsetsState.java
index 0931914..cf8c0707 100644
--- a/core/java/android/view/InsetsState.java
+++ b/core/java/android/view/InsetsState.java
@@ -107,6 +107,10 @@
         set(copy);
     }
 
+    public InsetsState(InsetsState copy, boolean copySources) {
+        set(copy, copySources);
+    }
+
     /**
      * Calculates {@link WindowInsets} based on the current source configuration.
      *
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index cd0e579..32974ac 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -76,8 +76,8 @@
 import android.os.Parcelable;
 import android.os.RemoteException;
 import android.os.SystemClock;
-import android.os.SystemProperties;
 import android.os.Trace;
+import android.sysprop.DisplayProperties;
 import android.text.InputType;
 import android.text.TextUtils;
 import android.util.AttributeSet;
@@ -96,6 +96,7 @@
 import android.view.AccessibilityIterators.TextSegmentIterator;
 import android.view.AccessibilityIterators.WordTextSegmentIterator;
 import android.view.ContextMenu.ContextMenuInfo;
+import android.view.WindowInsetsAnimationListener.InsetsAnimation;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityEventSource;
 import android.view.accessibility.AccessibilityManager;
@@ -811,14 +812,6 @@
     private static final String CONTENT_CAPTURE_LOG_TAG = "View.ContentCapture";
 
     /**
-     * When set to true, apps will draw debugging information about their layouts.
-     *
-     * @hide
-     */
-    @UnsupportedAppUsage
-    public static final String DEBUG_LAYOUT_PROPERTY = "debug.layout";
-
-    /**
      * When set to true, this view will save its attribute data.
      *
      * @hide
@@ -4547,6 +4540,8 @@
         OnCapturedPointerListener mOnCapturedPointerListener;
 
         private ArrayList<OnUnhandledKeyEventListener> mUnhandledKeyListeners;
+
+        private WindowInsetsAnimationListener mWindowInsetsAnimationListener;
     }
 
     @UnsupportedAppUsage
@@ -10488,6 +10483,37 @@
     }
 
     /**
+     * Sets a {@link WindowInsetsAnimationListener} to be notified about animations of windows that
+     * cause insets.
+     *
+     * @param listener The listener to set.
+     * @hide pending unhide
+     */
+    public void setWindowInsetsAnimationListener(WindowInsetsAnimationListener listener) {
+        getListenerInfo().mWindowInsetsAnimationListener = listener;
+    }
+
+    void dispatchWindowInsetsAnimationStarted(InsetsAnimation animation) {
+        if (mListenerInfo != null && mListenerInfo.mWindowInsetsAnimationListener != null) {
+            mListenerInfo.mWindowInsetsAnimationListener.onStarted(animation);
+        }
+    }
+
+    WindowInsets dispatchWindowInsetsAnimationProgress(WindowInsets insets) {
+        if (mListenerInfo != null && mListenerInfo.mWindowInsetsAnimationListener != null) {
+            return mListenerInfo.mWindowInsetsAnimationListener.onProgress(insets);
+        } else {
+            return insets;
+        }
+    }
+
+    void dispatchWindowInsetsAnimationFinished(InsetsAnimation animation) {
+        if (mListenerInfo != null && mListenerInfo.mOnApplyWindowInsetsListener != null) {
+            mListenerInfo.mWindowInsetsAnimationListener.onFinished(animation);
+        }
+    }
+
+    /**
      * Compute the view's coordinate within the surface.
      *
      * <p>Computes the coordinates of this view in its surface. The argument
@@ -27799,7 +27825,7 @@
         /**
          * Show where the margins, bounds and layout bounds are for each view.
          */
-        boolean mDebugLayout = SystemProperties.getBoolean(DEBUG_LAYOUT_PROPERTY, false);
+        boolean mDebugLayout = DisplayProperties.debug_layout().orElse(false);
 
         /**
          * Point used to compute visible regions.
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 9d11397..0986cfa 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -51,6 +51,7 @@
 import android.util.Pools.SynchronizedPool;
 import android.util.SparseArray;
 import android.util.SparseBooleanArray;
+import android.view.WindowInsetsAnimationListener.InsetsAnimation;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityManager;
 import android.view.accessibility.AccessibilityNodeInfo;
@@ -7139,6 +7140,34 @@
         return insets;
     }
 
+    @Override
+    void dispatchWindowInsetsAnimationStarted(InsetsAnimation animation) {
+        super.dispatchWindowInsetsAnimationStarted(animation);
+        final int count = getChildCount();
+        for (int i = 0; i < count; i++) {
+            getChildAt(i).dispatchWindowInsetsAnimationStarted(animation);
+        }
+    }
+
+    @Override
+    WindowInsets dispatchWindowInsetsAnimationProgress(WindowInsets insets) {
+        insets = super.dispatchWindowInsetsAnimationProgress(insets);
+        final int count = getChildCount();
+        for (int i = 0; i < count; i++) {
+            getChildAt(i).dispatchWindowInsetsAnimationProgress(insets);
+        }
+        return insets;
+    }
+
+    @Override
+    void dispatchWindowInsetsAnimationFinished(InsetsAnimation animation) {
+        super.dispatchWindowInsetsAnimationFinished(animation);
+        final int count = getChildCount();
+        for (int i = 0; i < count; i++) {
+            getChildAt(i).dispatchWindowInsetsAnimationFinished(animation);
+        }
+    }
+
     /**
      * Returns the animation listener to which layout animation events are
      * sent.
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 8e4dc67..27d4ea4 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -75,6 +75,7 @@
 import android.os.SystemClock;
 import android.os.SystemProperties;
 import android.os.Trace;
+import android.sysprop.DisplayProperties;
 import android.util.AndroidRuntimeException;
 import android.util.DisplayMetrics;
 import android.util.Log;
@@ -7071,7 +7072,7 @@
                 }
 
                 // Layout debugging
-                boolean layout = SystemProperties.getBoolean(View.DEBUG_LAYOUT_PROPERTY, false);
+                boolean layout = DisplayProperties.debug_layout().orElse(false);
                 if (layout != mAttachInfo.mDebugLayout) {
                     mAttachInfo.mDebugLayout = layout;
                     if (!mHandler.hasMessages(MSG_INVALIDATE_WORLD)) {
diff --git a/core/java/android/view/WindowInsetsAnimationController.java b/core/java/android/view/WindowInsetsAnimationController.java
index 9de517d..cf4415d 100644
--- a/core/java/android/view/WindowInsetsAnimationController.java
+++ b/core/java/android/view/WindowInsetsAnimationController.java
@@ -19,6 +19,7 @@
 import android.annotation.NonNull;
 import android.graphics.Insets;
 import android.view.WindowInsets.Type.InsetType;
+import android.view.WindowInsetsAnimationListener.InsetsAnimation;
 
 /**
  * Interface to control a window inset animation frame-by-frame.
@@ -28,8 +29,13 @@
 
     /**
      * Retrieves the {@link Insets} when the windows this animation is controlling are fully hidden.
+     * <p>
+     * If there are any animation listeners registered, this value is the same as
+     * {@link InsetsAnimation#getLowerBound()} that will be passed into the callbacks.
      *
      * @return Insets when the windows this animation is controlling are fully hidden.
+     *
+     * @see InsetsAnimation#getLowerBound()
      */
     @NonNull Insets getHiddenStateInsets();
 
@@ -38,8 +44,13 @@
      * <p>
      * In case the size of a window causing insets is changing in the middle of the animation, we
      * execute that height change after this animation has finished.
+     * <p>
+     * If there are any animation listeners registered, this value is the same as
+     * {@link InsetsAnimation#getUpperBound()} that will be passed into the callbacks.
      *
      * @return Insets when the windows this animation is controlling are fully shown.
+     *
+     * @see InsetsAnimation#getUpperBound()
      */
     @NonNull Insets getShownStateInsets();
 
@@ -59,8 +70,11 @@
      * <p>
      * Note that this will <b>not</b> inform the view system of a full inset change via
      * {@link View#dispatchApplyWindowInsets} in order to avoid a full layout pass during the
-     * animation. If you'd like to animate views during a window inset animation, use
-     * TODO add link to animation listeners.
+     * animation. If you'd like to animate views during a window inset animation, register a
+     * {@link WindowInsetsAnimationListener} by calling
+     * {@link View#setWindowInsetsAnimationListener(WindowInsetsAnimationListener)} that will be
+     * notified about any insets change via {@link WindowInsetsAnimationListener#onProgress} during
+     * the animation.
      * <p>
      * {@link View#dispatchApplyWindowInsets} will instead be called once the animation has
      * finished, i.e. once {@link #finish} has been called.
@@ -70,6 +84,9 @@
      *               the resulting insets of that configuration will match the passed in parameter.
      *               Note that these insets are being clamped to the range from
      *               {@link #getHiddenStateInsets} to {@link #getShownStateInsets}
+     *
+     * @see WindowInsetsAnimationListener
+     * @see View#setWindowInsetsAnimationListener(WindowInsetsAnimationListener)
      */
     void changeInsets(@NonNull Insets insets);
 
diff --git a/core/java/android/view/WindowInsetsAnimationListener.java b/core/java/android/view/WindowInsetsAnimationListener.java
new file mode 100644
index 0000000..682ab5b
--- /dev/null
+++ b/core/java/android/view/WindowInsetsAnimationListener.java
@@ -0,0 +1,122 @@
+/*
+ * 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;
+
+import android.graphics.Insets;
+
+/**
+ * Interface that allows the application to listen to animation events for windows that cause
+ * insets.
+ * @hide pending unhide
+ */
+public interface WindowInsetsAnimationListener {
+
+    /**
+     * Called when an inset animation gets started.
+     *
+     * @param animation The animation that is about to start.
+     */
+    void onStarted(InsetsAnimation animation);
+
+    /**
+     * Called when the insets change as part of running an animation. Note that even if multiple
+     * animations for different types are running, there will only be one progress callback per
+     * frame. The {@code insets} passed as an argument represents the overall state and will include
+     * all types, regardless of whether they are animating or not.
+     * <p>
+     * Note that insets dispatch is hierarchical: It will start at the root of the view hierarchy,
+     * and then traverse it and invoke the callback of the specific {@link View} being traversed.
+     * The callback may return a modified instance by calling {@link WindowInsets#inset(int, int, int, int)}
+     * to indicate that a part of the insets have been used to offset or clip its children, and the
+     * children shouldn't worry about that part anymore.
+     *
+     * @param insets The current insets.
+     * @return The insets to dispatch to the subtree of the hierarchy.
+     */
+    WindowInsets onProgress(WindowInsets insets);
+
+    /**
+     * Called when an inset animation has finished.
+     *
+     * @param animation The animation that has finished running.
+     */
+    void onFinished(InsetsAnimation animation);
+
+    /**
+     * Class representing an animation of a set of windows that cause insets.
+     */
+    class InsetsAnimation {
+
+        private final @WindowInsets.Type.InsetType int mTypeMask;
+        private final Insets mLowerBound;
+        private final Insets mUpperBound;
+
+        /**
+         * @hide
+         */
+        InsetsAnimation(int typeMask, Insets lowerBound, Insets upperBound) {
+            mTypeMask = typeMask;
+            mLowerBound = lowerBound;
+            mUpperBound = upperBound;
+        }
+
+        /**
+         * @return The bitmask of {@link WindowInsets.Type.InsetType}s that are animating.
+         */
+        public @WindowInsets.Type.InsetType int getTypeMask() {
+            return mTypeMask;
+        }
+
+        /**
+         * Queries the lower inset bound of the animation. If the animation is about showing or
+         * hiding a window that cause insets, the lower bound is {@link Insets#NONE} and the upper
+         * bound is the same as {@link WindowInsets#getInsets(int)} for the fully shown state. This
+         * is the same as {@link WindowInsetsAnimationController#getHiddenStateInsets} and
+         * {@link WindowInsetsAnimationController#getShownStateInsets} in case the listener gets
+         * invoked because of an animation that originates from
+         * {@link WindowInsetsAnimationController}.
+         * <p>
+         * However, if the size of a window that causes insets is changing, these are the
+         * lower/upper bounds of that size animation.
+         * <p>
+         * There are no overlapping animations for a specific type, but there may be two animations
+         * running at the same time for different inset types.
+         *
+         * @see #getUpperBound()
+         * @see WindowInsetsAnimationController#getHiddenStateInsets
+         * TODO: It's a bit weird that these are global per window but onProgress is hierarchical.
+         * TODO: If multiple types are animating, querying the bound per type isn't possible. Should
+         * we:
+         * 1. Offer bounds by type here?
+         * 2. Restrict one animation to one single type only?
+         * Returning WindowInsets here isn't feasible in case of overlapping animations: We can't
+         * fill in the insets for the types from the other animation into the WindowInsets object
+         * as it's changing as well.
+         */
+        public Insets getLowerBound() {
+            return mLowerBound;
+        }
+
+        /**
+         * @see #getLowerBound()
+         * @see WindowInsetsAnimationController#getShownStateInsets
+         */
+        public Insets getUpperBound() {
+            return mUpperBound;
+        }
+    }
+}
diff --git a/core/java/android/view/animation/Animation.java b/core/java/android/view/animation/Animation.java
index c630177..dfd9a2e 100644
--- a/core/java/android/view/animation/Animation.java
+++ b/core/java/android/view/animation/Animation.java
@@ -187,6 +187,7 @@
     /**
      * An animation listener to be notified when the animation starts, ends or repeats.
      */
+    @UnsupportedAppUsage
     private AnimationListener mListener;
 
     /**
diff --git a/core/java/android/view/autofill/AutofillId.java b/core/java/android/view/autofill/AutofillId.java
index cb1d89c..9c935af 100644
--- a/core/java/android/view/autofill/AutofillId.java
+++ b/core/java/android/view/autofill/AutofillId.java
@@ -15,6 +15,7 @@
  */
 package android.view.autofill;
 
+import android.annotation.NonNull;
 import android.annotation.TestApi;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -25,33 +26,47 @@
  */
 public final class AutofillId implements Parcelable {
 
+    /** @hide */
+    public static final int NO_SESSION = 0;
+
+    private static final int FLAG_IS_VIRTUAL = 0x1;
+    private static final int FLAG_HAS_SESSION = 0x2;
+
     private final int mViewId;
-    private final boolean mVirtual;
+    private final int mFlags;
     private final int mVirtualId;
+    private final int mSessionId;
 
     /** @hide */
     @TestApi
     public AutofillId(int id) {
-        mVirtual = false;
-        mViewId = id;
-        mVirtualId = View.NO_ID;
+        this(/* flags= */ 0, id, View.NO_ID, NO_SESSION);
     }
 
     /** @hide */
     @TestApi
-    public AutofillId(AutofillId parent, int virtualChildId) {
-        mVirtual = true;
-        mViewId = parent.mViewId;
-        mVirtualId = virtualChildId;
+    public AutofillId(@NonNull AutofillId parent, int virtualChildId) {
+        this(FLAG_IS_VIRTUAL, parent.mViewId, virtualChildId, NO_SESSION);
     }
 
     /** @hide */
     public AutofillId(int parentId, int virtualChildId) {
-        mVirtual = true;
+        this(FLAG_IS_VIRTUAL, parentId, virtualChildId, NO_SESSION);
+    }
+
+    /** @hide */
+    public AutofillId(@NonNull AutofillId parent, int virtualChildId, int sessionId) {
+        this(FLAG_IS_VIRTUAL | FLAG_HAS_SESSION, parent.mViewId, virtualChildId, sessionId);
+    }
+
+    private AutofillId(int flags, int parentId, int virtualChildId, int sessionId) {
+        mFlags = flags;
         mViewId = parentId;
         mVirtualId = virtualChildId;
+        mSessionId = sessionId;
     }
 
+
     /** @hide */
     public int getViewId() {
         return mViewId;
@@ -64,7 +79,16 @@
 
     /** @hide */
     public boolean isVirtual() {
-        return mVirtual;
+        return (mFlags & FLAG_IS_VIRTUAL) != 0;
+    }
+
+    private boolean hasSession() {
+        return (mFlags & FLAG_HAS_SESSION) != 0;
+    }
+
+    /** @hide */
+    public int getSessionId() {
+        return mSessionId;
     }
 
     /////////////////////////////////
@@ -77,6 +101,7 @@
         int result = 1;
         result = prime * result + mViewId;
         result = prime * result + mVirtualId;
+        result = prime * result + mSessionId;
         return result;
     }
 
@@ -88,15 +113,19 @@
         final AutofillId other = (AutofillId) obj;
         if (mViewId != other.mViewId) return false;
         if (mVirtualId != other.mVirtualId) return false;
+        if (mSessionId != other.mSessionId) return false;
         return true;
     }
 
     @Override
     public String toString() {
         final StringBuilder builder = new StringBuilder().append(mViewId);
-        if (mVirtual) {
+        if (isVirtual()) {
             builder.append(':').append(mVirtualId);
         }
+        if (hasSession()) {
+            builder.append('@').append(mSessionId);
+        }
         return builder.toString();
     }
 
@@ -108,21 +137,24 @@
     @Override
     public void writeToParcel(Parcel parcel, int flags) {
         parcel.writeInt(mViewId);
-        parcel.writeInt(mVirtual ? 1 : 0);
-        parcel.writeInt(mVirtualId);
-    }
-
-    private AutofillId(Parcel parcel) {
-        mViewId = parcel.readInt();
-        mVirtual = parcel.readInt() == 1;
-        mVirtualId = parcel.readInt();
+        parcel.writeInt(mFlags);
+        if (isVirtual()) {
+            parcel.writeInt(mVirtualId);
+        }
+        if (hasSession()) {
+            parcel.writeInt(mSessionId);
+        }
     }
 
     public static final Parcelable.Creator<AutofillId> CREATOR =
             new Parcelable.Creator<AutofillId>() {
         @Override
         public AutofillId createFromParcel(Parcel source) {
-            return new AutofillId(source);
+            final int viewId = source.readInt();
+            final int flags = source.readInt();
+            final int virtualId = (flags & FLAG_IS_VIRTUAL) != 0 ? source.readInt() : View.NO_ID;
+            final int sessionId = (flags & FLAG_HAS_SESSION) != 0 ? source.readInt() : NO_SESSION;
+            return new AutofillId(flags, viewId, virtualId, sessionId);
         }
 
         @Override
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index 90ccc25..888a4c5 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -26,6 +26,7 @@
 import android.annotation.Nullable;
 import android.annotation.RequiresFeature;
 import android.annotation.SystemService;
+import android.annotation.TestApi;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -329,6 +330,12 @@
     private static final int SYNC_CALLS_TIMEOUT_MS = 5000;
 
     /**
+     * @hide
+     */
+    @TestApi
+    public static final int MAX_TEMP_AUGMENTED_SERVICE_DURATION_MS = 1_000 * 60 * 2; // 2 minutes
+
+    /**
      * Makes an authentication id from a request id and a dataset id.
      *
      * @param requestId The request id.
@@ -2990,5 +2997,23 @@
                 afm.post(() -> afm.autofill(sessionId, ids, values));
             }
         }
+
+        @Override
+        public void requestShowFillUi(int sessionId, AutofillId id, int width, int height,
+                Rect anchorBounds, IAutofillWindowPresenter presenter) {
+            final AutofillManager afm = mAfm.get();
+            if (afm != null) {
+                afm.post(() -> afm.requestShowFillUi(sessionId, id, width, height, anchorBounds,
+                        presenter));
+            }
+        }
+
+        @Override
+        public void requestHideFillUi(int sessionId, AutofillId id) {
+            final AutofillManager afm = mAfm.get();
+            if (afm != null) {
+                afm.post(() -> afm.requestHideFillUi(id, false));
+            }
+        }
     }
 }
diff --git a/core/java/android/view/autofill/IAugmentedAutofillManagerClient.aidl b/core/java/android/view/autofill/IAugmentedAutofillManagerClient.aidl
index 67cd0bf..140507c 100644
--- a/core/java/android/view/autofill/IAugmentedAutofillManagerClient.aidl
+++ b/core/java/android/view/autofill/IAugmentedAutofillManagerClient.aidl
@@ -21,6 +21,7 @@
 import android.graphics.Rect;
 import android.view.autofill.AutofillId;
 import android.view.autofill.AutofillValue;
+import android.view.autofill.IAutofillWindowPresenter;
 
 /**
  * Object running in the application process and responsible to provide the functionalities
@@ -29,6 +30,24 @@
  * @hide
  */
 interface IAugmentedAutofillManagerClient {
-   Rect getViewCoordinates(in AutofillId id);
-   void autofill(int sessionId, in List<AutofillId> ids, in List<AutofillValue> values);
+    /**
+      * Gets the coordinates of the input field view.
+      */
+    Rect getViewCoordinates(in AutofillId id);
+
+    /**
+     * Autofills the activity with the contents of the values.
+     */
+    void autofill(int sessionId, in List<AutofillId> ids, in List<AutofillValue> values);
+
+    /**
+      * Requests showing the fill UI.
+      */
+    void requestShowFillUi(int sessionId, in AutofillId id, int width, int height,
+            in Rect anchorBounds, in IAutofillWindowPresenter presenter);
+
+    /**
+      * Requests hiding the fill UI.
+      */
+    void requestHideFillUi(int sessionId, in AutofillId id);
 }
diff --git a/core/java/android/view/contentcapture/ContentCaptureManager.java b/core/java/android/view/contentcapture/ContentCaptureManager.java
index ff45efd..81b2e01 100644
--- a/core/java/android/view/contentcapture/ContentCaptureManager.java
+++ b/core/java/android/view/contentcapture/ContentCaptureManager.java
@@ -29,12 +29,12 @@
 import android.os.RemoteException;
 import android.util.Log;
 
+import com.android.internal.annotations.GuardedBy;
 import com.android.internal.os.IResultReceiver;
 import com.android.internal.util.Preconditions;
 import com.android.internal.util.SyncResultReceiver;
 
 import java.io.PrintWriter;
-import java.util.concurrent.atomic.AtomicBoolean;
 
 /*
  * NOTE: all methods in this class should return right away, or do the real work in a handler
@@ -62,8 +62,10 @@
     static final boolean VERBOSE = false;
     static final boolean DEBUG = true; // STOPSHIP if not set to false
 
-    @NonNull
-    private final AtomicBoolean mDisabled = new AtomicBoolean();
+    private final Object mLock = new Object();
+
+    @GuardedBy("mLock")
+    private boolean mDisabled;
 
     @NonNull
     private final Context mContext;
@@ -71,11 +73,16 @@
     @Nullable
     private final IContentCaptureManager mService;
 
+    // Flags used for starting session.
+    @GuardedBy("mLock")
+    private int mFlags;
+
     // TODO(b/119220549): use UI Thread directly (as calls are one-way) or a shared thread / handler
     // held at the Application level
     @NonNull
     private final Handler mHandler;
 
+    @GuardedBy("mLock")
     private MainContentCaptureSession mMainSession;
 
     /** @hide */
@@ -114,20 +121,25 @@
     @NonNull
     @UiThread
     public MainContentCaptureSession getMainContentCaptureSession() {
-        if (mMainSession == null) {
-            mMainSession = new MainContentCaptureSession(mContext, mHandler, mService,
-                    mDisabled);
-            if (VERBOSE) {
-                Log.v(TAG, "getDefaultContentCaptureSession(): created " + mMainSession);
+        synchronized (mLock) {
+            if (mMainSession == null) {
+                mMainSession = new MainContentCaptureSession(mContext, mHandler, mService,
+                        mDisabled);
+                if (VERBOSE) {
+                    Log.v(TAG, "getDefaultContentCaptureSession(): created " + mMainSession);
+                }
             }
+            return mMainSession;
         }
-        return mMainSession;
     }
 
     /** @hide */
     public void onActivityStarted(@NonNull IBinder applicationToken,
             @NonNull ComponentName activityComponent, int flags) {
-        getMainContentCaptureSession().start(applicationToken, activityComponent, flags);
+        synchronized (mLock) {
+            mFlags |= flags;
+            getMainContentCaptureSession().start(applicationToken, activityComponent, mFlags);
+        }
     }
 
     /** @hide */
@@ -173,7 +185,9 @@
      * Checks whether content capture is enabled for this activity.
      */
     public boolean isContentCaptureEnabled() {
-        return mService != null && !mDisabled.get();
+        synchronized (mLock) {
+            return mService != null && !mDisabled;
+        }
     }
 
     /**
@@ -183,7 +197,9 @@
      * it on {@link android.app.Activity#onCreate(android.os.Bundle, android.os.PersistableBundle)}.
      */
     public void setContentCaptureEnabled(boolean enabled) {
-        //TODO(b/111276913): implement (need to finish / disable all sessions)
+        synchronized (mLock) {
+            mFlags |= enabled ? 0 : ContentCaptureContext.FLAG_DISABLED_BY_APP;
+        }
     }
 
     /**
@@ -198,20 +214,22 @@
 
     /** @hide */
     public void dump(String prefix, PrintWriter pw) {
-        pw.print(prefix); pw.println("ContentCaptureManager");
-
-        pw.print(prefix); pw.print("Disabled: "); pw.println(mDisabled.get());
-        pw.print(prefix); pw.print("Context: "); pw.println(mContext);
-        pw.print(prefix); pw.print("User: "); pw.println(mContext.getUserId());
-        if (mService != null) {
-            pw.print(prefix); pw.print("Service: "); pw.println(mService);
-        }
-        if (mMainSession != null) {
-            final String prefix2 = prefix + "  ";
-            pw.print(prefix); pw.println("Main session:");
-            mMainSession.dump(prefix2, pw);
-        } else {
-            pw.print(prefix); pw.println("No sessions");
+        synchronized (mLock) {
+            pw.print(prefix); pw.println("ContentCaptureManager");
+            pw.print(prefix); pw.print("Disabled: "); pw.println(mDisabled);
+            pw.print(prefix); pw.print("Context: "); pw.println(mContext);
+            pw.print(prefix); pw.print("User: "); pw.println(mContext.getUserId());
+            if (mService != null) {
+                pw.print(prefix); pw.print("Service: "); pw.println(mService);
+            }
+            pw.print(prefix); pw.print("Flags: "); pw.println(mFlags);
+            if (mMainSession != null) {
+                final String prefix2 = prefix + "  ";
+                pw.print(prefix); pw.println("Main session:");
+                mMainSession.dump(prefix2, pw);
+            } else {
+                pw.print(prefix); pw.println("No sessions");
+            }
         }
     }
 
diff --git a/core/java/android/view/contentcapture/ContentCaptureSession.java b/core/java/android/view/contentcapture/ContentCaptureSession.java
index 6890beaf..7bfc5b4 100644
--- a/core/java/android/view/contentcapture/ContentCaptureSession.java
+++ b/core/java/android/view/contentcapture/ContentCaptureSession.java
@@ -21,6 +21,7 @@
 import android.annotation.CallSuper;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.util.DebugUtils;
 import android.util.Log;
 import android.view.View;
 import android.view.ViewStructure;
@@ -28,6 +29,7 @@
 import android.view.contentcapture.ViewNode.ViewStructureImpl;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.Preconditions;
 
 import dalvik.system.CloseGuard;
@@ -55,42 +57,71 @@
      *
      * @hide
      */
-    public static final int STATE_UNKNOWN = 0;
+    // NOTE: not prefixed by STATE_ so it's not printed on getStateAsString()
+    public static final int UNKNWON_STATE = 0x0;
 
     /**
      * Service's startSession() was called, but server didn't confirm it was created yet.
      *
      * @hide
      */
-    public static final int STATE_WAITING_FOR_SERVER = 1;
+    public static final int STATE_WAITING_FOR_SERVER = 0x1;
 
     /**
      * Session is active.
      *
      * @hide
      */
-    public static final int STATE_ACTIVE = 2;
+    public static final int STATE_ACTIVE = 0x2;
 
     /**
      * Session is disabled because there is no service for this user.
      *
      * @hide
      */
-    public static final int STATE_DISABLED_NO_SERVICE = 3;
+    public static final int STATE_DISABLED = 0x4;
 
     /**
      * Session is disabled because its id already existed on server.
      *
      * @hide
      */
-    public static final int STATE_DISABLED_DUPLICATED_ID = 4;
+    public static final int STATE_DUPLICATED_ID = 0x8;
+
+    /**
+     * Session is disabled because service is not set for user.
+     *
+     * @hide
+     */
+    public static final int STATE_NO_SERVICE = 0x10;
 
     /**
      * Session is disabled by FLAG_SECURE
      *
      * @hide
      */
-    public static final int STATE_DISABLED_BY_FLAG_SECURE = 5;
+    public static final int STATE_FLAG_SECURE = 0x20;
+
+    /**
+     * Session is disabled manually by the specific app.
+     *
+     * @hide
+     */
+    public static final int STATE_BY_APP = 0x40;
+
+    /**
+     * Session is disabled because session start was never replied.
+     *
+     * @hide
+     */
+    public static final int STATE_NO_RESPONSE = 0x80;
+
+    /**
+     * Session is disabled because an internal error.
+     *
+     * @hide
+     */
+    public static final int STATE_INTERNAL_ERROR = 0x100;
 
     private static final int INITIAL_CHILDREN_CAPACITY = 5;
 
@@ -107,9 +138,9 @@
 
     /** @hide */
     @Nullable
-    protected final String mId = UUID.randomUUID().toString();
+    protected final String mId;
 
-    private int mState = STATE_UNKNOWN;
+    private int mState = UNKNWON_STATE;
 
     // Lazily created on demand.
     private ContentCaptureSessionId mContentCaptureSessionId;
@@ -123,6 +154,13 @@
 
     /** @hide */
     protected ContentCaptureSession() {
+        this(UUID.randomUUID().toString());
+    }
+
+    /** @hide */
+    @VisibleForTesting
+    public ContentCaptureSession(@NonNull String id) {
+        mId = Preconditions.checkNotNull(id);
         mCloseGuard.open("destroy");
     }
 
@@ -140,6 +178,13 @@
         return mContentCaptureSessionId;
     }
 
+    /** @hide */
+    @VisibleForTesting
+    public int getIdAsInt() {
+        // TODO(b/121197119): use sessionId instead of hashcode once it's changed to int
+        return mId.hashCode();
+    }
+
     /**
      * Creates a new {@link ContentCaptureSession}.
      *
@@ -315,9 +360,7 @@
     public @NonNull AutofillId newAutofillId(@NonNull AutofillId parentId, int virtualChildId) {
         Preconditions.checkNotNull(parentId);
         Preconditions.checkArgument(!parentId.isVirtual(), "virtual ids cannot have children");
-        // TODO(b/121197119): we need to add the session id to the AutofillId to make them unique
-        // per session
-        return new AutofillId(parentId, virtualChildId);
+        return new AutofillId(parentId, virtualChildId, getIdAsInt());
     }
 
     /**
@@ -333,8 +376,7 @@
     @NonNull
     public final ViewStructure newVirtualViewStructure(@NonNull AutofillId parentId,
             int virtualId) {
-        // TODO(b/121197119): use the constructor that takes a session id / assert on unit test.
-        return new ViewNode.ViewStructureImpl(parentId, virtualId);
+        return new ViewNode.ViewStructureImpl(parentId, virtualId, getIdAsInt());
     }
 
     boolean isContentCaptureEnabled() {
@@ -370,21 +412,7 @@
      */
     @NonNull
     protected static String getStateAsString(int state) {
-        switch (state) {
-            case STATE_UNKNOWN:
-                return "UNKNOWN";
-            case STATE_WAITING_FOR_SERVER:
-                return "WAITING_FOR_SERVER";
-            case STATE_ACTIVE:
-                return "ACTIVE";
-            case STATE_DISABLED_NO_SERVICE:
-                return "DISABLED_NO_SERVICE";
-            case STATE_DISABLED_DUPLICATED_ID:
-                return "DISABLED_DUPLICATED_ID";
-            case STATE_DISABLED_BY_FLAG_SECURE:
-                return "DISABLED_FLAG_SECURE";
-            default:
-                return "INVALID:" + state;
-        }
+        return state + " (" + (state == UNKNWON_STATE ? "UNKNOWN"
+                : DebugUtils.flagsToString(ContentCaptureSession.class, "STATE_", state)) + ")";
     }
 }
diff --git a/core/java/android/view/contentcapture/MainContentCaptureSession.java b/core/java/android/view/contentcapture/MainContentCaptureSession.java
index a29aaf0..a3ff8c0 100644
--- a/core/java/android/view/contentcapture/MainContentCaptureSession.java
+++ b/core/java/android/view/contentcapture/MainContentCaptureSession.java
@@ -88,6 +88,7 @@
      */
     public static final String EXTRA_BINDER = "binder";
 
+    // TODO(b/111276913): make sure disabled state is in sync with manager's disabled
     @NonNull
     private final AtomicBoolean mDisabled;
 
@@ -113,7 +114,7 @@
     @Nullable
     private DeathRecipient mDirectServiceVulture;
 
-    private int mState = STATE_UNKNOWN;
+    private int mState = UNKNWON_STATE;
 
     @Nullable
     private IBinder mApplicationToken;
@@ -133,11 +134,11 @@
     /** @hide */
     protected MainContentCaptureSession(@NonNull Context context, @NonNull Handler handler,
             @Nullable IContentCaptureManager systemServerInterface,
-            @NonNull AtomicBoolean disabled) {
+            @NonNull boolean disabled) {
         mContext = context;
         mHandler = handler;
         mSystemServerInterface = systemServerInterface;
-        mDisabled = disabled;
+        mDisabled = new AtomicBoolean(disabled);
     }
 
     @Override
@@ -184,10 +185,13 @@
 
     private void handleStartSession(@NonNull IBinder token, @NonNull ComponentName componentName,
             int flags) {
-        if (mState != STATE_UNKNOWN) {
-            // TODO(b/111276913): revisit this scenario
-            Log.w(TAG, "ignoring handleStartSession(" + token + ") while on state "
-                    + getStateAsString(mState));
+        if (handleHasStarted()) {
+            // TODO(b/122959591): make sure this is expected (and when), or use Log.w
+            if (DEBUG) {
+                Log.d(TAG, "ignoring handleStartSession(" + token + "/"
+                        + ComponentName.flattenToShortString(componentName) + " while on state "
+                        + getStateAsString(mState));
+            }
             return;
         }
         mState = STATE_WAITING_FOR_SERVER;
@@ -196,7 +200,7 @@
 
         if (VERBOSE) {
             Log.v(TAG, "handleStartSession(): token=" + token + ", act="
-                    + getActivityDebugName() + ", id=" + mId);
+                    + getDebugState() + ", id=" + mId);
         }
 
         try {
@@ -211,7 +215,7 @@
                                 binder = resultData.getBinder(EXTRA_BINDER);
                                 if (binder == null) {
                                     Log.wtf(TAG, "No " + EXTRA_BINDER + " extra result");
-                                    handleResetState();
+                                    handleResetSession(STATE_DISABLED | STATE_INTERNAL_ERROR);
                                     return;
                                 }
                             }
@@ -233,7 +237,6 @@
      * @param binder handle to {@code IContentCaptureDirectManager}
      */
     private void handleSessionStarted(int resultCode, @Nullable IBinder binder) {
-        mState = resultCode;
         if (binder != null) {
             mDirectServiceInterface = IContentCaptureDirectManager.Stub.asInterface(binder);
             mDirectServiceVulture = () -> {
@@ -247,26 +250,33 @@
             }
         }
 
-        // TODO(b/111276913): change the resultCode to use flags so there's just one flag for
-        // disabled stuff
-        if (resultCode == STATE_DISABLED_NO_SERVICE || resultCode == STATE_DISABLED_DUPLICATED_ID
-                || resultCode == STATE_DISABLED_BY_FLAG_SECURE) {
-            mDisabled.set(true);
-            handleResetSession(/* resetState= */ false);
+        if ((resultCode & STATE_DISABLED) != 0) {
+            handleResetSession(resultCode);
         } else {
+            mState = resultCode;
             mDisabled.set(false);
         }
         if (VERBOSE) {
-            Log.v(TAG, "handleSessionStarted() result: code=" + resultCode + ", id=" + mId
+            Log.v(TAG, "handleSessionStarted() result: id=" + mId + " resultCode=" + resultCode
                     + ", state=" + getStateAsString(mState) + ", disabled=" + mDisabled.get()
                     + ", binder=" + binder + ", events=" + (mEvents == null ? 0 : mEvents.size()));
         }
     }
 
     private void handleSendEvent(@NonNull ContentCaptureEvent event, boolean forceFlush) {
+        if (!handleHasStarted()
+                && event.getType() != ContentCaptureEvent.TYPE_SESSION_STARTED) {
+            // TODO(b/120494182): comment when this could happen (dialogs?)
+            Log.v(TAG, "handleSendEvent(" + getDebugState() + ", "
+                    + ContentCaptureEvent.getTypeAsString(event.getType())
+                    + "): session not started yet");
+            return;
+        }
         if (mEvents == null) {
             if (VERBOSE) {
-                Log.v(TAG, "Creating buffer for " + MAX_BUFFER_SIZE + " events");
+                Log.v(TAG, "handleSendEvent(" + getDebugState() + ", "
+                        + ContentCaptureEvent.getTypeAsString(event.getType())
+                        + "): cCreating buffer for " + MAX_BUFFER_SIZE + " events");
             }
             mEvents = new ArrayList<>(MAX_BUFFER_SIZE);
         }
@@ -301,15 +311,14 @@
         if (mState != STATE_ACTIVE && numberEvents >= MAX_BUFFER_SIZE) {
             // Callback from startSession hasn't been called yet - typically happens on system
             // apps that are started before the system service
-            // TODO(b/111276913): try to ignore session while system is not ready / boot
+            // TODO(b/122959591): try to ignore session while system is not ready / boot
             // not complete instead. Similarly, the manager service should return right away
             // when the user does not have a service set
             if (DEBUG) {
-                Log.d(TAG, "Closing session for " + getActivityDebugName()
-                        + " after " + numberEvents + " delayed events and state "
-                        + getStateAsString(mState));
+                Log.d(TAG, "Closing session for " + getDebugState()
+                        + " after " + numberEvents + " delayed events");
             }
-            handleResetState();
+            handleResetSession(STATE_DISABLED | STATE_NO_RESPONSE);
             // TODO(b/111276913): blacklist activity / use special flag to indicate that
             // when it's launched again
             return;
@@ -318,14 +327,23 @@
         handleForceFlush();
     }
 
+    private boolean handleHasStarted() {
+        return mState != UNKNWON_STATE;
+    }
+
     private void handleScheduleFlush(boolean checkExisting) {
+        if (!handleHasStarted()) {
+            Log.v(TAG, "handleScheduleFlush(" + getDebugState() + "): session not started yet");
+            return;
+        }
         if (checkExisting && mHandler.hasMessages(MSG_FLUSH)) {
             // "Renew" the flush message by removing the previous one
             mHandler.removeMessages(MSG_FLUSH);
         }
         mNextFlush = SystemClock.elapsedRealtime() + FLUSHING_FREQUENCY_MS;
         if (VERBOSE) {
-            Log.v(TAG, "Scheduled to flush in " + FLUSHING_FREQUENCY_MS + "ms: " + mNextFlush);
+            Log.v(TAG, "handleScheduleFlush(" + getDebugState() + "): scheduled to flush in "
+                    + FLUSHING_FREQUENCY_MS + "ms: " + TimeUtils.formatUptime(mNextFlush));
         }
         mHandler.sendMessageDelayed(
                 obtainMessage(MainContentCaptureSession::handleFlushIfNeeded, this)
@@ -345,7 +363,8 @@
 
         if (mDirectServiceInterface == null) {
             if (VERBOSE) {
-                Log.v(TAG, "handleForceFlush(): hold your horses, client not ready: " + mEvents);
+                Log.v(TAG, "handleForceFlush(" + getDebugState()
+                        + "): hold your horses, client not ready: " + mEvents);
             }
             if (!mHandler.hasMessages(MSG_FLUSH)) {
                 handleScheduleFlush(/* checkExisting= */ false);
@@ -356,14 +375,14 @@
         final int numberEvents = mEvents.size();
         try {
             if (DEBUG) {
-                Log.d(TAG, "Flushing " + numberEvents + " event(s) for " + getActivityDebugName());
+                Log.d(TAG, "Flushing " + numberEvents + " event(s) for " + getDebugState());
             }
             mHandler.removeMessages(MSG_FLUSH);
 
             final ParceledListSlice<ContentCaptureEvent> events = handleClearEvents();
             mDirectServiceInterface.sendEvents(events);
         } catch (RemoteException e) {
-            Log.w(TAG, "Error sending " + numberEvents + " for " + getActivityDebugName()
+            Log.w(TAG, "Error sending " + numberEvents + " for " + getDebugState()
                     + ": " + e);
         }
     }
@@ -386,7 +405,7 @@
         if (DEBUG) {
             Log.d(TAG, "Destroying session (ctx=" + mContext + ", id=" + mId + ") with "
                     + (mEvents == null ? 0 : mEvents.size()) + " event(s) for "
-                    + getActivityDebugName());
+                    + getDebugState());
         }
 
         try {
@@ -395,21 +414,19 @@
             mSystemServerInterface.finishSession(mContext.getUserId(), mId);
         } catch (RemoteException e) {
             Log.e(TAG, "Error destroying system-service session " + mId + " for "
-                    + getActivityDebugName() + ": " + e);
+                    + getDebugState() + ": " + e);
         }
     }
 
-    private void handleResetState() {
-        handleResetSession(/* resetState= */ true);
-    }
-
     // TODO(b/122454205): once we support multiple sessions, we might need to move some of these
     // clearings out.
-    private void handleResetSession(boolean resetState) {
-        if (resetState) {
-            mState = STATE_UNKNOWN;
+    private void handleResetSession(int newState) {
+        if (VERBOSE) {
+            Log.v(TAG, "handleResetSession(" + getActivityName() + "): from "
+                    + getStateAsString(mState) + " to " + getStateAsString(newState));
         }
-
+        mState = newState;
+        mDisabled.set((newState & STATE_DISABLED) != 0);
         // TODO(b/122454205): must reset children (which currently is owned by superclass)
         mApplicationToken = null;
         mComponentName = null;
@@ -496,8 +513,7 @@
         }
         pw.print(prefix); pw.print("mDisabled: "); pw.println(mDisabled.get());
         pw.print(prefix); pw.print("isEnabled(): "); pw.println(isContentCaptureEnabled());
-        pw.print(prefix); pw.print("state: "); pw.print(mState); pw.print(" (");
-        pw.print(getStateAsString(mState)); pw.println(")");
+        pw.print(prefix); pw.print("state: "); pw.println(getStateAsString(mState));
         if (mApplicationToken != null) {
             pw.print(prefix); pw.print("app token: "); pw.println(mApplicationToken);
         }
@@ -527,8 +543,13 @@
     /**
      * Gets a string that can be used to identify the activity on logging statements.
      */
-    private String getActivityDebugName() {
-        return mComponentName == null ? mContext.getPackageName()
-                : mComponentName.flattenToShortString();
+    private String getActivityName() {
+        return mComponentName == null
+                ? "pkg:" + mContext.getPackageName()
+                : "act:" + mComponentName.flattenToShortString();
+    }
+
+    private String getDebugState() {
+        return getActivityName() + " (state=" + getStateAsString(mState) + ")";
     }
 }
diff --git a/core/java/android/view/contentcapture/ViewNode.java b/core/java/android/view/contentcapture/ViewNode.java
index b7a486a..ddfecb0 100644
--- a/core/java/android/view/contentcapture/ViewNode.java
+++ b/core/java/android/view/contentcapture/ViewNode.java
@@ -672,9 +672,9 @@
         }
 
         @VisibleForTesting // Must be public to be accessed from FrameworkCoreTests' apk.
-        public ViewStructureImpl(@NonNull AutofillId parentId, int virtualId) {
+        public ViewStructureImpl(@NonNull AutofillId parentId, int virtualId, int sessionId) {
             mNode.mParentAutofillId = Preconditions.checkNotNull(parentId);
-            mNode.mAutofillId = new AutofillId(parentId, virtualId);
+            mNode.mAutofillId = new AutofillId(parentId, virtualId, sessionId);
         }
 
         @VisibleForTesting // Must be public to be accessed from FrameworkCoreTests' apk.
diff --git a/core/java/android/view/inspector/InspectableProperty.java b/core/java/android/view/inspector/InspectableProperty.java
index 5b957156..e2a763e 100644
--- a/core/java/android/view/inspector/InspectableProperty.java
+++ b/core/java/android/view/inspector/InspectableProperty.java
@@ -105,7 +105,6 @@
     /**
      * One entry in an enumeration packed into a primitive {int}.
      *
-     * @see IntEnumMapping
      * @hide
      */
     @Target({TYPE})
diff --git a/core/java/android/view/inspector/IntEnumMapping.java b/core/java/android/view/inspector/IntEnumMapping.java
deleted file mode 100644
index 69f6dce..0000000
--- a/core/java/android/view/inspector/IntEnumMapping.java
+++ /dev/null
@@ -1,118 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.view.inspector;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-
-import java.util.ArrayList;
-
-/**
- * Maps the values of an {int} property to string names for properties that encode enumerations.
- *
- * An {@link InspectionCompanion} may provide an instance of this class to a {@link PropertyMapper}
- * for enumerations packed into primitive {int} properties.
- *
- * This class is immutable, and must be constructed by a {@link Builder}.
- *
- * @see PropertyMapper#mapIntEnum(String, int, IntEnumMapping)
- */
-public final class IntEnumMapping {
-    private final Value[] mValues;
-
-    /**
-     * Map from a property value to a string name.
-     *
-     * @param value The value of a property
-     * @return The name of the enumeration value, null if the value is not mapped
-     */
-    @Nullable
-    public String nameOf(int value) {
-        for (Value valueTuple : mValues) {
-            if (valueTuple.mValue == value) {
-                return valueTuple.mName;
-            }
-        }
-
-        return null;
-    }
-
-    /**
-     * Create a new instance from a builder.
-     *
-     * This constructor is private, use {@link Builder#build()} instead.
-     *
-     * @param builder A builder to create from
-     */
-    private IntEnumMapping(Builder builder) {
-        mValues = builder.mValues.toArray(new Value[builder.mValues.size()]);
-    }
-
-    /**
-     * A builder for {@link IntEnumMapping}
-     */
-    public static final class Builder {
-        private final ArrayList<Value> mValues;
-
-        public Builder() {
-            mValues = new ArrayList<>();
-        }
-
-        /**
-         * Add a new entry to this mapping.
-         *
-         * @param name Name of the enumeration value
-         * @param value Int value of the enumeration value
-         * @return This builder
-         */
-        @NonNull
-        public Builder addValue(@NonNull String name, int value) {
-            mValues.add(new Value(name, value));
-            return this;
-        }
-
-        /**
-         * Clear the builder, allowing for recycling.
-         */
-        public void clear() {
-            mValues.clear();
-        }
-
-        /**
-         * Build a new {@link IntEnumMapping} from this builder
-         *
-         * @return A new mapping
-         */
-        @NonNull
-        public IntEnumMapping build() {
-            return new IntEnumMapping(this);
-        }
-    }
-
-    /**
-     * Inner class that holds the name and value of an enumeration value.
-     */
-    private static final class Value {
-        @NonNull private final String mName;
-        private final int mValue;
-
-        private Value(@NonNull String name, int value) {
-            mName = name;
-            mValue = value;
-        }
-    }
-}
diff --git a/core/java/android/view/inspector/IntFlagMapping.java b/core/java/android/view/inspector/IntFlagMapping.java
index dcb87e1..8f7dfd5 100644
--- a/core/java/android/view/inspector/IntFlagMapping.java
+++ b/core/java/android/view/inspector/IntFlagMapping.java
@@ -19,14 +19,20 @@
 import android.annotation.NonNull;
 
 import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
 
 /**
- * Maps the values of an {int} property to arrays of string for properties that encode flags.
+ * Maps the values of an {@code int} property to arrays of string for properties that encode flags.
  *
  * An {@link InspectionCompanion} may provide an instance of this class to a {@link PropertyMapper}
- * for flag values packed into primitive {int} properties.
+ * for flag values packed into primitive {@code int} properties.
  *
- * Each flag has a
+ * Each flag has a mask and a target value, for non-exclusive flags, the target can also be used as
+ * the mask. A given integer value is compared against each flag to find what flags are active for
+ * it by bitwise anding it with the mask and comparing the result against the target, that is,
+ * {@code (value & mask) == target}.
  *
  * This class is immutable, and must be constructed by a {@link Builder}.
  *
@@ -42,8 +48,8 @@
      * @return The names of the enabled flags
      */
     @NonNull
-    public String[] namesOf(int value) {
-        ArrayList<String> enabledFlagNames = new ArrayList<>(mFlags.length);
+    public Set<String> get(int value) {
+        final Set<String> enabledFlagNames = new HashSet<>(mFlags.length);
 
         for (Flag flag : mFlags) {
             if (flag.isEnabledFor(value)) {
@@ -51,7 +57,7 @@
             }
         }
 
-        return enabledFlagNames.toArray(new String[enabledFlagNames.size()]);
+        return Collections.unmodifiableSet(enabledFlagNames);
     }
 
     /**
@@ -81,7 +87,7 @@
          * The target value will be used as a mask, to handle the common case where flag values
          * are not mutually exclusive. The flag will be considered enabled for a property value if
          * the result of bitwise anding the target and the value equals the target, that is:
-         * {(value & target) == target}.
+         * {@code (value & target) == target}.
          *
          * @param name The name of the flag
          * @param target The value to compare against
@@ -97,7 +103,7 @@
          * Add a new flag with a mask.
          *
          * The flag will be considered enabled for a property value if the result of bitwise anding
-         * the value and the mask equals the target, that is: {(value & mask) == target}.
+         * the value and the mask equals the target, that is: {@code (value & mask) == target}.
          *
          * @param name The name of the flag
          * @param target The value to compare against
@@ -111,13 +117,6 @@
         }
 
         /**
-         * Clear the builder, allowing for recycling.
-         */
-        public void clear() {
-            mFlags.clear();
-        }
-
-        /**
          * Build a new {@link IntFlagMapping} from this builder.
          *
          * @return A new mapping
@@ -143,7 +142,7 @@
         }
 
         /**
-         * Compare the supplied property value against the mask and taget.
+         * Compare the supplied property value against the mask and target.
          *
          * @param value The value to check
          * @return True if this flag is enabled
diff --git a/core/java/android/view/inspector/PropertyMapper.java b/core/java/android/view/inspector/PropertyMapper.java
index 5fb291b..e20582b 100644
--- a/core/java/android/view/inspector/PropertyMapper.java
+++ b/core/java/android/view/inspector/PropertyMapper.java
@@ -18,6 +18,7 @@
 
 import android.annotation.AttrRes;
 import android.annotation.NonNull;
+import android.util.SparseArray;
 
 /**
  * An interface for mapping the string names of inspectable properties to integer identifiers.
@@ -154,7 +155,7 @@
     int mapIntEnum(
             @NonNull String name,
             @AttrRes int attributeId,
-            @NonNull IntEnumMapping mapping);
+            @NonNull SparseArray<String> mapping);
 
     /**
      * Map a string name to an integer ID for a flag set packed into an int property.
diff --git a/core/java/android/view/inspector/PropertyReader.java b/core/java/android/view/inspector/PropertyReader.java
index fd83e8d..a8b7ecc 100644
--- a/core/java/android/view/inspector/PropertyReader.java
+++ b/core/java/android/view/inspector/PropertyReader.java
@@ -25,7 +25,13 @@
 /**
  * An interface for reading the properties of an inspectable object.
  *
- * Used as the parameter for {@link InspectionCompanion#readProperties(Object, PropertyReader)}.
+ * {@code PropertyReader} is defined as an interface that will be called by
+ * {@link InspectionCompanion#readProperties(Object, PropertyReader)}. This approach allows a
+ * client inspector to read the values of primitive properties without the overhead of
+ * instantiating a class to hold the property values for each inspection pass. If an inspectable
+ * remains unchanged between reading passes, it should be possible for a {@code PropertyReader} to
+ * avoid new allocations for subsequent reading passes.
+ *
  * It has separate methods for all primitive types to avoid autoboxing overhead if a concrete
  * implementation is able to work with primitives. Implementations should be prepared to accept
  * {null} as the value of {@link PropertyReader#readObject(int, Object)}.
@@ -38,7 +44,7 @@
      *
      * @param id Identifier of the property from a {@link PropertyMapper}
      * @param value Value of the property
-     * @throws PropertyTypeMismatchException If the property ID is not mapped as a {boolean}
+     * @throws PropertyTypeMismatchException If the property ID is not mapped as a {@code boolean}
      */
     void readBoolean(int id, boolean value);
 
@@ -47,7 +53,7 @@
      *
      * @param id Identifier of the property from a {@link PropertyMapper}
      * @param value Value of the property
-     * @throws PropertyTypeMismatchException If the property ID is not mapped as a {byte}
+     * @throws PropertyTypeMismatchException If the property ID is not mapped as a {@code byte}
      */
     void readByte(int id, byte value);
 
@@ -56,7 +62,7 @@
      *
      * @param id Identifier of the property from a {@link PropertyMapper}
      * @param value Value of the property
-     * @throws PropertyTypeMismatchException If the property ID is not mapped as a {char}
+     * @throws PropertyTypeMismatchException If the property ID is not mapped as a {@code char}
      */
     void readChar(int id, char value);
 
@@ -65,7 +71,7 @@
      *
      * @param id Identifier of the property from a {@link PropertyMapper}
      * @param value Value of the property
-     * @throws PropertyTypeMismatchException If the property ID is not mapped as a {double}
+     * @throws PropertyTypeMismatchException If the property ID is not mapped as a {@code double}
      */
     void readDouble(int id, double value);
 
@@ -74,7 +80,7 @@
      *
      * @param id Identifier of the property from a {@link PropertyMapper}
      * @param value Value of the property
-     * @throws PropertyTypeMismatchException If the property ID is not mapped as a {float}
+     * @throws PropertyTypeMismatchException If the property ID is not mapped as a {@code float}
      */
     void readFloat(int id, float value);
 
@@ -83,7 +89,7 @@
      *
      * @param id Identifier of the property from a {@link PropertyMapper}
      * @param value Value of the property
-     * @throws PropertyTypeMismatchException If the property ID is not mapped as an {int}
+     * @throws PropertyTypeMismatchException If the property ID is not mapped as an {@code int}
      */
     void readInt(int id, int value);
 
@@ -92,7 +98,7 @@
      *
      * @param id Identifier of the property from a {@link PropertyMapper}
      * @param value Value of the property
-     * @throws PropertyTypeMismatchException If the property ID is not mapped as a {long}
+     * @throws PropertyTypeMismatchException If the property ID is not mapped as a {@code long}
      */
     void readLong(int id, long value);
 
@@ -101,7 +107,7 @@
      *
      * @param id Identifier of the property from a {@link PropertyMapper}
      * @param value Value of the property
-     * @throws PropertyTypeMismatchException If the property ID is not mapped as a {short}
+     * @throws PropertyTypeMismatchException If the property ID is not mapped as a {@code short}
      */
     void readShort(int id, short value);
 
diff --git a/core/java/android/view/textclassifier/ConversationAction.java b/core/java/android/view/textclassifier/ConversationAction.java
new file mode 100644
index 0000000..1a6e5d8
--- /dev/null
+++ b/core/java/android/view/textclassifier/ConversationAction.java
@@ -0,0 +1,266 @@
+/*
+ * 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.textclassifier;
+
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+import android.annotation.FloatRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.StringDef;
+import android.app.RemoteAction;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.Preconditions;
+
+import java.lang.annotation.Retention;
+
+/** Represents the action suggested by a {@link TextClassifier} on a given conversation. */
+public final class ConversationAction implements Parcelable {
+
+    /** @hide */
+    @Retention(SOURCE)
+    @StringDef(
+            value = {
+                    TYPE_VIEW_CALENDAR,
+                    TYPE_VIEW_MAP,
+                    TYPE_TRACK_FLIGHT,
+                    TYPE_OPEN_URL,
+                    TYPE_SEND_SMS,
+                    TYPE_CALL_PHONE,
+                    TYPE_SEND_EMAIL,
+                    TYPE_TEXT_REPLY,
+                    TYPE_CREATE_REMINDER,
+                    TYPE_SHARE_LOCATION
+            },
+            prefix = "TYPE_")
+    public @interface ActionType {}
+
+    /**
+     * Indicates an action to view a calendar at a specified time.
+     */
+    public static final String TYPE_VIEW_CALENDAR = "view_calendar";
+    /**
+     * Indicates an action to view the map at a specified location.
+     */
+    public static final String TYPE_VIEW_MAP = "view_map";
+    /**
+     * Indicates an action to track a flight.
+     */
+    public static final String TYPE_TRACK_FLIGHT = "track_flight";
+    /**
+     * Indicates an action to open an URL.
+     */
+    public static final String TYPE_OPEN_URL = "open_url";
+    /**
+     * Indicates an action to send a SMS.
+     */
+    public static final String TYPE_SEND_SMS = "send_sms";
+    /**
+     * Indicates an action to call a phone number.
+     */
+    public static final String TYPE_CALL_PHONE = "call_phone";
+    /**
+     * Indicates an action to send an email.
+     */
+    public static final String TYPE_SEND_EMAIL = "send_email";
+    /**
+     * Indicates an action to reply with a text message.
+     */
+    public static final String TYPE_TEXT_REPLY = "text_reply";
+    /**
+     * Indicates an action to create a reminder.
+     */
+    public static final String TYPE_CREATE_REMINDER = "create_reminder";
+    /**
+     * Indicates an action to reply with a location.
+     */
+    public static final String TYPE_SHARE_LOCATION = "share_location";
+
+    public static final Creator<ConversationAction> CREATOR =
+            new Creator<ConversationAction>() {
+                @Override
+                public ConversationAction createFromParcel(Parcel in) {
+                    return new ConversationAction(in);
+                }
+
+                @Override
+                public ConversationAction[] newArray(int size) {
+                    return new ConversationAction[size];
+                }
+            };
+
+    @NonNull
+    @ActionType
+    private final String mType;
+    @NonNull
+    private final CharSequence mTextReply;
+    @Nullable
+    private final RemoteAction mAction;
+
+    @FloatRange(from = 0, to = 1)
+    private final float mScore;
+
+    @NonNull
+    private final Bundle mExtras;
+
+    private ConversationAction(
+            @NonNull String type,
+            @Nullable RemoteAction action,
+            @Nullable CharSequence textReply,
+            float score,
+            @NonNull Bundle extras) {
+        mType = Preconditions.checkNotNull(type);
+        mAction = action;
+        mTextReply = textReply;
+        mScore = score;
+        mExtras = Preconditions.checkNotNull(extras);
+    }
+
+    private ConversationAction(Parcel in) {
+        mType = in.readString();
+        mAction = in.readParcelable(null);
+        mTextReply = in.readCharSequence();
+        mScore = in.readFloat();
+        mExtras = in.readBundle();
+    }
+
+    @Override
+    public void writeToParcel(Parcel parcel, int flags) {
+        parcel.writeString(mType);
+        parcel.writeParcelable(mAction, flags);
+        parcel.writeCharSequence(mTextReply);
+        parcel.writeFloat(mScore);
+        parcel.writeBundle(mExtras);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    /** Returns the type of this action, for example, {@link #TYPE_VIEW_CALENDAR}. */
+    @NonNull
+    @ActionType
+    public String getType() {
+        return mType;
+    }
+
+    /**
+     * Returns a RemoteAction object, which contains the icon, label and a PendingIntent, for
+     * the specified action type.
+     */
+    @Nullable
+    public RemoteAction getAction() {
+        return mAction;
+    }
+
+    /**
+     * Returns the confidence score for the specified action. The value ranges from 0 (low
+     * confidence) to 1 (high confidence).
+     */
+    @FloatRange(from = 0, to = 1)
+    public float getConfidenceScore() {
+        return mScore;
+    }
+
+    /**
+     * Returns the text reply that could be sent as a reply to the given conversation.
+     * <p>
+     * This is only available when the type of the action is {@link #TYPE_TEXT_REPLY}.
+     */
+    @Nullable
+    public CharSequence getTextReply() {
+        return mTextReply;
+    }
+
+    /**
+     * Returns the extended data related to this conversation action.
+     *
+     * <p><b>NOTE: </b>Each call to this method returns a new bundle copy so clients should
+     * prefer to hold a reference to the returned bundle rather than frequently calling this
+     * method.
+     */
+    @NonNull
+    public Bundle getExtras() {
+        return mExtras.deepCopy();
+    }
+
+    /** Builder class to construct {@link ConversationAction}. */
+    public static final class Builder {
+        @Nullable
+        @ActionType
+        private String mType;
+        @Nullable
+        private RemoteAction mAction;
+        @Nullable
+        private CharSequence mTextReply;
+        private float mScore;
+        @Nullable
+        private Bundle mExtras;
+
+        public Builder(@NonNull @ActionType String actionType) {
+            mType = Preconditions.checkNotNull(actionType);
+        }
+
+        /**
+         * Sets an action that may be performed on the given conversation.
+         */
+        @NonNull
+        public Builder setAction(@Nullable RemoteAction action) {
+            mAction = action;
+            return this;
+        }
+
+        /**
+         * Sets a text reply that may be performed on the given conversation.
+         */
+        @NonNull
+        public Builder setTextReply(@Nullable CharSequence textReply) {
+            mTextReply = textReply;
+            return this;
+        }
+
+        /** Sets the confident score. */
+        @NonNull
+        public Builder setConfidenceScore(@FloatRange(from = 0, to = 1) float score) {
+            mScore = score;
+            return this;
+        }
+
+        /**
+         * Sets the extended data for the conversation action object.
+         */
+        @NonNull
+        public Builder setExtras(@Nullable Bundle extras) {
+            mExtras = extras;
+            return this;
+        }
+
+        /** Builds the {@link ConversationAction} object. */
+        @NonNull
+        public ConversationAction build() {
+            return new ConversationAction(
+                    mType,
+                    mAction,
+                    mTextReply,
+                    mScore,
+                    mExtras == null ? Bundle.EMPTY : mExtras.deepCopy());
+        }
+    }
+}
diff --git a/core/java/android/view/textclassifier/ConversationActions.java b/core/java/android/view/textclassifier/ConversationActions.java
index 3f690f7..f7c1a26 100644
--- a/core/java/android/view/textclassifier/ConversationActions.java
+++ b/core/java/android/view/textclassifier/ConversationActions.java
@@ -17,18 +17,15 @@
 
 import static java.lang.annotation.RetentionPolicy.SOURCE;
 
-import android.annotation.FloatRange;
 import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.StringDef;
 import android.app.Person;
-import android.app.RemoteAction;
 import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.text.SpannedString;
-import android.util.ArraySet;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.Preconditions;
@@ -37,10 +34,8 @@
 import java.time.ZonedDateTime;
 import java.time.format.DateTimeFormatter;
 import java.util.ArrayList;
-import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
-import java.util.Set;
 
 /**
  * Represents a list of actions suggested by a {@link TextClassifier} on a given conversation.
@@ -62,83 +57,6 @@
                 }
             };
 
-    /** @hide */
-    @Retention(SOURCE)
-    @StringDef(
-            value = {
-                    TYPE_VIEW_CALENDAR,
-                    TYPE_VIEW_MAP,
-                    TYPE_TRACK_FLIGHT,
-                    TYPE_OPEN_URL,
-                    TYPE_SEND_SMS,
-                    TYPE_CALL_PHONE,
-                    TYPE_SEND_EMAIL,
-                    TYPE_TEXT_REPLY,
-                    TYPE_CREATE_REMINDER,
-                    TYPE_SHARE_LOCATION
-            },
-            prefix = "TYPE_")
-    public @interface ActionType {}
-
-    /**
-     * Indicates an action to view a calendar at a specified time.
-     */
-    public static final String TYPE_VIEW_CALENDAR = "view_calendar";
-    /**
-     * Indicates an action to view the map at a specified location.
-     */
-    public static final String TYPE_VIEW_MAP = "view_map";
-    /**
-     * Indicates an action to track a flight.
-     */
-    public static final String TYPE_TRACK_FLIGHT = "track_flight";
-    /**
-     * Indicates an action to open an URL.
-     */
-    public static final String TYPE_OPEN_URL = "open_url";
-    /**
-     * Indicates an action to send a SMS.
-     */
-    public static final String TYPE_SEND_SMS = "send_sms";
-    /**
-     * Indicates an action to call a phone number.
-     */
-    public static final String TYPE_CALL_PHONE = "call_phone";
-    /**
-     * Indicates an action to send an email.
-     */
-    public static final String TYPE_SEND_EMAIL = "send_email";
-    /**
-     * Indicates an action to reply with a text message.
-     */
-    public static final String TYPE_TEXT_REPLY = "text_reply";
-    /**
-     * Indicates an action to create a reminder.
-     */
-    public static final String TYPE_CREATE_REMINDER = "create_reminder";
-    /**
-     * Indicates an action to reply with a location.
-     */
-    public static final String TYPE_SHARE_LOCATION = "share_location";
-
-    /** @hide */
-    @Retention(SOURCE)
-    @StringDef(
-            value = {
-                    HINT_FOR_NOTIFICATION,
-                    HINT_FOR_IN_APP,
-            },
-            prefix = "HINT_")
-    public @interface Hint {}
-    /**
-     * To indicate the generated actions will be used within the app.
-     */
-    public static final String HINT_FOR_IN_APP = "in_app";
-    /**
-     * To indicate the generated actions will be used for notification.
-     */
-    public static final String HINT_FOR_NOTIFICATION = "notification";
-
     private final List<ConversationAction> mConversationActions;
     private final String mId;
 
@@ -184,182 +102,6 @@
         return mId;
     }
 
-    /** Represents the action suggested by a {@link TextClassifier} on a given conversation. */
-    public static final class ConversationAction implements Parcelable {
-
-        public static final Creator<ConversationAction> CREATOR =
-                new Creator<ConversationAction>() {
-                    @Override
-                    public ConversationAction createFromParcel(Parcel in) {
-                        return new ConversationAction(in);
-                    }
-
-                    @Override
-                    public ConversationAction[] newArray(int size) {
-                        return new ConversationAction[size];
-                    }
-                };
-
-        @NonNull
-        @ActionType
-        private final String mType;
-        @NonNull
-        private final CharSequence mTextReply;
-        @Nullable
-        private final RemoteAction mAction;
-
-        @FloatRange(from = 0, to = 1)
-        private final float mScore;
-
-        @NonNull
-        private final Bundle mExtras;
-
-        private ConversationAction(
-                @NonNull String type,
-                @Nullable RemoteAction action,
-                @Nullable CharSequence textReply,
-                float score,
-                @NonNull Bundle extras) {
-            mType = Preconditions.checkNotNull(type);
-            mAction = action;
-            mTextReply = textReply;
-            mScore = score;
-            mExtras = Preconditions.checkNotNull(extras);
-        }
-
-        private ConversationAction(Parcel in) {
-            mType = in.readString();
-            mAction = in.readParcelable(null);
-            mTextReply = in.readCharSequence();
-            mScore = in.readFloat();
-            mExtras = in.readBundle();
-        }
-
-        @Override
-        public void writeToParcel(Parcel parcel, int flags) {
-            parcel.writeString(mType);
-            parcel.writeParcelable(mAction, flags);
-            parcel.writeCharSequence(mTextReply);
-            parcel.writeFloat(mScore);
-            parcel.writeBundle(mExtras);
-        }
-
-        @Override
-        public int describeContents() {
-            return 0;
-        }
-
-        @NonNull
-        @ActionType
-        /** Returns the type of this action, for example, {@link #TYPE_VIEW_CALENDAR}. */
-        public String getType() {
-            return mType;
-        }
-
-        @Nullable
-        /**
-         * Returns a RemoteAction object, which contains the icon, label and a PendingIntent, for
-         * the specified action type.
-         */
-        public RemoteAction getAction() {
-            return mAction;
-        }
-
-        /**
-         * Returns the confidence score for the specified action. The value ranges from 0 (low
-         * confidence) to 1 (high confidence).
-         */
-        @FloatRange(from = 0, to = 1)
-        public float getConfidenceScore() {
-            return mScore;
-        }
-
-        /**
-         * Returns the text reply that could be sent as a reply to the given conversation.
-         * <p>
-         * This is only available when the type of the action is {@link #TYPE_TEXT_REPLY}.
-         */
-        @Nullable
-        public CharSequence getTextReply() {
-            return mTextReply;
-        }
-
-        /**
-         * Returns the extended data related to this conversation action.
-         *
-         * <p><b>NOTE: </b>Each call to this method returns a new bundle copy so clients should
-         * prefer to hold a reference to the returned bundle rather than frequently calling this
-         * method.
-         */
-        @NonNull
-        public Bundle getExtras() {
-            return mExtras.deepCopy();
-        }
-
-        /** Builder class to construct {@link ConversationAction}. */
-        public static final class Builder {
-            @Nullable
-            @ActionType
-            private String mType;
-            @Nullable
-            private RemoteAction mAction;
-            @Nullable
-            private CharSequence mTextReply;
-            private float mScore;
-            @Nullable
-            private Bundle mExtras;
-
-            public Builder(@NonNull @ActionType String actionType) {
-                mType = Preconditions.checkNotNull(actionType);
-            }
-
-            /**
-             * Sets an action that may be performed on the given conversation.
-             */
-            @NonNull
-            public Builder setAction(@Nullable RemoteAction action) {
-                mAction = action;
-                return this;
-            }
-
-            /**
-             * Sets a text reply that may be performed on the given conversation.
-             */
-            @NonNull
-            public Builder setTextReply(@Nullable CharSequence textReply) {
-                mTextReply = textReply;
-                return this;
-            }
-
-            /** Sets the confident score. */
-            @NonNull
-            public Builder setConfidenceScore(@FloatRange(from = 0, to = 1) float score) {
-                mScore = score;
-                return this;
-            }
-
-            /**
-             * Sets the extended data for the conversation action object.
-             */
-            @NonNull
-            public Builder setExtras(@Nullable Bundle extras) {
-                mExtras = extras;
-                return this;
-            }
-
-            /** Builds the {@link ConversationAction} object. */
-            @NonNull
-            public ConversationAction build() {
-                return new ConversationAction(
-                        mType,
-                        mAction,
-                        mTextReply,
-                        mScore,
-                        mExtras == null ? Bundle.EMPTY : mExtras.deepCopy());
-            }
-        }
-    }
-
     /** Represents a message in the conversation. */
     public static final class Message implements Parcelable {
         /**
@@ -538,156 +280,36 @@
         }
     }
 
-    /** Configuration object for specifying what action types to identify. */
-    public static final class TypeConfig implements Parcelable {
-        @NonNull
-        @ActionType
-        private final Set<String> mExcludedTypes;
-        @NonNull
-        @ActionType
-        private final Set<String> mIncludedTypes;
-        private final boolean mIncludeTypesFromTextClassifier;
-
-        private TypeConfig(
-                @NonNull Set<String> includedTypes,
-                @NonNull Set<String> excludedTypes,
-                boolean includeTypesFromTextClassifier) {
-            mIncludedTypes = Preconditions.checkNotNull(includedTypes);
-            mExcludedTypes = Preconditions.checkNotNull(excludedTypes);
-            mIncludeTypesFromTextClassifier = includeTypesFromTextClassifier;
-        }
-
-        private TypeConfig(Parcel in) {
-            mIncludedTypes = new ArraySet<>(in.createStringArrayList());
-            mExcludedTypes = new ArraySet<>(in.createStringArrayList());
-            mIncludeTypesFromTextClassifier = in.readByte() != 0;
-        }
-
-        @Override
-        public void writeToParcel(Parcel parcel, int flags) {
-            parcel.writeStringList(new ArrayList<>(mIncludedTypes));
-            parcel.writeStringList(new ArrayList<>(mExcludedTypes));
-            parcel.writeByte((byte) (mIncludeTypesFromTextClassifier ? 1 : 0));
-        }
-
-        @Override
-        public int describeContents() {
-            return 0;
-        }
-
-        public static final Creator<TypeConfig> CREATOR =
-                new Creator<TypeConfig>() {
-                    @Override
-                    public TypeConfig createFromParcel(Parcel in) {
-                        return new TypeConfig(in);
-                    }
-
-                    @Override
-                    public TypeConfig[] newArray(int size) {
-                        return new TypeConfig[size];
-                    }
-                };
-
-        /**
-         * Returns a final list of types that the text classifier should look for.
-         *
-         * <p>NOTE: This method is intended for use by a text classifier.
-         *
-         * @param defaultTypes types the text classifier thinks should be included before factoring
-         *    in the included/excluded types given by the client.
-         */
-        @NonNull
-        public Collection<String> resolveTypes(@Nullable Collection<String> defaultTypes) {
-            Set<String> types = new ArraySet<>();
-            if (mIncludeTypesFromTextClassifier && defaultTypes != null) {
-                types.addAll(defaultTypes);
-            }
-            types.addAll(mIncludedTypes);
-            types.removeAll(mExcludedTypes);
-            return Collections.unmodifiableCollection(types);
-        }
-
-        /**
-         * Return whether the client allows the text classifier to include its own list of default
-         * types. If this function returns {@code true}, the text classifier can consider specifying
-         * a default list of entity types in {@link #resolveTypes(Collection)}.
-         *
-         * <p>NOTE: This method is intended for use by a text classifier.
-         *
-         * @see #resolveTypes(Collection)
-         */
-        public boolean shouldIncludeTypesFromTextClassifier() {
-            return mIncludeTypesFromTextClassifier;
-        }
-
-        /** Builder class to construct the {@link TypeConfig} object. */
-        public static final class Builder {
-            @Nullable
-            private Collection<String> mExcludedTypes;
-            @Nullable
-            private Collection<String> mIncludedTypes;
-            private boolean mIncludeTypesFromTextClassifier = true;
-
-            /**
-             * Sets a collection of types that are explicitly included, for example, {@link
-             * #TYPE_VIEW_CALENDAR}.
-             */
-            @NonNull
-            public Builder setIncludedTypes(
-                    @Nullable @ActionType Collection<String> includedTypes) {
-                mIncludedTypes = includedTypes;
-                return this;
-            }
-
-            /**
-             * Sets a collection of types that are explicitly excluded, for example, {@link
-             * #TYPE_VIEW_CALENDAR}.
-             */
-            @NonNull
-            public Builder setExcludedTypes(
-                    @Nullable @ActionType Collection<String> excludedTypes) {
-                mExcludedTypes = excludedTypes;
-                return this;
-            }
-
-            /**
-             * Specifies whether or not to include the types suggested by the text classifier. By
-             * default, it is included.
-             */
-            @NonNull
-            public Builder includeTypesFromTextClassifier(boolean includeTypesFromTextClassifier) {
-                mIncludeTypesFromTextClassifier = includeTypesFromTextClassifier;
-                return this;
-            }
-
-            /**
-             * Combines all of the options that have been set and returns a new {@link TypeConfig}
-             * object.
-             */
-            @NonNull
-            public TypeConfig build() {
-                return new TypeConfig(
-                        mIncludedTypes == null
-                                ? Collections.emptySet()
-                                : new ArraySet<>(mIncludedTypes),
-                        mExcludedTypes == null
-                                ? Collections.emptySet()
-                                : new ArraySet<>(mExcludedTypes),
-                        mIncludeTypesFromTextClassifier);
-            }
-        }
-    }
-
     /**
      * A request object for generating conversation action suggestions.
      *
      * @see TextClassifier#suggestConversationActions(Request)
      */
     public static final class Request implements Parcelable {
+
+        /** @hide */
+        @Retention(SOURCE)
+        @StringDef(
+                value = {
+                        HINT_FOR_NOTIFICATION,
+                        HINT_FOR_IN_APP,
+                },
+                prefix = "HINT_")
+        public @interface Hint {}
+
+        /**
+         * To indicate the generated actions will be used within the app.
+         */
+        public static final String HINT_FOR_IN_APP = "in_app";
+        /**
+         * To indicate the generated actions will be used for notification.
+         */
+        public static final String HINT_FOR_NOTIFICATION = "notification";
+
         @NonNull
         private final List<Message> mConversation;
         @NonNull
-        private final TypeConfig mTypeConfig;
+        private final TextClassifier.EntityConfig mTypeConfig;
         private final int mMaxSuggestions;
         @NonNull
         @Hint
@@ -699,7 +321,7 @@
 
         private Request(
                 @NonNull List<Message> conversation,
-                @NonNull TypeConfig typeConfig,
+                @NonNull TextClassifier.EntityConfig typeConfig,
                 int maxSuggestions,
                 String conversationId,
                 @Nullable @Hint List<String> hints) {
@@ -713,7 +335,7 @@
         private static Request readFromParcel(Parcel in) {
             List<Message> conversation = new ArrayList<>();
             in.readParcelableList(conversation, null);
-            TypeConfig typeConfig = in.readParcelable(null);
+            TextClassifier.EntityConfig typeConfig = in.readParcelable(null);
             int maxSuggestions = in.readInt();
             String conversationId = in.readString();
             List<String> hints = new ArrayList<>();
@@ -760,7 +382,7 @@
 
         /** Returns the type config. */
         @NonNull
-        public TypeConfig getTypeConfig() {
+        public TextClassifier.EntityConfig getTypeConfig() {
             return mTypeConfig;
         }
 
@@ -820,7 +442,7 @@
             @NonNull
             private List<Message> mConversation;
             @Nullable
-            private TypeConfig mTypeConfig;
+            private TextClassifier.EntityConfig mTypeConfig;
             private int mMaxSuggestions;
             @Nullable
             private String mConversationId;
@@ -849,7 +471,7 @@
 
             /** Sets the type config. */
             @NonNull
-            public Builder setTypeConfig(@Nullable TypeConfig typeConfig) {
+            public Builder setTypeConfig(@Nullable TextClassifier.EntityConfig typeConfig) {
                 mTypeConfig = typeConfig;
                 return this;
             }
@@ -879,7 +501,9 @@
             public Request build() {
                 return new Request(
                         Collections.unmodifiableList(mConversation),
-                        mTypeConfig == null ? new TypeConfig.Builder().build() : mTypeConfig,
+                        mTypeConfig == null
+                                ? new TextClassifier.EntityConfig.Builder().build()
+                                : mTypeConfig,
                         mMaxSuggestions,
                         mConversationId,
                         mHints == null
diff --git a/core/java/android/view/textclassifier/TextClassificationConstants.java b/core/java/android/view/textclassifier/TextClassificationConstants.java
index 50801a2..ce680ec 100644
--- a/core/java/android/view/textclassifier/TextClassificationConstants.java
+++ b/core/java/android/view/textclassifier/TextClassificationConstants.java
@@ -117,15 +117,15 @@
             .add(TextClassifier.TYPE_FLIGHT_NUMBER).toString();
     private static final String CONVERSATION_ACTIONS_TYPES_DEFAULT_VALUES =
             new StringJoiner(ENTITY_LIST_DELIMITER)
-                    .add(ConversationActions.TYPE_TEXT_REPLY)
-                    .add(ConversationActions.TYPE_CREATE_REMINDER)
-                    .add(ConversationActions.TYPE_CALL_PHONE)
-                    .add(ConversationActions.TYPE_OPEN_URL)
-                    .add(ConversationActions.TYPE_SEND_EMAIL)
-                    .add(ConversationActions.TYPE_SEND_SMS)
-                    .add(ConversationActions.TYPE_TRACK_FLIGHT)
-                    .add(ConversationActions.TYPE_VIEW_CALENDAR)
-                    .add(ConversationActions.TYPE_VIEW_MAP)
+                    .add(ConversationAction.TYPE_TEXT_REPLY)
+                    .add(ConversationAction.TYPE_CREATE_REMINDER)
+                    .add(ConversationAction.TYPE_CALL_PHONE)
+                    .add(ConversationAction.TYPE_OPEN_URL)
+                    .add(ConversationAction.TYPE_SEND_EMAIL)
+                    .add(ConversationAction.TYPE_SEND_SMS)
+                    .add(ConversationAction.TYPE_TRACK_FLIGHT)
+                    .add(ConversationAction.TYPE_VIEW_CALENDAR)
+                    .add(ConversationAction.TYPE_VIEW_MAP)
                     .toString();
 
     private final boolean mSystemTextClassifierEnabled;
diff --git a/core/java/android/view/textclassifier/TextClassifier.java b/core/java/android/view/textclassifier/TextClassifier.java
index 8709e09..5a56136 100644
--- a/core/java/android/view/textclassifier/TextClassifier.java
+++ b/core/java/android/view/textclassifier/TextClassifier.java
@@ -32,7 +32,6 @@
 import android.text.util.Linkify;
 import android.text.util.Linkify.LinkifyMask;
 import android.util.ArrayMap;
-import android.util.ArraySet;
 
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.internal.util.Preconditions;
@@ -43,6 +42,7 @@
 import java.util.Collection;
 import java.util.Collections;
 import java.util.HashSet;
+import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
@@ -324,7 +324,7 @@
     }
 
     /**
-     * Detects the language of the specified text.
+     * Detects the language of the text in the given request.
      *
      * <p><strong>NOTE: </strong>Call on a worker thread.
      *
@@ -403,42 +403,59 @@
     default void dump(@NonNull IndentingPrintWriter printWriter) {}
 
     /**
-     * Configuration object for specifying what entities to identify.
+     * Configuration object for specifying what entity types to identify.
      *
      * Configs are initially based on a predefined preset, and can be modified from there.
      */
     final class EntityConfig implements Parcelable {
-        private final Collection<String> mHints;
-        private final Collection<String> mExcludedEntityTypes;
-        private final Collection<String> mIncludedEntityTypes;
-        private final boolean mUseHints;
+        private final List<String> mIncludedTypes;
+        private final List<String> mExcludedTypes;
+        private final List<String> mHints;
+        private final boolean mIncludeTypesFromTextClassifier;
 
-        private EntityConfig(boolean useHints, Collection<String> hints,
-                Collection<String> includedEntityTypes, Collection<String> excludedEntityTypes) {
-            mHints = hints == null
-                    ? Collections.EMPTY_LIST
-                    : Collections.unmodifiableCollection(new ArraySet<>(hints));
-            mExcludedEntityTypes = excludedEntityTypes == null
-                    ? Collections.EMPTY_LIST : new ArraySet<>(excludedEntityTypes);
-            mIncludedEntityTypes = includedEntityTypes == null
-                    ? Collections.EMPTY_LIST : new ArraySet<>(includedEntityTypes);
-            mUseHints = useHints;
+        private EntityConfig(
+                List<String> includedEntityTypes,
+                List<String> excludedEntityTypes,
+                List<String> hints,
+                boolean includeTypesFromTextClassifier) {
+            mIncludedTypes = Preconditions.checkNotNull(includedEntityTypes);
+            mExcludedTypes = Preconditions.checkNotNull(excludedEntityTypes);
+            mHints = Preconditions.checkNotNull(hints);
+            mIncludeTypesFromTextClassifier = includeTypesFromTextClassifier;
+        }
+
+        private EntityConfig(Parcel in) {
+            mIncludedTypes = new ArrayList<>();
+            in.readStringList(mIncludedTypes);
+            mExcludedTypes = new ArrayList<>();
+            in.readStringList(mExcludedTypes);
+            List<String> tmpHints = new ArrayList<>();
+            in.readStringList(tmpHints);
+            mHints = Collections.unmodifiableList(tmpHints);
+            mIncludeTypesFromTextClassifier = in.readByte() != 0;
+        }
+
+        @Override
+        public void writeToParcel(Parcel parcel, int flags) {
+            parcel.writeStringList(mIncludedTypes);
+            parcel.writeStringList(mExcludedTypes);
+            parcel.writeStringList(mHints);
+            parcel.writeByte((byte) (mIncludeTypesFromTextClassifier ? 1 : 0));
         }
 
         /**
          * Creates an EntityConfig.
          *
          * @param hints Hints for the TextClassifier to determine what types of entities to find.
+         *
+         * @deprecated Use {@link Builder} instead.
          */
+        @Deprecated
         public static EntityConfig createWithHints(@Nullable Collection<String> hints) {
-            return new EntityConfig(/* useHints */ true, hints,
-                    /* includedEntityTypes */null, /* excludedEntityTypes */ null);
-        }
-
-        // TODO: Remove once apps can build against the latest sdk.
-        /** @hide */
-        public static EntityConfig create(@Nullable Collection<String> hints) {
-            return createWithHints(hints);
+            return new EntityConfig.Builder()
+                    .includeTypesFromTextClassifier(true)
+                    .setHints(hints)
+                    .build();
         }
 
         /**
@@ -450,12 +467,19 @@
          *
          *
          * Note that if an entity has been excluded, the exclusion will take precedence.
+         *
+         * @deprecated Use {@link Builder} instead.
          */
+        @Deprecated
         public static EntityConfig create(@Nullable Collection<String> hints,
                 @Nullable Collection<String> includedEntityTypes,
                 @Nullable Collection<String> excludedEntityTypes) {
-            return new EntityConfig(/* useHints */ true, hints,
-                    includedEntityTypes, excludedEntityTypes);
+            return new EntityConfig.Builder()
+                    .setIncludedTypes(includedEntityTypes)
+                    .setExcludedTypes(excludedEntityTypes)
+                    .setHints(hints)
+                    .includeTypesFromTextClassifier(true)
+                    .build();
         }
 
         /**
@@ -463,34 +487,33 @@
          *
          * @param entityTypes Complete set of entities, e.g. {@link #TYPE_URL} to find.
          *
+         * @deprecated Use {@link Builder} instead.
          */
+        @Deprecated
         public static EntityConfig createWithExplicitEntityList(
                 @Nullable Collection<String> entityTypes) {
-            return new EntityConfig(/* useHints */ false, /* hints */ null,
-                    /* includedEntityTypes */ entityTypes, /* excludedEntityTypes */ null);
-        }
-
-        // TODO: Remove once apps can build against the latest sdk.
-        /** @hide */
-        public static EntityConfig createWithEntityList(@Nullable Collection<String> entityTypes) {
-            return createWithExplicitEntityList(entityTypes);
+            return new EntityConfig.Builder()
+                    .setIncludedTypes(entityTypes)
+                    .includeTypesFromTextClassifier(false)
+                    .build();
         }
 
         /**
-         * Returns a list of the final set of entities to find.
+         * Returns a final list of entity types to find.
          *
-         * @param entities Entities we think should be found before factoring in includes/excludes
+         * @param entityTypes Entity types we think should be found before factoring in
+         *                    includes/excludes
          *
          * This method is intended for use by TextClassifier implementations.
          */
         public Collection<String> resolveEntityListModifications(
-                @NonNull Collection<String> entities) {
-            final Set<String> finalSet = new HashSet();
-            if (mUseHints) {
-                finalSet.addAll(entities);
+                @NonNull Collection<String> entityTypes) {
+            final Set<String> finalSet = new HashSet<>();
+            if (mIncludeTypesFromTextClassifier) {
+                finalSet.addAll(entityTypes);
             }
-            finalSet.addAll(mIncludedEntityTypes);
-            finalSet.removeAll(mExcludedEntityTypes);
+            finalSet.addAll(mIncludedTypes);
+            finalSet.removeAll(mExcludedTypes);
             return finalSet;
         }
 
@@ -503,17 +526,22 @@
             return mHints;
         }
 
-        @Override
-        public int describeContents() {
-            return 0;
+        /**
+         * Return whether the client allows the text classifier to include its own list of
+         * default types. If this function returns {@code true}, a default list of types suggested
+         * from a text classifier will be taking into account.
+         *
+         * <p>NOTE: This method is intended for use by a text classifier.
+         *
+         * @see #resolveEntityListModifications(Collection)
+         */
+        public boolean shouldIncludeTypesFromTextClassifier() {
+            return mIncludeTypesFromTextClassifier;
         }
 
         @Override
-        public void writeToParcel(Parcel dest, int flags) {
-            dest.writeStringList(new ArrayList<>(mHints));
-            dest.writeStringList(new ArrayList<>(mExcludedEntityTypes));
-            dest.writeStringList(new ArrayList<>(mIncludedEntityTypes));
-            dest.writeInt(mUseHints ? 1 : 0);
+        public int describeContents() {
+            return 0;
         }
 
         public static final Parcelable.Creator<EntityConfig> CREATOR =
@@ -529,11 +557,75 @@
                     }
                 };
 
-        private EntityConfig(Parcel in) {
-            mHints = new ArraySet<>(in.createStringArrayList());
-            mExcludedEntityTypes = new ArraySet<>(in.createStringArrayList());
-            mIncludedEntityTypes = new ArraySet<>(in.createStringArrayList());
-            mUseHints = in.readInt() == 1;
+
+
+        /** Builder class to construct the {@link EntityConfig} object. */
+        public static final class Builder {
+            @Nullable
+            private Collection<String> mIncludedTypes;
+            @Nullable
+            private Collection<String> mExcludedTypes;
+            @Nullable
+            private Collection<String> mHints;
+            private boolean mIncludeTypesFromTextClassifier = true;
+
+            /**
+             * Sets a collection of types that are explicitly included.
+             */
+            @NonNull
+            public Builder setIncludedTypes(@Nullable Collection<String> includedTypes) {
+                mIncludedTypes = includedTypes;
+                return this;
+            }
+
+            /**
+             * Sets a collection of types that are explicitly excluded.
+             */
+            @NonNull
+            public Builder setExcludedTypes(@Nullable Collection<String> excludedTypes) {
+                mExcludedTypes = excludedTypes;
+                return this;
+            }
+
+            /**
+             * Specifies whether or not to include the types suggested by the text classifier. By
+             * default, it is included.
+             */
+            @NonNull
+            public Builder includeTypesFromTextClassifier(boolean includeTypesFromTextClassifier) {
+                mIncludeTypesFromTextClassifier = includeTypesFromTextClassifier;
+                return this;
+            }
+
+
+            /**
+             * Sets the hints for the TextClassifier to determine what types of entities to find.
+             * These hints will only be used if {@link #includeTypesFromTextClassifier} is
+             * set to be true.
+             */
+            public Builder setHints(Collection<String> hints) {
+                mHints = hints;
+                return this;
+            }
+
+            /**
+             * Combines all of the options that have been set and returns a new {@link EntityConfig}
+             * object.
+             */
+            @NonNull
+            public EntityConfig build() {
+                return new EntityConfig(
+                        mIncludedTypes == null
+                                ? Collections.emptyList()
+                                : new ArrayList<>(mIncludedTypes),
+                        mExcludedTypes == null
+                                ? Collections.emptyList()
+                                : new ArrayList<>(mExcludedTypes),
+                        mHints == null
+                                ? Collections.emptyList()
+                                : Collections.unmodifiableList(new ArrayList<>(mHints)),
+                        mIncludeTypesFromTextClassifier);
+            }
         }
     }
 
diff --git a/core/java/android/view/textclassifier/TextClassifierImpl.java b/core/java/android/view/textclassifier/TextClassifierImpl.java
index d5b9eb1..9ab963e 100644
--- a/core/java/android/view/textclassifier/TextClassifierImpl.java
+++ b/core/java/android/view/textclassifier/TextClassifierImpl.java
@@ -393,7 +393,7 @@
                     actionsImpl.suggestActions(nativeConversation, null);
 
             Collection<String> expectedTypes = resolveActionTypesFromRequest(request);
-            List<ConversationActions.ConversationAction> conversationActions = new ArrayList<>();
+            List<ConversationAction> conversationActions = new ArrayList<>();
             int maxSuggestions = nativeSuggestions.length;
             if (request.getMaxSuggestions() > 0) {
                 maxSuggestions = Math.min(request.getMaxSuggestions(), nativeSuggestions.length);
@@ -405,7 +405,7 @@
                     continue;
                 }
                 conversationActions.add(
-                        new ConversationActions.ConversationAction.Builder(actionType)
+                        new ConversationAction.Builder(actionType)
                                 .setTextReply(nativeSuggestion.getResponseText())
                                 .setConfidenceScore(nativeSuggestion.getScore())
                                 .build());
@@ -445,10 +445,10 @@
 
     private Collection<String> resolveActionTypesFromRequest(ConversationActions.Request request) {
         List<String> defaultActionTypes =
-                request.getHints().contains(ConversationActions.HINT_FOR_NOTIFICATION)
+                request.getHints().contains(ConversationActions.Request.HINT_FOR_NOTIFICATION)
                         ? mSettings.getNotificationConversationActionTypes()
                         : mSettings.getInAppConversationActionTypes();
-        return request.getTypeConfig().resolveTypes(defaultActionTypes);
+        return request.getTypeConfig().resolveEntityListModifications(defaultActionTypes);
     }
 
     private AnnotatorModel getAnnotatorImpl(LocaleList localeList)
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 51b8734..0d16998 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -3685,7 +3685,8 @@
      * @attr ref android.R.styleable#TextView_textCursorDrawable
      */
     public void setTextCursorDrawable(@DrawableRes int textCursorDrawable) {
-        setTextCursorDrawable(mContext.getDrawable(textCursorDrawable));
+        setTextCursorDrawable(
+                textCursorDrawable != 0 ? mContext.getDrawable(textCursorDrawable) : null);
     }
 
     /**
diff --git a/core/java/com/android/internal/app/procstats/ProcessStats.java b/core/java/com/android/internal/app/procstats/ProcessStats.java
index 9b9b771..8e88c51 100644
--- a/core/java/com/android/internal/app/procstats/ProcessStats.java
+++ b/core/java/com/android/internal/app/procstats/ProcessStats.java
@@ -23,6 +23,7 @@
 import android.os.SystemClock;
 import android.os.SystemProperties;
 import android.os.UserHandle;
+import android.service.procstats.ProcessStatsAvailablePagesProto;
 import android.service.procstats.ProcessStatsPackageProto;
 import android.service.procstats.ProcessStatsSectionProto;
 import android.text.format.DateFormat;
@@ -178,7 +179,7 @@
             {"proc", "pkg-proc", "pkg-svc", "pkg-asc", "pkg-all", "all"};
 
     // Current version of the parcel format.
-    private static final int PARCEL_VERSION = 35;
+    private static final int PARCEL_VERSION = 36;
     // In-memory Parcel magic number, used to detect attempts to unmarshall bad data
     private static final int MAGIC = 0x50535454;
 
@@ -237,10 +238,11 @@
     ArrayList<String> mIndexToCommonString;
 
     private static final Pattern sPageTypeRegex = Pattern.compile(
-            "^Node\\s+(\\d+),.*. type\\s+(\\w+)\\s+([\\s\\d]+?)\\s*$");
-    private final ArrayList<Integer> mPageTypeZones = new ArrayList<Integer>();
-    private final ArrayList<String> mPageTypeLabels = new ArrayList<String>();
-    private final ArrayList<int[]> mPageTypeSizes = new ArrayList<int[]>();
+            "^Node\\s+(\\d+),.* zone\\s+(\\w+),.* type\\s+(\\w+)\\s+([\\s\\d]+?)\\s*$");
+    private final ArrayList<Integer> mPageTypeNodes = new ArrayList<>();
+    private final ArrayList<String> mPageTypeZones = new ArrayList<>();
+    private final ArrayList<String> mPageTypeLabels = new ArrayList<>();
+    private final ArrayList<int[]> mPageTypeSizes = new ArrayList<>();
 
     public ProcessStats(boolean running) {
         mRunning = running;
@@ -621,6 +623,7 @@
         try {
             reader = new BufferedReader(new FileReader("/proc/pagetypeinfo"));
             final Matcher matcher = sPageTypeRegex.matcher("");
+            mPageTypeNodes.clear();
             mPageTypeZones.clear();
             mPageTypeLabels.clear();
             mPageTypeSizes.clear();
@@ -631,16 +634,18 @@
                 }
                 matcher.reset(line);
                 if (matcher.matches()) {
-                    final Integer zone = Integer.valueOf(matcher.group(1), 10);
-                    if (zone == null) {
+                    final Integer node = Integer.valueOf(matcher.group(1), 10);
+                    if (node == null) {
                         continue;
                     }
-                    mPageTypeZones.add(zone);
-                    mPageTypeLabels.add(matcher.group(2));
-                    mPageTypeSizes.add(splitAndParseNumbers(matcher.group(3)));
+                    mPageTypeNodes.add(node);
+                    mPageTypeZones.add(matcher.group(2));
+                    mPageTypeLabels.add(matcher.group(3));
+                    mPageTypeSizes.add(splitAndParseNumbers(matcher.group(4)));
                 }
             }
         } catch (IOException ex) {
+            mPageTypeNodes.clear();
             mPageTypeZones.clear();
             mPageTypeLabels.clear();
             mPageTypeSizes.clear();
@@ -935,7 +940,8 @@
         final int NPAGETYPES = mPageTypeLabels.size();
         out.writeInt(NPAGETYPES);
         for (int i=0; i<NPAGETYPES; i++) {
-            out.writeInt(mPageTypeZones.get(i));
+            out.writeInt(mPageTypeNodes.get(i));
+            out.writeString(mPageTypeZones.get(i));
             out.writeString(mPageTypeLabels.get(i));
             out.writeIntArray(mPageTypeSizes.get(i));
         }
@@ -1244,6 +1250,8 @@
 
         // Fragmentation info
         final int NPAGETYPES = in.readInt();
+        mPageTypeNodes.clear();
+        mPageTypeNodes.ensureCapacity(NPAGETYPES);
         mPageTypeZones.clear();
         mPageTypeZones.ensureCapacity(NPAGETYPES);
         mPageTypeLabels.clear();
@@ -1251,7 +1259,8 @@
         mPageTypeSizes.clear();
         mPageTypeSizes.ensureCapacity(NPAGETYPES);
         for (int i=0; i<NPAGETYPES; i++) {
-            mPageTypeZones.add(in.readInt());
+            mPageTypeNodes.add(in.readInt());
+            mPageTypeZones.add(in.readString());
             mPageTypeLabels.add(in.readString());
             mPageTypeSizes.add(in.createIntArray());
         }
@@ -1764,7 +1773,8 @@
         pw.println("Available pages by page size:");
         final int NPAGETYPES = mPageTypeLabels.size();
         for (int i=0; i<NPAGETYPES; i++) {
-            pw.format("Zone %3d  %14s ", mPageTypeZones.get(i), mPageTypeLabels.get(i));
+            pw.format("Node %3d Zone %7s  %14s ", mPageTypeNodes.get(i), mPageTypeZones.get(i),
+                    mPageTypeLabels.get(i));
             final int[] sizes = mPageTypeSizes.get(i);
             final int N = sizes == null ? 0 : sizes.length;
             for (int j=0; j<N; j++) {
@@ -2095,6 +2105,9 @@
             pw.print(",");
             pw.print(mPageTypeZones.get(i));
             pw.print(",");
+            // Wasn't included in original output.
+            //pw.print(mPageTypeNodes.get(i));
+            //pw.print(",");
             final int[] sizes = mPageTypeSizes.get(i);
             final int N = sizes == null ? 0 : sizes.length;
             for (int j=0; j<N; j++) {
@@ -2135,6 +2148,20 @@
             proto.write(ProcessStatsSectionProto.STATUS, ProcessStatsSectionProto.STATUS_PARTIAL);
         }
 
+        final int NPAGETYPES = mPageTypeLabels.size();
+        for (int i = 0; i < NPAGETYPES; i++) {
+            final long token = proto.start(ProcessStatsSectionProto.AVAILABLE_PAGES);
+            proto.write(ProcessStatsAvailablePagesProto.NODE, mPageTypeNodes.get(i));
+            proto.write(ProcessStatsAvailablePagesProto.ZONE, mPageTypeZones.get(i));
+            proto.write(ProcessStatsAvailablePagesProto.LABEL, mPageTypeLabels.get(i));
+            final int[] sizes = mPageTypeSizes.get(i);
+            final int N = sizes == null ? 0 : sizes.length;
+            for (int j = 0; j < N; j++) {
+                proto.write(ProcessStatsAvailablePagesProto.PAGES_PER_ORDER, sizes[j]);
+            }
+            proto.end(token);
+        }
+
         final ArrayMap<String, SparseArray<ProcessState>> procMap = mProcesses.getMap();
         if ((section & REPORT_PROC_STATS) != 0) {
             for (int ip = 0; ip < procMap.size(); ip++) {
diff --git a/core/java/com/android/internal/hardware/AmbientDisplayConfiguration.java b/core/java/com/android/internal/hardware/AmbientDisplayConfiguration.java
index 2995a8f..7a00a51 100644
--- a/core/java/com/android/internal/hardware/AmbientDisplayConfiguration.java
+++ b/core/java/com/android/internal/hardware/AmbientDisplayConfiguration.java
@@ -58,6 +58,15 @@
         return mContext.getResources().getBoolean(R.bool.config_dozePulsePickup);
     }
 
+    public boolean tapGestureEnabled(int user) {
+        return boolSettingDefaultOn(Settings.Secure.DOZE_TAP_SCREEN_GESTURE, user)
+                && tapSensorAvailable();
+    }
+
+    public boolean tapSensorAvailable() {
+        return !TextUtils.isEmpty(tapSensorType());
+    }
+
     public boolean doubleTapGestureEnabled(int user) {
         return boolSettingDefaultOn(Settings.Secure.DOZE_DOUBLE_TAP_GESTURE, user)
                 && doubleTapSensorAvailable();
@@ -86,6 +95,10 @@
         return mContext.getResources().getString(R.string.config_dozeDoubleTapSensorType);
     }
 
+    public String tapSensorType() {
+        return mContext.getResources().getString(R.string.config_dozeTapSensorType);
+    }
+
     public String longPressSensorType() {
         return mContext.getResources().getString(R.string.config_dozeLongPressSensorType);
     }
diff --git a/core/java/com/android/internal/infra/AbstractRemoteService.java b/core/java/com/android/internal/infra/AbstractRemoteService.java
index e8ac223..5945958 100644
--- a/core/java/com/android/internal/infra/AbstractRemoteService.java
+++ b/core/java/com/android/internal/infra/AbstractRemoteService.java
@@ -213,6 +213,7 @@
         }
         mService = null;
         mServiceDied = true;
+        cancelScheduledUnbind();
         @SuppressWarnings("unchecked") // TODO(b/117779333): fix this warning
         final S castService = (S) this;
         mVultureCallback.onServiceDied(castService);
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index cc8da5c..17cc6af 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -30,6 +30,7 @@
 import android.database.ContentObserver;
 import android.hardware.usb.UsbManager;
 import android.net.ConnectivityManager;
+import android.net.INetworkStatsService;
 import android.net.NetworkStats;
 import android.net.Uri;
 import android.net.wifi.WifiActivityEnergyInfo;
@@ -86,7 +87,6 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.location.gnssmetrics.GnssMetrics;
-import com.android.internal.net.NetworkStatsFactory;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.FastPrintWriter;
 import com.android.internal.util.FastXmlSerializer;
@@ -337,6 +337,25 @@
 
     private final PlatformIdleStateCallback mPlatformIdleStateCallback;
 
+    private final Runnable mDeferSetCharging = new Runnable() {
+        @Override
+        public void run() {
+            synchronized (BatteryStatsImpl.this) {
+                if (mOnBattery) {
+                    // if the device gets unplugged in the time between this runnable being
+                    // executed and the lock being taken, we don't want to set charging state
+                    return;
+                }
+                boolean changed = setChargingLocked(true);
+                if (changed) {
+                    final long uptime = mClocks.uptimeMillis();
+                    final long elapsedRealtime = mClocks.elapsedRealtime();
+                    addHistoryRecordLocked(elapsedRealtime, uptime);
+                }
+            }
+        }
+    };
+
     /**
      * This handler is running on {@link BackgroundThread}.
      */
@@ -11012,7 +11031,6 @@
         }
     }
 
-    private final NetworkStatsFactory mNetworkStatsFactory = new NetworkStatsFactory();
     private final Pools.Pool<NetworkStats> mNetworkStatsPool = new Pools.SynchronizedPool<>(6);
 
     private final Object mWifiNetworkLock = new Object();
@@ -11034,11 +11052,16 @@
     private NetworkStats readNetworkStatsLocked(String[] ifaces) {
         try {
             if (!ArrayUtils.isEmpty(ifaces)) {
-                return mNetworkStatsFactory.readNetworkStatsDetail(NetworkStats.UID_ALL, ifaces,
-                        NetworkStats.TAG_NONE, mNetworkStatsPool.acquire());
+                INetworkStatsService statsService = INetworkStatsService.Stub.asInterface(
+                        ServiceManager.getService(Context.NETWORK_STATS_SERVICE));
+                if (statsService != null) {
+                    return statsService.getDetailedUidStats(ifaces);
+                } else {
+                    Slog.e(TAG, "Failed to get networkStatsService ");
+                }
             }
-        } catch (IOException e) {
-            Slog.e(TAG, "failed to read network stats for ifaces: " + Arrays.toString(ifaces));
+        } catch (RemoteException e) {
+            Slog.e(TAG, "failed to read network stats for ifaces: " + Arrays.toString(ifaces) + e);
         }
         return null;
     }
@@ -12336,6 +12359,14 @@
     }
 
     boolean setChargingLocked(boolean charging) {
+        // if the device is no longer charging, remove the callback
+        // if the device is now charging, it means that this is either called
+        // 1. directly when level >= 90
+        // 2. or from within the runnable that we deferred
+        // For 1. if we have an existing callback, remove it, since we will immediatelly send a
+        // ACTION_CHARGING
+        // For 2. we remove existing callback so we don't send multiple ACTION_CHARGING
+        mHandler.removeCallbacks(mDeferSetCharging);
         if (mCharging != charging) {
             mCharging = charging;
             if (charging) {
@@ -12674,12 +12705,23 @@
                     // charging even if it happens to go down a level.
                     changed |= setChargingLocked(true);
                     mLastChargeStepLevel = level;
-                } if (!mCharging) {
+                }
+                if (!mCharging) {
                     if (mLastChargeStepLevel < level) {
-                        // We have not reporting that we are charging, but the level has now
-                        // gone up, so consider the state to be charging.
-                        changed |= setChargingLocked(true);
+                        // We have not reported that we are charging, but the level has gone up,
+                        // but we would like to not have tons of activity from charging-constraint
+                        // jobs, so instead of reporting ACTION_CHARGING immediately, we defer it.
                         mLastChargeStepLevel = level;
+                        if (!mHandler.hasCallbacks(mDeferSetCharging)) {
+                            mHandler.postDelayed(
+                                    mDeferSetCharging,
+                                    mConstants.BATTERY_CHARGED_DELAY_MS);
+                        }
+                    } else if (mLastChargeStepLevel > level) {
+                        // if we had deferred a runnable due to charge level increasing, but then
+                        // later the charge level drops (could be due to thermal issues), we don't
+                        // want to trigger the deferred runnable, so remove it here
+                        mHandler.removeCallbacks(mDeferSetCharging);
                     }
                 } else {
                     if (mLastChargeStepLevel > level) {
@@ -13296,6 +13338,8 @@
                 = "battery_level_collection_delay_ms";
         public static final String KEY_MAX_HISTORY_FILES = "max_history_files";
         public static final String KEY_MAX_HISTORY_BUFFER_KB = "max_history_buffer_kb";
+        public static final String KEY_BATTERY_CHARGED_DELAY_MS =
+                "battery_charged_delay_ms";
 
         private static final boolean DEFAULT_TRACK_CPU_TIMES_BY_PROC_STATE = false;
         private static final boolean DEFAULT_TRACK_CPU_ACTIVE_CLUSTER_TIME = true;
@@ -13308,6 +13352,7 @@
         private static final int DEFAULT_MAX_HISTORY_BUFFER_KB = 128; /*Kilo Bytes*/
         private static final int DEFAULT_MAX_HISTORY_FILES_LOW_RAM_DEVICE = 64;
         private static final int DEFAULT_MAX_HISTORY_BUFFER_LOW_RAM_DEVICE_KB = 64; /*Kilo Bytes*/
+        private static final int DEFAULT_BATTERY_CHARGED_DELAY_MS = 900000; /* 15 min */
 
         public boolean TRACK_CPU_TIMES_BY_PROC_STATE = DEFAULT_TRACK_CPU_TIMES_BY_PROC_STATE;
         public boolean TRACK_CPU_ACTIVE_CLUSTER_TIME = DEFAULT_TRACK_CPU_ACTIVE_CLUSTER_TIME;
@@ -13320,6 +13365,7 @@
                 = DEFAULT_BATTERY_LEVEL_COLLECTION_DELAY_MS;
         public int MAX_HISTORY_FILES;
         public int MAX_HISTORY_BUFFER; /*Bytes*/
+        public int BATTERY_CHARGED_DELAY_MS = DEFAULT_BATTERY_CHARGED_DELAY_MS;
 
         private ContentResolver mResolver;
         private final KeyValueListParser mParser = new KeyValueListParser(',');
@@ -13388,6 +13434,9 @@
                                 DEFAULT_MAX_HISTORY_BUFFER_LOW_RAM_DEVICE_KB
                                 : DEFAULT_MAX_HISTORY_BUFFER_KB)
                         * 1024;
+                BATTERY_CHARGED_DELAY_MS = mParser.getInt(
+                        KEY_BATTERY_CHARGED_DELAY_MS,
+                        DEFAULT_BATTERY_CHARGED_DELAY_MS);
             }
         }
 
@@ -13445,6 +13494,8 @@
             pw.println(MAX_HISTORY_FILES);
             pw.print(KEY_MAX_HISTORY_BUFFER_KB); pw.print("=");
             pw.println(MAX_HISTORY_BUFFER/1024);
+            pw.print(KEY_BATTERY_CHARGED_DELAY_MS); pw.print("=");
+            pw.println(BATTERY_CHARGED_DELAY_MS);
         }
     }
 
diff --git a/core/java/com/android/internal/os/RoSystemProperties.java b/core/java/com/android/internal/os/RoSystemProperties.java
index a319d83..b529bbe 100644
--- a/core/java/com/android/internal/os/RoSystemProperties.java
+++ b/core/java/com/android/internal/os/RoSystemProperties.java
@@ -30,6 +30,7 @@
     public static final String CONTROL_PRIVAPP_PERMISSIONS =
             SystemProperties.get("ro.control_privapp_permissions");
 
+    // ------ ro.hdmi.* -------- //
     /**
      * Property to indicate if a CEC audio device should forward volume keys when system audio
      * mode is off.
@@ -38,6 +39,14 @@
             SystemProperties.getBoolean(
                     "ro.hdmi.cec_audio_device_forward_volume_keys_system_audio_mode_off", false);
 
+    /**
+     * Property to indicate if the current device is a cec switch device.
+     *
+     * <p> Default is false.
+     */
+    public static final String PROPERTY_HDMI_IS_DEVICE_HDMI_CEC_SWITCH =
+            "ro.hdmi.property_is_device_hdmi_cec_switch";
+
     // ------ ro.config.* -------- //
     public static final boolean CONFIG_AVOID_GFX_ACCEL =
             SystemProperties.getBoolean("ro.config.avoid_gfx_accel", false);
diff --git a/core/java/com/android/internal/util/ContrastColorUtil.java b/core/java/com/android/internal/util/ContrastColorUtil.java
index a403c06..e0ba317f 100644
--- a/core/java/com/android/internal/util/ContrastColorUtil.java
+++ b/core/java/com/android/internal/util/ContrastColorUtil.java
@@ -586,7 +586,7 @@
      *
      * @param color the base color to use
      * @param amount the amount from 1 to 100 how much to modify the color
-     * @return the now color that was modified
+     * @return the new color that was modified
      */
     public static int getShiftedColor(int color, int amount) {
         final double[] result = ColorUtilsFromCompat.getTempDouble3Array();
@@ -599,6 +599,19 @@
         return ColorUtilsFromCompat.LABToColor(result[0], result[1], result[2]);
     }
 
+    /**
+     * Blends the provided color with white to create a muted version.
+     *
+     * @param color the color to mute
+     * @param alpha the amount from 0 to 1 to set the alpha component of the white scrim
+     * @return the new color that was modified
+     */
+    public static int getMutedColor(int color, float alpha) {
+        int whiteScrim = ColorUtilsFromCompat.setAlphaComponent(
+                Color.WHITE, (int) (255 * alpha));
+        return compositeColors(whiteScrim, color);
+    }
+
     private static boolean shouldUseDark(int backgroundColor, boolean defaultBackgroundIsDark) {
         if (backgroundColor == Notification.COLOR_DEFAULT) {
             return !defaultBackgroundIsDark;
@@ -675,6 +688,18 @@
         }
 
         /**
+         * Set the alpha component of {@code color} to be {@code alpha}.
+         */
+        @ColorInt
+        public static int setAlphaComponent(@ColorInt int color,
+                @IntRange(from = 0x0, to = 0xFF) int alpha) {
+            if (alpha < 0 || alpha > 255) {
+                throw new IllegalArgumentException("alpha must be between 0 and 255.");
+            }
+            return (color & 0x00ffffff) | (alpha << 24);
+        }
+
+        /**
          * Returns the luminance of a color as a float between {@code 0.0} and {@code 1.0}.
          * <p>Defined as the Y component in the XYZ representation of {@code color}.</p>
          */
diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp
index eb7338a..876bd4f 100755
--- a/core/jni/android/graphics/Bitmap.cpp
+++ b/core/jni/android/graphics/Bitmap.cpp
@@ -8,7 +8,6 @@
 #include "SkImageInfo.h"
 #include "SkColor.h"
 #include "SkColorSpace.h"
-#include "SkMatrix44.h"
 #include "GraphicsJNI.h"
 #include "SkStream.h"
 
@@ -356,8 +355,8 @@
     if (xyzD50 == nullptr || transferParameters == nullptr) {
         colorSpace = SkColorSpace::MakeSRGB();
     } else {
-        SkColorSpaceTransferFn p = GraphicsJNI::getNativeTransferParameters(env, transferParameters);
-        SkMatrix44 xyzMatrix = GraphicsJNI::getNativeXYZMatrix(env, xyzD50);
+        skcms_TransferFunction p = GraphicsJNI::getNativeTransferParameters(env, transferParameters);
+        skcms_Matrix3x3 xyzMatrix = GraphicsJNI::getNativeXYZMatrix(env, xyzD50);
         colorSpace = SkColorSpace::MakeRGB(p, xyzMatrix);
     }
 
@@ -549,8 +548,7 @@
     if (skbitmap.colorType() == kRGBA_F16_SkColorType) {
         // Convert to P3 before encoding. This matches SkAndroidCodec::computeOutputColorSpace
         // for wide gamuts.
-        auto cs = SkColorSpace::MakeRGB(SkColorSpace::kSRGB_RenderTargetGamma,
-                                        SkColorSpace::kDCIP3_D65_Gamut);
+        auto cs = SkColorSpace::MakeRGB(SkNamedTransferFn::kSRGB, SkNamedGamut::kDCIP3);
         auto info = skbitmap.info().makeColorType(kRGBA_8888_SkColorType)
                                    .makeColorSpace(std::move(cs));
         SkBitmap p3;
@@ -568,11 +566,34 @@
     return SkEncodeImage(strm.get(), skbitmap, fm, quality) ? JNI_TRUE : JNI_FALSE;
 }
 
+static inline void bitmapErase(SkBitmap bitmap, const SkColor4f& color,
+        const sk_sp<SkColorSpace>& colorSpace) {
+    SkPaint p;
+    p.setColor4f(color, colorSpace.get());
+    p.setBlendMode(SkBlendMode::kSrc);
+    SkCanvas canvas(bitmap);
+    canvas.drawPaint(p);
+}
+
 static void Bitmap_erase(JNIEnv* env, jobject, jlong bitmapHandle, jint color) {
     LocalScopedBitmap bitmap(bitmapHandle);
     SkBitmap skBitmap;
     bitmap->getSkBitmap(&skBitmap);
-    skBitmap.eraseColor(color);
+    bitmapErase(skBitmap, SkColor4f::FromColor(color), SkColorSpace::MakeSRGB());
+}
+
+static void Bitmap_eraseLong(JNIEnv* env, jobject, jlong bitmapHandle, jobject jColorSpace,
+        jfloat r, jfloat g, jfloat b, jfloat a) {
+    sk_sp<SkColorSpace> cs = GraphicsJNI::getNativeColorSpace(env, jColorSpace);
+    if (GraphicsJNI::hasException(env)) {
+        return;
+    }
+
+    LocalScopedBitmap bitmap(bitmapHandle);
+    SkBitmap skBitmap;
+    bitmap->getSkBitmap(&skBitmap);
+    SkColor4f color = SkColor4f{r, g, b, a};
+    bitmapErase(skBitmap, color, cs);
 }
 
 static jint Bitmap_rowBytes(JNIEnv* env, jobject, jlong bitmapHandle) {
@@ -717,7 +738,7 @@
 #endif
         // Dup the file descriptor so we can keep a reference to it after the Parcel
         // is disposed.
-        int dupFd = dup(blob.fd());
+        int dupFd = fcntl(blob.fd(), F_DUPFD_CLOEXEC, 0);
         if (dupFd < 0) {
             ALOGE("Error allocating dup fd. Error:%d", errno);
             blob.release();
@@ -910,32 +931,32 @@
     SkColorSpace* colorSpace = bitmapHolder->info().colorSpace();
     if (colorSpace == nullptr) return JNI_FALSE;
 
-    SkMatrix44 xyzMatrix(SkMatrix44::kUninitialized_Constructor);
+    skcms_Matrix3x3 xyzMatrix;
     if (!colorSpace->toXYZD50(&xyzMatrix)) return JNI_FALSE;
 
     jfloat* xyz = env->GetFloatArrayElements(xyzArray, NULL);
-    xyz[0] = xyzMatrix.getFloat(0, 0);
-    xyz[1] = xyzMatrix.getFloat(1, 0);
-    xyz[2] = xyzMatrix.getFloat(2, 0);
-    xyz[3] = xyzMatrix.getFloat(0, 1);
-    xyz[4] = xyzMatrix.getFloat(1, 1);
-    xyz[5] = xyzMatrix.getFloat(2, 1);
-    xyz[6] = xyzMatrix.getFloat(0, 2);
-    xyz[7] = xyzMatrix.getFloat(1, 2);
-    xyz[8] = xyzMatrix.getFloat(2, 2);
+    xyz[0] = xyzMatrix.vals[0][0];
+    xyz[1] = xyzMatrix.vals[1][0];
+    xyz[2] = xyzMatrix.vals[2][0];
+    xyz[3] = xyzMatrix.vals[0][1];
+    xyz[4] = xyzMatrix.vals[1][1];
+    xyz[5] = xyzMatrix.vals[2][1];
+    xyz[6] = xyzMatrix.vals[0][2];
+    xyz[7] = xyzMatrix.vals[1][2];
+    xyz[8] = xyzMatrix.vals[2][2];
     env->ReleaseFloatArrayElements(xyzArray, xyz, 0);
 
-    SkColorSpaceTransferFn transferParams;
+    skcms_TransferFunction transferParams;
     if (!colorSpace->isNumericalTransferFn(&transferParams)) return JNI_FALSE;
 
     jfloat* params = env->GetFloatArrayElements(paramsArray, NULL);
-    params[0] = transferParams.fA;
-    params[1] = transferParams.fB;
-    params[2] = transferParams.fC;
-    params[3] = transferParams.fD;
-    params[4] = transferParams.fE;
-    params[5] = transferParams.fF;
-    params[6] = transferParams.fG;
+    params[0] = transferParams.a;
+    params[1] = transferParams.b;
+    params[2] = transferParams.c;
+    params[3] = transferParams.d;
+    params[4] = transferParams.e;
+    params[5] = transferParams.f;
+    params[6] = transferParams.g;
     env->ReleaseFloatArrayElements(paramsArray, params, 0);
 
     return JNI_TRUE;
@@ -1121,8 +1142,8 @@
 
 static jobject Bitmap_wrapHardwareBufferBitmap(JNIEnv* env, jobject, jobject hardwareBuffer,
                                                jfloatArray xyzD50, jobject transferParameters) {
-    SkColorSpaceTransferFn p = GraphicsJNI::getNativeTransferParameters(env, transferParameters);
-    SkMatrix44 xyzMatrix = GraphicsJNI::getNativeXYZMatrix(env, xyzD50);
+    skcms_TransferFunction p = GraphicsJNI::getNativeTransferParameters(env, transferParameters);
+    skcms_Matrix3x3 xyzMatrix = GraphicsJNI::getNativeXYZMatrix(env, xyzD50);
     sk_sp<SkColorSpace> colorSpace = SkColorSpace::MakeRGB(p, xyzMatrix);
     AHardwareBuffer* hwBuf = android_hardware_HardwareBuffer_getNativeHardwareBuffer(env,
         hardwareBuffer);
@@ -1183,6 +1204,7 @@
     {   "nativeCompress",           "(JIILjava/io/OutputStream;[B)Z",
         (void*)Bitmap_compress },
     {   "nativeErase",              "(JI)V", (void*)Bitmap_erase },
+    {   "nativeErase",              "(JLandroid/graphics/ColorSpace;FFFF)V", (void*)Bitmap_eraseLong },
     {   "nativeRowBytes",           "(J)I", (void*)Bitmap_rowBytes },
     {   "nativeConfig",             "(J)I", (void*)Bitmap_config },
     {   "nativeHasAlpha",           "(J)Z", (void*)Bitmap_hasAlpha },
diff --git a/core/jni/android/graphics/Graphics.cpp b/core/jni/android/graphics/Graphics.cpp
index 67d0c8a..9e74b88 100644
--- a/core/jni/android/graphics/Graphics.cpp
+++ b/core/jni/android/graphics/Graphics.cpp
@@ -424,30 +424,30 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
-SkColorSpaceTransferFn GraphicsJNI::getNativeTransferParameters(JNIEnv* env, jobject transferParams) {
-    SkColorSpaceTransferFn p;
-    p.fA = (float) env->GetDoubleField(transferParams, gTransferParams_aFieldID);
-    p.fB = (float) env->GetDoubleField(transferParams, gTransferParams_bFieldID);
-    p.fC = (float) env->GetDoubleField(transferParams, gTransferParams_cFieldID);
-    p.fD = (float) env->GetDoubleField(transferParams, gTransferParams_dFieldID);
-    p.fE = (float) env->GetDoubleField(transferParams, gTransferParams_eFieldID);
-    p.fF = (float) env->GetDoubleField(transferParams, gTransferParams_fFieldID);
-    p.fG = (float) env->GetDoubleField(transferParams, gTransferParams_gFieldID);
+skcms_TransferFunction GraphicsJNI::getNativeTransferParameters(JNIEnv* env, jobject transferParams) {
+    skcms_TransferFunction p;
+    p.a = (float) env->GetDoubleField(transferParams, gTransferParams_aFieldID);
+    p.b = (float) env->GetDoubleField(transferParams, gTransferParams_bFieldID);
+    p.c = (float) env->GetDoubleField(transferParams, gTransferParams_cFieldID);
+    p.d = (float) env->GetDoubleField(transferParams, gTransferParams_dFieldID);
+    p.e = (float) env->GetDoubleField(transferParams, gTransferParams_eFieldID);
+    p.f = (float) env->GetDoubleField(transferParams, gTransferParams_fFieldID);
+    p.g = (float) env->GetDoubleField(transferParams, gTransferParams_gFieldID);
     return p;
 }
 
-SkMatrix44 GraphicsJNI::getNativeXYZMatrix(JNIEnv* env, jfloatArray xyzD50) {
-    SkMatrix44 xyzMatrix(SkMatrix44::kIdentity_Constructor);
+skcms_Matrix3x3 GraphicsJNI::getNativeXYZMatrix(JNIEnv* env, jfloatArray xyzD50) {
+    skcms_Matrix3x3 xyzMatrix;
     jfloat* array = env->GetFloatArrayElements(xyzD50, NULL);
-    xyzMatrix.setFloat(0, 0, array[0]);
-    xyzMatrix.setFloat(1, 0, array[1]);
-    xyzMatrix.setFloat(2, 0, array[2]);
-    xyzMatrix.setFloat(0, 1, array[3]);
-    xyzMatrix.setFloat(1, 1, array[4]);
-    xyzMatrix.setFloat(2, 1, array[5]);
-    xyzMatrix.setFloat(0, 2, array[6]);
-    xyzMatrix.setFloat(1, 2, array[7]);
-    xyzMatrix.setFloat(2, 2, array[8]);
+    xyzMatrix.vals[0][0] = array[0];
+    xyzMatrix.vals[1][0] = array[1];
+    xyzMatrix.vals[2][0] = array[2];
+    xyzMatrix.vals[0][1] = array[3];
+    xyzMatrix.vals[1][1] = array[4];
+    xyzMatrix.vals[2][1] = array[5];
+    xyzMatrix.vals[0][2] = array[6];
+    xyzMatrix.vals[1][2] = array[7];
+    xyzMatrix.vals[2][2] = array[8];
     env->ReleaseFloatArrayElements(xyzD50, array, 0);
     return xyzMatrix;
 }
@@ -456,12 +456,14 @@
     if (colorSpace == nullptr) return nullptr;
     if (!env->IsInstanceOf(colorSpace, gColorSpaceRGB_class)) {
         doThrowIAE(env, "The color space must be an RGB color space");
+        return nullptr;
     }
 
     jobject transferParams = env->CallObjectMethod(colorSpace,
             gColorSpaceRGB_getTransferParametersMethodID);
     if (transferParams == nullptr) {
         doThrowIAE(env, "The color space must use an ICC parametric transfer function");
+        return nullptr;
     }
 
     jfloatArray illuminantD50 = (jfloatArray) env->GetStaticObjectField(gColorSpace_class,
@@ -472,8 +474,8 @@
     jfloatArray xyzD50 = (jfloatArray) env->CallObjectMethod(colorSpaceD50,
             gColorSpaceRGB_getTransformMethodID);
 
-    SkMatrix44 xyzMatrix = getNativeXYZMatrix(env, xyzD50);
-    SkColorSpaceTransferFn transferFunction = getNativeTransferParameters(env, transferParams);
+    skcms_Matrix3x3 xyzMatrix = getNativeXYZMatrix(env, xyzD50);
+    skcms_TransferFunction transferFunction = getNativeTransferParameters(env, transferParams);
 
     return SkColorSpace::MakeRGB(transferFunction, xyzMatrix);
 }
@@ -499,30 +501,30 @@
         } else if (decodeColorSpace.get() != nullptr) {
             // Try to match against known RGB color spaces using the CIE XYZ D50
             // conversion matrix and numerical transfer function parameters
-            SkMatrix44 xyzMatrix(SkMatrix44::kUninitialized_Constructor);
+            skcms_Matrix3x3 xyzMatrix;
             LOG_ALWAYS_FATAL_IF(!decodeColorSpace->toXYZD50(&xyzMatrix));
 
-            SkColorSpaceTransferFn transferParams;
+            skcms_TransferFunction transferParams;
             // We can only handle numerical transfer functions at the moment
             LOG_ALWAYS_FATAL_IF(!decodeColorSpace->isNumericalTransferFn(&transferParams));
 
             jobject params = env->NewObject(gTransferParameters_class,
                     gTransferParameters_constructorMethodID,
-                    transferParams.fA, transferParams.fB, transferParams.fC,
-                    transferParams.fD, transferParams.fE, transferParams.fF,
-                    transferParams.fG);
+                    transferParams.a, transferParams.b, transferParams.c,
+                    transferParams.d, transferParams.e, transferParams.f,
+                    transferParams.g);
 
             jfloatArray xyzArray = env->NewFloatArray(9);
             jfloat xyz[9] = {
-                    xyzMatrix.getFloat(0, 0),
-                    xyzMatrix.getFloat(1, 0),
-                    xyzMatrix.getFloat(2, 0),
-                    xyzMatrix.getFloat(0, 1),
-                    xyzMatrix.getFloat(1, 1),
-                    xyzMatrix.getFloat(2, 1),
-                    xyzMatrix.getFloat(0, 2),
-                    xyzMatrix.getFloat(1, 2),
-                    xyzMatrix.getFloat(2, 2)
+                    xyzMatrix.vals[0][0],
+                    xyzMatrix.vals[1][0],
+                    xyzMatrix.vals[2][0],
+                    xyzMatrix.vals[0][1],
+                    xyzMatrix.vals[1][1],
+                    xyzMatrix.vals[2][1],
+                    xyzMatrix.vals[0][2],
+                    xyzMatrix.vals[1][2],
+                    xyzMatrix.vals[2][2]
             };
             env->SetFloatArrayRegion(xyzArray, 0, 9, xyz);
 
diff --git a/core/jni/android/graphics/GraphicsJNI.h b/core/jni/android/graphics/GraphicsJNI.h
index b0bd683..699d153 100644
--- a/core/jni/android/graphics/GraphicsJNI.h
+++ b/core/jni/android/graphics/GraphicsJNI.h
@@ -10,7 +10,6 @@
 #include "SkPoint.h"
 #include "SkRect.h"
 #include "SkColorSpace.h"
-#include "SkMatrix44.h"
 #include <jni.h>
 #include <hwui/Canvas.h>
 #include <hwui/Bitmap.h>
@@ -101,8 +100,8 @@
             int srcStride, int x, int y, int width, int height,
             SkBitmap* dstBitmap);
 
-    static SkColorSpaceTransferFn getNativeTransferParameters(JNIEnv* env, jobject transferParams);
-    static SkMatrix44 getNativeXYZMatrix(JNIEnv* env, jfloatArray xyzD50);
+    static skcms_TransferFunction getNativeTransferParameters(JNIEnv* env, jobject transferParams);
+    static skcms_Matrix3x3 getNativeXYZMatrix(JNIEnv* env, jfloatArray xyzD50);
     static sk_sp<SkColorSpace> getNativeColorSpace(JNIEnv* env, jobject colorSpace);
 
     static jobject getColorSpace(JNIEnv* env, sk_sp<SkColorSpace>& decodeColorSpace,
diff --git a/core/jni/android/graphics/Paint.cpp b/core/jni/android/graphics/Paint.cpp
index e97c9bc..bc49771 100644
--- a/core/jni/android/graphics/Paint.cpp
+++ b/core/jni/android/graphics/Paint.cpp
@@ -557,6 +557,38 @@
         return result;
     }
 
+    // FIXME: Should this be FastNative?
+    static void setColorLong(JNIEnv* env, jobject clazz, jlong paintHandle, jobject jColorSpace,
+            jfloat r, jfloat g, jfloat b, jfloat a) {
+        sk_sp<SkColorSpace> cs = GraphicsJNI::getNativeColorSpace(env, jColorSpace);
+        if (GraphicsJNI::hasException(env)) {
+            return;
+        }
+
+        SkColor4f color = SkColor4f{r, g, b, a};
+        reinterpret_cast<Paint*>(paintHandle)->setColor4f(color, cs.get());
+    }
+
+    static void setShadowLayerLong(JNIEnv* env, jobject clazz, jlong paintHandle, jfloat radius,
+                                   jfloat dx, jfloat dy, jobject jColorSpace,
+                                   jfloat r, jfloat g, jfloat b, jfloat a) {
+        sk_sp<SkColorSpace> cs = GraphicsJNI::getNativeColorSpace(env, jColorSpace);
+        if (GraphicsJNI::hasException(env)) {
+            return;
+        }
+
+        SkColor4f color = SkColor4f{r, g, b, a};
+
+        Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+        if (radius <= 0) {
+            paint->setLooper(nullptr);
+        }
+        else {
+            SkScalar sigma = android::uirenderer::Blur::convertRadiusToSigma(radius);
+            paint->setLooper(SkBlurDrawLooper::Make(color, cs.get(), sigma, dx, dy));
+        }
+    }
+
     // ------------------ @FastNative ---------------------------
 
     static jint setTextLocales(JNIEnv* env, jobject clazz, jlong objHandle, jstring locales) {
@@ -1075,6 +1107,9 @@
     {"nGetRunAdvance", "(J[CIIIIZI)F", (void*) PaintGlue::getRunAdvance___CIIIIZI_F},
     {"nGetOffsetForAdvance", "(J[CIIIIZF)I",
             (void*) PaintGlue::getOffsetForAdvance___CIIIIZF_I},
+    {"nSetColor","(JLandroid/graphics/ColorSpace;FFFF)V", (void*) PaintGlue::setColorLong},
+    {"nSetShadowLayer", "(JFFFLandroid/graphics/ColorSpace;FFFF)V",
+            (void*)PaintGlue::setShadowLayerLong},
 
     // --------------- @FastNative ----------------------
 
diff --git a/core/jni/android_app_NativeActivity.cpp b/core/jni/android_app_NativeActivity.cpp
index 49a24a3..b2d3651 100644
--- a/core/jni/android_app_NativeActivity.cpp
+++ b/core/jni/android_app_NativeActivity.cpp
@@ -281,15 +281,18 @@
     std::unique_ptr<NativeCode> code;
     bool needs_native_bridge = false;
 
+    char* nativeloader_error_msg = nullptr;
     void* handle = OpenNativeLibrary(env,
                                      sdkVersion,
                                      pathStr.c_str(),
                                      classLoader,
                                      libraryPath,
                                      &needs_native_bridge,
-                                     &g_error_msg);
+                                     &nativeloader_error_msg);
 
     if (handle == nullptr) {
+        g_error_msg = nativeloader_error_msg;
+        NativeLoaderFreeErrorMessage(nativeloader_error_msg);
         ALOGW("NativeActivity LoadNativeLibrary(\"%s\") failed: %s",
               pathStr.c_str(),
               g_error_msg.c_str());
diff --git a/core/jni/android_os_Debug.cpp b/core/jni/android_os_Debug.cpp
index 888dab1..7d63ec9 100644
--- a/core/jni/android_os_Debug.cpp
+++ b/core/jni/android_os_Debug.cpp
@@ -29,7 +29,6 @@
 #include <time.h>
 #include <unistd.h>
 
-#include <atomic>
 #include <iomanip>
 #include <string>
 #include <vector>
@@ -42,6 +41,7 @@
 #include <nativehelper/JNIHelp.h>
 #include <nativehelper/ScopedUtfChars.h>
 #include "jni.h"
+#include <meminfo/procmeminfo.h>
 #include <meminfo/sysmeminfo.h>
 #include <memtrack/memtrack.h>
 #include <memunreachable/memunreachable.h>
@@ -150,14 +150,6 @@
     int swappedOutPss;
 };
 
-enum pss_rollup_support {
-  PSS_ROLLUP_UNTRIED,
-  PSS_ROLLUP_SUPPORTED,
-  PSS_ROLLUP_UNSUPPORTED
-};
-
-static std::atomic<pss_rollup_support> g_pss_rollup_support;
-
 #define BINDER_STATS "/proc/binder/stats"
 
 static jlong android_os_Debug_getNativeHeapSize(JNIEnv *env, jobject clazz)
@@ -555,37 +547,9 @@
     android_os_Debug_getDirtyPagesPid(env, clazz, getpid(), object);
 }
 
-UniqueFile OpenSmapsOrRollup(int pid)
-{
-    enum pss_rollup_support rollup_support =
-            g_pss_rollup_support.load(std::memory_order_relaxed);
-    if (rollup_support != PSS_ROLLUP_UNSUPPORTED) {
-        std::string smaps_rollup_path =
-                base::StringPrintf("/proc/%d/smaps_rollup", pid);
-        UniqueFile fp_rollup = MakeUniqueFile(smaps_rollup_path.c_str(), "re");
-        if (fp_rollup == nullptr && errno != ENOENT) {
-            return fp_rollup;  // Actual error, not just old kernel.
-        }
-        if (fp_rollup != nullptr) {
-            if (rollup_support == PSS_ROLLUP_UNTRIED) {
-                ALOGI("using rollup pss collection");
-                g_pss_rollup_support.store(PSS_ROLLUP_SUPPORTED,
-                                           std::memory_order_relaxed);
-            }
-            return fp_rollup;
-        }
-        g_pss_rollup_support.store(PSS_ROLLUP_UNSUPPORTED,
-                                   std::memory_order_relaxed);
-    }
-
-    std::string smaps_path = base::StringPrintf("/proc/%d/smaps", pid);
-    return MakeUniqueFile(smaps_path.c_str(), "re");
-}
-
 static jlong android_os_Debug_getPssPid(JNIEnv *env, jobject clazz, jint pid,
         jlongArray outUssSwapPssRss, jlongArray outMemtrack)
 {
-    char lineBuffer[1024];
     jlong pss = 0;
     jlong rss = 0;
     jlong swapPss = 0;
@@ -597,59 +561,14 @@
         pss = uss = rss = memtrack = graphics_mem.graphics + graphics_mem.gl + graphics_mem.other;
     }
 
-    {
-        UniqueFile fp = OpenSmapsOrRollup(pid);
-
-        if (fp != nullptr) {
-            char* line;
-
-            while (true) {
-                if (fgets(lineBuffer, sizeof (lineBuffer), fp.get()) == NULL) {
-                    break;
-                }
-                line = lineBuffer;
-
-                switch (line[0]) {
-                    case 'P':
-                        if (strncmp(line, "Pss:", 4) == 0) {
-                            char* c = line + 4;
-                            while (*c != 0 && (*c < '0' || *c > '9')) {
-                                c++;
-                            }
-                            pss += atoi(c);
-                        } else if (strncmp(line, "Private_Clean:", 14) == 0
-                                    || strncmp(line, "Private_Dirty:", 14) == 0) {
-                            char* c = line + 14;
-                            while (*c != 0 && (*c < '0' || *c > '9')) {
-                                c++;
-                            }
-                            uss += atoi(c);
-                        }
-                        break;
-                    case 'R':
-                        if (strncmp(line, "Rss:", 4) == 0) {
-                            char* c = line + 4;
-                            while (*c != 0 && (*c < '0' || *c > '9')) {
-                                c++;
-                            }
-                            rss += atoi(c);
-                        }
-                        break;
-                    case 'S':
-                        if (strncmp(line, "SwapPss:", 8) == 0) {
-                            char* c = line + 8;
-                            jlong lSwapPss;
-                            while (*c != 0 && (*c < '0' || *c > '9')) {
-                                c++;
-                            }
-                            lSwapPss = atoi(c);
-                            swapPss += lSwapPss;
-                            pss += lSwapPss; // Also in swap, those pages would be accounted as Pss without SWAP
-                        }
-                        break;
-                }
-            }
-        }
+    ::android::meminfo::ProcMemInfo proc_mem(pid);
+    ::android::meminfo::MemUsage stats;
+    if (proc_mem.SmapsOrRollup(&stats)) {
+        pss += stats.pss;
+        uss += stats.uss;
+        rss += stats.rss;
+        swapPss = stats.swap_pss;
+        pss += swapPss; // Also in swap, those pages would be accounted as Pss without SWAP
     }
 
     if (outUssSwapPssRss != NULL) {
@@ -686,34 +605,6 @@
     return android_os_Debug_getPssPid(env, clazz, getpid(), NULL, NULL);
 }
 
-static long get_allocated_vmalloc_memory() {
-    char line[1024];
-
-    long vmalloc_allocated_size = 0;
-
-    UniqueFile fp = MakeUniqueFile("/proc/vmallocinfo", "re");
-    if (fp == nullptr) {
-        return 0;
-    }
-
-    while (true) {
-        if (fgets(line, 1024, fp.get()) == NULL) {
-            break;
-        }
-
-        // check to see if there are pages mapped in vmalloc area
-        if (!strstr(line, "pages=")) {
-            continue;
-        }
-
-        long nr_pages;
-        if (sscanf(line, "%*x-%*x %*ld %*s pages=%ld", &nr_pages) == 1) {
-            vmalloc_allocated_size += (nr_pages * getpagesize());
-        }
-    }
-    return vmalloc_allocated_size;
-}
-
 // The 1:1 mapping of MEMINFO_* enums here must match with the constants from
 // Debug.java.
 enum {
@@ -763,9 +654,8 @@
     if (outArray != NULL) {
         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;
+                outArray[i] = smi.ReadVmallocInfo() / 1024;
                 continue;
             }
             outArray[i] = mem[i];
@@ -775,7 +665,6 @@
     env->ReleaseLongArrayElements(out, outArray, 0);
 }
 
-
 static jint read_binder_stat(const char* stat)
 {
     UniqueFile fp = MakeUniqueFile(BINDER_STATS, "re");
diff --git a/core/jni/android_os_Debug.h b/core/jni/android_os_Debug.h
index c7b731b..747776a 100644
--- a/core/jni/android_os_Debug.h
+++ b/core/jni/android_os_Debug.h
@@ -19,6 +19,7 @@
 
 #include <memory>
 #include <stdio.h>
+#include <meminfo/meminfo.h>
 #include <android-base/stringprintf.h>
 #include <android-base/unique_fd.h>
 
@@ -34,8 +35,6 @@
     return UniqueFile(fopen(path, mode), safeFclose);
 }
 
-UniqueFile OpenSmapsOrRollup(int pid);
-
 }  // namespace android
 
 #endif  // ANDROID_OS_HW_BLOB_H
diff --git a/core/jni/android_os_Parcel.cpp b/core/jni/android_os_Parcel.cpp
index 7ef06dc..3b59321 100644
--- a/core/jni/android_os_Parcel.cpp
+++ b/core/jni/android_os_Parcel.cpp
@@ -473,7 +473,7 @@
     if (parcel != NULL) {
         int fd = parcel->readFileDescriptor();
         if (fd < 0) return NULL;
-        fd = dup(fd);
+        fd = fcntl(fd, F_DUPFD_CLOEXEC, 0);
         if (fd < 0) return NULL;
         return jniCreateFileDescriptor(env, fd);
     }
diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp
index 7b564ae..4101c04 100644
--- a/core/jni/android_util_AssetManager.cpp
+++ b/core/jni/android_util_AssetManager.cpp
@@ -44,6 +44,8 @@
 #include "androidfw/MutexGuard.h"
 #include "androidfw/PosixUtils.h"
 #include "androidfw/ResourceTypes.h"
+#include "androidfw/ResourceUtils.h"
+
 #include "core_jni_helpers.h"
 #include "jni.h"
 #include "nativehelper/JNIHelp.h"
@@ -975,34 +977,7 @@
     return nullptr;
   }
 
-  std::string result;
-  if (name.package != nullptr) {
-    result.append(name.package, name.package_len);
-  }
-
-  if (name.type != nullptr || name.type16 != nullptr) {
-    if (!result.empty()) {
-      result += ":";
-    }
-
-    if (name.type != nullptr) {
-      result.append(name.type, name.type_len);
-    } else {
-      result += util::Utf16ToUtf8(StringPiece16(name.type16, name.type_len));
-    }
-  }
-
-  if (name.entry != nullptr || name.entry16 != nullptr) {
-    if (!result.empty()) {
-      result += "/";
-    }
-
-    if (name.entry != nullptr) {
-      result.append(name.entry, name.entry_len);
-    } else {
-      result += util::Utf16ToUtf8(StringPiece16(name.entry16, name.entry_len));
-    }
-  }
+  std::string result = ToFormattedResourceString(&name);
   return env->NewStringUTF(result.c_str());
 }
 
@@ -1049,6 +1024,26 @@
   return nullptr;
 }
 
+static void NativeSetResourceResolutionLoggingEnabled(JNIEnv* /*env*/,
+                                                      jclass /*clazz*/,
+                                                      jlong ptr,
+                                                      jboolean enabled) {
+  ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
+  assetmanager->SetResourceResolutionLoggingEnabled(enabled);
+}
+
+static jstring NativeGetLastResourceResolution(JNIEnv* env,
+                                               jclass /*clazz*/,
+                                               jlong ptr) {
+  ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
+  std::string resolution = assetmanager->GetLastResourceResolution();
+  if (resolution.empty()) {
+    return nullptr;
+  } else {
+    return env->NewStringUTF(resolution.c_str());
+  }
+}
+
 static jobjectArray NativeGetLocales(JNIEnv* env, jclass /*class*/, jlong ptr,
                                      jboolean exclude_system) {
   ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
@@ -1452,6 +1447,10 @@
     {"nativeGetResourcePackageName", "(JI)Ljava/lang/String;", (void*)NativeGetResourcePackageName},
     {"nativeGetResourceTypeName", "(JI)Ljava/lang/String;", (void*)NativeGetResourceTypeName},
     {"nativeGetResourceEntryName", "(JI)Ljava/lang/String;", (void*)NativeGetResourceEntryName},
+    {"nativeSetResourceResolutionLoggingEnabled", "(JZ)V",
+     (void*) NativeSetResourceResolutionLoggingEnabled},
+    {"nativeGetLastResourceResolution", "(J)Ljava/lang/String;",
+     (void*) NativeGetLastResourceResolution},
     {"nativeGetLocales", "(JZ)[Ljava/lang/String;", (void*)NativeGetLocales},
     {"nativeGetSizeConfigurations", "(J)[Landroid/content/res/Configuration;",
      (void*)NativeGetSizeConfigurations},
diff --git a/core/jni/android_util_Process.cpp b/core/jni/android_util_Process.cpp
index 0c1a8aa..798825f 100644
--- a/core/jni/android_util_Process.cpp
+++ b/core/jni/android_util_Process.cpp
@@ -25,6 +25,7 @@
 #include <cutils/sched_policy.h>
 #include <utils/String8.h>
 #include <utils/Vector.h>
+#include <meminfo/procmeminfo.h>
 #include <meminfo/sysmeminfo.h>
 #include <processgroup/processgroup.h>
 
@@ -1083,21 +1084,12 @@
 
 static jlong android_os_Process_getPss(JNIEnv* env, jobject clazz, jint pid)
 {
-    UniqueFile file = OpenSmapsOrRollup(pid);
-    if (file == nullptr) {
+    ::android::meminfo::ProcMemInfo proc_mem(pid);
+    uint64_t pss;
+    if (!proc_mem.SmapsOrRollupPss(&pss)) {
         return (jlong) -1;
     }
 
-    // Tally up all of the Pss from the various maps
-    char line[256];
-    jlong pss = 0;
-    while (fgets(line, sizeof(line), file.get())) {
-        jlong v;
-        if (sscanf(line, "Pss: %" SCNd64 " kB", &v) == 1) {
-            pss += v;
-        }
-    }
-
     // Return the Pss value in bytes, not kilobytes
     return pss * 1024;
 }
diff --git a/core/jni/fd_utils.cpp b/core/jni/fd_utils.cpp
index bd87dcc..41e00b9 100644
--- a/core/jni/fd_utils.cpp
+++ b/core/jni/fd_utils.cpp
@@ -33,6 +33,7 @@
 
 // Static whitelist of open paths that the zygote is allowed to keep open.
 static const char* kPathWhitelist[] = {
+  "/apex/com.android.conscrypt/javalib/conscrypt.jar",
   "/dev/null",
   "/dev/socket/zygote",
   "/dev/socket/zygote_secondary",
diff --git a/core/proto/Android.bp b/core/proto/Android.bp
new file mode 100644
index 0000000..80cc2d4
--- /dev/null
+++ b/core/proto/Android.bp
@@ -0,0 +1,27 @@
+// 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.
+
+// C++ library for Bluetooth platform wide protobuf definitions
+cc_library_static {
+    name: "libbt-platform-protos-lite",
+    host_supported: true,
+    proto: {
+        export_proto_headers: true,
+        type: "lite",
+    },
+    srcs: [
+        "android/bluetooth/enums.proto",
+        "android/bluetooth/hci/enums.proto",
+    ],
+}
diff --git a/core/proto/android/app/settings_enums.proto b/core/proto/android/app/settings_enums.proto
index 3e1c5a3..2148273 100644
--- a/core/proto/android/app/settings_enums.proto
+++ b/core/proto/android/app/settings_enums.proto
@@ -29,6 +29,12 @@
 
     // ACTION: Settings > Any preference is changed
     ACTION_SETTINGS_PREFERENCE_CHANGE = 853;
+
+    // ACTION: Tap & Pay -> Default Application Setting -> Use Forground
+    ACTION_NFC_PAYMENT_FOREGROUND_SETTING = 1622;
+
+    // ACTION: Tap & Pay -> Default Application Setting -> Use Default
+    ACTION_NFC_PAYMENT_ALWAYS_SETTING = 1623;
 }
 
 /**
@@ -85,4 +91,13 @@
 
     // OPEN: Settings > Apps & Notifications -> Special app access -> Financial Apps Sms Access
     SETTINGS_FINANCIAL_APPS_SMS_ACCESS = 1597;
+
+    // OPEN: Settings > System > Input & Gesture > Skip songs
+    SETTINGS_GESTURE_SKIP = 1624;
+
+    // OPEN: Settings > System > Input & Gesture > Silence alerts
+    SETTINGS_GESTURE_SILENCE = 1625;
+
+    // OPEN: Settings > System > Input & Gesture > Tap to check
+    SETTINGS_GESTURE_TAP_SCREEN = 1626;
 }
diff --git a/core/proto/android/bluetooth/enums.proto b/core/proto/android/bluetooth/enums.proto
index d0c9226..76c240e 100644
--- a/core/proto/android/bluetooth/enums.proto
+++ b/core/proto/android/bluetooth/enums.proto
@@ -41,3 +41,18 @@
     ENABLE_DISABLE_REASON_USER_SWITCH = 8;
     ENABLE_DISABLE_REASON_RESTORE_USER_SETTING = 9;
 }
+
+enum DirectionEnum {
+    DIRECTION_UNKNOWN = 0;
+    DIRECTION_OUTGOING = 1;
+    DIRECTION_INCOMING = 2;
+}
+
+// First item is the default value, other values follow Bluetooth spec definition
+enum LinkTypeEnum {
+    // Link type is at most 1 byte (0xFF), thus 0xFFF must not be a valid value
+    LINK_TYPE_UNKNOWN = 0xFFF;
+    LINK_TYPE_SCO = 0x00;
+    LINK_TYPE_ACL = 0x01;
+    LINK_TYPE_ESCO = 0x02;
+}
diff --git a/core/proto/android/bluetooth/hci/enums.proto b/core/proto/android/bluetooth/hci/enums.proto
new file mode 100644
index 0000000..e1d96bb
--- /dev/null
+++ b/core/proto/android/bluetooth/hci/enums.proto
@@ -0,0 +1,519 @@
+/*
+ * 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.
+ */
+
+syntax = "proto2";
+package android.bluetooth.hci;
+
+option java_outer_classname = "BluetoothHciProtoEnums";
+option java_multiple_files = true;
+
+// HCI command opcodes (OCF+OGF) from Bluetooth 5.0 specification Vol 2, Part E, Section 7
+// Original definition: system/bt/stack/include/hcidefs.h
+enum CommandEnum {
+    // Opcode is at most 2 bytes (0xFFFF), thus 0xFFFFF must not be a valid value
+    CMD_UNKNOWN = 0xFFFFF;
+    // Link control commands 0x0400
+    CMD_INQUIRY = 0x0401;
+    CMD_INQUIRY_CANCEL = 0x0402;
+    CMD_PERIODIC_INQUIRY_MODE = 0x0403;
+    CMD_EXIT_PERIODIC_INQUIRY_MODE = 0x0404;
+    CMD_CREATE_CONNECTION = 0x0405;
+    CMD_DISCONNECT = 0x0406;
+    CMD_ADD_SCO_CONNECTION = 0x0407; // Deprecated since Bluetooth 1.2
+    CMD_CREATE_CONNECTION_CANCEL = 0x0408;
+    CMD_ACCEPT_CONNECTION_REQUEST = 0x0409;
+    CMD_REJECT_CONNECTION_REQUEST = 0x040A;
+    CMD_LINK_KEY_REQUEST_REPLY = 0x040B;
+    CMD_LINK_KEY_REQUEST_NEG_REPLY = 0x040C;
+    CMD_PIN_CODE_REQUEST_REPLY = 0x040D;
+    CMD_PIN_CODE_REQUEST_NEG_REPLY = 0x040E;
+    CMD_CHANGE_CONN_PACKET_TYPE = 0x040F;
+    CMD_AUTHENTICATION_REQUESTED = 0x0411;
+    CMD_SET_CONN_ENCRYPTION = 0x0413;
+    CMD_CHANGE_CONN_LINK_KEY = 0x0415;
+    CMD_MASTER_LINK_KEY = 0x0417;
+    CMD_RMT_NAME_REQUEST = 0x0419;
+    CMD_RMT_NAME_REQUEST_CANCEL = 0x041A;
+    CMD_READ_RMT_FEATURES = 0x041B;
+    CMD_READ_RMT_EXT_FEATURES = 0x041C;
+    CMD_READ_RMT_VERSION_INFO = 0x041D;
+    CMD_READ_RMT_CLOCK_OFFSET = 0x041F;
+    CMD_READ_LMP_HANDLE = 0x0420;
+    CMD_SETUP_ESCO_CONNECTION = 0x0428;
+    CMD_ACCEPT_ESCO_CONNECTION = 0x0429;
+    CMD_REJECT_ESCO_CONNECTION = 0x042A;
+    CMD_IO_CAPABILITY_REQUEST_REPLY = 0x042B;
+    CMD_USER_CONF_REQUEST_REPLY = 0x042C;
+    CMD_USER_CONF_VALUE_NEG_REPLY = 0x042D;
+    CMD_USER_PASSKEY_REQ_REPLY = 0x042E;
+    CMD_USER_PASSKEY_REQ_NEG_REPLY = 0x042F;
+    CMD_REM_OOB_DATA_REQ_REPLY = 0x0430;
+    CMD_REM_OOB_DATA_REQ_NEG_REPLY = 0x0433;
+    CMD_IO_CAP_REQ_NEG_REPLY = 0x0434;
+    // BEGIN: AMP commands (not used in system/bt)
+    CMD_CREATE_PHYSICAL_LINK = 0x0435;
+    CMD_ACCEPT_PHYSICAL_LINK = 0x0436;
+    CMD_DISCONNECT_PHYSICAL_LINK = 0x0437;
+    CMD_CREATE_LOGICAL_LINK = 0x0438;
+    CMD_ACCEPT_LOGICAL_LINK = 0x0439;
+    CMD_DISCONNECT_LOGICAL_LINK = 0x043A;
+    CMD_LOGICAL_LINK_CANCEL = 0x043B;
+    CMD_FLOW_SPEC_MODIFY = 0x043C;
+    // END: AMP commands
+    CMD_ENH_SETUP_ESCO_CONNECTION = 0x043D;
+    CMD_ENH_ACCEPT_ESCO_CONNECTION = 0x043E;
+    CMD_TRUNCATED_PAGE = 0x043F;
+    CMD_TRUNCATED_PAGE_CANCEL = 0x0440;
+    CMD_SET_CLB = 0x0441;
+    CMD_RECEIVE_CLB = 0x0442;
+    CMD_START_SYNC_TRAIN = 0x0443;
+    CMD_RECEIVE_SYNC_TRAIN = 0x0444;
+    CMD_REM_OOB_EXTENDED_DATA_REQ_REPLY = 0x0445; // Not currently used in system/bt
+    // Link policy commands 0x0800
+    CMD_HOLD_MODE = 0x0801;
+    CMD_SNIFF_MODE = 0x0803;
+    CMD_EXIT_SNIFF_MODE = 0x0804;
+    CMD_PARK_MODE = 0x0805;
+    CMD_EXIT_PARK_MODE = 0x0806;
+    CMD_QOS_SETUP = 0x0807;
+    CMD_ROLE_DISCOVERY = 0x0809;
+    CMD_SWITCH_ROLE = 0x080B;
+    CMD_READ_POLICY_SETTINGS = 0x080C;
+    CMD_WRITE_POLICY_SETTINGS = 0x080D;
+    CMD_READ_DEF_POLICY_SETTINGS = 0x080E;
+    CMD_WRITE_DEF_POLICY_SETTINGS = 0x080F;
+    CMD_FLOW_SPECIFICATION = 0x0810;
+    CMD_SNIFF_SUB_RATE = 0x0811;
+    // Host controller baseband commands 0x0C00
+    CMD_SET_EVENT_MASK = 0x0C01;
+    CMD_RESET = 0x0C03;
+    CMD_SET_EVENT_FILTER = 0x0C05;
+    CMD_FLUSH = 0x0C08;
+    CMD_READ_PIN_TYPE = 0x0C09;
+    CMD_WRITE_PIN_TYPE = 0x0C0A;
+    CMD_CREATE_NEW_UNIT_KEY = 0x0C0B;
+    CMD_GET_MWS_TRANS_LAYER_CFG = 0x0C0C; // Deprecated (not used in spec)
+    CMD_READ_STORED_LINK_KEY = 0x0C0D;
+    CMD_WRITE_STORED_LINK_KEY = 0x0C11;
+    CMD_DELETE_STORED_LINK_KEY = 0x0C12;
+    CMD_CHANGE_LOCAL_NAME = 0x0C13;
+    CMD_READ_LOCAL_NAME = 0x0C14;
+    CMD_READ_CONN_ACCEPT_TOUT = 0x0C15;
+    CMD_WRITE_CONN_ACCEPT_TOUT = 0x0C16;
+    CMD_READ_PAGE_TOUT = 0x0C17;
+    CMD_WRITE_PAGE_TOUT = 0x0C18;
+    CMD_READ_SCAN_ENABLE = 0x0C19;
+    CMD_WRITE_SCAN_ENABLE = 0x0C1A;
+    CMD_READ_PAGESCAN_CFG = 0x0C1B;
+    CMD_WRITE_PAGESCAN_CFG = 0x0C1C;
+    CMD_READ_INQUIRYSCAN_CFG = 0x0C1D;
+    CMD_WRITE_INQUIRYSCAN_CFG = 0x0C1E;
+    CMD_READ_AUTHENTICATION_ENABLE = 0x0C1F;
+    CMD_WRITE_AUTHENTICATION_ENABLE = 0x0C20;
+    CMD_READ_ENCRYPTION_MODE = 0x0C21; // Deprecated
+    CMD_WRITE_ENCRYPTION_MODE = 0x0C22; // Deprecated
+    CMD_READ_CLASS_OF_DEVICE = 0x0C23;
+    CMD_WRITE_CLASS_OF_DEVICE = 0x0C24;
+    CMD_READ_VOICE_SETTINGS = 0x0C25;
+    CMD_WRITE_VOICE_SETTINGS = 0x0C26;
+    CMD_READ_AUTOMATIC_FLUSH_TIMEOUT = 0x0C27;
+    CMD_WRITE_AUTOMATIC_FLUSH_TIMEOUT = 0x0C28;
+    CMD_READ_NUM_BCAST_REXMITS = 0x0C29;
+    CMD_WRITE_NUM_BCAST_REXMITS = 0x0C2A;
+    CMD_READ_HOLD_MODE_ACTIVITY = 0x0C2B;
+    CMD_WRITE_HOLD_MODE_ACTIVITY = 0x0C2C;
+    CMD_READ_TRANSMIT_POWER_LEVEL = 0x0C2D;
+    CMD_READ_SCO_FLOW_CTRL_ENABLE = 0x0C2E;
+    CMD_WRITE_SCO_FLOW_CTRL_ENABLE = 0x0C2F;
+    CMD_SET_HC_TO_HOST_FLOW_CTRL = 0x0C31;
+    CMD_HOST_BUFFER_SIZE = 0x0C33;
+    CMD_HOST_NUM_PACKETS_DONE = 0x0C35;
+    CMD_READ_LINK_SUPER_TOUT = 0x0C36;
+    CMD_WRITE_LINK_SUPER_TOUT = 0x0C37;
+    CMD_READ_NUM_SUPPORTED_IAC = 0x0C38;
+    CMD_READ_CURRENT_IAC_LAP = 0x0C39;
+    CMD_WRITE_CURRENT_IAC_LAP = 0x0C3A;
+    CMD_READ_PAGESCAN_PERIOD_MODE = 0x0C3B; // Deprecated
+    CMD_WRITE_PAGESCAN_PERIOD_MODE = 0x0C3C; // Deprecated
+    CMD_READ_PAGESCAN_MODE = 0x0C3D; // Deprecated
+    CMD_WRITE_PAGESCAN_MODE = 0x0C3E; // Deprecated
+    CMD_SET_AFH_CHANNELS = 0x0C3F;
+    CMD_READ_INQSCAN_TYPE = 0x0C42;
+    CMD_WRITE_INQSCAN_TYPE = 0x0C43;
+    CMD_READ_INQUIRY_MODE = 0x0C44;
+    CMD_WRITE_INQUIRY_MODE = 0x0C45;
+    CMD_READ_PAGESCAN_TYPE = 0x0C46;
+    CMD_WRITE_PAGESCAN_TYPE = 0x0C47;
+    CMD_READ_AFH_ASSESSMENT_MODE = 0x0C48;
+    CMD_WRITE_AFH_ASSESSMENT_MODE = 0x0C49;
+    CMD_READ_EXT_INQ_RESPONSE = 0x0C51;
+    CMD_WRITE_EXT_INQ_RESPONSE = 0x0C52;
+    CMD_REFRESH_ENCRYPTION_KEY = 0x0C53;
+    CMD_READ_SIMPLE_PAIRING_MODE = 0x0C55;
+    CMD_WRITE_SIMPLE_PAIRING_MODE = 0x0C56;
+    CMD_READ_LOCAL_OOB_DATA = 0x0C57;
+    CMD_READ_INQ_TX_POWER_LEVEL = 0x0C58;
+    CMD_WRITE_INQ_TX_POWER_LEVEL = 0x0C59;
+    CMD_READ_ERRONEOUS_DATA_RPT = 0x0C5A;
+    CMD_WRITE_ERRONEOUS_DATA_RPT = 0x0C5B;
+    CMD_ENHANCED_FLUSH = 0x0C5F;
+    CMD_SEND_KEYPRESS_NOTIF = 0x0C60;
+    CMD_READ_LOGICAL_LINK_ACCEPT_TIMEOUT = 0x0C61;
+    CMD_WRITE_LOGICAL_LINK_ACCEPT_TIMEOUT = 0x0C62;
+    CMD_SET_EVENT_MASK_PAGE_2 = 0x0C63;
+    CMD_READ_LOCATION_DATA = 0x0C64;
+    CMD_WRITE_LOCATION_DATA = 0x0C65;
+    CMD_READ_FLOW_CONTROL_MODE = 0x0C66;
+    CMD_WRITE_FLOW_CONTROL_MODE = 0x0C67;
+    CMD_READ_ENHANCED_TX_PWR_LEVEL = 0x0C68; // Not currently used in system/bt
+    CMD_READ_BE_FLUSH_TOUT = 0x0C69;
+    CMD_WRITE_BE_FLUSH_TOUT = 0x0C6A;
+    CMD_SHORT_RANGE_MODE = 0x0C6B;
+    CMD_READ_BLE_HOST_SUPPORT = 0x0C6C;
+    CMD_WRITE_BLE_HOST_SUPPORT = 0x0C6D;
+    CMD_SET_MWS_CHANNEL_PARAMETERS = 0x0C6E;
+    CMD_SET_EXTERNAL_FRAME_CONFIGURATION = 0x0C6F;
+    CMD_SET_MWS_SIGNALING = 0x0C70;
+    CMD_SET_MWS_TRANSPORT_LAYER = 0x0C71;
+    CMD_SET_MWS_SCAN_FREQUENCY_TABLE = 0x0C72;
+    CMD_SET_MWS_PATTERN_CONFIGURATION = 0x0C73;
+    CMD_SET_RESERVED_LT_ADDR = 0x0C74;
+    CMD_DELETE_RESERVED_LT_ADDR = 0x0C75;
+    CMD_WRITE_CLB_DATA = 0x0C76;
+    CMD_READ_SYNC_TRAIN_PARAM = 0x0C77;
+    CMD_WRITE_SYNC_TRAIN_PARAM = 0x0C78;
+    CMD_READ_SECURE_CONNS_SUPPORT = 0x0C79;
+    CMD_WRITE_SECURE_CONNS_SUPPORT = 0x0C7A;
+    CMD_READ_AUTHED_PAYLOAD_TIMEOUT = 0x0C7B; // Not currently used in system/bt
+    CMD_WRITE_AUTHED_PAYLOAD_TIMEOUT = 0x0C7C; // Not currently used in system/bt
+    CMD_READ_LOCAL_OOB_EXTENDED_DATA = 0x0C7D; // Not currently used in system/bt
+    CMD_READ_EXTENDED_PAGE_TIMEOUT = 0x0C7E; // Not currently used in system/bt
+    CMD_WRITE_EXTENDED_PAGE_TIMEOUT = 0x0C7F; // Not currently used in system/bt
+    CMD_READ_EXTENDED_INQUIRY_LENGTH = 0x0C80; // Not currently used in system/bt
+    CMD_WRITE_EXTENDED_INQUIRY_LENGTH = 0x0C81; // Not currently used in system/bt
+    // Informational parameter commands 0x1000
+    CMD_READ_LOCAL_VERSION_INFO = 0x1001;
+    CMD_READ_LOCAL_SUPPORTED_CMDS = 0x1002;
+    CMD_READ_LOCAL_FEATURES = 0x1003;
+    CMD_READ_LOCAL_EXT_FEATURES = 0x1004;
+    CMD_READ_BUFFER_SIZE = 0x1005;
+    CMD_READ_COUNTRY_CODE = 0x1007; // Deprecated
+    CMD_READ_BD_ADDR = 0x1009;
+    CMD_READ_DATA_BLOCK_SIZE = 0x100A;
+    CMD_READ_LOCAL_SUPPORTED_CODECS = 0x100B;
+    // Status parameter commands 0x1400
+    CMD_READ_FAILED_CONTACT_COUNTER = 0x1401;
+    CMD_RESET_FAILED_CONTACT_COUNTER = 0x1402;
+    CMD_GET_LINK_QUALITY = 0x1403;
+    CMD_READ_RSSI = 0x1405;
+    CMD_READ_AFH_CH_MAP = 0x1406;
+    CMD_READ_CLOCK = 0x1407;
+    CMD_READ_ENCR_KEY_SIZE = 0x1408;
+    CMD_READ_LOCAL_AMP_INFO = 0x1409;
+    CMD_READ_LOCAL_AMP_ASSOC = 0x140A;
+    CMD_WRITE_REMOTE_AMP_ASSOC = 0x140B;
+    CMD_GET_MWS_TRANSPORT_CFG = 0x140C; // Not currently used in system/bt
+    CMD_SET_TRIGGERED_CLK_CAPTURE = 0x140D; // Not currently used in system/bt
+    // Testing commands 0x1800
+    CMD_READ_LOOPBACK_MODE = 0x1801;
+    CMD_WRITE_LOOPBACK_MODE = 0x1802;
+    CMD_ENABLE_DEV_UNDER_TEST_MODE = 0x1803;
+    CMD_WRITE_SIMP_PAIR_DEBUG_MODE = 0x1804;
+    CMD_ENABLE_AMP_RCVR_REPORTS = 0x1807;
+    CMD_AMP_TEST_END = 0x1808;
+    CMD_AMP_TEST = 0x1809;
+    CMD_WRITE_SECURE_CONN_TEST_MODE = 0x180A; // Not currently used in system/bt
+    // BLE commands 0x2000
+    CMD_BLE_SET_EVENT_MASK = 0x2001;
+    CMD_BLE_READ_BUFFER_SIZE = 0x2002;
+    CMD_BLE_READ_LOCAL_SPT_FEAT = 0x2003;
+    CMD_BLE_WRITE_LOCAL_SPT_FEAT = 0x2004;
+    CMD_BLE_WRITE_RANDOM_ADDR = 0x2005;
+    CMD_BLE_WRITE_ADV_PARAMS = 0x2006;
+    CMD_BLE_READ_ADV_CHNL_TX_POWER = 0x2007;
+    CMD_BLE_WRITE_ADV_DATA = 0x2008;
+    CMD_BLE_WRITE_SCAN_RSP_DATA = 0x2009;
+    CMD_BLE_WRITE_ADV_ENABLE = 0x200A;
+    CMD_BLE_WRITE_SCAN_PARAMS = 0x200B;
+    CMD_BLE_WRITE_SCAN_ENABLE = 0x200C;
+    CMD_BLE_CREATE_LL_CONN = 0x200D;
+    CMD_BLE_CREATE_CONN_CANCEL = 0x200E;
+    CMD_BLE_READ_WHITE_LIST_SIZE = 0x200F;
+    CMD_BLE_CLEAR_WHITE_LIST = 0x2010;
+    CMD_BLE_ADD_WHITE_LIST = 0x2011;
+    CMD_BLE_REMOVE_WHITE_LIST = 0x2012;
+    CMD_BLE_UPD_LL_CONN_PARAMS = 0x2013;
+    CMD_BLE_SET_HOST_CHNL_CLASS = 0x2014;
+    CMD_BLE_READ_CHNL_MAP = 0x2015;
+    CMD_BLE_READ_REMOTE_FEAT = 0x2016;
+    CMD_BLE_ENCRYPT = 0x2017;
+    CMD_BLE_RAND = 0x2018;
+    CMD_BLE_START_ENC = 0x2019;
+    CMD_BLE_LTK_REQ_REPLY = 0x201A;
+    CMD_BLE_LTK_REQ_NEG_REPLY = 0x201B;
+    CMD_BLE_READ_SUPPORTED_STATES = 0x201C;
+    CMD_BLE_RECEIVER_TEST = 0x201D;
+    CMD_BLE_TRANSMITTER_TEST = 0x201E;
+    CMD_BLE_TEST_END = 0x201F;
+    CMD_BLE_RC_PARAM_REQ_REPLY = 0x2020;
+    CMD_BLE_RC_PARAM_REQ_NEG_REPLY = 0x2021;
+    CMD_BLE_SET_DATA_LENGTH = 0x2022;
+    CMD_BLE_READ_DEFAULT_DATA_LENGTH = 0x2023;
+    CMD_BLE_WRITE_DEFAULT_DATA_LENGTH = 0x2024;
+    CMD_BLE_GENERATE_DHKEY = 0x2026; // Not currently used in system/bt
+    CMD_BLE_ADD_DEV_RESOLVING_LIST = 0x2027;
+    CMD_BLE_RM_DEV_RESOLVING_LIST = 0x2028;
+    CMD_BLE_CLEAR_RESOLVING_LIST = 0x2029;
+    CMD_BLE_READ_RESOLVING_LIST_SIZE = 0x202A;
+    CMD_BLE_READ_RESOLVABLE_ADDR_PEER = 0x202B;
+    CMD_BLE_READ_RESOLVABLE_ADDR_LOCAL = 0x202C;
+    CMD_BLE_SET_ADDR_RESOLUTION_ENABLE = 0x202D;
+    CMD_BLE_SET_RAND_PRIV_ADDR_TIMOUT = 0x202E;
+    CMD_BLE_READ_MAXIMUM_DATA_LENGTH = 0x202F;
+    CMD_BLE_READ_PHY = 0x2030;
+    CMD_BLE_SET_DEFAULT_PHY = 0x2031;
+    CMD_BLE_SET_PHY = 0x2032;
+    CMD_BLE_ENH_RECEIVER_TEST = 0x2033;
+    CMD_BLE_ENH_TRANSMITTER_TEST = 0x2034;
+    CMD_BLE_SET_EXT_ADVERTISING_RANDOM_ADDRESS = 0x2035;
+    CMD_BLE_SET_EXT_ADVERTISING_PARAM = 0x2036;
+    CMD_BLE_SET_EXT_ADVERTISING_DATA = 0x2037;
+    CMD_BLE_SET_EXT_ADVERTISING_SCAN_RESP = 0x2038;
+    CMD_BLE_SET_EXT_ADVERTISING_ENABLE = 0x2039;
+    CMD_BLE_READ_MAXIMUM_ADVERTISING_DATA_LENGTH = 0x203A;
+    CMD_BLE_READ_NUMBER_OF_SUPPORTED_ADVERTISING_SETS = 0x203B;
+    CMD_BLE_REMOVE_ADVERTISING_SET = 0x203C;
+    CMD_BLE_CLEAR_ADVERTISING_SETS = 0x203D;
+    CMD_BLE_SET_PERIODIC_ADVERTISING_PARAM = 0x203E;
+    CMD_BLE_SET_PERIODIC_ADVERTISING_DATA = 0x203F;
+    CMD_BLE_SET_PERIODIC_ADVERTISING_ENABLE = 0x2040;
+    CMD_BLE_SET_EXTENDED_SCAN_PARAMETERS = 0x2041;
+    CMD_BLE_SET_EXTENDED_SCAN_ENABLE = 0x2042;
+    CMD_BLE_EXTENDED_CREATE_CONNECTION = 0x2043;
+    CMD_BLE_PERIODIC_ADVERTISING_CREATE_SYNC = 0x2044;
+    CMD_BLE_PERIODIC_ADVERTISING_CREATE_SYNC_CANCEL = 0x2045;
+    CMD_BLE_PERIODIC_ADVERTISING_TERMINATE_SYNC = 0x2046;
+    CMD_BLE_ADD_DEVICE_TO_PERIODIC_ADVERTISING_LIST = 0x2047;
+    CMD_BLE_RM_DEVICE_FROM_PERIODIC_ADVERTISING_LIST = 0x2048;
+    CMD_BLE_CLEAR_PERIODIC_ADVERTISING_LIST = 0x2049;
+    CMD_BLE_READ_PERIODIC_ADVERTISING_LIST_SIZE = 0x204A;
+    CMD_BLE_READ_TRANSMIT_POWER = 0x204B;
+    CMD_BLE_READ_RF_COMPENS_POWER = 0x204C;
+    CMD_BLE_WRITE_RF_COMPENS_POWER = 0x204D;
+    CMD_BLE_SET_PRIVACY_MODE = 0x204E;
+    // Vendor specific commands 0xFC00 and above
+    // Android vendor specific commands defined in
+    // https://source.android.com/devices/bluetooth/hci_requirements#vendor-specific-capabilities
+    CMD_BLE_VENDOR_CAP = 0xFD53;
+    CMD_BLE_MULTI_ADV = 0xFD54;
+    CMD_BLE_BATCH_SCAN = 0xFD56;
+    CMD_BLE_ADV_FILTER = 0xFD57;
+    CMD_BLE_TRACK_ADV = 0xFD58;
+    CMD_BLE_ENERGY_INFO = 0xFD59;
+    CMD_BLE_EXTENDED_SCAN_PARAMS = 0xFD5A;
+    CMD_CONTROLLER_DEBUG_INFO = 0xFD5B;
+    CMD_CONTROLLER_A2DP_OPCODE = 0xFD5D;
+    CMD_BRCM_SET_ACL_PRIORITY = 0xFC57;
+    // Other vendor specific commands below here
+}
+
+// HCI event codes from the Bluetooth 5.0 specification Vol 2, Part 7, Section 7
+// Original definition: system/bt/stack/include/hcidefs.h
+enum EventEnum {
+    // Event is at most 1 byte (0xFF), thus 0xFFF must not be a valid value
+    EVT_UNKNOWN = 0xFFF;
+    EVT_INQUIRY_COMP = 0x01;
+    EVT_INQUIRY_RESULT = 0x02;
+    EVT_CONNECTION_COMP = 0x03;
+    EVT_CONNECTION_REQUEST = 0x04;
+    EVT_DISCONNECTION_COMP = 0x05;
+    EVT_AUTHENTICATION_COMP = 0x06;
+    EVT_RMT_NAME_REQUEST_COMP = 0x07;
+    EVT_ENCRYPTION_CHANGE = 0x08;
+    EVT_CHANGE_CONN_LINK_KEY = 0x09;
+    EVT_MASTER_LINK_KEY_COMP = 0x0A;
+    EVT_READ_RMT_FEATURES_COMP = 0x0B;
+    EVT_READ_RMT_VERSION_COMP = 0x0C;
+    EVT_QOS_SETUP_COMP = 0x0D;
+    EVT_COMMAND_COMPLETE = 0x0E;
+    EVT_COMMAND_STATUS = 0x0F;
+    EVT_HARDWARE_ERROR = 0x10;
+    EVT_FLUSH_OCCURED = 0x11;
+    EVT_ROLE_CHANGE = 0x12;
+    EVT_NUM_COMPL_DATA_PKTS = 0x13;
+    EVT_MODE_CHANGE = 0x14;
+    EVT_RETURN_LINK_KEYS = 0x15;
+    EVT_PIN_CODE_REQUEST = 0x16;
+    EVT_LINK_KEY_REQUEST = 0x17;
+    EVT_LINK_KEY_NOTIFICATION = 0x18;
+    EVT_LOOPBACK_COMMAND = 0x19;
+    EVT_DATA_BUF_OVERFLOW = 0x1A;
+    EVT_MAX_SLOTS_CHANGED = 0x1B;
+    EVT_READ_CLOCK_OFF_COMP = 0x1C;
+    EVT_CONN_PKT_TYPE_CHANGE = 0x1D;
+    EVT_QOS_VIOLATION = 0x1E;
+    EVT_PAGE_SCAN_MODE_CHANGE = 0x1F; // Deprecated
+    EVT_PAGE_SCAN_REP_MODE_CHNG = 0x20;
+    EVT_FLOW_SPECIFICATION_COMP = 0x21;
+    EVT_INQUIRY_RSSI_RESULT = 0x22;
+    EVT_READ_RMT_EXT_FEATURES_COMP = 0x23;
+    EVT_ESCO_CONNECTION_COMP = 0x2C;
+    EVT_ESCO_CONNECTION_CHANGED = 0x2D;
+    EVT_SNIFF_SUB_RATE = 0x2E;
+    EVT_EXTENDED_INQUIRY_RESULT = 0x2F;
+    EVT_ENCRYPTION_KEY_REFRESH_COMP = 0x30;
+    EVT_IO_CAPABILITY_REQUEST = 0x31;
+    EVT_IO_CAPABILITY_RESPONSE = 0x32;
+    EVT_USER_CONFIRMATION_REQUEST = 0x33;
+    EVT_USER_PASSKEY_REQUEST = 0x34;
+    EVT_REMOTE_OOB_DATA_REQUEST = 0x35;
+    EVT_SIMPLE_PAIRING_COMPLETE = 0x36;
+    EVT_LINK_SUPER_TOUT_CHANGED = 0x38;
+    EVT_ENHANCED_FLUSH_COMPLETE = 0x39;
+    EVT_USER_PASSKEY_NOTIFY = 0x3B;
+    EVT_KEYPRESS_NOTIFY = 0x3C;
+    EVT_RMT_HOST_SUP_FEAT_NOTIFY = 0x3D;
+    EVT_BLE_META = 0x3E;
+    EVT_PHYSICAL_LINK_COMP = 0x40;
+    EVT_CHANNEL_SELECTED = 0x41;
+    EVT_DISC_PHYSICAL_LINK_COMP = 0x42;
+    EVT_PHY_LINK_LOSS_EARLY_WARNING = 0x43;
+    EVT_PHY_LINK_RECOVERY = 0x44;
+    EVT_LOGICAL_LINK_COMP = 0x45;
+    EVT_DISC_LOGICAL_LINK_COMP = 0x46;
+    EVT_FLOW_SPEC_MODIFY_COMP = 0x47;
+    EVT_NUM_COMPL_DATA_BLOCKS = 0x48;
+    EVT_AMP_TEST_START = 0x49; // Not currently used in system/bt
+    EVT_AMP_TEST_END = 0x4A; // Not currently used in system/bt
+    EVT_AMP_RECEIVER_RPT = 0x4B; // Not currently used in system/bt
+    EVT_SHORT_RANGE_MODE_COMPLETE = 0x4C;
+    EVT_AMP_STATUS_CHANGE = 0x4D;
+    EVT_SET_TRIGGERED_CLOCK_CAPTURE = 0x4E;
+    EVT_SYNC_TRAIN_CMPL = 0x4F; // Not currently used in system/bt
+    EVT_SYNC_TRAIN_RCVD = 0x50; // Not currently used in system/bt
+    EVT_CONNLESS_SLAVE_BROADCAST_RCVD = 0x51; // Not currently used in system/bt
+    EVT_CONNLESS_SLAVE_BROADCAST_TIMEOUT = 0x52; // Not currently used in system/bt
+    EVT_TRUNCATED_PAGE_CMPL = 0x53; // Not currently used in system/bt
+    EVT_SLAVE_PAGE_RES_TIMEOUT = 0x54; // Not currently used in system/bt
+    EVT_CONNLESS_SLAVE_BROADCAST_CHNL_MAP_CHANGE = 0x55; // Not currently used in system/bt
+    EVT_INQUIRY_RES_NOTIFICATION = 0x56; // Not currently used in system/bt
+    EVT_AUTHED_PAYLOAD_TIMEOUT = 0x57; // Not currently used in system/bt
+    EVT_SAM_STATUS_CHANGE = 0x58; // Not currently used in system/bt
+}
+
+// Bluetooth low energy related meta event codes
+// from the Bluetooth 5.0 specification Vol 2, Part E, Section 7.7.65
+// Original definition: system/bt/stack/include/hcidefs.h
+enum BleMetaEventEnum {
+    // BLE meta event code is at most 1 byte (0xFF), thus 0xFFF must not be a valid value
+    BLE_EVT_UNKNOWN = 0xFFF;
+    BLE_EVT_CONN_COMPLETE_EVT = 0x01;
+    BLE_EVT_ADV_PKT_RPT_EVT = 0x02;
+    BLE_EVT_LL_CONN_PARAM_UPD_EVT = 0x03;
+    BLE_EVT_READ_REMOTE_FEAT_CMPL_EVT = 0x04;
+    BLE_EVT_LTK_REQ_EVT = 0x05;
+    BLE_EVT_RC_PARAM_REQ_EVT = 0x06;
+    BLE_EVT_DATA_LENGTH_CHANGE_EVT = 0x07;
+    BLE_EVT_READ_LOCAL_P256_PUB_KEY = 0x08; // Not currently used in system/bt
+    BLE_EVT_GEN_DHKEY_CMPL = 0x09; // Not currently used in system/bt
+    BLE_EVT_ENHANCED_CONN_COMPLETE_EVT = 0x0a;
+    BLE_EVT_DIRECT_ADV_EVT = 0x0b;
+    BLE_EVT_PHY_UPDATE_COMPLETE_EVT = 0x0c;
+    BLE_EVT_EXTENDED_ADVERTISING_REPORT_EVT = 0x0D;
+    BLE_EVT_PERIODIC_ADV_SYNC_EST_EVT = 0x0E;
+    BLE_EVT_PERIODIC_ADV_REPORT_EVT = 0x0F;
+    BLE_EVT_PERIODIC_ADV_SYNC_LOST_EVT = 0x10;
+    BLE_EVT_SCAN_TIMEOUT_EVT = 0x11;
+    BLE_EVT_ADVERTISING_SET_TERMINATED_EVT = 0x12;
+    BLE_EVT_SCAN_REQ_RX_EVT = 0x13;
+    BLE_EVT_CHNL_SELECTION_ALGORITHM = 0x14; // Not currently used in system/bt
+}
+
+// HCI status code from the Bluetooth 5.0 specification Vol 2, Part D.
+// Original definition: system/bt/stack/include/hcidefs.h
+enum StatusEnum {
+    // Status is at most 1 byte (0xFF), thus 0xFFF must not be a valid value
+    STATUS_UNKNOWN = 0xFFF;
+    STATUS_SUCCESS = 0x00;
+    STATUS_ILLEGAL_COMMAND = 0x01;
+    STATUS_NO_CONNECTION = 0x02;
+    STATUS_HW_FAILURE = 0x03;
+    STATUS_PAGE_TIMEOUT = 0x04;
+    STATUS_AUTH_FAILURE = 0x05;
+    STATUS_KEY_MISSING = 0x06;
+    STATUS_MEMORY_FULL = 0x07;
+    STATUS_CONNECTION_TOUT = 0x08;
+    STATUS_MAX_NUM_OF_CONNECTIONS = 0x09;
+    STATUS_MAX_NUM_OF_SCOS = 0x0A;
+    STATUS_CONNECTION_EXISTS = 0x0B;
+    STATUS_COMMAND_DISALLOWED = 0x0C;
+    STATUS_HOST_REJECT_RESOURCES = 0x0D;
+    STATUS_HOST_REJECT_SECURITY = 0x0E;
+    STATUS_HOST_REJECT_DEVICE = 0x0F;
+    STATUS_HOST_TIMEOUT = 0x10;
+    STATUS_UNSUPPORTED_VALUE = 0x11;
+    STATUS_ILLEGAL_PARAMETER_FMT = 0x12;
+    STATUS_PEER_USER = 0x13;
+    STATUS_PEER_LOW_RESOURCES = 0x14;
+    STATUS_PEER_POWER_OFF = 0x15;
+    STATUS_CONN_CAUSE_LOCAL_HOST = 0x16;
+    STATUS_REPEATED_ATTEMPTS = 0x17;
+    STATUS_PAIRING_NOT_ALLOWED = 0x18;
+    STATUS_UNKNOWN_LMP_PDU = 0x19;
+    STATUS_UNSUPPORTED_REM_FEATURE = 0x1A;
+    STATUS_SCO_OFFSET_REJECTED = 0x1B;
+    STATUS_SCO_INTERVAL_REJECTED = 0x1C;
+    STATUS_SCO_AIR_MODE = 0x1D;
+    STATUS_INVALID_LMP_PARAM = 0x1E;
+    STATUS_UNSPECIFIED = 0x1F;
+    STATUS_UNSUPPORTED_LMP_FEATURE = 0x20;
+    STATUS_ROLE_CHANGE_NOT_ALLOWED = 0x21;
+    STATUS_LMP_RESPONSE_TIMEOUT = 0x22;
+    STATUS_LMP_STATUS_TRANS_COLLISION = 0x23;
+    STATUS_LMP_PDU_NOT_ALLOWED = 0x24;
+    STATUS_ENCRY_MODE_NOT_ACCEPTABLE = 0x25;
+    STATUS_UNIT_KEY_USED = 0x26;
+    STATUS_QOS_NOT_SUPPORTED = 0x27;
+    STATUS_INSTANT_PASSED = 0x28;
+    STATUS_PAIRING_WITH_UNIT_KEY_NOT_SUPPORTED = 0x29;
+    STATUS_DIFF_TRANSACTION_COLLISION = 0x2A;
+    STATUS_UNDEFINED_0x2B = 0x2B; // Not used
+    STATUS_QOS_UNACCEPTABLE_PARAM = 0x2C;
+    STATUS_QOS_REJECTED = 0x2D;
+    STATUS_CHAN_CLASSIF_NOT_SUPPORTED = 0x2E;
+    STATUS_INSUFFCIENT_SECURITY = 0x2F;
+    STATUS_PARAM_OUT_OF_RANGE = 0x30;
+    STATUS_UNDEFINED_0x31 = 0x31; // Not used
+    STATUS_ROLE_SWITCH_PENDING = 0x32;
+    STATUS_UNDEFINED_0x33 = 0x33;
+    STATUS_RESERVED_SLOT_VIOLATION = 0x34;
+    STATUS_ROLE_SWITCH_FAILED = 0x35;
+    STATUS_INQ_RSP_DATA_TOO_LARGE = 0x36;
+    STATUS_SIMPLE_PAIRING_NOT_SUPPORTED = 0x37;
+    STATUS_HOST_BUSY_PAIRING = 0x38;
+    STATUS_REJ_NO_SUITABLE_CHANNEL = 0x39;
+    STATUS_CONTROLLER_BUSY = 0x3A;
+    STATUS_UNACCEPT_CONN_INTERVAL = 0x3B;
+    STATUS_ADVERTISING_TIMEOUT = 0x3C;
+    STATUS_CONN_TOUT_DUE_TO_MIC_FAILURE = 0x3D;
+    STATUS_CONN_FAILED_ESTABLISHMENT = 0x3E;
+    STATUS_MAC_CONNECTION_FAILED = 0x3F;
+    STATUS_LT_ADDR_ALREADY_IN_USE = 0x40;
+    STATUS_LT_ADDR_NOT_ALLOCATED = 0x41;
+    STATUS_CLB_NOT_ENABLED = 0x42;
+    STATUS_CLB_DATA_TOO_BIG = 0x43;
+    STATUS_OPERATION_CANCELED_BY_HOST = 0x44; // Not currently used in system/bt
+}
diff --git a/core/proto/android/bluetooth/hfp/enums.proto b/core/proto/android/bluetooth/hfp/enums.proto
new file mode 100644
index 0000000..d286e4b
--- /dev/null
+++ b/core/proto/android/bluetooth/hfp/enums.proto
@@ -0,0 +1,28 @@
+/*
+ * 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.
+ */
+
+syntax = "proto2";
+package android.bluetooth.hfp;
+
+option java_outer_classname = "BluetoothHfpProtoEnums";
+option java_multiple_files = true;
+
+enum ScoCodec {
+    SCO_CODEC_UNKNOWN = 0;
+    SCO_CODEC_CVSD = 1;
+    // Default codec behind Wide Band Speech
+    SCO_CODEC_MSBC = 2;
+}
\ No newline at end of file
diff --git a/core/proto/android/providers/settings/global.proto b/core/proto/android/providers/settings/global.proto
index cc5aa20..4158577 100644
--- a/core/proto/android/providers/settings/global.proto
+++ b/core/proto/android/providers/settings/global.proto
@@ -436,12 +436,19 @@
         // Ordered GPU debug layer list for GLES
         // i.e. <layer1>:<layer2>:...:<layerN>
         optional SettingProto debug_layers_gles = 7;
-        // GUP - List of Apps selected to use Game Update Packages
-        optional SettingProto gup_dev_opt_in_apps = 8;
-        // GUP - List of Apps selected not to use Game Update Packages
-        optional SettingProto gup_dev_opt_out_apps = 9;
-        // GUP - List of Apps that are forbidden to use Game Update Packages
-        optional SettingProto gup_black_list = 10;
+        // GUP - Game Update Package global preference for all Apps
+        // 0 = Default
+        // 1 = All Apps use Game Update Package
+        // 2 = All Apps use system graphics driver
+        optional SettingProto gup_dev_all_apps = 8;
+        // GUP - List of Apps selected to use Game Update Package
+        // i.e. <pkg1>,<pkg2>,...,<pkgN>
+        optional SettingProto gup_dev_opt_in_apps = 9;
+        // GUP - List of Apps selected not to use Game Update Package
+        // i.e. <pkg1>,<pkg2>,...,<pkgN>
+        optional SettingProto gup_dev_opt_out_apps = 10;
+        // GUP - List of Apps that are forbidden to use Game Update Package
+        optional SettingProto gup_blacklist = 11;
     }
     optional Gpu gpu = 59;
 
diff --git a/core/proto/android/providers/settings/secure.proto b/core/proto/android/providers/settings/secure.proto
index 0e052fe..4bfd4d2 100644
--- a/core/proto/android/providers/settings/secure.proto
+++ b/core/proto/android/providers/settings/secure.proto
@@ -182,6 +182,7 @@
         optional SettingProto pulse_on_pick_up = 3 [ (android.privacy).dest = DEST_AUTOMATIC ];
         optional SettingProto pulse_on_long_press = 4 [ (android.privacy).dest = DEST_AUTOMATIC ];
         optional SettingProto pulse_on_double_tap = 5 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto pulse_on_tap = 6 [ (android.privacy).dest = DEST_AUTOMATIC ];
     }
     optional Doze doze = 21;
 
@@ -525,7 +526,10 @@
     }
     optional Zen zen = 71;
 
+    optional SettingProto skip_gesture_enabled = 74 [ (android.privacy).dest = DEST_AUTOMATIC ];
+    optional SettingProto silence_gesture_enabled = 75 [ (android.privacy).dest = DEST_AUTOMATIC ];
+
     // Please insert fields in alphabetical order and group them into messages
     // if possible (to avoid reaching the method limit).
-    // Next tag = 74;
+    // Next tag = 76;
 }
diff --git a/core/proto/android/server/connectivity/data_stall_event.proto b/core/proto/android/server/connectivity/data_stall_event.proto
new file mode 100644
index 0000000..b70bb67
--- /dev/null
+++ b/core/proto/android/server/connectivity/data_stall_event.proto
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+syntax = "proto2";
+
+package com.android.server.connectivity;
+option java_multiple_files = true;
+option java_outer_classname = "DataStallEventProto";
+
+enum ProbeResult {
+    UNKNOWN = 0;
+    VALID = 1;
+    INVALID = 2;
+    PORTAL = 3;
+}
+
+enum ApBand {
+    AP_BAND_UNKNOWN = 0;
+    AP_BAND_2GHZ = 1;
+    AP_BAND_5GHZ = 2;
+}
+
+// Refer to definition in ServiceState.java.
+enum RadioTech {
+  RADIO_TECHNOLOGY_UNKNOWN = 0;
+  RADIO_TECHNOLOGY_GPRS = 1;
+  RADIO_TECHNOLOGY_EDGE = 2;
+  RADIO_TECHNOLOGY_UMTS = 3;
+  RADIO_TECHNOLOGY_IS95A = 4;
+  RADIO_TECHNOLOGY_IS95B = 5;
+  RADIO_TECHNOLOGY_1xRTT = 6;
+  RADIO_TECHNOLOGY_EVDO_0 = 7;
+  RADIO_TECHNOLOGY_EVDO_A = 8;
+  RADIO_TECHNOLOGY_HSDPA = 9;
+  RADIO_TECHNOLOGY_HSUPA = 10;
+  RADIO_TECHNOLOGY_HSPA = 11;
+  RADIO_TECHNOLOGY_EVDO_B = 12;
+  RADIO_TECHNOLOGY_EHRPD = 13;
+  RADIO_TECHNOLOGY_LTE = 14;
+  RADIO_TECHNOLOGY_HSPAP = 15;
+  RADIO_TECHNOLOGY_GSM = 16;
+  RADIO_TECHNOLOGY_TD_SCDMA = 17;
+  RADIO_TECHNOLOGY_IWLAN = 18;
+  RADIO_TECHNOLOGY_LTE_CA = 19;
+  RADIO_TECHNOLOGY_NR = 20;
+}
+
+// Cellular specific information.
+message CellularData {
+    // Indicate the radio technology at the time of data stall suspected.
+    optional RadioTech rat_type = 1;
+    // True if device is in roaming network at the time of data stall suspected.
+    optional bool is_roaming = 2;
+    // Registered network MccMnc when data stall happen
+    optional string network_mccmnc = 3;
+    // Indicate the SIM card carrier.
+    optional string sim_mccmnc = 4;
+    // Signal strength level at the time of data stall suspected.
+    optional int32 signal_strength = 5;
+}
+
+// Wifi specific information.
+message WifiData {
+    // Signal strength at the time of data stall suspected.
+    // RSSI range is between -55 to -110.
+    optional int32 signal_strength = 1;
+    // AP band.
+    optional ApBand wifi_band = 2;
+}
+
+message DnsEvent {
+    // The dns return code.
+    repeated int32 dns_return_code = 1;
+    // Indicate the timestamp of the dns event.
+    repeated int64 dns_time = 2;
+}
\ No newline at end of file
diff --git a/core/proto/android/server/jobscheduler.proto b/core/proto/android/server/jobscheduler.proto
index 0ec8c1a..7f3ea7a 100644
--- a/core/proto/android/server/jobscheduler.proto
+++ b/core/proto/android/server/jobscheduler.proto
@@ -403,18 +403,23 @@
         optional bool is_charging = 1;
         optional bool is_in_parole = 2;
 
+        // List of UIDs currently in the foreground.
+        repeated int32 foreground_uids = 3;
+
         message TrackedJob {
             option (.android.msg_privacy).dest = DEST_AUTOMATIC;
 
             optional JobStatusShortInfoProto info = 1;
             optional int32 source_uid = 2;
             optional JobStatusDumpProto.Bucket effective_standby_bucket = 3;
-            optional bool has_quota = 4;
+            // If the job started while the app was in the TOP state.
+            optional bool is_top_started_job = 4;
+            optional bool has_quota = 5;
             // The amount of time that this job has remaining in its quota. This
             // can be negative if the job is out of quota.
-            optional int64 remaining_quota_ms = 5;
+            optional int64 remaining_quota_ms = 6;
         }
-        repeated TrackedJob tracked_jobs = 3;
+        repeated TrackedJob tracked_jobs = 4;
 
         message Package {
             option (.android.msg_privacy).dest = DEST_AUTOMATIC;
@@ -456,7 +461,7 @@
 
             repeated TimingSession saved_sessions = 3;
         }
-        repeated PackageStats package_stats = 4;
+        repeated PackageStats package_stats = 5;
     }
     message StorageController {
         option (.android.msg_privacy).dest = DEST_AUTOMATIC;
diff --git a/core/proto/android/service/package.proto b/core/proto/android/service/package.proto
index 4ecf52c..7f96d70 100644
--- a/core/proto/android/service/package.proto
+++ b/core/proto/android/service/package.proto
@@ -111,6 +111,7 @@
         optional EnabledState enabled_state = 7;
         optional string last_disabled_app_caller = 8;
         optional string suspending_package = 9;
+        optional int32 distraction_flags = 10;
     }
 
     // Name of package. e.g. "com.android.providers.telephony".
diff --git a/core/proto/android/service/procstats.proto b/core/proto/android/service/procstats.proto
index 71ebcc1..da801ff 100644
--- a/core/proto/android/service/procstats.proto
+++ b/core/proto/android/service/procstats.proto
@@ -43,7 +43,7 @@
  * Data model from /frameworks/base/core/java/com/android/internal/app/procstats/ProcessStats.java
  * This proto is defined based on the writeToParcel method.
  *
- * Next Tag: 10
+ * Next Tag: 11
  */
 message ProcessStatsSectionProto {
     option (android.msg_privacy).dest = DEST_AUTOMATIC;
@@ -76,6 +76,9 @@
     }
     repeated Status status = 7;
 
+    // Number of pages available of various types and sizes, representation fragmentation.
+    repeated ProcessStatsAvailablePagesProto available_pages = 10;
+
     // Stats for each process.
     repeated ProcessStatsProto process_stats = 8;
 
@@ -83,6 +86,24 @@
     repeated ProcessStatsPackageProto package_stats = 9;
 }
 
+// Next Tag: 5
+message ProcessStatsAvailablePagesProto {
+    option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+    // Node these pages are in (as per /proc/pagetypeinfo)
+    optional int32 node = 1;
+
+    // Zone these pages are in (as per /proc/pagetypeinfo)
+    optional string zone = 2;
+
+    // Label for the type of these pages (as per /proc/pagetypeinfo)
+    optional string label = 3;
+
+    // Distribution of number of pages available by order size.  First entry in array is
+    // order 0, second is order 1, etc.  Each order increase is a doubling of page size.
+    repeated int32 pages_per_order = 4;
+}
+
 // Next Tag: 10
 message ProcessStatsStateProto {
     option (android.msg_privacy).dest = DEST_AUTOMATIC;
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 8b85a28..7813128 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -51,6 +51,7 @@
     <protected-broadcast android:name="android.intent.action.PACKAGE_VERIFIED" />
     <protected-broadcast android:name="android.intent.action.PACKAGES_SUSPENDED" />
     <protected-broadcast android:name="android.intent.action.PACKAGES_UNSUSPENDED" />
+    <protected-broadcast android:name="android.intent.action.DISTRACTING_PACKAGES_CHANGED" />
     <protected-broadcast android:name="android.intent.action.ACTION_PREFERRED_ACTIVITY_CHANGED" />
     <protected-broadcast android:name="android.intent.action.UID_REMOVED" />
     <protected-broadcast android:name="android.intent.action.QUERY_PACKAGE_RESTART" />
@@ -2268,6 +2269,11 @@
     <permission android:name="android.permission.START_ANY_ACTIVITY"
         android:protectionLevel="signature" />
 
+    <!-- Allows an application to start activities from background
+         @hide -->
+    <permission android:name="android.permission.START_ACTIVITIES_FROM_BACKGROUND"
+        android:protectionLevel="signature|privileged|vendorPrivileged|oem" />
+
     <!-- @SystemApi Must be required by activities that handle the intent action
          {@link Intent#ACTION_SEND_SHOW_SUSPENDED_APP_DETAILS}. This is for use by apps that
          hold {@link Manifest.permission#SUSPEND_APPS} to interact with the system.
@@ -3190,9 +3196,10 @@
 
     <!-- @SystemApi Required to add or remove another application as a device admin.
          <p>Not for use by third-party applications.
-         @hide -->
+         @hide
+         @removed -->
     <permission android:name="android.permission.MANAGE_DEVICE_ADMINS"
-        android:protectionLevel="signature|privileged" />
+        android:protectionLevel="signature" />
 
     <!-- @SystemApi Allows an app to reset the device password.
          <p>Not for use by third-party applications.
@@ -3514,19 +3521,25 @@
     <permission android:name="android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS"
         android:protectionLevel="signature|privileged" />
 
+    <!-- @SystemApi Allows an application to provide remote displays.
+         <p>Not for use by third-party applications.</p>
+         @hide -->
+    <permission android:name="android.permission.REMOTE_DISPLAY_PROVIDER"
+        android:protectionLevel="signature|privileged" />
+
     <!-- Allows an application to capture video output.
          <p>Not for use by third-party applications.</p>
           @hide
           @removed -->
     <permission android:name="android.permission.CAPTURE_VIDEO_OUTPUT"
-        android:protectionLevel="signature|privileged" />
+        android:protectionLevel="signature" />
 
     <!-- Allows an application to capture secure video output.
          <p>Not for use by third-party applications.</p>
           @hide
           @removed -->
     <permission android:name="android.permission.CAPTURE_SECURE_VIDEO_OUTPUT"
-        android:protectionLevel="signature|privileged" />
+        android:protectionLevel="signature" />
 
     <!-- Allows an application to know what content is playing and control its playback.
          <p>Not for use by third-party applications due to privacy of media consumption</p>  -->
@@ -3861,7 +3874,7 @@
 
     <!-- @SystemApi @hide Allows managing apk level rollbacks. -->
     <permission android:name="android.permission.MANAGE_ROLLBACKS"
-        android:protectionLevel="signature|installer" />
+        android:protectionLevel="signature|verifier" />
 
     <!-- @SystemApi @hide Allows an application to mark other applications as harmful -->
     <permission android:name="android.permission.SET_HARMFUL_APP_WARNINGS"
@@ -3906,7 +3919,8 @@
     <permission android:name="android.permission.ACCESS_NOTIFICATIONS"
         android:protectionLevel="signature|privileged|appop" />
 
-    <!-- Marker permission for applications that wish to access notification policy.
+    <!-- Marker permission for applications that wish to access notification policy. This permission
+        is not supported on managed profiles.
          <p>Protection level: normal
     -->
     <permission android:name="android.permission.ACCESS_NOTIFICATION_POLICY"
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 54f6c63..18a42bc 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -593,7 +593,7 @@
          to be set for all windows of this activity -->
     <attr name="showForAllUsers" format="boolean" />
 
-    <!-- Specifies whether an {@link android.app.Activity} should be shown on top of the the lock screen
+    <!-- Specifies whether an {@link android.app.Activity} should be shown on top of the lock screen
          whenever the lockscreen is up and the activity is resumed. Normally an activity will be
          transitioned to the stopped state if it is started while the lockscreen is up, but with
          this flag set the activity will remain in the resumed state visible on-top of the lock
@@ -2412,6 +2412,19 @@
         <attr name="showForAllUsers" />
 
         <attr name="showWhenLocked" />
+        <!-- @hide @SystemApi Specifies whether this {@link android.app.Activity} should be shown on
+             top of the lock screen whenever the lockscreen is up and this activity has another
+             activity behind it with the {@link android.R.attr#showWhenLocked} attribute set. That
+             is, this activity is only visible on the lock screen if there is another activity with
+             the {@link android.R.attr#showWhenLocked} attribute visible at the same time on the
+             lock screen. A use case for this is permission dialogs, that should only be visible on
+             the lock screen if their requesting activity is also visible.
+
+         The default value of this attribute is <code>false</code>. -->
+    <attr name="inheritShowWhenLocked" format="boolean" />
+
+
+        <attr name="inheritShowWhenLocked" />
         <attr name="turnScreenOn" />
 
         <attr name="directBootAware" />
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 1feb59a..9a1360b 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -929,9 +929,6 @@
          in hardware. -->
     <bool name="config_setColorTransformAccelerated">false</bool>
 
-    <!-- Boolean indicating whether display white balance is supported. -->
-    <bool name="config_displayWhiteBalanceAvailable">false</bool>
-
     <!-- Control whether Night display is available. This should only be enabled on devices
          that have a HWC implementation that can apply the matrix passed to setColorTransform
          without impacting power, performance, and app compatibility (e.g. protected content). -->
@@ -987,6 +984,44 @@
         <!-- B y-intercept --> <item>-0.198650895</item>
     </string-array>
 
+    <!-- Boolean indicating whether display white balance is supported. -->
+    <bool name="config_displayWhiteBalanceAvailable">false</bool>
+
+    <!-- Minimum color temperature, in Kelvin, supported by display white balance. -->
+    <integer name="config_displayWhiteBalanceColorTemperatureMin">4000</integer>
+
+    <!-- Maximum color temperature, in Kelvin, supported by display white balance. -->
+    <integer name="config_displayWhiteBalanceColorTemperatureMax">8000</integer>
+
+    <!-- Default color temperature, in Kelvin, used by display white balance. -->
+    <integer name="config_displayWhiteBalanceColorTemperatureDefault">6500</integer>
+
+    <!-- The display primaries, in CIE1931 XYZ color space, for display
+         white balance to use in its calculations. -->
+    <string-array name="config_displayWhiteBalanceDisplayPrimaries">
+        <!-- Red X -->   <item>0.412315</item>
+        <!-- Red Y -->   <item>0.212600</item>
+        <!-- Red Z -->   <item>0.019327</item>
+        <!-- Green X --> <item>0.357600</item>
+        <!-- Green Y --> <item>0.715200</item>
+        <!-- Green Z --> <item>0.119200</item>
+        <!-- Blue X -->  <item>0.180500</item>
+        <!-- Blue Y -->  <item>0.072200</item>
+        <!-- Blue Z -->  <item>0.950633</item>
+        <!-- White X --> <item>0.950456</item>
+        <!-- White Y --> <item>1.000000</item>
+        <!-- White Z --> <item>1.089058</item>
+    </string-array>
+
+    <!-- The nominal white coordinates, in CIE1931 XYZ color space, for Display White Balance to
+         use in its calculations. AWB will adapt this white point to the target ambient white
+         point. -->
+    <string-array name="config_displayWhiteBalanceDisplayNominalWhite">
+        <!-- Nominal White X --> <item>0.950456</item>
+        <!-- Nominal White Y --> <item>1.000000</item>
+        <!-- Nominal White Z --> <item>1.089058</item>
+    </string-array>
+
 
     <!-- Indicate available ColorDisplayController.COLOR_MODE_xxx. -->
     <integer-array name="config_availableColorModes">
@@ -1654,6 +1689,8 @@
          config_enableFusedLocationOverlay is false. -->
     <string name="config_fusedLocationProviderPackageName" translatable="false">com.android.location.fused</string>
 
+    <string-array name="config_locationExtraPackageNames" translatable="false"></string-array>
+
     <!-- The package name of the default network recommendation app.
          A network recommendation provider must:
              * Be granted the SCORE_NETWORKS permission.
@@ -2168,6 +2205,9 @@
     <!-- Type of the double tap sensor. Empty if double tap is not supported. -->
     <string name="config_dozeDoubleTapSensorType" translatable="false"></string>
 
+    <!-- Type of the tap sensor. Empty if tap is not supported. -->
+    <string name="config_dozeTapSensorType" translatable="false"></string>
+
     <!-- Type of the long press sensor. Empty if long press is not supported. -->
     <string name="config_dozeLongPressSensorType" translatable="false"></string>
 
@@ -3661,7 +3701,7 @@
     <string name="config_inputEventCompatProcessorOverrideClassName" translatable="false"></string>
 
     <!-- Component name for the default module metadata provider on this device -->
-    <string name="config_defaultModuleMetadataProvider">com.android.modulemetadata</string>
+    <string name="config_defaultModuleMetadataProvider" translatable="false">com.android.modulemetadata</string>
 
     <!-- This is the default launcher component to use on secondary displays that support system
          decorations.
@@ -3673,4 +3713,10 @@
     <!-- If device supports corner radius on windows.
          This should be turned off on low-end devices to improve animation performance. -->
     <bool name="config_supportsRoundedCornersOnWindows">true</bool>
+
+    <!-- If the sensor that skips media is available or not. -->
+    <bool name="config_skipSensorAvailable">false</bool>
+
+    <!-- If the sensor that silences alerts is available or not. -->
+    <bool name="config_silenceSensorAvailable">false</bool>
 </resources>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 799d9d8..b3b30e9 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2934,6 +2934,8 @@
         <public name="foregroundServiceType" />
         <public name="hasFragileUserData" />
         <public name="minAspectRatio" />
+        <!-- @hide @SystemApi -->
+        <public name="inheritShowWhenLocked" />
     </public-group>
 
     <public-group type="drawable" first-id="0x010800b4">
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 010accf..cd1ee2b 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1840,6 +1840,7 @@
   <java-symbol type="array" name="radioAttributes" />
   <java-symbol type="array" name="config_oemUsbModeOverride" />
   <java-symbol type="array" name="config_locationProviderPackageNames" />
+  <java-symbol type="array" name="config_locationExtraPackageNames" />
   <java-symbol type="array" name="config_testLocationProviders" />
   <java-symbol type="array" name="config_defaultNotificationVibePattern" />
   <java-symbol type="array" name="config_notificationFallbackVibePattern" />
@@ -3001,6 +3002,7 @@
   <java-symbol type="array" name="config_emergency_mcc_codes" />
 
   <java-symbol type="string" name="config_dozeDoubleTapSensorType" />
+  <java-symbol type="string" name="config_dozeTapSensorType" />
   <java-symbol type="bool" name="config_dozePulsePickup" />
 
   <!-- Used for MimeIconUtils. -->
@@ -3042,6 +3044,13 @@
   <java-symbol type="array" name="config_nightDisplayColorTemperatureCoefficientsNative" />
   <java-symbol type="array" name="config_availableColorModes" />
 
+  <java-symbol type="bool" name="config_displayWhiteBalanceAvailable" />
+  <java-symbol type="integer" name="config_displayWhiteBalanceColorTemperatureMin" />
+  <java-symbol type="integer" name="config_displayWhiteBalanceColorTemperatureMax" />
+  <java-symbol type="integer" name="config_displayWhiteBalanceColorTemperatureDefault" />
+  <java-symbol type="array" name="config_displayWhiteBalanceDisplayPrimaries" />
+  <java-symbol type="array" name="config_displayWhiteBalanceDisplayNominalWhite" />
+
   <!-- Default first user restrictions -->
   <java-symbol type="array" name="config_defaultFirstUserRestrictions" />
 
@@ -3529,4 +3538,7 @@
   <java-symbol type="string" name="dynamic_mode_notification_title" />
   <java-symbol type="string" name="dynamic_mode_notification_summary" />
   <java-symbol type="drawable" name="ic_battery" />
+
+  <java-symbol type="bool" name="config_skipSensorAvailable" />
+  <java-symbol type="bool" name="config_silenceSensorAvailable" />
 </resources>
diff --git a/core/tests/coretests/Android.mk b/core/tests/coretests/Android.mk
index 74943c7..0fc3bd2 100644
--- a/core/tests/coretests/Android.mk
+++ b/core/tests/coretests/Android.mk
@@ -36,12 +36,12 @@
     frameworks-core-util-lib \
     mockwebserver \
     guava \
-    android-support-test \
+    androidx.test.runner \
+    androidx.test.rules \
     mockito-target-minus-junit4 \
     espresso-core \
     ub-uiautomator \
     platform-test-annotations \
-    compatibility-device-util \
     truth-prebuilt \
     print-test-util-lib \
     testng # TODO: remove once Android migrates to JUnit 4.12, which provide assertThrows
diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml
index 46d4a47..e80cb6d 100644
--- a/core/tests/coretests/AndroidManifest.xml
+++ b/core/tests/coretests/AndroidManifest.xml
@@ -1425,7 +1425,7 @@
 
     </application>
 
-    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+    <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
             android:targetPackage="com.android.frameworks.coretests"
             android:label="Frameworks Core Tests" />
     <key-sets>
diff --git a/core/tests/coretests/AndroidTest.xml b/core/tests/coretests/AndroidTest.xml
index 68ef34b..b40aa87 100644
--- a/core/tests/coretests/AndroidTest.xml
+++ b/core/tests/coretests/AndroidTest.xml
@@ -25,6 +25,7 @@
     <option name="test-tag" value="FrameworksCoreTests" />
     <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
         <option name="package" value="com.android.frameworks.coretests" />
+        <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
         <option name="hidden-api-checks" value="false"/>
     </test>
 </configuration>
diff --git a/core/tests/coretests/src/android/animation/AnimatorInflaterTest.java b/core/tests/coretests/src/android/animation/AnimatorInflaterTest.java
index e26bdf5..be1d44c 100644
--- a/core/tests/coretests/src/android/animation/AnimatorInflaterTest.java
+++ b/core/tests/coretests/src/android/animation/AnimatorInflaterTest.java
@@ -13,17 +13,17 @@
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
+
 package android.animation;
 
-import android.support.test.filters.LargeTest;
 import android.test.ActivityInstrumentationTestCase2;
 
+import androidx.test.filters.LargeTest;
+
+import com.android.frameworks.coretests.R;
+
 import java.util.HashSet;
 import java.util.Set;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-
-import com.android.frameworks.coretests.R;
 
 @LargeTest
 public class AnimatorInflaterTest extends ActivityInstrumentationTestCase2<BasicAnimatorActivity>  {
diff --git a/core/tests/coretests/src/android/animation/AnimatorSetActivity.java b/core/tests/coretests/src/android/animation/AnimatorSetActivity.java
index 501ea48..af265af 100644
--- a/core/tests/coretests/src/android/animation/AnimatorSetActivity.java
+++ b/core/tests/coretests/src/android/animation/AnimatorSetActivity.java
@@ -1,10 +1,26 @@
-package android.animation;
+/*
+ * Copyright (C) 2015 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.
+ */
 
-import com.android.frameworks.coretests.R;
+package android.animation;
 
 import android.app.Activity;
 import android.os.Bundle;
 
+import com.android.frameworks.coretests.R;
+
 public class AnimatorSetActivity extends Activity {
     @Override
     public void onCreate(Bundle savedBundleInstance) {
diff --git a/core/tests/coretests/src/android/animation/AnimatorSetActivityTest.java b/core/tests/coretests/src/android/animation/AnimatorSetActivityTest.java
index 922bc59..55837ba 100644
--- a/core/tests/coretests/src/android/animation/AnimatorSetActivityTest.java
+++ b/core/tests/coretests/src/android/animation/AnimatorSetActivityTest.java
@@ -1,12 +1,29 @@
+/*
+ * Copyright (C) 2015 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.animation;
 
-import com.android.frameworks.coretests.R;
-
 import android.test.ActivityInstrumentationTestCase2;
-import android.test.UiThreadTest;
-import android.test.suitebuilder.annotation.SmallTest;
 import android.view.View;
 
+import androidx.test.annotation.UiThreadTest;
+import androidx.test.filters.SmallTest;
+
+import com.android.frameworks.coretests.R;
+
 import java.util.ArrayList;
 
 public class AnimatorSetActivityTest extends ActivityInstrumentationTestCase2<AnimatorSetActivity> {
diff --git a/core/tests/coretests/src/android/animation/AnimatorSetEventsTest.java b/core/tests/coretests/src/android/animation/AnimatorSetEventsTest.java
index 7eb32ee..4e90d1a 100644
--- a/core/tests/coretests/src/android/animation/AnimatorSetEventsTest.java
+++ b/core/tests/coretests/src/android/animation/AnimatorSetEventsTest.java
@@ -13,12 +13,14 @@
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
+
 package android.animation;
 
 import android.os.Handler;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.test.suitebuilder.annotation.SmallTest;
 import android.widget.Button;
+
+import androidx.test.filters.MediumTest;
+
 import com.android.frameworks.coretests.R;
 
 import java.util.concurrent.TimeUnit;
diff --git a/core/tests/coretests/src/android/animation/AutoCancelTest.java b/core/tests/coretests/src/android/animation/AutoCancelTest.java
index b1f88db..b3ec92c 100644
--- a/core/tests/coretests/src/android/animation/AutoCancelTest.java
+++ b/core/tests/coretests/src/android/animation/AutoCancelTest.java
@@ -13,11 +13,13 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package android.animation;
 
 import android.os.Handler;
 import android.test.ActivityInstrumentationTestCase2;
-import android.test.suitebuilder.annotation.SmallTest;
+
+import androidx.test.filters.SmallTest;
 
 import java.util.HashMap;
 import java.util.concurrent.TimeUnit;
diff --git a/core/tests/coretests/src/android/animation/BasicAnimatorActivity.java b/core/tests/coretests/src/android/animation/BasicAnimatorActivity.java
index 0e1e6ac..2b9866d 100644
--- a/core/tests/coretests/src/android/animation/BasicAnimatorActivity.java
+++ b/core/tests/coretests/src/android/animation/BasicAnimatorActivity.java
@@ -13,14 +13,15 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package android.animation;
 
-import com.android.frameworks.coretests.R;
+package android.animation;
 
 import android.app.Activity;
 import android.os.Bundle;
 import android.widget.Button;
 
+import com.android.frameworks.coretests.R;
+
 public class BasicAnimatorActivity extends Activity {
     public Button mAnimatingButton;
     @Override
diff --git a/core/tests/coretests/src/android/animation/EventsTest.java b/core/tests/coretests/src/android/animation/EventsTest.java
index 28cfe3d..ba7413a 100644
--- a/core/tests/coretests/src/android/animation/EventsTest.java
+++ b/core/tests/coretests/src/android/animation/EventsTest.java
@@ -13,13 +13,15 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package android.animation;
 
 import android.os.Handler;
 import android.test.ActivityInstrumentationTestCase2;
-import android.test.UiThreadTest;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.test.suitebuilder.annotation.SmallTest;
+
+import androidx.test.annotation.UiThreadTest;
+import androidx.test.filters.MediumTest;
+import androidx.test.filters.SmallTest;
 
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeoutException;
diff --git a/core/tests/coretests/src/android/animation/FutureWaiter.java b/core/tests/coretests/src/android/animation/FutureWaiter.java
index 0c65e20..0c09a4a 100644
--- a/core/tests/coretests/src/android/animation/FutureWaiter.java
+++ b/core/tests/coretests/src/android/animation/FutureWaiter.java
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package android.animation;
 
 import com.google.common.util.concurrent.AbstractFuture;
diff --git a/core/tests/coretests/src/android/animation/ObjectAnimatorEventsTest.java b/core/tests/coretests/src/android/animation/ObjectAnimatorEventsTest.java
index 606a939..53f9472 100644
--- a/core/tests/coretests/src/android/animation/ObjectAnimatorEventsTest.java
+++ b/core/tests/coretests/src/android/animation/ObjectAnimatorEventsTest.java
@@ -13,9 +13,11 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package android.animation;
 
 import android.widget.Button;
+
 import com.android.frameworks.coretests.R;
 
 /**
diff --git a/core/tests/coretests/src/android/animation/StateListAnimatorTest.java b/core/tests/coretests/src/android/animation/StateListAnimatorTest.java
index a9961e1..e755b89 100644
--- a/core/tests/coretests/src/android/animation/StateListAnimatorTest.java
+++ b/core/tests/coretests/src/android/animation/StateListAnimatorTest.java
@@ -14,16 +14,16 @@
 * limitations under the License.
 */
 
-
 package android.animation;
 
-import android.support.test.filters.LargeTest;
 import android.test.ActivityInstrumentationTestCase2;
-import android.test.UiThreadTest;
 import android.util.StateSet;
 import android.view.View;
 import android.view.ViewGroup;
 
+import androidx.test.annotation.UiThreadTest;
+import androidx.test.filters.LargeTest;
+
 import com.android.frameworks.coretests.R;
 
 import java.util.concurrent.atomic.AtomicInteger;
diff --git a/core/tests/coretests/src/android/animation/ValueAnimatorEventsTest.java b/core/tests/coretests/src/android/animation/ValueAnimatorEventsTest.java
index c25d050..f6d71b8 100644
--- a/core/tests/coretests/src/android/animation/ValueAnimatorEventsTest.java
+++ b/core/tests/coretests/src/android/animation/ValueAnimatorEventsTest.java
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package android.animation;
 
 /**
diff --git a/core/tests/coretests/src/android/animation/ValueAnimatorTests.java b/core/tests/coretests/src/android/animation/ValueAnimatorTests.java
index 4facf77..dee0a3e 100644
--- a/core/tests/coretests/src/android/animation/ValueAnimatorTests.java
+++ b/core/tests/coretests/src/android/animation/ValueAnimatorTests.java
@@ -13,8 +13,11 @@
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
+
 package android.animation;
 
+import static android.test.MoreAsserts.assertNotEqual;
+
 import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.assertFalse;
 import static junit.framework.Assert.assertNotNull;
@@ -24,23 +27,20 @@
 import android.os.Looper;
 import android.os.Message;
 import android.os.SystemClock;
-import android.support.test.annotation.UiThreadTest;
-import android.support.test.filters.MediumTest;
-import android.support.test.rule.ActivityTestRule;
-import android.support.test.runner.AndroidJUnit4;
 import android.view.Choreographer;
 import android.view.animation.LinearInterpolator;
 
+import androidx.test.filters.MediumTest;
+import androidx.test.rule.ActivityTestRule;
+import androidx.test.runner.AndroidJUnit4;
+
 import org.junit.After;
 import org.junit.Before;
-import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
 import java.util.ArrayList;
 
-import static android.test.MoreAsserts.assertNotEqual;
-
 @RunWith(AndroidJUnit4.class)
 @MediumTest
 public class ValueAnimatorTests {
diff --git a/core/tests/coretests/src/android/animation/ViewPropertyAnimatorTest.java b/core/tests/coretests/src/android/animation/ViewPropertyAnimatorTest.java
index 30ec182..997af00 100644
--- a/core/tests/coretests/src/android/animation/ViewPropertyAnimatorTest.java
+++ b/core/tests/coretests/src/android/animation/ViewPropertyAnimatorTest.java
@@ -13,15 +13,18 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package android.animation;
 
 import android.os.Handler;
 import android.test.ActivityInstrumentationTestCase2;
-import android.test.UiThreadTest;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.test.suitebuilder.annotation.SmallTest;
 import android.view.ViewPropertyAnimator;
 import android.widget.Button;
+
+import androidx.test.annotation.UiThreadTest;
+import androidx.test.filters.MediumTest;
+import androidx.test.filters.SmallTest;
+
 import com.android.frameworks.coretests.R;
 
 import java.util.concurrent.TimeUnit;
diff --git a/core/tests/coretests/src/android/app/ApplicationErrorReportTest.java b/core/tests/coretests/src/android/app/ApplicationErrorReportTest.java
index 19a390a..8dc5ad6 100644
--- a/core/tests/coretests/src/android/app/ApplicationErrorReportTest.java
+++ b/core/tests/coretests/src/android/app/ApplicationErrorReportTest.java
@@ -16,16 +16,17 @@
 
 package android.app;
 
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import android.app.ApplicationErrorReport.CrashInfo;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
-
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 
+import android.app.ApplicationErrorReport.CrashInfo;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
 @RunWith(AndroidJUnit4.class)
 @SmallTest
 public class ApplicationErrorReportTest {
diff --git a/core/tests/coretests/src/android/app/ApplicationPackageManagerTest.java b/core/tests/coretests/src/android/app/ApplicationPackageManagerTest.java
index 063bef7..4b0ed65 100644
--- a/core/tests/coretests/src/android/app/ApplicationPackageManagerTest.java
+++ b/core/tests/coretests/src/android/app/ApplicationPackageManagerTest.java
@@ -16,13 +16,17 @@
 
 package android.app;
 
+import static android.os.storage.VolumeInfo.STATE_MOUNTED;
+import static android.os.storage.VolumeInfo.STATE_UNMOUNTED;
+
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.IPackageManager;
 import android.content.pm.PackageInfo;
 import android.os.storage.StorageManager;
 import android.os.storage.VolumeInfo;
-import android.support.test.filters.LargeTest;
+
+import androidx.test.filters.LargeTest;
 
 import junit.framework.TestCase;
 
@@ -32,9 +36,6 @@
 import java.util.Arrays;
 import java.util.List;
 
-import static android.os.storage.VolumeInfo.STATE_MOUNTED;
-import static android.os.storage.VolumeInfo.STATE_UNMOUNTED;
-
 @LargeTest
 public class ApplicationPackageManagerTest extends TestCase {
     private static final String sInternalVolPath = "/data";
diff --git a/core/tests/coretests/src/android/app/DownloadManagerFunctionalTest.java b/core/tests/coretests/src/android/app/DownloadManagerFunctionalTest.java
index c1d4be0..33e0402 100644
--- a/core/tests/coretests/src/android/app/DownloadManagerFunctionalTest.java
+++ b/core/tests/coretests/src/android/app/DownloadManagerFunctionalTest.java
@@ -22,7 +22,8 @@
 import android.net.Uri;
 import android.os.Environment;
 import android.os.ParcelFileDescriptor;
-import android.test.suitebuilder.annotation.LargeTest;
+
+import androidx.test.filters.LargeTest;
 
 import com.google.mockwebserver.MockResponse;
 
diff --git a/core/tests/coretests/src/android/app/DownloadManagerStressTest.java b/core/tests/coretests/src/android/app/DownloadManagerStressTest.java
index 39d9a8e..adfe76f 100644
--- a/core/tests/coretests/src/android/app/DownloadManagerStressTest.java
+++ b/core/tests/coretests/src/android/app/DownloadManagerStressTest.java
@@ -23,10 +23,11 @@
 import android.os.Environment;
 import android.os.ParcelFileDescriptor;
 import android.os.StatFs;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.test.suitebuilder.annotation.Suppress;
 import android.util.Log;
 
+import androidx.test.filters.LargeTest;
+import androidx.test.filters.Suppress;
+
 import java.io.File;
 import java.io.FileOutputStream;
 import java.io.IOException;
diff --git a/core/tests/coretests/src/android/app/InstrumentationTest.java b/core/tests/coretests/src/android/app/InstrumentationTest.java
index 9b59da4..93b5aec 100644
--- a/core/tests/coretests/src/android/app/InstrumentationTest.java
+++ b/core/tests/coretests/src/android/app/InstrumentationTest.java
@@ -17,9 +17,10 @@
 package android.app;
 
 import android.os.Bundle;
-import android.support.test.filters.LargeTest;
 import android.test.InstrumentationTestCase;
 
+import androidx.test.filters.LargeTest;
+
 @LargeTest
 public class InstrumentationTest extends InstrumentationTestCase {
 
diff --git a/core/tests/coretests/src/android/app/LoaderLifecycleTest.java b/core/tests/coretests/src/android/app/LoaderLifecycleTest.java
index c83e798..e343383 100644
--- a/core/tests/coretests/src/android/app/LoaderLifecycleTest.java
+++ b/core/tests/coretests/src/android/app/LoaderLifecycleTest.java
@@ -14,7 +14,6 @@
  * limitations under the License.
  */
 
-
 package android.app;
 
 import static junit.framework.TestCase.assertNotNull;
@@ -27,11 +26,12 @@
 import android.content.Context;
 import android.os.Handler;
 import android.os.Parcelable;
-import android.support.test.filters.MediumTest;
-import android.support.test.rule.ActivityTestRule;
-import android.support.test.runner.AndroidJUnit4;
 import android.util.ArrayMap;
 
+import androidx.test.filters.MediumTest;
+import androidx.test.rule.ActivityTestRule;
+import androidx.test.runner.AndroidJUnit4;
+
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/app/NotificationTest.java b/core/tests/coretests/src/android/app/NotificationTest.java
index e89a4d3..c17aa92 100644
--- a/core/tests/coretests/src/android/app/NotificationTest.java
+++ b/core/tests/coretests/src/android/app/NotificationTest.java
@@ -33,11 +33,12 @@
 import android.os.Build;
 import android.os.Parcel;
 import android.os.Parcelable;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
 import android.widget.RemoteViews;
 
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/app/SearchManagerTest.java b/core/tests/coretests/src/android/app/SearchManagerTest.java
index 08b7f60..14370c8 100644
--- a/core/tests/coretests/src/android/app/SearchManagerTest.java
+++ b/core/tests/coretests/src/android/app/SearchManagerTest.java
@@ -17,17 +17,13 @@
 package android.app;
 
 import android.app.activity.LocalActivity;
-
-import android.app.Activity;
-import android.app.ISearchManager;
-import android.app.SearchManager;
-import android.app.SearchableInfo;
 import android.content.ComponentName;
 import android.content.Context;
 import android.os.ServiceManager;
 import android.test.ActivityInstrumentationTestCase2;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.test.suitebuilder.annotation.MediumTest;
+
+import androidx.test.filters.LargeTest;
+import androidx.test.filters.MediumTest;
 
 /**
  * To launch this test from the command line:
diff --git a/core/tests/coretests/src/android/app/activity/ActivityManagerTest.java b/core/tests/coretests/src/android/app/activity/ActivityManagerTest.java
index 61d73bc..bbd442d 100644
--- a/core/tests/coretests/src/android/app/activity/ActivityManagerTest.java
+++ b/core/tests/coretests/src/android/app/activity/ActivityManagerTest.java
@@ -21,8 +21,9 @@
 import android.content.pm.ConfigurationInfo;
 import android.content.res.Configuration;
 import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
-import android.test.suitebuilder.annotation.Suppress;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.filters.Suppress;
 
 import java.util.Iterator;
 import java.util.List;
diff --git a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
index 0f83a29..9cb3489 100644
--- a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
+++ b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
@@ -36,13 +36,14 @@
 import android.content.Intent;
 import android.content.res.Configuration;
 import android.os.IBinder;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.MediumTest;
-import android.support.test.rule.ActivityTestRule;
-import android.support.test.runner.AndroidJUnit4;
 import android.util.MergedConfiguration;
 import android.view.Display;
 
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.MediumTest;
+import androidx.test.rule.ActivityTestRule;
+import androidx.test.runner.AndroidJUnit4;
+
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
diff --git a/core/tests/coretests/src/android/app/activity/BroadcastTest.java b/core/tests/coretests/src/android/app/activity/BroadcastTest.java
index 13e70eb..0f81896 100644
--- a/core/tests/coretests/src/android/app/activity/BroadcastTest.java
+++ b/core/tests/coretests/src/android/app/activity/BroadcastTest.java
@@ -27,11 +27,10 @@
 import android.os.IBinder;
 import android.os.Parcel;
 import android.os.UserHandle;
-import android.support.test.filters.LargeTest;
-import android.test.FlakyTest;
 import android.util.Log;
 
-import java.util.Arrays;
+import androidx.test.filters.FlakyTest;
+import androidx.test.filters.LargeTest;
 
 @LargeTest
 public class BroadcastTest extends ActivityTestsBase {
@@ -231,7 +230,7 @@
     };
 
     // Mark flaky until http://b/issue?id=1191607 is resolved.
-    @FlakyTest(tolerance=2)
+    @FlakyTest
     public void testRegistered() throws Exception {
         runLaunchpad(LaunchpadActivity.BROADCAST_REGISTERED);
     }
@@ -248,12 +247,12 @@
         runLaunchpad(LaunchpadActivity.BROADCAST_ABORT);
     }
 
-    @FlakyTest(tolerance=2)
+    @FlakyTest
     public void testAll() throws Exception {
         runLaunchpad(LaunchpadActivity.BROADCAST_ALL);
     }
 
-    @FlakyTest(tolerance=2)
+    @FlakyTest
     public void testMulti() throws Exception {
         runLaunchpad(LaunchpadActivity.BROADCAST_MULTI);
     }
@@ -348,7 +347,7 @@
     }
 
     // Marking flaky until http://b/issue?id=1191337 is resolved
-    @FlakyTest(tolerance=2)
+    @FlakyTest
     public void testReceiveSticky() throws Exception {
         Intent intent = new Intent(LaunchpadActivity.BROADCAST_STICKY1, null);
         intent.putExtra("test", LaunchpadActivity.DATA_1);
@@ -358,7 +357,7 @@
     }
 
     // Marking flaky until http://b/issue?id=1191337 is resolved
-    @FlakyTest(tolerance=2)
+    @FlakyTest
     public void testReceive2Sticky() throws Exception {
         Intent intent = new Intent(LaunchpadActivity.BROADCAST_STICKY1, null);
         intent.putExtra("test", LaunchpadActivity.DATA_1);
diff --git a/core/tests/coretests/src/android/app/activity/IntentSenderTest.java b/core/tests/coretests/src/android/app/activity/IntentSenderTest.java
index 8c1d79b..19ddb52 100644
--- a/core/tests/coretests/src/android/app/activity/IntentSenderTest.java
+++ b/core/tests/coretests/src/android/app/activity/IntentSenderTest.java
@@ -21,7 +21,8 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.os.Bundle;
-import android.support.test.filters.LargeTest;
+
+import androidx.test.filters.LargeTest;
 
 @LargeTest
 public class IntentSenderTest extends BroadcastTest {
diff --git a/core/tests/coretests/src/android/app/activity/LaunchTest.java b/core/tests/coretests/src/android/app/activity/LaunchTest.java
index 5b86dce..6846ea7 100644
--- a/core/tests/coretests/src/android/app/activity/LaunchTest.java
+++ b/core/tests/coretests/src/android/app/activity/LaunchTest.java
@@ -17,8 +17,9 @@
 package android.app.activity;
 
 import android.content.ComponentName;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.test.suitebuilder.annotation.Suppress;
+
+import androidx.test.filters.LargeTest;
+import androidx.test.filters.Suppress;
 
 @Suppress  // Flaky.
 public class LaunchTest extends ActivityTestsBase {
diff --git a/core/tests/coretests/src/android/app/activity/LifecycleTest.java b/core/tests/coretests/src/android/app/activity/LifecycleTest.java
index ed01fac..5aa0380 100644
--- a/core/tests/coretests/src/android/app/activity/LifecycleTest.java
+++ b/core/tests/coretests/src/android/app/activity/LifecycleTest.java
@@ -18,8 +18,9 @@
 
 import android.content.ComponentName;
 import android.content.Intent;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.test.suitebuilder.annotation.Suppress;
+
+import androidx.test.filters.MediumTest;
+import androidx.test.filters.Suppress;
 
 public class LifecycleTest extends ActivityTestsBase {
     private Intent mTopIntent;
diff --git a/core/tests/coretests/src/android/app/activity/MetaDataTest.java b/core/tests/coretests/src/android/app/activity/MetaDataTest.java
index 5b9c0e9..cf27878 100644
--- a/core/tests/coretests/src/android/app/activity/MetaDataTest.java
+++ b/core/tests/coretests/src/android/app/activity/MetaDataTest.java
@@ -27,8 +27,11 @@
 import android.content.res.XmlResourceParser;
 import android.os.Bundle;
 import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
+
+import androidx.test.filters.SmallTest;
+
 import com.android.frameworks.coretests.R;
+
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
 
diff --git a/core/tests/coretests/src/android/app/activity/RemoteSubActivityScreen.java b/core/tests/coretests/src/android/app/activity/RemoteSubActivityScreen.java
index 9f402a5..8184627 100644
--- a/core/tests/coretests/src/android/app/activity/RemoteSubActivityScreen.java
+++ b/core/tests/coretests/src/android/app/activity/RemoteSubActivityScreen.java
@@ -1,19 +1,18 @@
-/* //device/apps/AndroidTests/src/com.android.unit_tests/activity/TestedScreen.java
-**
-** Copyright 2006, 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.
-*/
+/*
+ * Copyright (C) 2006 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.app.activity;
 
@@ -21,7 +20,8 @@
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Process;
-import android.util.Log;
+
+//device/apps/AndroidTests/src/com.android.unit_tests/activity/TestedScreen.java
 
 public class RemoteSubActivityScreen extends SubActivityScreen {
     Handler mHandler = new Handler();
diff --git a/core/tests/coretests/src/android/app/activity/ServiceTest.java b/core/tests/coretests/src/android/app/activity/ServiceTest.java
index d3ae415..9d2aebd 100644
--- a/core/tests/coretests/src/android/app/activity/ServiceTest.java
+++ b/core/tests/coretests/src/android/app/activity/ServiceTest.java
@@ -22,13 +22,12 @@
 import android.content.ServiceConnection;
 import android.os.Binder;
 import android.os.Bundle;
-import android.os.RemoteException;
 import android.os.IBinder;
 import android.os.Parcel;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.test.suitebuilder.annotation.SmallTest;
-import android.test.suitebuilder.annotation.Suppress;
-import android.util.Log;
+import android.os.RemoteException;
+
+import androidx.test.filters.MediumTest;
+import androidx.test.filters.Suppress;
 
 // These test binders purport to support an interface whose canonical
 // interface name is ServiceTest.SERVICE_LOCAL
diff --git a/core/tests/coretests/src/android/app/activity/SetTimeZonePermissionsTest.java b/core/tests/coretests/src/android/app/activity/SetTimeZonePermissionsTest.java
index 41b9547..8e17295 100644
--- a/core/tests/coretests/src/android/app/activity/SetTimeZonePermissionsTest.java
+++ b/core/tests/coretests/src/android/app/activity/SetTimeZonePermissionsTest.java
@@ -19,7 +19,8 @@
 import android.app.AlarmManager;
 import android.content.Context;
 import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.LargeTest;
+
+import androidx.test.filters.LargeTest;
 
 import java.util.TimeZone;
 
diff --git a/core/tests/coretests/src/android/app/activity/SubActivityTest.java b/core/tests/coretests/src/android/app/activity/SubActivityTest.java
index 35dde8a..53f89fe 100644
--- a/core/tests/coretests/src/android/app/activity/SubActivityTest.java
+++ b/core/tests/coretests/src/android/app/activity/SubActivityTest.java
@@ -16,9 +16,10 @@
 
 package android.app.activity;
 
-import android.test.suitebuilder.annotation.Suppress;
 import android.content.ComponentName;
 
+import androidx.test.filters.Suppress;
+
 @Suppress
 public class SubActivityTest extends ActivityTestsBase {
 
diff --git a/core/tests/coretests/src/android/app/admin/PasswordMetricsTest.java b/core/tests/coretests/src/android/app/admin/PasswordMetricsTest.java
index 9b5b725..8d42c74 100644
--- a/core/tests/coretests/src/android/app/admin/PasswordMetricsTest.java
+++ b/core/tests/coretests/src/android/app/admin/PasswordMetricsTest.java
@@ -27,8 +27,9 @@
 
 import android.app.admin.PasswordMetrics.PasswordComplexityBucket;
 import android.os.Parcel;
-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/coretests/src/android/app/assist/AssistStructureTest.java b/core/tests/coretests/src/android/app/assist/AssistStructureTest.java
index 689e683..1f4e5df 100644
--- a/core/tests/coretests/src/android/app/assist/AssistStructureTest.java
+++ b/core/tests/coretests/src/android/app/assist/AssistStructureTest.java
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package android.app.assist;
 
 import static android.view.View.AUTOFILL_TYPE_TEXT;
@@ -22,13 +23,9 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import android.app.assist.AssistStructure.ViewNode;
-import android.content.ComponentName;
 import android.content.Context;
 import android.os.Parcel;
 import android.os.SystemClock;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.rule.ActivityTestRule;
-import android.support.test.runner.AndroidJUnit4;
 import android.text.InputFilter;
 import android.util.Log;
 import android.view.autofill.AutofillId;
@@ -37,6 +34,10 @@
 import android.widget.LinearLayout;
 import android.widget.TextView;
 
+import androidx.test.InstrumentationRegistry;
+import androidx.test.rule.ActivityTestRule;
+import androidx.test.runner.AndroidJUnit4;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/app/assist/EmptyLayoutActivity.java b/core/tests/coretests/src/android/app/assist/EmptyLayoutActivity.java
index f4b6bed..defec43 100644
--- a/core/tests/coretests/src/android/app/assist/EmptyLayoutActivity.java
+++ b/core/tests/coretests/src/android/app/assist/EmptyLayoutActivity.java
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package android.app.assist;
 
 import android.app.Activity;
diff --git a/core/tests/coretests/src/android/app/backup/BackupDataTest.java b/core/tests/coretests/src/android/app/backup/BackupDataTest.java
index 5b8e481..18ff54f 100644
--- a/core/tests/coretests/src/android/app/backup/BackupDataTest.java
+++ b/core/tests/coretests/src/android/app/backup/BackupDataTest.java
@@ -16,31 +16,22 @@
 
 package android.app.backup;
 
-import android.app.backup.BackupDataInput;
-import android.app.backup.BackupDataOutput;
-import android.content.res.AssetFileDescriptor;
 import android.content.res.AssetManager;
 import android.os.Bundle;
 import android.os.Environment;
 import android.os.ParcelFileDescriptor;
-import android.support.test.filters.LargeTest;
 import android.test.AndroidTestCase;
-import android.test.InstrumentationTestCase;
 import android.util.Base64;
-import android.util.Log;
-import org.json.JSONObject;
+
+import androidx.test.filters.LargeTest;
 
 import java.io.BufferedReader;
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
-import java.io.FileReader;
 import java.io.IOException;
-import java.io.InputStream;
 import java.io.InputStreamReader;
-import java.lang.Exception;
-import java.nio.ByteBuffer;
 
 @LargeTest
 public class BackupDataTest extends AndroidTestCase {
diff --git a/core/tests/coretests/src/android/app/backup/FullBackupTest.java b/core/tests/coretests/src/android/app/backup/FullBackupTest.java
index 5db416b..08edb4e 100644
--- a/core/tests/coretests/src/android/app/backup/FullBackupTest.java
+++ b/core/tests/coretests/src/android/app/backup/FullBackupTest.java
@@ -18,11 +18,12 @@
 
 import android.app.backup.FullBackup.BackupScheme.PathWithRequiredFlags;
 import android.content.Context;
-import android.support.test.filters.LargeTest;
 import android.test.AndroidTestCase;
 import android.util.ArrayMap;
 import android.util.ArraySet;
 
+import androidx.test.filters.LargeTest;
+
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
 import org.xmlpull.v1.XmlPullParserFactory;
diff --git a/core/tests/coretests/src/android/app/servertransaction/ClientTransactionTests.java b/core/tests/coretests/src/android/app/servertransaction/ClientTransactionTests.java
index b1f8552..52b2658 100644
--- a/core/tests/coretests/src/android/app/servertransaction/ClientTransactionTests.java
+++ b/core/tests/coretests/src/android/app/servertransaction/ClientTransactionTests.java
@@ -23,8 +23,9 @@
 import android.app.ClientTransactionHandler;
 import android.os.IBinder;
 import android.platform.test.annotations.Presubmit;
-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/coretests/src/android/app/servertransaction/ObjectPoolTests.java b/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java
index fb0f534..ad28d13 100644
--- a/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java
+++ b/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java
@@ -35,8 +35,9 @@
 import android.os.Bundle;
 import android.os.PersistableBundle;
 import android.platform.test.annotations.Presubmit;
-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/coretests/src/android/app/servertransaction/TransactionExecutorTests.java b/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java
index a788a93..f730a24 100644
--- a/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java
+++ b/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java
@@ -45,10 +45,11 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.platform.test.annotations.Presubmit;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
 import android.util.ArrayMap;
 
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
index 2801f324..8604b0c 100644
--- a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
+++ b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
@@ -50,8 +50,9 @@
 import android.os.RemoteCallback;
 import android.os.RemoteException;
 import android.platform.test.annotations.Presubmit;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
 
 import com.android.internal.app.IVoiceInteractor;
 
diff --git a/core/tests/coretests/src/android/app/timezone/DistroFormatVersionTest.java b/core/tests/coretests/src/android/app/timezone/DistroFormatVersionTest.java
index e20645c..0efc0ab 100644
--- a/core/tests/coretests/src/android/app/timezone/DistroFormatVersionTest.java
+++ b/core/tests/coretests/src/android/app/timezone/DistroFormatVersionTest.java
@@ -21,7 +21,8 @@
 import static org.junit.Assert.assertTrue;
 
 import android.os.Parcel;
-import android.support.test.filters.LargeTest;
+
+import androidx.test.filters.LargeTest;
 
 import org.junit.Test;
 
diff --git a/core/tests/coretests/src/android/app/timezone/DistroRulesVersionTest.java b/core/tests/coretests/src/android/app/timezone/DistroRulesVersionTest.java
index b69054c..b519bf8 100644
--- a/core/tests/coretests/src/android/app/timezone/DistroRulesVersionTest.java
+++ b/core/tests/coretests/src/android/app/timezone/DistroRulesVersionTest.java
@@ -21,7 +21,8 @@
 import static org.junit.Assert.assertTrue;
 
 import android.os.Parcel;
-import android.support.test.filters.LargeTest;
+
+import androidx.test.filters.LargeTest;
 
 import org.junit.Test;
 
diff --git a/core/tests/coretests/src/android/app/timezone/RulesStateTest.java b/core/tests/coretests/src/android/app/timezone/RulesStateTest.java
index dd46240..bb535b6 100644
--- a/core/tests/coretests/src/android/app/timezone/RulesStateTest.java
+++ b/core/tests/coretests/src/android/app/timezone/RulesStateTest.java
@@ -16,14 +16,13 @@
 
 package android.app.timezone;
 
-import static junit.framework.Assert.fail;
-
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
 import android.os.Parcel;
-import android.support.test.filters.LargeTest;
+
+import androidx.test.filters.LargeTest;
 
 import org.junit.Test;
 
diff --git a/core/tests/coretests/src/android/app/timezone/RulesUpdaterContractTest.java b/core/tests/coretests/src/android/app/timezone/RulesUpdaterContractTest.java
index 4004086..df9ddea 100644
--- a/core/tests/coretests/src/android/app/timezone/RulesUpdaterContractTest.java
+++ b/core/tests/coretests/src/android/app/timezone/RulesUpdaterContractTest.java
@@ -25,7 +25,8 @@
 import android.content.Context;
 import android.content.Intent;
 import android.os.UserHandle;
-import android.support.test.filters.LargeTest;
+
+import androidx.test.filters.LargeTest;
 
 import org.hamcrest.BaseMatcher;
 import org.hamcrest.Description;
diff --git a/core/tests/coretests/src/android/app/usage/EventListTest.java b/core/tests/coretests/src/android/app/usage/EventListTest.java
index 9dc0d43..685fcae 100644
--- a/core/tests/coretests/src/android/app/usage/EventListTest.java
+++ b/core/tests/coretests/src/android/app/usage/EventListTest.java
@@ -20,10 +20,11 @@
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
 import android.util.Log;
 
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
diff --git a/core/tests/coretests/src/android/app/usage/UsageStatsTest.java b/core/tests/coretests/src/android/app/usage/UsageStatsTest.java
index 28aaf1e..1633e1a 100644
--- a/core/tests/coretests/src/android/app/usage/UsageStatsTest.java
+++ b/core/tests/coretests/src/android/app/usage/UsageStatsTest.java
@@ -21,6 +21,7 @@
 import static android.app.usage.UsageEvents.Event.ACTIVITY_RESUMED;
 import static android.app.usage.UsageEvents.Event.ACTIVITY_STOPPED;
 import static android.app.usage.UsageEvents.Event.CONTINUING_FOREGROUND_SERVICE;
+import static android.app.usage.UsageEvents.Event.DEVICE_SHUTDOWN;
 import static android.app.usage.UsageEvents.Event.END_OF_DAY;
 import static android.app.usage.UsageEvents.Event.FLUSH_TO_DISK;
 import static android.app.usage.UsageEvents.Event.FOREGROUND_SERVICE_START;
@@ -33,8 +34,9 @@
 
 import android.app.usage.UsageEvents.Event;
 import android.os.Parcel;
-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.Before;
 import org.junit.Test;
@@ -528,6 +530,11 @@
     }
 
     @Test
+    public void testEvent_DEVICE_SHUTDOWN() {
+        testClosingEvent(DEVICE_SHUTDOWN);
+    }
+
+    @Test
     public void testEvent_FLUSH_TO_DISK() {
         testClosingEvent(FLUSH_TO_DISK);
     }
@@ -535,8 +542,9 @@
     private void testClosingEvent(int eventType) {
         // When these three closing events are received, all open activities/services need to be
         // closed and usage stats are updated.
-        if (eventType != FLUSH_TO_DISK) {
-            fail("Closing eventType must be one of FLUSH_TO_DISK");
+        if (eventType != DEVICE_SHUTDOWN
+                && eventType != FLUSH_TO_DISK) {
+            fail("Closing eventType must be one of DEVICE_SHUTDOWN, FLUSH_TO_DISK");
         }
 
         left.mPackageName = "com.test";
diff --git a/core/tests/coretests/src/android/content/AbstractCrossUserContentResolverTest.java b/core/tests/coretests/src/android/content/AbstractCrossUserContentResolverTest.java
index 978ea7a..c307e64 100644
--- a/core/tests/coretests/src/android/content/AbstractCrossUserContentResolverTest.java
+++ b/core/tests/coretests/src/android/content/AbstractCrossUserContentResolverTest.java
@@ -16,7 +16,6 @@
 
 package android.content;
 
-
 import static org.junit.Assert.fail;
 
 import android.app.ActivityManager;
@@ -30,9 +29,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;
diff --git a/core/tests/coretests/src/android/content/AssetTest.java b/core/tests/coretests/src/android/content/AssetTest.java
index b66574c..8e55e8a 100644
--- a/core/tests/coretests/src/android/content/AssetTest.java
+++ b/core/tests/coretests/src/android/content/AssetTest.java
@@ -18,7 +18,8 @@
 
 import android.content.res.AssetManager;
 import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
+
+import androidx.test.filters.SmallTest;
 
 import java.io.IOException;
 import java.io.InputStream;
diff --git a/core/tests/coretests/src/android/content/BrickDeniedTest.java b/core/tests/coretests/src/android/content/BrickDeniedTest.java
index 3d246b4..d8c9baa 100644
--- a/core/tests/coretests/src/android/content/BrickDeniedTest.java
+++ b/core/tests/coretests/src/android/content/BrickDeniedTest.java
@@ -17,7 +17,8 @@
 package android.content;
 
 import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
+
+import androidx.test.filters.SmallTest;
 
 /** Test to make sure brick intents <b>don't</b> work without permission. */
 public class BrickDeniedTest extends AndroidTestCase {
diff --git a/core/tests/coretests/src/android/content/BroadcastReceiverTests.java b/core/tests/coretests/src/android/content/BroadcastReceiverTests.java
index 8deccb7..1509ff9 100644
--- a/core/tests/coretests/src/android/content/BroadcastReceiverTests.java
+++ b/core/tests/coretests/src/android/content/BroadcastReceiverTests.java
@@ -18,9 +18,9 @@
 
 import static org.junit.Assert.fail;
 
-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/coretests/src/android/content/ContentProviderOperationTest.java b/core/tests/coretests/src/android/content/ContentProviderOperationTest.java
index aea124b..b142761 100644
--- a/core/tests/coretests/src/android/content/ContentProviderOperationTest.java
+++ b/core/tests/coretests/src/android/content/ContentProviderOperationTest.java
@@ -16,22 +16,23 @@
 
 package android.content;
 
-import android.content.ContentValues;
 import android.database.Cursor;
 import android.database.MatrixCursor;
 import android.net.Uri;
 import android.os.Parcel;
-import android.test.suitebuilder.annotation.SmallTest;
 import android.text.TextUtils;
+
+import androidx.test.filters.SmallTest;
+
 import junit.framework.TestCase;
 
 import java.lang.reflect.Constructor;
 import java.lang.reflect.Field;
 import java.lang.reflect.InvocationTargetException;
 import java.util.HashMap;
-import java.util.Set;
 import java.util.Map;
 import java.util.Map.Entry;
+import java.util.Set;
 
 @SmallTest
 public class ContentProviderOperationTest extends TestCase {
diff --git a/core/tests/coretests/src/android/content/ContentProviderTest.java b/core/tests/coretests/src/android/content/ContentProviderTest.java
index 2142f27..8895f9b 100644
--- a/core/tests/coretests/src/android/content/ContentProviderTest.java
+++ b/core/tests/coretests/src/android/content/ContentProviderTest.java
@@ -23,7 +23,8 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.ProviderInfo;
 import android.net.Uri;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Before;
 import org.junit.Test;
diff --git a/core/tests/coretests/src/android/content/ContentQueryMapTest.java b/core/tests/coretests/src/android/content/ContentQueryMapTest.java
index f47bfdb..7106234 100644
--- a/core/tests/coretests/src/android/content/ContentQueryMapTest.java
+++ b/core/tests/coretests/src/android/content/ContentQueryMapTest.java
@@ -16,16 +16,14 @@
 
 package android.content;
 
-import android.content.ContentQueryMap;
-import android.content.ContentResolver;
-import android.content.ContentValues;
 import android.database.Cursor;
 import android.os.Handler;
 import android.os.Looper;
 import android.provider.Settings;
 import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.test.suitebuilder.annotation.Suppress;
+
+import androidx.test.filters.MediumTest;
+import androidx.test.filters.Suppress;
 
 import java.util.Observable;
 import java.util.Observer;
diff --git a/core/tests/coretests/src/android/content/ContentResolverTest.java b/core/tests/coretests/src/android/content/ContentResolverTest.java
index 9940bf7..f14f289 100644
--- a/core/tests/coretests/src/android/content/ContentResolverTest.java
+++ b/core/tests/coretests/src/android/content/ContentResolverTest.java
@@ -31,10 +31,11 @@
 import android.net.Uri;
 import android.os.MemoryFile;
 import android.os.ParcelFileDescriptor;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.runner.AndroidJUnit4;
 import android.util.Size;
 
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
diff --git a/core/tests/coretests/src/android/content/ContentValuesTest.java b/core/tests/coretests/src/android/content/ContentValuesTest.java
index 7b39939..0ab79e7 100644
--- a/core/tests/coretests/src/android/content/ContentValuesTest.java
+++ b/core/tests/coretests/src/android/content/ContentValuesTest.java
@@ -17,8 +17,8 @@
 package android.content;
 
 import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
 
+import androidx.test.filters.SmallTest;
 
 /*
   runtest -c android.content.ContentValuesTest frameworks-core
@@ -29,7 +29,7 @@
     adb shell pm uninstall -k com.android.frameworks.coretests && \
     adb install out/target/product/bullhead/testcases/FrameworksCoreTests/FrameworksCoreTests.apk && \
     adb shell am instrument -w -e package android.content \
-      com.android.frameworks.coretests/android.support.test.runner.AndroidJUnitRunner
+      com.android.frameworks.coretests/androidx.test.runner.AndroidJUnitRunner
 */
 public class ContentValuesTest extends AndroidTestCase {
 
diff --git a/core/tests/coretests/src/android/content/ContextTest.java b/core/tests/coretests/src/android/content/ContextTest.java
index c8a3098..2f442c3 100644
--- a/core/tests/coretests/src/android/content/ContextTest.java
+++ b/core/tests/coretests/src/android/content/ContextTest.java
@@ -19,11 +19,12 @@
 import static org.junit.Assert.assertEquals;
 
 import android.app.ActivityThread;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
 import android.view.WindowManager;
 
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
diff --git a/core/tests/coretests/src/android/content/ManagedUserContentResolverTest.java b/core/tests/coretests/src/android/content/ManagedUserContentResolverTest.java
index 4362ec3..22b2314 100644
--- a/core/tests/coretests/src/android/content/ManagedUserContentResolverTest.java
+++ b/core/tests/coretests/src/android/content/ManagedUserContentResolverTest.java
@@ -19,7 +19,8 @@
 import android.content.pm.UserInfo;
 import android.os.RemoteException;
 import android.os.UserHandle;
-import android.support.test.filters.LargeTest;
+
+import androidx.test.filters.LargeTest;
 
 /**
  * To run the tests, use
@@ -32,7 +33,7 @@
  * Install: adb install -r \
  *     ${ANDROID_PRODUCT_OUT}/data/app/FrameworksCoreTests/FrameworksCoreTests.apk
  * Run: adb shell am instrument -e class android.content.ManagedUserContentResolverTest -w \
- *     com.android.frameworks.coretests/android.support.test.runner.AndroidJUnitRunner
+ *     com.android.frameworks.coretests/androidx.test.runner.AndroidJUnitRunner
  */
 @LargeTest
 public class ManagedUserContentResolverTest extends AbstractCrossUserContentResolverTest {
diff --git a/core/tests/coretests/src/android/content/MemoryFileProviderTest.java b/core/tests/coretests/src/android/content/MemoryFileProviderTest.java
index bbe7c10..7cd4862 100644
--- a/core/tests/coretests/src/android/content/MemoryFileProviderTest.java
+++ b/core/tests/coretests/src/android/content/MemoryFileProviderTest.java
@@ -18,9 +18,10 @@
 
 import android.net.Uri;
 import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.test.suitebuilder.annotation.SmallTest;
+
+import androidx.test.filters.LargeTest;
+import androidx.test.filters.MediumTest;
+import androidx.test.filters.SmallTest;
 
 import java.io.InputStream;
 import java.util.Arrays;
diff --git a/core/tests/coretests/src/android/content/RestrictionsManagerTest.java b/core/tests/coretests/src/android/content/RestrictionsManagerTest.java
index 10d74f7..fd5de32 100644
--- a/core/tests/coretests/src/android/content/RestrictionsManagerTest.java
+++ b/core/tests/coretests/src/android/content/RestrictionsManagerTest.java
@@ -13,13 +13,15 @@
  * See the License for the specific language governing permissions and
  * limitations under the License
  */
+
 package android.content;
 
 import android.os.Bundle;
 import android.os.Parcelable;
-import android.support.test.filters.LargeTest;
 import android.test.AndroidTestCase;
 
+import androidx.test.filters.LargeTest;
+
 import java.util.Arrays;
 import java.util.HashSet;
 import java.util.List;
diff --git a/core/tests/coretests/src/android/content/SecondaryUserContentResolverTest.java b/core/tests/coretests/src/android/content/SecondaryUserContentResolverTest.java
index f8b13f0..dbe0278 100644
--- a/core/tests/coretests/src/android/content/SecondaryUserContentResolverTest.java
+++ b/core/tests/coretests/src/android/content/SecondaryUserContentResolverTest.java
@@ -18,7 +18,8 @@
 
 import android.content.pm.UserInfo;
 import android.os.RemoteException;
-import android.support.test.filters.LargeTest;
+
+import androidx.test.filters.LargeTest;
 
 /**
  * To run the tests, use
@@ -31,7 +32,7 @@
  * Install: adb install -r \
  *     ${ANDROID_PRODUCT_OUT}/data/app/FrameworksCoreTests/FrameworksCoreTests.apk
  * Run: adb shell am instrument -e class android.content.SecondaryUserContentResolverTest -w \
- *     com.android.frameworks.coretests/android.support.test.runner.AndroidJUnitRunner
+ *     com.android.frameworks.coretests/androidx.test.runner.AndroidJUnitRunner
  */
 @LargeTest
 public class SecondaryUserContentResolverTest extends AbstractCrossUserContentResolverTest {
diff --git a/core/tests/coretests/src/android/content/UriMatcherTest.java b/core/tests/coretests/src/android/content/UriMatcherTest.java
index f3b9e76..6cef46b 100644
--- a/core/tests/coretests/src/android/content/UriMatcherTest.java
+++ b/core/tests/coretests/src/android/content/UriMatcherTest.java
@@ -17,14 +17,14 @@
 package android.content;
 
 import android.net.Uri;
-import android.test.suitebuilder.annotation.SmallTest;
+
+import androidx.test.filters.SmallTest;
 
 import junit.framework.TestCase;
 
 import java.lang.reflect.Field;
 import java.util.ArrayList;
 
-
 public class UriMatcherTest extends TestCase {
 
     static final int ROOT = 0;
diff --git a/core/tests/coretests/src/android/content/pm/AndroidHidlUpdaterTest.java b/core/tests/coretests/src/android/content/pm/AndroidHidlUpdaterTest.java
index 2acb08d..9b360db 100644
--- a/core/tests/coretests/src/android/content/pm/AndroidHidlUpdaterTest.java
+++ b/core/tests/coretests/src/android/content/pm/AndroidHidlUpdaterTest.java
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package android.content.pm;
 
 import static android.content.pm.PackageBuilder.builder;
@@ -20,7 +21,8 @@
 import static android.content.pm.SharedLibraryNames.ANDROID_HIDL_MANAGER;
 
 import android.os.Build;
-import android.support.test.filters.SmallTest;
+
+import androidx.test.filters.SmallTest;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/content/pm/AndroidTestBaseUpdaterTest.java b/core/tests/coretests/src/android/content/pm/AndroidTestBaseUpdaterTest.java
index dce22ce..0ed76dc 100644
--- a/core/tests/coretests/src/android/content/pm/AndroidTestBaseUpdaterTest.java
+++ b/core/tests/coretests/src/android/content/pm/AndroidTestBaseUpdaterTest.java
@@ -13,13 +13,15 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package android.content.pm;
 
 import static android.content.pm.PackageBuilder.builder;
 import static android.content.pm.SharedLibraryNames.ANDROID_TEST_BASE;
 
 import android.os.Build;
-import android.support.test.filters.SmallTest;
+
+import androidx.test.filters.SmallTest;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/content/pm/AndroidTestRunnerSplitUpdaterTest.java b/core/tests/coretests/src/android/content/pm/AndroidTestRunnerSplitUpdaterTest.java
index 866de93..7f817d6 100644
--- a/core/tests/coretests/src/android/content/pm/AndroidTestRunnerSplitUpdaterTest.java
+++ b/core/tests/coretests/src/android/content/pm/AndroidTestRunnerSplitUpdaterTest.java
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package android.content.pm;
 
 import static android.content.pm.PackageBuilder.builder;
@@ -20,7 +21,8 @@
 import static android.content.pm.SharedLibraryNames.ANDROID_TEST_RUNNER;
 
 import android.content.pm.PackageBackwardCompatibility.AndroidTestRunnerSplitUpdater;
-import android.support.test.filters.SmallTest;
+
+import androidx.test.filters.SmallTest;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/content/pm/ComponentTest.java b/core/tests/coretests/src/android/content/pm/ComponentTest.java
index cc75641..f31f0b5 100644
--- a/core/tests/coretests/src/android/content/pm/ComponentTest.java
+++ b/core/tests/coretests/src/android/content/pm/ComponentTest.java
@@ -21,7 +21,14 @@
 import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
 import static android.content.pm.PackageManager.GET_DISABLED_COMPONENTS;
 
-import android.test.suitebuilder.annotation.Suppress;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.test.AndroidTestCase;
+
+import androidx.test.filters.MediumTest;
+import androidx.test.filters.SmallTest;
+import androidx.test.filters.Suppress;
+
 import com.android.frameworks.coretests.enabled_app.DisabledActivity;
 import com.android.frameworks.coretests.enabled_app.DisabledProvider;
 import com.android.frameworks.coretests.enabled_app.DisabledReceiver;
@@ -31,12 +38,6 @@
 import com.android.frameworks.coretests.enabled_app.EnabledReceiver;
 import com.android.frameworks.coretests.enabled_app.EnabledService;
 
-import android.content.ComponentName;
-import android.content.Intent;
-import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.test.suitebuilder.annotation.SmallTest;
-
 import java.util.List;
 
 /**
diff --git a/core/tests/coretests/src/android/content/pm/LimitedLengthInputStreamTest.java b/core/tests/coretests/src/android/content/pm/LimitedLengthInputStreamTest.java
index 1f762fd..1c703ab 100644
--- a/core/tests/coretests/src/android/content/pm/LimitedLengthInputStreamTest.java
+++ b/core/tests/coretests/src/android/content/pm/LimitedLengthInputStreamTest.java
@@ -17,7 +17,8 @@
 package android.content.pm;
 
 import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
+
+import androidx.test.filters.MediumTest;
 
 import java.io.ByteArrayInputStream;
 import java.io.IOException;
diff --git a/core/tests/coretests/src/android/content/pm/MacAuthenticatedInputStreamTest.java b/core/tests/coretests/src/android/content/pm/MacAuthenticatedInputStreamTest.java
index 659f9ea..1ddd753 100644
--- a/core/tests/coretests/src/android/content/pm/MacAuthenticatedInputStreamTest.java
+++ b/core/tests/coretests/src/android/content/pm/MacAuthenticatedInputStreamTest.java
@@ -16,9 +16,12 @@
 
 package android.content.pm;
 
-import android.support.test.filters.LargeTest;
 import android.test.AndroidTestCase;
 
+import androidx.test.filters.LargeTest;
+
+import libcore.io.Streams;
+
 import java.io.ByteArrayInputStream;
 import java.util.Arrays;
 
@@ -26,8 +29,6 @@
 import javax.crypto.SecretKey;
 import javax.crypto.spec.SecretKeySpec;
 
-import libcore.io.Streams;
-
 @LargeTest
 public class MacAuthenticatedInputStreamTest extends AndroidTestCase {
 
diff --git a/core/tests/coretests/src/android/content/pm/OptionalClassRunner.java b/core/tests/coretests/src/android/content/pm/OptionalClassRunner.java
index 91697c0..05db8ee 100644
--- a/core/tests/coretests/src/android/content/pm/OptionalClassRunner.java
+++ b/core/tests/coretests/src/android/content/pm/OptionalClassRunner.java
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package android.content.pm;
 
 import org.junit.Assume;
diff --git a/core/tests/coretests/src/android/content/pm/OrgApacheHttpLegacyUpdaterTest.java b/core/tests/coretests/src/android/content/pm/OrgApacheHttpLegacyUpdaterTest.java
index dcd2707..834a0bb 100644
--- a/core/tests/coretests/src/android/content/pm/OrgApacheHttpLegacyUpdaterTest.java
+++ b/core/tests/coretests/src/android/content/pm/OrgApacheHttpLegacyUpdaterTest.java
@@ -13,13 +13,15 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package android.content.pm;
 
 import static android.content.pm.PackageBuilder.builder;
 import static android.content.pm.SharedLibraryNames.ORG_APACHE_HTTP_LEGACY;
 
 import android.os.Build;
-import android.support.test.filters.SmallTest;
+
+import androidx.test.filters.SmallTest;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/content/pm/PackageBackwardCompatibilityTest.java b/core/tests/coretests/src/android/content/pm/PackageBackwardCompatibilityTest.java
index c64d520..3d7aab0 100644
--- a/core/tests/coretests/src/android/content/pm/PackageBackwardCompatibilityTest.java
+++ b/core/tests/coretests/src/android/content/pm/PackageBackwardCompatibilityTest.java
@@ -24,7 +24,8 @@
 
 import android.content.pm.PackageBackwardCompatibility.RemoveUnnecessaryAndroidTestBaseLibrary;
 import android.os.Build;
-import android.support.test.filters.SmallTest;
+
+import androidx.test.filters.SmallTest;
 
 import org.junit.Assume;
 import org.junit.Test;
diff --git a/core/tests/coretests/src/android/content/pm/PackageBuilder.java b/core/tests/coretests/src/android/content/pm/PackageBuilder.java
index 4ceed83..c5db962 100644
--- a/core/tests/coretests/src/android/content/pm/PackageBuilder.java
+++ b/core/tests/coretests/src/android/content/pm/PackageBuilder.java
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package android.content.pm;
 
 import static org.junit.Assert.assertEquals;
diff --git a/core/tests/coretests/src/android/content/pm/PackageManagerTests.java b/core/tests/coretests/src/android/content/pm/PackageManagerTests.java
index 8ac9451d..0ab5367 100644
--- a/core/tests/coretests/src/android/content/pm/PackageManagerTests.java
+++ b/core/tests/coretests/src/android/content/pm/PackageManagerTests.java
@@ -54,11 +54,12 @@
 import android.system.Os;
 import android.system.StructStat;
 import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.test.suitebuilder.annotation.SmallTest;
-import android.test.suitebuilder.annotation.Suppress;
 import android.util.Log;
 
+import androidx.test.filters.LargeTest;
+import androidx.test.filters.SmallTest;
+import androidx.test.filters.Suppress;
+
 import com.android.frameworks.coretests.R;
 import com.android.internal.content.PackageHelper;
 
diff --git a/core/tests/coretests/src/android/content/pm/PackageParserCacheHelperTest.java b/core/tests/coretests/src/android/content/pm/PackageParserCacheHelperTest.java
index 00be822..e852f98 100644
--- a/core/tests/coretests/src/android/content/pm/PackageParserCacheHelperTest.java
+++ b/core/tests/coretests/src/android/content/pm/PackageParserCacheHelperTest.java
@@ -22,8 +22,9 @@
 import android.content.pm.PackageParserCacheHelper.WriteHelper;
 import android.os.Bundle;
 import android.os.Parcel;
-import android.support.test.runner.AndroidJUnit4;
-import android.test.suitebuilder.annotation.SmallTest;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/content/pm/PackageParserTest.java b/core/tests/coretests/src/android/content/pm/PackageParserTest.java
index 267267e..be1b1ce 100644
--- a/core/tests/coretests/src/android/content/pm/PackageParserTest.java
+++ b/core/tests/coretests/src/android/content/pm/PackageParserTest.java
@@ -29,9 +29,10 @@
 import android.os.Bundle;
 import android.os.FileUtils;
 import android.os.SystemProperties;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.runner.AndroidJUnit4;
-import android.test.suitebuilder.annotation.SmallTest;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
 
 import com.android.frameworks.coretests.R;
 
@@ -51,6 +52,12 @@
     private static final String PRE_RELEASE = "B";
     private static final String NEWER_PRE_RELEASE = "C";
 
+    // Codenames with a fingerprint attached to them. These may only be present in the apps
+    // declared min SDK and not as platform codenames.
+    private static final String OLDER_PRE_RELEASE_WITH_FINGERPRINT = "A.fingerprint";
+    private static final String PRE_RELEASE_WITH_FINGERPRINT = "B.fingerprint";
+    private static final String NEWER_PRE_RELEASE_WITH_FINGERPRINT = "C.fingerprint";
+
     private static final String[] CODENAMES_RELEASED = { /* empty */ };
     private static final String[] CODENAMES_PRE_RELEASE = { PRE_RELEASE };
 
@@ -68,7 +75,7 @@
                 isPlatformReleased ? CODENAMES_RELEASED : CODENAMES_PRE_RELEASE,
                 outError);
 
-        assertEquals(result, expectedMinSdk);
+        assertEquals("Error msg: " + outError[0], expectedMinSdk, result);
 
         if (expectedMinSdk == -1) {
             assertNotNull(outError[0]);
@@ -98,6 +105,7 @@
         // APP: Pre-release API 10
         // DEV: Pre-release API 20
         verifyComputeMinSdkVersion(OLDER_VERSION, OLDER_PRE_RELEASE, false, -1);
+        verifyComputeMinSdkVersion(OLDER_VERSION, OLDER_PRE_RELEASE_WITH_FINGERPRINT, false, -1);
 
         // Do allow same pre-release minSdkVersion on pre-release platform,
         // but overwrite the specified version with CUR_DEVELOPMENT.
@@ -105,11 +113,15 @@
         // DEV: Pre-release API 20
         verifyComputeMinSdkVersion(PLATFORM_VERSION, PRE_RELEASE, false,
                 Build.VERSION_CODES.CUR_DEVELOPMENT);
+        verifyComputeMinSdkVersion(PLATFORM_VERSION, PRE_RELEASE_WITH_FINGERPRINT, false,
+                Build.VERSION_CODES.CUR_DEVELOPMENT);
+
 
         // Don't allow newer pre-release minSdkVersion on pre-release platform.
         // APP: Pre-release API 30
         // DEV: Pre-release API 20
         verifyComputeMinSdkVersion(NEWER_VERSION, NEWER_PRE_RELEASE, false, -1);
+        verifyComputeMinSdkVersion(NEWER_VERSION, NEWER_PRE_RELEASE_WITH_FINGERPRINT, false, -1);
     }
 
     @Test
@@ -133,16 +145,20 @@
         // APP: Pre-release API 10
         // DEV: Released API 20
         verifyComputeMinSdkVersion(OLDER_VERSION, OLDER_PRE_RELEASE, true, -1);
+        verifyComputeMinSdkVersion(OLDER_VERSION, OLDER_PRE_RELEASE_WITH_FINGERPRINT, true, -1);
 
         // Don't allow same pre-release minSdkVersion on released platform.
         // APP: Pre-release API 20
         // DEV: Released API 20
         verifyComputeMinSdkVersion(PLATFORM_VERSION, PRE_RELEASE, true, -1);
+        verifyComputeMinSdkVersion(PLATFORM_VERSION, PRE_RELEASE_WITH_FINGERPRINT, true, -1);
+
 
         // Don't allow newer pre-release minSdkVersion on released platform.
         // APP: Pre-release API 30
         // DEV: Released API 20
         verifyComputeMinSdkVersion(NEWER_VERSION, NEWER_PRE_RELEASE, true, -1);
+        verifyComputeMinSdkVersion(NEWER_VERSION, NEWER_PRE_RELEASE_WITH_FINGERPRINT, true, -1);
     }
 
     private void verifyComputeTargetSdkVersion(int targetSdkVersion, String targetSdkCodename,
@@ -189,6 +205,9 @@
         // DEV: Pre-release API 20
         verifyComputeTargetSdkVersion(OLDER_VERSION, OLDER_PRE_RELEASE, false, -1,
                 false /* forceCurrentDev */);
+        verifyComputeTargetSdkVersion(OLDER_VERSION, OLDER_PRE_RELEASE_WITH_FINGERPRINT, false, -1,
+                false /* forceCurrentDev */);
+
 
         // Do allow same pre-release targetSdkVersion on pre-release platform,
         // but overwrite the specified version with CUR_DEVELOPMENT.
@@ -196,18 +215,26 @@
         // DEV: Pre-release API 20
         verifyComputeTargetSdkVersion(PLATFORM_VERSION, PRE_RELEASE, false,
                 Build.VERSION_CODES.CUR_DEVELOPMENT, false /* forceCurrentDev */);
+        verifyComputeTargetSdkVersion(PLATFORM_VERSION, PRE_RELEASE_WITH_FINGERPRINT, false,
+                Build.VERSION_CODES.CUR_DEVELOPMENT, false /* forceCurrentDev */);
+
 
         // Don't allow newer pre-release targetSdkVersion on pre-release platform.
         // APP: Pre-release API 30
         // DEV: Pre-release API 20
         verifyComputeTargetSdkVersion(NEWER_VERSION, NEWER_PRE_RELEASE, false, -1,
                 false /* forceCurrentDev */);
+        verifyComputeTargetSdkVersion(NEWER_VERSION, NEWER_PRE_RELEASE_WITH_FINGERPRINT, false, -1,
+                false /* forceCurrentDev */);
+
 
         // Force newer pre-release targetSdkVersion to current pre-release platform.
         // APP: Pre-release API 30
         // DEV: Pre-release API 20
         verifyComputeTargetSdkVersion(NEWER_VERSION, NEWER_PRE_RELEASE, false,
                 Build.VERSION_CODES.CUR_DEVELOPMENT, true /* forceCurrentDev */);
+        verifyComputeTargetSdkVersion(NEWER_VERSION, NEWER_PRE_RELEASE_WITH_FINGERPRINT, false,
+                Build.VERSION_CODES.CUR_DEVELOPMENT, true /* forceCurrentDev */);
     }
 
     @Test
@@ -235,18 +262,25 @@
         // DEV: Released API 20
         verifyComputeTargetSdkVersion(OLDER_VERSION, OLDER_PRE_RELEASE, true, -1,
                 false /* forceCurrentDev */);
+        verifyComputeTargetSdkVersion(OLDER_VERSION, OLDER_PRE_RELEASE_WITH_FINGERPRINT, true, -1,
+                false /* forceCurrentDev */);
 
         // Don't allow same pre-release targetSdkVersion on released platform.
         // APP: Pre-release API 20
         // DEV: Released API 20
         verifyComputeTargetSdkVersion(PLATFORM_VERSION, PRE_RELEASE, true, -1,
                 false /* forceCurrentDev */);
+        verifyComputeTargetSdkVersion(PLATFORM_VERSION, PRE_RELEASE_WITH_FINGERPRINT, true, -1,
+                false /* forceCurrentDev */);
+
 
         // Don't allow newer pre-release targetSdkVersion on released platform.
         // APP: Pre-release API 30
         // DEV: Released API 20
         verifyComputeTargetSdkVersion(NEWER_VERSION, NEWER_PRE_RELEASE, true, -1,
                 false /* forceCurrentDev */);
+        verifyComputeTargetSdkVersion(NEWER_VERSION, NEWER_PRE_RELEASE_WITH_FINGERPRINT, true, -1,
+                false /* forceCurrentDev */);
     }
 
     /**
diff --git a/core/tests/coretests/src/android/content/pm/PackageSharedLibraryUpdaterTest.java b/core/tests/coretests/src/android/content/pm/PackageSharedLibraryUpdaterTest.java
index d5d3d7a..71a0e5e 100644
--- a/core/tests/coretests/src/android/content/pm/PackageSharedLibraryUpdaterTest.java
+++ b/core/tests/coretests/src/android/content/pm/PackageSharedLibraryUpdaterTest.java
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package android.content.pm;
 
 import java.util.function.Supplier;
diff --git a/core/tests/coretests/src/android/content/pm/ParceledListSliceTest.java b/core/tests/coretests/src/android/content/pm/ParceledListSliceTest.java
index 952bb55..8874525 100644
--- a/core/tests/coretests/src/android/content/pm/ParceledListSliceTest.java
+++ b/core/tests/coretests/src/android/content/pm/ParceledListSliceTest.java
@@ -1,8 +1,25 @@
+/*
+ * Copyright (C) 2014 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.content.pm;
 
 import android.os.Parcel;
 import android.os.Parcelable;
-import android.support.test.filters.LargeTest;
+
+import androidx.test.filters.LargeTest;
 
 import junit.framework.TestCase;
 
diff --git a/core/tests/coretests/src/android/content/pm/RegisteredServicesCacheTest.java b/core/tests/coretests/src/android/content/pm/RegisteredServicesCacheTest.java
index d3d1f22a..365e97d 100644
--- a/core/tests/coretests/src/android/content/pm/RegisteredServicesCacheTest.java
+++ b/core/tests/coretests/src/android/content/pm/RegisteredServicesCacheTest.java
@@ -21,11 +21,12 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.UserHandle;
-import android.support.test.filters.LargeTest;
 import android.test.AndroidTestCase;
 import android.util.AttributeSet;
 import android.util.SparseArray;
 
+import androidx.test.filters.LargeTest;
+
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
 import org.xmlpull.v1.XmlSerializer;
diff --git a/core/tests/coretests/src/android/content/pm/RemoveUnnecessaryAndroidTestBaseLibraryTest.java b/core/tests/coretests/src/android/content/pm/RemoveUnnecessaryAndroidTestBaseLibraryTest.java
index 3dba440..216b0c8 100644
--- a/core/tests/coretests/src/android/content/pm/RemoveUnnecessaryAndroidTestBaseLibraryTest.java
+++ b/core/tests/coretests/src/android/content/pm/RemoveUnnecessaryAndroidTestBaseLibraryTest.java
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package android.content.pm;
 
 import static android.content.pm.PackageBuilder.builder;
@@ -20,7 +21,8 @@
 
 import android.content.pm.PackageBackwardCompatibility.RemoveUnnecessaryAndroidTestBaseLibrary;
 import android.os.Build;
-import android.support.test.filters.SmallTest;
+
+import androidx.test.filters.SmallTest;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/content/pm/RemoveUnnecessaryOrgApacheHttpLegacyLibraryTest.java b/core/tests/coretests/src/android/content/pm/RemoveUnnecessaryOrgApacheHttpLegacyLibraryTest.java
index 15b27d7..fc60980 100644
--- a/core/tests/coretests/src/android/content/pm/RemoveUnnecessaryOrgApacheHttpLegacyLibraryTest.java
+++ b/core/tests/coretests/src/android/content/pm/RemoveUnnecessaryOrgApacheHttpLegacyLibraryTest.java
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package android.content.pm;
 
 import static android.content.pm.PackageBuilder.builder;
@@ -20,7 +21,8 @@
 
 import android.content.pm.PackageBackwardCompatibility.RemoveUnnecessaryOrgApacheHttpLegacyLibrary;
 import android.os.Build;
-import android.support.test.filters.SmallTest;
+
+import androidx.test.filters.SmallTest;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/content/pm/SignatureTest.java b/core/tests/coretests/src/android/content/pm/SignatureTest.java
index a3fa1a9..f0b4af6 100644
--- a/core/tests/coretests/src/android/content/pm/SignatureTest.java
+++ b/core/tests/coretests/src/android/content/pm/SignatureTest.java
@@ -16,7 +16,7 @@
 
 package android.content.pm;
 
-import android.support.test.filters.LargeTest;
+import androidx.test.filters.LargeTest;
 
 import junit.framework.TestCase;
 
diff --git a/core/tests/coretests/src/android/content/pm/VerificationParamsTest.java b/core/tests/coretests/src/android/content/pm/VerificationParamsTest.java
index 68942cb..f6527da 100644
--- a/core/tests/coretests/src/android/content/pm/VerificationParamsTest.java
+++ b/core/tests/coretests/src/android/content/pm/VerificationParamsTest.java
@@ -16,12 +16,12 @@
 
 package android.content.pm;
 
-import android.content.pm.VerificationParams;
 import android.net.Uri;
 import android.os.Parcel;
-import android.support.test.filters.LargeTest;
 import android.test.AndroidTestCase;
 
+import androidx.test.filters.LargeTest;
+
 /**
  * Tests the android.content.pm.VerificationParams class
  *
diff --git a/core/tests/coretests/src/android/content/pm/VerifierDeviceIdentityTest.java b/core/tests/coretests/src/android/content/pm/VerifierDeviceIdentityTest.java
index 88d7a59..e7cd02d 100644
--- a/core/tests/coretests/src/android/content/pm/VerifierDeviceIdentityTest.java
+++ b/core/tests/coretests/src/android/content/pm/VerifierDeviceIdentityTest.java
@@ -17,7 +17,8 @@
 package android.content.pm;
 
 import android.os.Parcel;
-import android.support.test.filters.LargeTest;
+
+import androidx.test.filters.LargeTest;
 
 import java.util.Random;
 
diff --git a/core/tests/coretests/src/android/content/pm/dex/DexMetadataHelperTest.java b/core/tests/coretests/src/android/content/pm/dex/DexMetadataHelperTest.java
index e248a77..1ca879c 100644
--- a/core/tests/coretests/src/android/content/pm/dex/DexMetadataHelperTest.java
+++ b/core/tests/coretests/src/android/content/pm/dex/DexMetadataHelperTest.java
@@ -29,12 +29,21 @@
 import android.content.pm.PackageParser.PackageLite;
 import android.content.pm.PackageParser.PackageParserException;
 import android.os.FileUtils;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.runner.AndroidJUnit4;
-import android.test.suitebuilder.annotation.SmallTest;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
 
 import com.android.frameworks.coretests.R;
 
+import libcore.testing.io.TestIoUtils;
+
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.FileOutputStream;
@@ -45,14 +54,6 @@
 import java.util.zip.ZipEntry;
 import java.util.zip.ZipOutputStream;
 
-import libcore.testing.io.TestIoUtils;
-
-import org.junit.After;
-import org.junit.Assert;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 public class DexMetadataHelperTest {
diff --git a/core/tests/coretests/src/android/content/res/ConfigurationBoundResourceCacheTest.java b/core/tests/coretests/src/android/content/res/ConfigurationBoundResourceCacheTest.java
index 47554a6..47b14bb 100644
--- a/core/tests/coretests/src/android/content/res/ConfigurationBoundResourceCacheTest.java
+++ b/core/tests/coretests/src/android/content/res/ConfigurationBoundResourceCacheTest.java
@@ -17,9 +17,10 @@
 package android.content.res;
 
 import android.test.ActivityInstrumentationTestCase2;
-import android.test.suitebuilder.annotation.SmallTest;
 import android.util.TypedValue;
 
+import androidx.test.filters.SmallTest;
+
 import com.android.frameworks.coretests.R;
 
 import java.lang.reflect.InvocationTargetException;
diff --git a/core/tests/coretests/src/android/content/res/ConfigurationTest.java b/core/tests/coretests/src/android/content/res/ConfigurationTest.java
index 72b9197..2fc3e36 100644
--- a/core/tests/coretests/src/android/content/res/ConfigurationTest.java
+++ b/core/tests/coretests/src/android/content/res/ConfigurationTest.java
@@ -13,19 +13,18 @@
  * License for the specific language governing permissions and limitations under
  * the License.
  */
+
 package android.content.res;
 
-import org.junit.runner.RunWith;
-import org.junit.Test;
-import org.junit.runners.JUnit4;
-
-import android.content.res.Configuration;
-import android.support.test.filters.SmallTest;
 import android.platform.test.annotations.Presubmit;
 
+import androidx.test.filters.SmallTest;
+
 import junit.framework.TestCase;
 
-import static org.junit.Assert.assertEquals;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
 
 /**
  * Build/install/run: bit FrameworksCoreTests:android.content.res.ConfigurationTest
diff --git a/core/tests/coretests/src/android/content/res/FontResourcesParserTest.java b/core/tests/coretests/src/android/content/res/FontResourcesParserTest.java
index 42ff2e9..7ab9d7f 100644
--- a/core/tests/coretests/src/android/content/res/FontResourcesParserTest.java
+++ b/core/tests/coretests/src/android/content/res/FontResourcesParserTest.java
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package android.content.res;
 
 import static android.content.res.FontResourcesParser.FamilyResourceEntry;
@@ -26,9 +27,10 @@
 import static org.junit.Assert.assertNotNull;
 
 import android.app.Instrumentation;
-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 com.android.frameworks.coretests.R;
 
diff --git a/core/tests/coretests/src/android/content/res/ResourcesLocaleTest.java b/core/tests/coretests/src/android/content/res/ResourcesLocaleTest.java
index e85666e..aa1a534 100644
--- a/core/tests/coretests/src/android/content/res/ResourcesLocaleTest.java
+++ b/core/tests/coretests/src/android/content/res/ResourcesLocaleTest.java
@@ -13,14 +13,15 @@
  * License for the specific language governing permissions and limitations under
  * the License.
  */
+
 package android.content.res;
 
 import android.os.FileUtils;
 import android.os.LocaleList;
-import android.support.test.filters.SmallTest;
 import android.test.AndroidTestCase;
 import android.util.DisplayMetrics;
-import android.view.Display;
+
+import androidx.test.filters.SmallTest;
 
 import com.android.frameworks.coretests.R;
 
diff --git a/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java b/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java
index b2ff927..a2dab99 100644
--- a/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java
+++ b/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java
@@ -13,18 +13,20 @@
  * License for the specific language governing permissions and limitations under
  * the License.
  */
+
 package android.content.res;
 
 import android.annotation.NonNull;
 import android.app.ResourcesManager;
 import android.os.Binder;
 import android.os.LocaleList;
-import android.support.test.filters.SmallTest;
 import android.util.DisplayMetrics;
 import android.util.TypedValue;
 import android.view.Display;
 import android.view.DisplayAdjustments;
 
+import androidx.test.filters.SmallTest;
+
 import junit.framework.TestCase;
 
 public class ResourcesManagerTest extends TestCase {
diff --git a/core/tests/coretests/src/android/database/CursorWindowTest.java b/core/tests/coretests/src/android/database/CursorWindowTest.java
index 075f5b7..123da3e 100644
--- a/core/tests/coretests/src/android/database/CursorWindowTest.java
+++ b/core/tests/coretests/src/android/database/CursorWindowTest.java
@@ -16,14 +16,14 @@
 
 package android.database;
 
-import android.test.suitebuilder.annotation.SmallTest;
-import android.database.CursorWindow;
 import android.test.PerformanceTestCase;
 
-import java.util.Arrays;
+import androidx.test.filters.SmallTest;
 
 import junit.framework.TestCase;
 
+import java.util.Arrays;
+
 public class CursorWindowTest extends TestCase implements PerformanceTestCase {
     public boolean isPerformanceOnly() {
         return false;
diff --git a/core/tests/coretests/src/android/database/DatabaseCursorTest.java b/core/tests/coretests/src/android/database/DatabaseCursorTest.java
index 3507223..eb4fd70 100644
--- a/core/tests/coretests/src/android/database/DatabaseCursorTest.java
+++ b/core/tests/coretests/src/android/database/DatabaseCursorTest.java
@@ -18,10 +18,6 @@
 
 import android.content.ContentValues;
 import android.content.Context;
-import android.database.Cursor;
-import android.database.CursorIndexOutOfBoundsException;
-import android.database.DataSetObserver;
-import android.database.DatabaseUtils;
 import android.database.sqlite.SQLiteCursor;
 import android.database.sqlite.SQLiteCursorDriver;
 import android.database.sqlite.SQLiteDatabase;
@@ -29,11 +25,12 @@
 import android.os.Looper;
 import android.test.AndroidTestCase;
 import android.test.PerformanceTestCase;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.test.suitebuilder.annotation.Suppress;
 import android.util.Log;
 
+import androidx.test.filters.LargeTest;
+import androidx.test.filters.MediumTest;
+import androidx.test.filters.Suppress;
+
 import java.io.File;
 import java.util.ArrayList;
 import java.util.Arrays;
diff --git a/core/tests/coretests/src/android/database/DatabaseGeneralTest.java b/core/tests/coretests/src/android/database/DatabaseGeneralTest.java
index 9d75c78..49fb75b 100644
--- a/core/tests/coretests/src/android/database/DatabaseGeneralTest.java
+++ b/core/tests/coretests/src/android/database/DatabaseGeneralTest.java
@@ -25,16 +25,17 @@
 import android.database.sqlite.SQLiteDebug;
 import android.database.sqlite.SQLiteException;
 import android.os.Parcel;
-import android.support.test.InstrumentationRegistry;
 import android.support.test.uiautomator.UiDevice;
 import android.test.AndroidTestCase;
 import android.test.PerformanceTestCase;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.test.suitebuilder.annotation.SmallTest;
 import android.util.Log;
 import android.util.Pair;
 
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.LargeTest;
+import androidx.test.filters.MediumTest;
+import androidx.test.filters.SmallTest;
+
 import junit.framework.Assert;
 
 import java.io.File;
diff --git a/core/tests/coretests/src/android/database/DatabaseLocaleTest.java b/core/tests/coretests/src/android/database/DatabaseLocaleTest.java
index b3282941..ee7936f 100644
--- a/core/tests/coretests/src/android/database/DatabaseLocaleTest.java
+++ b/core/tests/coretests/src/android/database/DatabaseLocaleTest.java
@@ -17,17 +17,17 @@
 package android.database;
 
 import android.database.sqlite.SQLiteDatabase;
-import android.database.Cursor;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.test.suitebuilder.annotation.SmallTest;
-import android.util.Log;
 import android.test.MoreAsserts;
+import android.util.Log;
+
+import androidx.test.filters.MediumTest;
+import androidx.test.filters.SmallTest;
+
+import junit.framework.TestCase;
 
 import java.util.ArrayList;
 import java.util.Locale;
 
-import junit.framework.TestCase;
-
 public class DatabaseLocaleTest extends TestCase {
 
     private SQLiteDatabase mDatabase;
diff --git a/core/tests/coretests/src/android/database/DatabaseLockTest.java b/core/tests/coretests/src/android/database/DatabaseLockTest.java
index 8d3cf5a..e8936c9 100644
--- a/core/tests/coretests/src/android/database/DatabaseLockTest.java
+++ b/core/tests/coretests/src/android/database/DatabaseLockTest.java
@@ -17,11 +17,13 @@
 package android.database;
 
 import android.database.sqlite.SQLiteDatabase;
-import android.test.suitebuilder.annotation.Suppress;
+import android.test.AndroidTestCase;
 import android.util.Log;
+
+import androidx.test.filters.Suppress;
+
 import java.io.File;
 import java.util.concurrent.atomic.AtomicInteger;
-import android.test.AndroidTestCase;
 
 /* 
  * This is a series of unit tests for database locks.
diff --git a/core/tests/coretests/src/android/database/DatabaseStatementTest.java b/core/tests/coretests/src/android/database/DatabaseStatementTest.java
index 895d715..4b34650 100644
--- a/core/tests/coretests/src/android/database/DatabaseStatementTest.java
+++ b/core/tests/coretests/src/android/database/DatabaseStatementTest.java
@@ -17,14 +17,14 @@
 package android.database;
 
 import android.content.Context;
-import android.database.Cursor;
 import android.database.sqlite.SQLiteConstraintException;
 import android.database.sqlite.SQLiteDatabase;
 import android.database.sqlite.SQLiteDoneException;
 import android.database.sqlite.SQLiteStatement;
 import android.test.AndroidTestCase;
 import android.test.PerformanceTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
+
+import androidx.test.filters.MediumTest;
 
 import java.io.File;
 
diff --git a/core/tests/coretests/src/android/database/DatabaseStressTest.java b/core/tests/coretests/src/android/database/DatabaseStressTest.java
index 30e46e7..bfea1fc 100644
--- a/core/tests/coretests/src/android/database/DatabaseStressTest.java
+++ b/core/tests/coretests/src/android/database/DatabaseStressTest.java
@@ -17,11 +17,11 @@
 package android.database;
 
 import android.content.Context;
-import android.database.sqlite.*;
+import android.database.sqlite.SQLiteDatabase;
+import android.test.AndroidTestCase;
 import android.util.Log;
 
-import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.Suppress;
+import androidx.test.filters.Suppress;
 
 import java.io.File;
 
diff --git a/core/tests/coretests/src/android/database/DatabaseUtilsTest.java b/core/tests/coretests/src/android/database/DatabaseUtilsTest.java
index 7c206d7..be156c8 100644
--- a/core/tests/coretests/src/android/database/DatabaseUtilsTest.java
+++ b/core/tests/coretests/src/android/database/DatabaseUtilsTest.java
@@ -20,7 +20,7 @@
 
 import static org.junit.Assert.assertEquals;
 
-import android.support.test.runner.AndroidJUnit4;
+import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/database/RedactingCursorTest.java b/core/tests/coretests/src/android/database/RedactingCursorTest.java
index 93998f3..e2d2bae 100644
--- a/core/tests/coretests/src/android/database/RedactingCursorTest.java
+++ b/core/tests/coretests/src/android/database/RedactingCursorTest.java
@@ -22,8 +22,9 @@
 
 import android.content.Context;
 import android.net.Uri;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/database/SQLiteOpenHelperTest.java b/core/tests/coretests/src/android/database/SQLiteOpenHelperTest.java
index 9ed3f11b..730a3cb 100644
--- a/core/tests/coretests/src/android/database/SQLiteOpenHelperTest.java
+++ b/core/tests/coretests/src/android/database/SQLiteOpenHelperTest.java
@@ -26,11 +26,12 @@
 import android.database.sqlite.SQLiteDatabaseConfiguration;
 import android.database.sqlite.SQLiteDebug;
 import android.database.sqlite.SQLiteOpenHelper;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
 import android.util.Log;
 
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
diff --git a/core/tests/coretests/src/android/database/run_newdb_perf_test.sh b/core/tests/coretests/src/android/database/run_newdb_perf_test.sh
index c5b2c97..95f1f83 100755
--- a/core/tests/coretests/src/android/database/run_newdb_perf_test.sh
+++ b/core/tests/coretests/src/android/database/run_newdb_perf_test.sh
@@ -23,7 +23,7 @@
 
 for (( i=0; i<$RUN_N; i++ ))
 do
-    adb  shell am instrument -e class 'android.database.NewDatabasePerformanceTestSuite' -w 'com.android.frameworks.coretests/android.support.test.runner.AndroidJUnitRunner'
+    adb  shell am instrument -e class 'android.database.NewDatabasePerformanceTestSuite' -w 'com.android.frameworks.coretests/androidx.test.runner.AndroidJUnitRunner'
 done
 
 adb logcat -d > /tmp/testlogcat.txt
diff --git a/core/tests/coretests/src/android/database/sqlite/SQLiteCompatibilityWalFlagsTest.java b/core/tests/coretests/src/android/database/sqlite/SQLiteCompatibilityWalFlagsTest.java
index 551a58e..5dbcb3c 100644
--- a/core/tests/coretests/src/android/database/sqlite/SQLiteCompatibilityWalFlagsTest.java
+++ b/core/tests/coretests/src/android/database/sqlite/SQLiteCompatibilityWalFlagsTest.java
@@ -22,9 +22,10 @@
 
 import android.content.Context;
 import android.database.DatabaseUtils;
-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.After;
 import org.junit.Test;
diff --git a/core/tests/coretests/src/android/database/sqlite/SQLiteConnectionPoolTest.java b/core/tests/coretests/src/android/database/sqlite/SQLiteConnectionPoolTest.java
index ed14a53..f1d27d4 100644
--- a/core/tests/coretests/src/android/database/sqlite/SQLiteConnectionPoolTest.java
+++ b/core/tests/coretests/src/android/database/sqlite/SQLiteConnectionPoolTest.java
@@ -21,11 +21,12 @@
 
 import android.content.Context;
 import android.os.HandlerThread;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
 import android.util.Log;
 
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
diff --git a/core/tests/coretests/src/android/database/sqlite/SQLiteCursorTest.java b/core/tests/coretests/src/android/database/sqlite/SQLiteCursorTest.java
index c52cf6e..78d3c41 100644
--- a/core/tests/coretests/src/android/database/sqlite/SQLiteCursorTest.java
+++ b/core/tests/coretests/src/android/database/sqlite/SQLiteCursorTest.java
@@ -23,7 +23,8 @@
 import android.database.CursorWindow;
 import android.platform.test.annotations.Presubmit;
 import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.LargeTest;
+
+import androidx.test.filters.LargeTest;
 
 import java.io.File;
 import java.util.Arrays;
diff --git a/core/tests/coretests/src/android/graphics/BitmapFactoryTest.java b/core/tests/coretests/src/android/graphics/BitmapFactoryTest.java
index e9e2a4d..564460e 100644
--- a/core/tests/coretests/src/android/graphics/BitmapFactoryTest.java
+++ b/core/tests/coretests/src/android/graphics/BitmapFactoryTest.java
@@ -17,14 +17,14 @@
 package android.graphics;
 
 import android.os.ParcelFileDescriptor;
-import android.test.suitebuilder.annotation.SmallTest;
+
+import androidx.test.filters.SmallTest;
 
 import junit.framework.TestCase;
 
 import java.io.ByteArrayOutputStream;
 import java.io.FileDescriptor;
 
-
 public class BitmapFactoryTest extends TestCase {
 
     // tests that we can decode bitmaps from MemoryFiles
diff --git a/core/tests/coretests/src/android/graphics/BitmapTest.java b/core/tests/coretests/src/android/graphics/BitmapTest.java
index 3666ddd..e79d2ae 100644
--- a/core/tests/coretests/src/android/graphics/BitmapTest.java
+++ b/core/tests/coretests/src/android/graphics/BitmapTest.java
@@ -16,11 +16,10 @@
 
 package android.graphics;
 
-import android.test.suitebuilder.annotation.SmallTest;
+import androidx.test.filters.SmallTest;
 
 import junit.framework.TestCase;
 
-
 public class BitmapTest extends TestCase {
 
     @SmallTest
diff --git a/core/tests/coretests/src/android/graphics/ColorSpaceRendererTest.java b/core/tests/coretests/src/android/graphics/ColorSpaceRendererTest.java
index 6e38fb6..8e9b38c 100644
--- a/core/tests/coretests/src/android/graphics/ColorSpaceRendererTest.java
+++ b/core/tests/coretests/src/android/graphics/ColorSpaceRendererTest.java
@@ -19,8 +19,8 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 
-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/coretests/src/android/graphics/ColorStateListTest.java b/core/tests/coretests/src/android/graphics/ColorStateListTest.java
index 374d142..1d34f93 100644
--- a/core/tests/coretests/src/android/graphics/ColorStateListTest.java
+++ b/core/tests/coretests/src/android/graphics/ColorStateListTest.java
@@ -19,7 +19,8 @@
 import android.content.res.ColorStateList;
 import android.content.res.Resources;
 import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
+
+import androidx.test.filters.SmallTest;
 
 import com.android.frameworks.coretests.R;
 
diff --git a/core/tests/coretests/src/android/graphics/FontFileUtilTest.java b/core/tests/coretests/src/android/graphics/FontFileUtilTest.java
index 76267b2..1771671 100644
--- a/core/tests/coretests/src/android/graphics/FontFileUtilTest.java
+++ b/core/tests/coretests/src/android/graphics/FontFileUtilTest.java
@@ -22,11 +22,12 @@
 import android.content.res.AssetManager;
 import android.graphics.fonts.FontFileUtil;
 import android.graphics.fonts.FontVariationAxis;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SmallTest;
 import android.util.Log;
 import android.util.Pair;
 
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+
 import org.junit.Test;
 
 import java.io.File;
diff --git a/core/tests/coretests/src/android/graphics/GraphicsPerformanceTests.java b/core/tests/coretests/src/android/graphics/GraphicsPerformanceTests.java
index 164c1aa..3cfeb25 100644
--- a/core/tests/coretests/src/android/graphics/GraphicsPerformanceTests.java
+++ b/core/tests/coretests/src/android/graphics/GraphicsPerformanceTests.java
@@ -20,7 +20,8 @@
 import android.content.res.Resources;
 import android.test.AndroidTestCase;
 import android.test.PerformanceTestCase;
-import android.test.suitebuilder.annotation.Suppress;
+
+import androidx.test.filters.Suppress;
 
 import com.android.frameworks.coretests.R;
 
diff --git a/core/tests/coretests/src/android/graphics/PaintTest.java b/core/tests/coretests/src/android/graphics/PaintTest.java
index b5ed01f..bf56df1 100644
--- a/core/tests/coretests/src/android/graphics/PaintTest.java
+++ b/core/tests/coretests/src/android/graphics/PaintTest.java
@@ -18,9 +18,9 @@
 
 import static org.junit.Assert.assertNotEquals;
 
-import android.graphics.Paint;
 import android.test.InstrumentationTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
+
+import androidx.test.filters.SmallTest;
 
 import java.util.Arrays;
 import java.util.HashSet;
diff --git a/core/tests/coretests/src/android/graphics/PathOffsetTest.java b/core/tests/coretests/src/android/graphics/PathOffsetTest.java
index 950f873..6cc42f6 100644
--- a/core/tests/coretests/src/android/graphics/PathOffsetTest.java
+++ b/core/tests/coretests/src/android/graphics/PathOffsetTest.java
@@ -16,13 +16,13 @@
 
 package android.graphics;
 
+import static org.junit.Assert.assertTrue;
 
 import android.graphics.Bitmap.Config;
 import android.graphics.Path.Direction;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
 
-import static org.junit.Assert.assertTrue;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/graphics/PathTest.java b/core/tests/coretests/src/android/graphics/PathTest.java
index 78e4959..c6d6d1f 100644
--- a/core/tests/coretests/src/android/graphics/PathTest.java
+++ b/core/tests/coretests/src/android/graphics/PathTest.java
@@ -16,11 +16,10 @@
 
 package android.graphics;
 
-import android.test.suitebuilder.annotation.SmallTest;
+import androidx.test.filters.SmallTest;
 
 import junit.framework.TestCase;
 
-
 public class PathTest extends TestCase {
 
     @SmallTest
diff --git a/core/tests/coretests/src/android/graphics/RectTest.java b/core/tests/coretests/src/android/graphics/RectTest.java
index d31d7d5..2918f44 100644
--- a/core/tests/coretests/src/android/graphics/RectTest.java
+++ b/core/tests/coretests/src/android/graphics/RectTest.java
@@ -23,8 +23,9 @@
 import static org.junit.Assert.assertNull;
 
 import android.platform.test.annotations.Presubmit;
-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/coretests/src/android/graphics/ThreadBitmapTest.java b/core/tests/coretests/src/android/graphics/ThreadBitmapTest.java
index 909a8d9..e1ca7df 100644
--- a/core/tests/coretests/src/android/graphics/ThreadBitmapTest.java
+++ b/core/tests/coretests/src/android/graphics/ThreadBitmapTest.java
@@ -16,7 +16,7 @@
 
 package android.graphics;
 
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
 
 import junit.framework.TestCase;
 
diff --git a/core/tests/coretests/src/android/graphics/TypefaceSystemFallbackTest.java b/core/tests/coretests/src/android/graphics/TypefaceSystemFallbackTest.java
index 6fdb71f..c66bac6 100644
--- a/core/tests/coretests/src/android/graphics/TypefaceSystemFallbackTest.java
+++ b/core/tests/coretests/src/android/graphics/TypefaceSystemFallbackTest.java
@@ -27,12 +27,13 @@
 import android.graphics.fonts.FontCustomizationParser;
 import android.graphics.fonts.FontFamily;
 import android.graphics.fonts.SystemFonts;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
 import android.text.FontConfig;
 import android.util.ArrayMap;
 
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
diff --git a/core/tests/coretests/src/android/graphics/TypefaceTest.java b/core/tests/coretests/src/android/graphics/TypefaceTest.java
index b0c7976..2d16f82 100644
--- a/core/tests/coretests/src/android/graphics/TypefaceTest.java
+++ b/core/tests/coretests/src/android/graphics/TypefaceTest.java
@@ -22,13 +22,12 @@
 import android.content.Context;
 import android.content.res.AssetManager;
 import android.content.res.Resources;
-import android.graphics.Paint;
-import android.graphics.Typeface;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.runner.AndroidJUnit4;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.test.suitebuilder.annotation.SmallTest;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.LargeTest;
+import androidx.test.filters.MediumTest;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
 
 import com.android.frameworks.coretests.R;
 
diff --git a/core/tests/coretests/src/android/graphics/drawable/AdaptiveIconDrawableTest.java b/core/tests/coretests/src/android/graphics/drawable/AdaptiveIconDrawableTest.java
index 781e343..3dc9987 100644
--- a/core/tests/coretests/src/android/graphics/drawable/AdaptiveIconDrawableTest.java
+++ b/core/tests/coretests/src/android/graphics/drawable/AdaptiveIconDrawableTest.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright (C) 2017 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.graphics.drawable;
 
 import android.content.res.Resources;
@@ -11,11 +27,12 @@
 import android.graphics.Rect;
 import android.graphics.RectF;
 import android.graphics.Region;
-import android.support.test.filters.LargeTest;
 import android.test.AndroidTestCase;
 import android.util.Log;
 import android.util.PathParser;
 
+import androidx.test.filters.LargeTest;
+
 import org.junit.Test;
 
 import java.io.File;
diff --git a/core/tests/coretests/src/android/graphics/drawable/DrawableWrapperTest.java b/core/tests/coretests/src/android/graphics/drawable/DrawableWrapperTest.java
index 655efb5..d0a6ff9 100644
--- a/core/tests/coretests/src/android/graphics/drawable/DrawableWrapperTest.java
+++ b/core/tests/coretests/src/android/graphics/drawable/DrawableWrapperTest.java
@@ -24,8 +24,9 @@
 import android.graphics.PorterDuff.Mode;
 import android.graphics.PorterDuffXfermode;
 import android.graphics.Xfermode;
-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/coretests/src/android/graphics/drawable/IconTest.java b/core/tests/coretests/src/android/graphics/drawable/IconTest.java
index 64fadc0..2bdcc28 100644
--- a/core/tests/coretests/src/android/graphics/drawable/IconTest.java
+++ b/core/tests/coretests/src/android/graphics/drawable/IconTest.java
@@ -25,9 +25,10 @@
 import android.os.HandlerThread;
 import android.os.Parcel;
 import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
 import android.util.Log;
 
+import androidx.test.filters.SmallTest;
+
 import com.android.frameworks.coretests.R;
 
 import java.io.ByteArrayOutputStream;
diff --git a/core/tests/coretests/src/android/hardware/display/AmbientBrightnessDayStatsTest.java b/core/tests/coretests/src/android/hardware/display/AmbientBrightnessDayStatsTest.java
index f90ae34..0a25bd7 100644
--- a/core/tests/coretests/src/android/hardware/display/AmbientBrightnessDayStatsTest.java
+++ b/core/tests/coretests/src/android/hardware/display/AmbientBrightnessDayStatsTest.java
@@ -23,8 +23,9 @@
 import static org.junit.Assert.fail;
 
 import android.os.Parcel;
-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/coretests/src/android/hardware/display/BrightnessConfigurationTest.java b/core/tests/coretests/src/android/hardware/display/BrightnessConfigurationTest.java
index dcc51e1..823fca5 100644
--- a/core/tests/coretests/src/android/hardware/display/BrightnessConfigurationTest.java
+++ b/core/tests/coretests/src/android/hardware/display/BrightnessConfigurationTest.java
@@ -22,10 +22,11 @@
 import static org.junit.Assert.fail;
 
 import android.os.Parcel;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
 import android.util.Pair;
 
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
diff --git a/core/tests/coretests/src/android/hardware/display/VirtualDisplayTest.java b/core/tests/coretests/src/android/hardware/display/VirtualDisplayTest.java
index f30b1a2..daf6139 100644
--- a/core/tests/coretests/src/android/hardware/display/VirtualDisplayTest.java
+++ b/core/tests/coretests/src/android/hardware/display/VirtualDisplayTest.java
@@ -22,15 +22,12 @@
 import android.graphics.PixelFormat;
 import android.graphics.Point;
 import android.graphics.drawable.ColorDrawable;
-import android.hardware.display.DisplayManager;
-import android.hardware.display.VirtualDisplay;
 import android.media.Image;
 import android.media.ImageReader;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.SystemClock;
-import android.support.test.filters.LargeTest;
 import android.test.AndroidTestCase;
 import android.util.DisplayMetrics;
 import android.util.Log;
@@ -40,6 +37,8 @@
 import android.view.WindowManager;
 import android.widget.ImageView;
 
+import androidx.test.filters.LargeTest;
+
 import java.nio.ByteBuffer;
 import java.util.concurrent.locks.Lock;
 import java.util.concurrent.locks.ReentrantLock;
diff --git a/core/tests/coretests/src/android/hardware/hdmi/HdmiUtilsTest.java b/core/tests/coretests/src/android/hardware/hdmi/HdmiUtilsTest.java
new file mode 100644
index 0000000..16be0b0
--- /dev/null
+++ b/core/tests/coretests/src/android/hardware/hdmi/HdmiUtilsTest.java
@@ -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.
+ */
+package android.hardware.hdmi;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.support.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@SmallTest
+@RunWith(JUnit4.class)
+/** Tests for {@link HdmiUtils} class. */
+public class HdmiUtilsTest {
+
+    @Test
+    public void pathToPort_isMe() {
+        int targetPhysicalAddress = 0x1000;
+        int myPhysicalAddress = 0x1000;
+        assertThat(HdmiUtils.getLocalPortFromPhysicalAddress(
+                targetPhysicalAddress, myPhysicalAddress)).isEqualTo(
+                HdmiUtils.TARGET_SAME_PHYSICAL_ADDRESS);
+    }
+
+    @Test
+    public void pathToPort_isDirectlyBelow() {
+        int targetPhysicalAddress = 0x1100;
+        int myPhysicalAddress = 0x1000;
+        assertThat(HdmiUtils.getLocalPortFromPhysicalAddress(
+            targetPhysicalAddress, myPhysicalAddress)).isEqualTo(1);
+    }
+
+    @Test
+    public void pathToPort_isBelow() {
+        int targetPhysicalAddress = 0x1110;
+        int myPhysicalAddress = 0x1000;
+        assertThat(HdmiUtils.getLocalPortFromPhysicalAddress(
+            targetPhysicalAddress, myPhysicalAddress)).isEqualTo(1);
+    }
+
+    @Test
+    public void pathToPort_neitherMeNorBelow() {
+        int targetPhysicalAddress = 0x3000;
+        int myPhysicalAddress = 0x2000;
+        assertThat(HdmiUtils.getLocalPortFromPhysicalAddress(
+                targetPhysicalAddress, myPhysicalAddress)).isEqualTo(
+                HdmiUtils.TARGET_NOT_UNDER_LOCAL_DEVICE);
+
+        targetPhysicalAddress = 0x2200;
+        myPhysicalAddress = 0x3300;
+        assertThat(HdmiUtils.getLocalPortFromPhysicalAddress(
+                targetPhysicalAddress, myPhysicalAddress)).isEqualTo(
+                HdmiUtils.TARGET_NOT_UNDER_LOCAL_DEVICE);
+
+        targetPhysicalAddress = 0x2213;
+        myPhysicalAddress = 0x2212;
+        assertThat(HdmiUtils.getLocalPortFromPhysicalAddress(
+                targetPhysicalAddress, myPhysicalAddress)).isEqualTo(
+                HdmiUtils.TARGET_NOT_UNDER_LOCAL_DEVICE);
+
+        targetPhysicalAddress = 0x2340;
+        myPhysicalAddress = 0x2310;
+        assertThat(HdmiUtils.getLocalPortFromPhysicalAddress(
+                targetPhysicalAddress, myPhysicalAddress)).isEqualTo(
+                HdmiUtils.TARGET_NOT_UNDER_LOCAL_DEVICE);
+    }
+}
diff --git a/core/tests/coretests/src/android/metrics/LogMakerTest.java b/core/tests/coretests/src/android/metrics/LogMakerTest.java
index 3be776d..aabfc28 100644
--- a/core/tests/coretests/src/android/metrics/LogMakerTest.java
+++ b/core/tests/coretests/src/android/metrics/LogMakerTest.java
@@ -13,9 +13,10 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package android.metrics;
 
-import android.support.test.filters.LargeTest;
+import androidx.test.filters.LargeTest;
 
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 
diff --git a/core/tests/coretests/src/android/metrics/MetricsReaderTest.java b/core/tests/coretests/src/android/metrics/MetricsReaderTest.java
index 784a12f..96dac64 100644
--- a/core/tests/coretests/src/android/metrics/MetricsReaderTest.java
+++ b/core/tests/coretests/src/android/metrics/MetricsReaderTest.java
@@ -13,10 +13,12 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package android.metrics;
 
 import android.metrics.MetricsReader.Event;
-import android.support.test.filters.LargeTest;
+
+import androidx.test.filters.LargeTest;
 
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 
diff --git a/core/tests/coretests/src/android/net/LocalSocketTest.java b/core/tests/coretests/src/android/net/LocalSocketTest.java
index 1286b13..9172237 100644
--- a/core/tests/coretests/src/android/net/LocalSocketTest.java
+++ b/core/tests/coretests/src/android/net/LocalSocketTest.java
@@ -16,12 +16,9 @@
 
 package android.net;
 
-import android.net.Credentials;
-import android.net.LocalServerSocket;
-import android.net.LocalSocket;
-import android.net.LocalSocketAddress;
 import android.test.MoreAsserts;
-import android.test.suitebuilder.annotation.SmallTest;
+
+import androidx.test.filters.SmallTest;
 
 import junit.framework.TestCase;
 
diff --git a/core/tests/coretests/src/android/net/NetworkKeyTest.java b/core/tests/coretests/src/android/net/NetworkKeyTest.java
index fff23a0..0f1c71d 100644
--- a/core/tests/coretests/src/android/net/NetworkKeyTest.java
+++ b/core/tests/coretests/src/android/net/NetworkKeyTest.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright (C) 2014 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 static org.junit.Assert.assertEquals;
@@ -7,7 +23,8 @@
 import android.net.wifi.ScanResult;
 import android.net.wifi.WifiInfo;
 import android.net.wifi.WifiSsid;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Before;
 import org.junit.Test;
diff --git a/core/tests/coretests/src/android/net/NetworkPolicyManagerTest.java b/core/tests/coretests/src/android/net/NetworkPolicyManagerTest.java
index c6758ce..29e212f 100644
--- a/core/tests/coretests/src/android/net/NetworkPolicyManagerTest.java
+++ b/core/tests/coretests/src/android/net/NetworkPolicyManagerTest.java
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package android.net;
 
 import static android.net.NetworkPolicyManager.MASK_ALL_NETWORKS;
diff --git a/core/tests/coretests/src/android/net/NetworkRecommendationProviderTest.java b/core/tests/coretests/src/android/net/NetworkRecommendationProviderTest.java
index ff9816a..3e45a79 100644
--- a/core/tests/coretests/src/android/net/NetworkRecommendationProviderTest.java
+++ b/core/tests/coretests/src/android/net/NetworkRecommendationProviderTest.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
 package android.net;
 
 import static junit.framework.Assert.assertFalse;
@@ -9,7 +25,8 @@
 
 import android.Manifest.permission;
 import android.content.Context;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Before;
 import org.junit.Test;
diff --git a/core/tests/coretests/src/android/net/SSLCertificateSocketFactoryTest.java b/core/tests/coretests/src/android/net/SSLCertificateSocketFactoryTest.java
index 5cbf02a..bc12e72 100644
--- a/core/tests/coretests/src/android/net/SSLCertificateSocketFactoryTest.java
+++ b/core/tests/coretests/src/android/net/SSLCertificateSocketFactoryTest.java
@@ -19,7 +19,7 @@
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
-import android.support.test.runner.AndroidJUnit4;
+import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/net/SSLSessionCacheTest.java b/core/tests/coretests/src/android/net/SSLSessionCacheTest.java
index 11d066b..eec09e6 100644
--- a/core/tests/coretests/src/android/net/SSLSessionCacheTest.java
+++ b/core/tests/coretests/src/android/net/SSLSessionCacheTest.java
@@ -19,10 +19,10 @@
 import com.android.org.conscrypt.ClientSessionContext;
 import com.android.org.conscrypt.SSLClientSessionCache;
 
-import org.mockito.Mockito;
-
 import junit.framework.TestCase;
 
+import org.mockito.Mockito;
+
 import java.security.KeyManagementException;
 import java.security.SecureRandom;
 
diff --git a/core/tests/coretests/src/android/net/ScoredNetworkTest.java b/core/tests/coretests/src/android/net/ScoredNetworkTest.java
index 109f32e..d984d86 100644
--- a/core/tests/coretests/src/android/net/ScoredNetworkTest.java
+++ b/core/tests/coretests/src/android/net/ScoredNetworkTest.java
@@ -16,11 +16,17 @@
 
 package android.net;
 
-import static org.junit.Assert.*;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
 
 import android.os.Bundle;
 import android.os.Parcel;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/net/SntpClientTest.java b/core/tests/coretests/src/android/net/SntpClientTest.java
index d72738c..87edb6e 100644
--- a/core/tests/coretests/src/android/net/SntpClientTest.java
+++ b/core/tests/coretests/src/android/net/SntpClientTest.java
@@ -20,9 +20,10 @@
 import static junit.framework.Assert.assertFalse;
 import static junit.framework.Assert.assertTrue;
 
-import android.support.test.runner.AndroidJUnit4;
 import android.util.Log;
 
+import androidx.test.runner.AndroidJUnit4;
+
 import libcore.util.HexEncoding;
 
 import org.junit.Before;
diff --git a/core/tests/coretests/src/android/net/UriTest.java b/core/tests/coretests/src/android/net/UriTest.java
index ea0347d..a33de7b 100644
--- a/core/tests/coretests/src/android/net/UriTest.java
+++ b/core/tests/coretests/src/android/net/UriTest.java
@@ -18,7 +18,8 @@
 
 import android.content.ContentUris;
 import android.os.Parcel;
-import android.test.suitebuilder.annotation.SmallTest;
+
+import androidx.test.filters.SmallTest;
 
 import junit.framework.TestCase;
 
diff --git a/core/tests/coretests/src/android/net/WebAddressTest.java b/core/tests/coretests/src/android/net/WebAddressTest.java
index 6fcb97e..70a6253 100644
--- a/core/tests/coretests/src/android/net/WebAddressTest.java
+++ b/core/tests/coretests/src/android/net/WebAddressTest.java
@@ -16,8 +16,8 @@
 
 package android.net;
 
-import android.net.WebAddress;
-import android.test.suitebuilder.annotation.SmallTest;
+import androidx.test.filters.SmallTest;
+
 import junit.framework.TestCase;
 
 public class WebAddressTest extends TestCase {
diff --git a/core/tests/coretests/src/android/net/http/SslCertificateTest.java b/core/tests/coretests/src/android/net/http/SslCertificateTest.java
index 6a30c6c..1beb1de 100644
--- a/core/tests/coretests/src/android/net/http/SslCertificateTest.java
+++ b/core/tests/coretests/src/android/net/http/SslCertificateTest.java
@@ -17,12 +17,13 @@
 
 package android.net.http;
 
-import android.net.http.SslCertificate;
-import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.filters.LargeTest;
+
+import junit.framework.TestCase;
+
 import java.io.ByteArrayInputStream;
 import java.security.cert.CertificateFactory;
 import java.security.cert.X509Certificate;
-import junit.framework.TestCase;
 
 public class SslCertificateTest extends TestCase {
 
diff --git a/core/tests/coretests/src/android/os/AidlTest.java b/core/tests/coretests/src/android/os/AidlTest.java
index bf11d56..4c51415 100644
--- a/core/tests/coretests/src/android/os/AidlTest.java
+++ b/core/tests/coretests/src/android/os/AidlTest.java
@@ -16,11 +16,10 @@
 
 package android.os;
 
-import android.os.IInterface;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.test.suitebuilder.annotation.SmallTest;
+import androidx.test.filters.SmallTest;
+
 import com.google.android.collect.Lists;
+
 import junit.framework.TestCase;
 
 import java.util.List;
diff --git a/core/tests/coretests/src/android/os/BinderProxyCountingTest.java b/core/tests/coretests/src/android/os/BinderProxyCountingTest.java
index 6cdb35ab..ce6ad87 100644
--- a/core/tests/coretests/src/android/os/BinderProxyCountingTest.java
+++ b/core/tests/coretests/src/android/os/BinderProxyCountingTest.java
@@ -14,7 +14,6 @@
  * limitations under the License.
  */
 
-
 package android.os;
 
 import static org.junit.Assert.assertEquals;
@@ -25,12 +24,13 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.ServiceConnection;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.LargeTest;
-import android.support.test.runner.AndroidJUnit4;
 import android.support.test.uiautomator.UiDevice;
 import android.util.Log;
 
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
+
 import com.android.frameworks.coretests.aidl.IBpcCallbackObserver;
 import com.android.frameworks.coretests.aidl.IBpcTestAppCmdService;
 import com.android.frameworks.coretests.aidl.IBpcTestServiceCmdService;
@@ -63,7 +63,7 @@
  * Install: adb install -r \
  * ${ANDROID_PRODUCT_OUT}/data/app/FrameworksCoreTests/FrameworksCoreTests.apk
  * Run: adb shell am instrument -e class android.os.BinderProxyCountingTest -w \
- * com.android.frameworks.coretests/android.support.test.runner.AndroidJUnitRunner
+ * com.android.frameworks.coretests/androidx.test.runner.AndroidJUnitRunner
  *
  * or
  *
diff --git a/core/tests/coretests/src/android/os/BinderProxyTest.java b/core/tests/coretests/src/android/os/BinderProxyTest.java
index 4c36b5c..aceda2d 100644
--- a/core/tests/coretests/src/android/os/BinderProxyTest.java
+++ b/core/tests/coretests/src/android/os/BinderProxyTest.java
@@ -19,7 +19,8 @@
 import android.annotation.Nullable;
 import android.content.Context;
 import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
+
+import androidx.test.filters.MediumTest;
 
 public class BinderProxyTest extends AndroidTestCase {
     private static class CountingListener implements Binder.ProxyTransactListener {
diff --git a/core/tests/coretests/src/android/os/BinderTest.java b/core/tests/coretests/src/android/os/BinderTest.java
index 6c9c3c1..a354195 100644
--- a/core/tests/coretests/src/android/os/BinderTest.java
+++ b/core/tests/coretests/src/android/os/BinderTest.java
@@ -16,7 +16,7 @@
 
 package android.os;
 
-import android.test.suitebuilder.annotation.SmallTest;
+import androidx.test.filters.SmallTest;
 
 import junit.framework.TestCase;
 
diff --git a/core/tests/coretests/src/android/os/BinderThreadPriorityService.java b/core/tests/coretests/src/android/os/BinderThreadPriorityService.java
index 47a4483..ed42058 100644
--- a/core/tests/coretests/src/android/os/BinderThreadPriorityService.java
+++ b/core/tests/coretests/src/android/os/BinderThreadPriorityService.java
@@ -18,7 +18,6 @@
 
 import android.app.Service;
 import android.content.Intent;
-import android.text.TextUtils;
 import android.util.Log;
 
 /**
diff --git a/core/tests/coretests/src/android/os/BinderThreadPriorityTest.java b/core/tests/coretests/src/android/os/BinderThreadPriorityTest.java
index 56e977c..48c9df6 100644
--- a/core/tests/coretests/src/android/os/BinderThreadPriorityTest.java
+++ b/core/tests/coretests/src/android/os/BinderThreadPriorityTest.java
@@ -21,7 +21,6 @@
 import android.content.Intent;
 import android.content.ServiceConnection;
 import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.util.Log;
 
 import java.io.File;
diff --git a/core/tests/coretests/src/android/os/BinderWorkSourceTest.java b/core/tests/coretests/src/android/os/BinderWorkSourceTest.java
index 5664df6..e16a3db 100644
--- a/core/tests/coretests/src/android/os/BinderWorkSourceTest.java
+++ b/core/tests/coretests/src/android/os/BinderWorkSourceTest.java
@@ -24,9 +24,10 @@
 import android.content.Intent;
 import android.content.ServiceConnection;
 import android.platform.test.annotations.Presubmit;
-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;
diff --git a/core/tests/coretests/src/android/os/BrightnessLimit.java b/core/tests/coretests/src/android/os/BrightnessLimit.java
index fabcf3d..5a3724f 100644
--- a/core/tests/coretests/src/android/os/BrightnessLimit.java
+++ b/core/tests/coretests/src/android/os/BrightnessLimit.java
@@ -18,7 +18,6 @@
 
 import android.app.Activity;
 import android.hardware.display.DisplayManager;
-import android.os.Bundle;
 import android.provider.Settings;
 import android.view.View;
 import android.view.View.OnClickListener;
diff --git a/core/tests/coretests/src/android/os/BroadcasterTest.java b/core/tests/coretests/src/android/os/BroadcasterTest.java
index 551ea8d..b4c47af9 100644
--- a/core/tests/coretests/src/android/os/BroadcasterTest.java
+++ b/core/tests/coretests/src/android/os/BroadcasterTest.java
@@ -16,11 +16,8 @@
 
 package android.os;
 
-import android.os.Broadcaster;
-import android.os.Handler;
-import android.os.Message;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.test.suitebuilder.annotation.SmallTest;
+import androidx.test.filters.MediumTest;
+
 import junit.framework.TestCase;
 
 public class BroadcasterTest extends TestCase {
diff --git a/core/tests/coretests/src/android/os/BuildTest.java b/core/tests/coretests/src/android/os/BuildTest.java
index 3758627..decc768 100644
--- a/core/tests/coretests/src/android/os/BuildTest.java
+++ b/core/tests/coretests/src/android/os/BuildTest.java
@@ -16,9 +16,8 @@
 
 package android.os;
 
-import android.os.Build;
-import android.test.suitebuilder.annotation.SmallTest;
-import android.util.Log;
+import androidx.test.filters.SmallTest;
+
 import junit.framework.Assert;
 import junit.framework.TestCase;
 
diff --git a/core/tests/coretests/src/android/os/BundleTest.java b/core/tests/coretests/src/android/os/BundleTest.java
index 9fcf96d..e4dc993 100644
--- a/core/tests/coretests/src/android/os/BundleTest.java
+++ b/core/tests/coretests/src/android/os/BundleTest.java
@@ -20,8 +20,8 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
-import android.support.test.runner.AndroidJUnit4;
-import android.test.suitebuilder.annotation.SmallTest;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/os/EnvironmentTest.java b/core/tests/coretests/src/android/os/EnvironmentTest.java
index 5189df5..d98ceaf 100644
--- a/core/tests/coretests/src/android/os/EnvironmentTest.java
+++ b/core/tests/coretests/src/android/os/EnvironmentTest.java
@@ -25,8 +25,9 @@
 import static org.junit.Assert.assertEquals;
 
 import android.content.Context;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.After;
 import org.junit.Before;
diff --git a/core/tests/coretests/src/android/os/FileObserverTest.java b/core/tests/coretests/src/android/os/FileObserverTest.java
index 93e27af..ece7645 100644
--- a/core/tests/coretests/src/android/os/FileObserverTest.java
+++ b/core/tests/coretests/src/android/os/FileObserverTest.java
@@ -16,13 +16,14 @@
 
 package android.os;
 
+import android.test.AndroidTestCase;
+import android.util.Log;
+
+import androidx.test.filters.MediumTest;
+
 import com.google.android.collect.Lists;
 import com.google.android.collect.Maps;
 
-import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.util.Log;
-
 import java.io.File;
 import java.io.FileOutputStream;
 import java.util.Iterator;
diff --git a/core/tests/coretests/src/android/os/FileUtilsTest.java b/core/tests/coretests/src/android/os/FileUtilsTest.java
index 514ea0c..5bce227c 100644
--- a/core/tests/coretests/src/android/os/FileUtilsTest.java
+++ b/core/tests/coretests/src/android/os/FileUtilsTest.java
@@ -51,8 +51,9 @@
 import android.content.Context;
 import android.os.FileUtils.MemoryPipe;
 import android.provider.DocumentsContract.Document;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
 
 import libcore.io.Streams;
 
diff --git a/core/tests/coretests/src/android/os/HandlerTester.java b/core/tests/coretests/src/android/os/HandlerTester.java
index a216a0b..fa442f4 100644
--- a/core/tests/coretests/src/android/os/HandlerTester.java
+++ b/core/tests/coretests/src/android/os/HandlerTester.java
@@ -16,10 +16,6 @@
 
 package android.os;
 
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
-
 public abstract class HandlerTester extends Thread {
     public abstract void go();
     public abstract void handleMessage(Message msg);
diff --git a/core/tests/coretests/src/android/os/HandlerThreadTest.java b/core/tests/coretests/src/android/os/HandlerThreadTest.java
index 9772aa4..93cfc40 100644
--- a/core/tests/coretests/src/android/os/HandlerThreadTest.java
+++ b/core/tests/coretests/src/android/os/HandlerThreadTest.java
@@ -16,13 +16,9 @@
 
 package android.os;
 
+import androidx.test.filters.MediumTest;
+
 import junit.framework.TestCase;
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.Message;
-import android.os.Process;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.test.suitebuilder.annotation.SmallTest;
 
 public class HandlerThreadTest extends TestCase {
     private static final int TEST_WHAT = 1;
diff --git a/core/tests/coretests/src/android/os/IdleHandlerTest.java b/core/tests/coretests/src/android/os/IdleHandlerTest.java
index 6c0a862..d8886c9 100644
--- a/core/tests/coretests/src/android/os/IdleHandlerTest.java
+++ b/core/tests/coretests/src/android/os/IdleHandlerTest.java
@@ -16,11 +16,10 @@
 
 package android.os;
 
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
 import android.os.MessageQueue.IdleHandler;
-import android.test.suitebuilder.annotation.MediumTest;
+
+import androidx.test.filters.MediumTest;
+
 import junit.framework.TestCase;
 
 public class IdleHandlerTest extends TestCase {
diff --git a/core/tests/coretests/src/android/os/LocaleListTest.java b/core/tests/coretests/src/android/os/LocaleListTest.java
index 17ef773..1f00a7a 100644
--- a/core/tests/coretests/src/android/os/LocaleListTest.java
+++ b/core/tests/coretests/src/android/os/LocaleListTest.java
@@ -13,14 +13,15 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package android.os;
 
-import android.test.suitebuilder.annotation.SmallTest;
-
-import java.util.Locale;
+import androidx.test.filters.SmallTest;
 
 import junit.framework.TestCase;
 
+import java.util.Locale;
+
 public class LocaleListTest extends TestCase {
     @SmallTest
     public void testConstructor() throws Exception {
diff --git a/core/tests/coretests/src/android/os/MemoryFileTest.java b/core/tests/coretests/src/android/os/MemoryFileTest.java
index 20b298d..05c2995 100644
--- a/core/tests/coretests/src/android/os/MemoryFileTest.java
+++ b/core/tests/coretests/src/android/os/MemoryFileTest.java
@@ -17,8 +17,9 @@
 package android.os;
 
 import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.test.suitebuilder.annotation.SmallTest;
+
+import androidx.test.filters.LargeTest;
+import androidx.test.filters.SmallTest;
 
 import java.io.IOException;
 import java.io.InputStream;
diff --git a/core/tests/coretests/src/android/os/MessageQueueTest.java b/core/tests/coretests/src/android/os/MessageQueueTest.java
index 1cd1020..2c5588e 100644
--- a/core/tests/coretests/src/android/os/MessageQueueTest.java
+++ b/core/tests/coretests/src/android/os/MessageQueueTest.java
@@ -16,11 +16,9 @@
 
 package android.os;
 
-import android.os.Handler;
-import android.os.Message;
-import android.os.SystemClock;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.test.suitebuilder.annotation.Suppress;
+import androidx.test.filters.MediumTest;
+import androidx.test.filters.Suppress;
+
 import junit.framework.TestCase;
 
 @Suppress  // Failing.
diff --git a/core/tests/coretests/src/android/os/MessengerService.java b/core/tests/coretests/src/android/os/MessengerService.java
index f15e134..db33471 100644
--- a/core/tests/coretests/src/android/os/MessengerService.java
+++ b/core/tests/coretests/src/android/os/MessengerService.java
@@ -18,11 +18,6 @@
 
 import android.app.Service;
 import android.content.Intent;
-import android.os.RemoteException;
-import android.os.IBinder;
-import android.os.Handler;
-import android.os.Message;
-import android.os.Messenger;
 
 public class MessengerService extends Service {
     private final Handler mHandler = new Handler() {
diff --git a/core/tests/coretests/src/android/os/MessengerTest.java b/core/tests/coretests/src/android/os/MessengerTest.java
index 473ffe2..9143ff1 100644
--- a/core/tests/coretests/src/android/os/MessengerTest.java
+++ b/core/tests/coretests/src/android/os/MessengerTest.java
@@ -20,13 +20,9 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.ServiceConnection;
-import android.os.RemoteException;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.Message;
-import android.os.Messenger;
 import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
+
+import androidx.test.filters.MediumTest;
 
 public class MessengerTest extends AndroidTestCase {
     private Messenger mServiceMessenger;
diff --git a/core/tests/coretests/src/android/os/OsTests.java b/core/tests/coretests/src/android/os/OsTests.java
index 2b84126..08fb945 100644
--- a/core/tests/coretests/src/android/os/OsTests.java
+++ b/core/tests/coretests/src/android/os/OsTests.java
@@ -16,12 +16,8 @@
 
 package android.os;
 
-import com.google.android.collect.Lists;
 import junit.framework.TestSuite;
 
-import java.util.Enumeration;
-import java.util.List;
-
 public class OsTests {
     public static TestSuite suite() {
         TestSuite suite = new TestSuite(OsTests.class.getName());
diff --git a/core/tests/coretests/src/android/os/ParcelNullabilityTest.java b/core/tests/coretests/src/android/os/ParcelNullabilityTest.java
index 516dc0a..a6b296d 100644
--- a/core/tests/coretests/src/android/os/ParcelNullabilityTest.java
+++ b/core/tests/coretests/src/android/os/ParcelNullabilityTest.java
@@ -20,10 +20,11 @@
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
-import android.support.test.runner.AndroidJUnit4;
-import android.test.suitebuilder.annotation.SmallTest;
 import android.util.ArrayMap;
 
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
diff --git a/core/tests/coretests/src/android/os/PatternMatcherTest.java b/core/tests/coretests/src/android/os/PatternMatcherTest.java
index 9645ccc..82350cd 100644
--- a/core/tests/coretests/src/android/os/PatternMatcherTest.java
+++ b/core/tests/coretests/src/android/os/PatternMatcherTest.java
@@ -1,9 +1,27 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
 package android.os;
 
-import android.test.suitebuilder.annotation.SmallTest;
+import androidx.test.filters.SmallTest;
+
 import junit.framework.TestCase;
-import org.junit.runner.RunWith;
+
 import org.junit.Test;
+import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
 
 @RunWith(JUnit4.class)
diff --git a/core/tests/coretests/src/android/os/PerformanceCollectorTest.java b/core/tests/coretests/src/android/os/PerformanceCollectorTest.java
index 7533c84..38ad90f 100644
--- a/core/tests/coretests/src/android/os/PerformanceCollectorTest.java
+++ b/core/tests/coretests/src/android/os/PerformanceCollectorTest.java
@@ -17,15 +17,16 @@
 package android.os;
 
 import android.os.PerformanceCollector.PerformanceResultsWriter;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.test.suitebuilder.annotation.SmallTest;
+
+import androidx.test.filters.MediumTest;
+import androidx.test.filters.SmallTest;
+
+import junit.framework.TestCase;
 
 import java.lang.reflect.Field;
 import java.util.ArrayList;
 import java.util.Random;
 
-import junit.framework.TestCase;
-
 public class PerformanceCollectorTest extends TestCase {
 
     private PerformanceCollector mPerfCollector;
diff --git a/core/tests/coretests/src/android/os/PowerManagerTest.java b/core/tests/coretests/src/android/os/PowerManagerTest.java
index a828f44..1b587dd 100644
--- a/core/tests/coretests/src/android/os/PowerManagerTest.java
+++ b/core/tests/coretests/src/android/os/PowerManagerTest.java
@@ -21,10 +21,11 @@
 import static org.mockito.Mockito.verify;
 
 import android.content.Context;
-import android.support.test.InstrumentationRegistry;
 import android.support.test.uiautomator.UiDevice;
 import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
 
 import org.junit.After;
 import org.junit.Test;
diff --git a/core/tests/coretests/src/android/os/PowerManagerVrTest.java b/core/tests/coretests/src/android/os/PowerManagerVrTest.java
index e01e5fa..5d2c65b 100644
--- a/core/tests/coretests/src/android/os/PowerManagerVrTest.java
+++ b/core/tests/coretests/src/android/os/PowerManagerVrTest.java
@@ -19,16 +19,14 @@
 import android.content.ComponentName;
 import android.content.ContentResolver;
 import android.content.Context;
-import android.os.PowerManager;
-import android.os.RemoteException;
-import android.os.ServiceManager;
 import android.provider.Settings;
-import android.service.dreams.IDreamManager;
 import android.service.dreams.DreamService;
-import android.support.test.filters.MediumTest;
-import android.support.test.filters.SmallTest;
+import android.service.dreams.IDreamManager;
 import android.test.ActivityInstrumentationTestCase2;
 
+import androidx.test.filters.MediumTest;
+import androidx.test.filters.SmallTest;
+
 /**
  * Tests dream aspects of PowerManager.
  */
diff --git a/core/tests/coretests/src/android/os/ProcessTest.java b/core/tests/coretests/src/android/os/ProcessTest.java
index 1f5b7c8..b749e71 100644
--- a/core/tests/coretests/src/android/os/ProcessTest.java
+++ b/core/tests/coretests/src/android/os/ProcessTest.java
@@ -17,14 +17,10 @@
 
 package android.os;
 
-import android.os.Process;
-import android.os.UserHandle;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.test.suitebuilder.annotation.SmallTest;
+import androidx.test.filters.MediumTest;
 
 import junit.framework.TestCase;
 
-
 public class ProcessTest extends TestCase {
 
     @MediumTest
diff --git a/core/tests/coretests/src/android/os/RedactingFileDescriptorTest.java b/core/tests/coretests/src/android/os/RedactingFileDescriptorTest.java
index 9e15231..d5163e1 100644
--- a/core/tests/coretests/src/android/os/RedactingFileDescriptorTest.java
+++ b/core/tests/coretests/src/android/os/RedactingFileDescriptorTest.java
@@ -24,10 +24,11 @@
 import static org.junit.Assert.assertEquals;
 
 import android.content.Context;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.runner.AndroidJUnit4;
 import android.system.Os;
 
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
diff --git a/core/tests/coretests/src/android/os/SetPersistentVrThreadTest.java b/core/tests/coretests/src/android/os/SetPersistentVrThreadTest.java
index 9e44554..8085993 100644
--- a/core/tests/coretests/src/android/os/SetPersistentVrThreadTest.java
+++ b/core/tests/coretests/src/android/os/SetPersistentVrThreadTest.java
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package android.os;
 
 import android.app.ActivityManager;
@@ -20,10 +21,10 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.pm.PackageManager;
-import android.os.Process;
 import android.provider.Settings;
 import android.test.ActivityInstrumentationTestCase2;
-import android.test.suitebuilder.annotation.SmallTest;
+
+import androidx.test.filters.SmallTest;
 
 /**
  * Tests ActivityManager#setPersistentVrThread and ActivityManager#setVrThread's
diff --git a/core/tests/coretests/src/android/os/TestHandlerThread.java b/core/tests/coretests/src/android/os/TestHandlerThread.java
index 7e84af3..8c0330b 100644
--- a/core/tests/coretests/src/android/os/TestHandlerThread.java
+++ b/core/tests/coretests/src/android/os/TestHandlerThread.java
@@ -16,11 +16,6 @@
 
 package android.os;
 
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
-import android.os.MessageQueue.IdleHandler;
-
 abstract class TestHandlerThread {
     private boolean mDone = false;
     private boolean mSuccess = false;
diff --git a/core/tests/coretests/src/android/os/TestVrActivity.java b/core/tests/coretests/src/android/os/TestVrActivity.java
index 33ff164..75df7c2 100644
--- a/core/tests/coretests/src/android/os/TestVrActivity.java
+++ b/core/tests/coretests/src/android/os/TestVrActivity.java
@@ -17,7 +17,6 @@
 package android.os;
 
 import android.app.Activity;
-import android.os.Bundle;
 import android.service.vr.VrListenerService;
 
 import java.util.concurrent.CountDownLatch;
diff --git a/core/tests/coretests/src/android/os/TraceTest.java b/core/tests/coretests/src/android/os/TraceTest.java
index 1541553..5cad549 100644
--- a/core/tests/coretests/src/android/os/TraceTest.java
+++ b/core/tests/coretests/src/android/os/TraceTest.java
@@ -16,13 +16,13 @@
 
 package android.os;
 
-import android.os.Debug;
 import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.test.suitebuilder.annotation.SmallTest;
-import android.test.suitebuilder.annotation.Suppress;
 import android.util.Log;
 
+import androidx.test.filters.LargeTest;
+import androidx.test.filters.SmallTest;
+import androidx.test.filters.Suppress;
+
 /**
  * This class is used to test the native tracing support.  Run this test
  * while tracing on the emulator and then run traceview to view the trace.
diff --git a/core/tests/coretests/src/android/os/UserHandleTest.java b/core/tests/coretests/src/android/os/UserHandleTest.java
index af559fd..4a1cdbf 100644
--- a/core/tests/coretests/src/android/os/UserHandleTest.java
+++ b/core/tests/coretests/src/android/os/UserHandleTest.java
@@ -25,7 +25,7 @@
 
 import static org.junit.Assert.assertEquals;
 
-import android.support.test.runner.AndroidJUnit4;
+import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/os/VintfObjectTest.java b/core/tests/coretests/src/android/os/VintfObjectTest.java
index 44510c2..af3660a 100644
--- a/core/tests/coretests/src/android/os/VintfObjectTest.java
+++ b/core/tests/coretests/src/android/os/VintfObjectTest.java
@@ -16,7 +16,6 @@
 
 package android.os;
 
-import junit.framework.Assert;
 import junit.framework.TestCase;
 
 public class VintfObjectTest extends TestCase {
diff --git a/core/tests/coretests/src/android/os/storage/StorageManagerIntegrationTest.java b/core/tests/coretests/src/android/os/storage/StorageManagerIntegrationTest.java
index 3ec297c..62f2ac2 100644
--- a/core/tests/coretests/src/android/os/storage/StorageManagerIntegrationTest.java
+++ b/core/tests/coretests/src/android/os/storage/StorageManagerIntegrationTest.java
@@ -19,7 +19,8 @@
 import android.os.ParcelFileDescriptor;
 import android.os.ProxyFileDescriptorCallback;
 import android.system.ErrnoException;
-import android.test.suitebuilder.annotation.LargeTest;
+
+import androidx.test.filters.LargeTest;
 
 import com.android.frameworks.coretests.R;
 
diff --git a/core/tests/coretests/src/android/preference/ListPreferenceTest.java b/core/tests/coretests/src/android/preference/ListPreferenceTest.java
index 72f62f1..51dbb64 100644
--- a/core/tests/coretests/src/android/preference/ListPreferenceTest.java
+++ b/core/tests/coretests/src/android/preference/ListPreferenceTest.java
@@ -16,10 +16,10 @@
 
 package android.preference;
 
-import android.preference.ListPreference;
-import android.support.test.filters.LargeTest;
 import android.test.AndroidTestCase;
 
+import androidx.test.filters.LargeTest;
+
 @LargeTest
 public class ListPreferenceTest extends AndroidTestCase {
     public void testListPreferenceSummaryFromEntries() {
diff --git a/core/tests/coretests/src/android/preference/PreferenceIconSpaceTest.java b/core/tests/coretests/src/android/preference/PreferenceIconSpaceTest.java
index 654474c..0deb77e 100644
--- a/core/tests/coretests/src/android/preference/PreferenceIconSpaceTest.java
+++ b/core/tests/coretests/src/android/preference/PreferenceIconSpaceTest.java
@@ -16,20 +16,20 @@
 
 package android.preference;
 
-
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import android.content.Context;
 import android.graphics.Color;
 import android.graphics.drawable.ColorDrawable;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.ImageView;
 
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/print/IPrintManagerParametersTest.java b/core/tests/coretests/src/android/print/IPrintManagerParametersTest.java
index 5d12f7e..94d85e6 100644
--- a/core/tests/coretests/src/android/print/IPrintManagerParametersTest.java
+++ b/core/tests/coretests/src/android/print/IPrintManagerParametersTest.java
@@ -41,9 +41,10 @@
 import android.print.test.services.PrinterDiscoverySessionCallbacks;
 import android.print.test.services.StubbablePrinterDiscoverySession;
 import android.printservice.recommendation.IRecommendationsChangeListener;
-import android.support.test.filters.LargeTest;
-import android.support.test.filters.MediumTest;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.filters.LargeTest;
+import androidx.test.filters.MediumTest;
+import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Before;
 import org.junit.Test;
diff --git a/core/tests/coretests/src/android/provider/DeviceConfigTest.java b/core/tests/coretests/src/android/provider/DeviceConfigTest.java
index 800b864..17e9654 100644
--- a/core/tests/coretests/src/android/provider/DeviceConfigTest.java
+++ b/core/tests/coretests/src/android/provider/DeviceConfigTest.java
@@ -28,9 +28,10 @@
 import android.os.Bundle;
 import android.os.SystemClock;
 import android.platform.test.annotations.Presubmit;
-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.After;
 import org.junit.Test;
diff --git a/core/tests/coretests/src/android/provider/DocumentsProviderTest.java b/core/tests/coretests/src/android/provider/DocumentsProviderTest.java
index 1465d0a9..02a9adf 100644
--- a/core/tests/coretests/src/android/provider/DocumentsProviderTest.java
+++ b/core/tests/coretests/src/android/provider/DocumentsProviderTest.java
@@ -20,9 +20,10 @@
 import android.content.ContentResolver;
 import android.net.Uri;
 import android.provider.DocumentsContract.Path;
-import android.support.test.filters.SmallTest;
 import android.test.ProviderTestCase2;
 
+import androidx.test.filters.SmallTest;
+
 import java.util.Arrays;
 
 /**
diff --git a/core/tests/coretests/src/android/provider/FontsContractE2ETest.java b/core/tests/coretests/src/android/provider/FontsContractE2ETest.java
index 2955869..7e02be8 100644
--- a/core/tests/coretests/src/android/provider/FontsContractE2ETest.java
+++ b/core/tests/coretests/src/android/provider/FontsContractE2ETest.java
@@ -25,25 +25,25 @@
 import android.app.Instrumentation;
 import android.content.Context;
 import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.Signature;
 import android.graphics.Typeface;
 import android.os.Handler;
-import android.provider.FontsContract.Columns;
 import android.provider.FontsContract.FontFamilyResult;
-import android.provider.FontsContract.FontInfo;
-import android.provider.FontsContract;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
-import java.util.ArrayList;
-import java.util.List;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.util.ArrayList;
+import java.util.List;
+
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 public class FontsContractE2ETest {
diff --git a/core/tests/coretests/src/android/provider/FontsContractTest.java b/core/tests/coretests/src/android/provider/FontsContractTest.java
index d42d79e..c5d6f7f 100644
--- a/core/tests/coretests/src/android/provider/FontsContractTest.java
+++ b/core/tests/coretests/src/android/provider/FontsContractTest.java
@@ -13,31 +13,33 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package android.provider;
 
+import static android.provider.FontsContract.Columns.RESULT_CODE_FONT_NOT_FOUND;
+import static android.provider.FontsContract.Columns.RESULT_CODE_FONT_UNAVAILABLE;
+import static android.provider.FontsContract.Columns.RESULT_CODE_MALFORMED_QUERY;
+import static android.provider.FontsContract.Columns.RESULT_CODE_OK;
+
 import static org.mockito.Matchers.anyInt;
 import static org.mockito.Matchers.anyString;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
-import static android.provider.FontsContract.Columns.RESULT_CODE_OK;
-import static android.provider.FontsContract.Columns.RESULT_CODE_FONT_NOT_FOUND;
-import static android.provider.FontsContract.Columns.RESULT_CODE_FONT_UNAVAILABLE;
-import static android.provider.FontsContract.Columns.RESULT_CODE_MALFORMED_QUERY;
-
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.ProviderInfo;
 import android.content.pm.Signature;
 import android.database.MatrixCursor;
 import android.graphics.fonts.FontVariationAxis;
 import android.provider.FontsContract.FontInfo;
-import android.support.test.filters.SmallTest;
 import android.test.ProviderTestCase2;
 import android.util.Base64;
 
+import androidx.test.filters.SmallTest;
+
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
diff --git a/core/tests/coretests/src/android/provider/MockFontProvider.java b/core/tests/coretests/src/android/provider/MockFontProvider.java
index ad5b130..e527dec 100644
--- a/core/tests/coretests/src/android/provider/MockFontProvider.java
+++ b/core/tests/coretests/src/android/provider/MockFontProvider.java
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package android.provider;
 
 import static android.provider.FontsContract.Columns;
@@ -21,16 +22,13 @@
 import android.content.ContentUris;
 import android.content.ContentValues;
 import android.content.Context;
-import android.content.res.AssetFileDescriptor;
 import android.content.res.AssetManager;
 import android.database.Cursor;
 import android.database.MatrixCursor;
-import android.graphics.fonts.FontVariationAxis;
 import android.net.Uri;
-import android.os.CancellationSignal;
 import android.os.ParcelFileDescriptor;
-import android.util.ArraySet;
-import android.util.SparseArray;
+
+import com.android.internal.annotations.GuardedBy;
 
 import java.io.File;
 import java.io.FileNotFoundException;
@@ -46,8 +44,6 @@
 import java.util.concurrent.locks.Lock;
 import java.util.concurrent.locks.ReentrantLock;
 
-import com.android.internal.annotations.GuardedBy;
-
 public class MockFontProvider extends ContentProvider {
     final static String AUTHORITY = "android.provider.fonts.font";
 
diff --git a/core/tests/coretests/src/android/provider/SearchRecentSuggestionsProviderTest.java b/core/tests/coretests/src/android/provider/SearchRecentSuggestionsProviderTest.java
index 7458de5..f84355f 100644
--- a/core/tests/coretests/src/android/provider/SearchRecentSuggestionsProviderTest.java
+++ b/core/tests/coretests/src/android/provider/SearchRecentSuggestionsProviderTest.java
@@ -21,9 +21,9 @@
 import android.database.Cursor;
 import android.net.Uri;
 import android.test.ProviderTestCase2;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.test.suitebuilder.annotation.Suppress;
+
+import androidx.test.filters.MediumTest;
+import androidx.test.filters.Suppress;
 
 /**
  * ProviderTestCase that performs unit tests of SearchRecentSuggestionsProvider.
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index 1ed5ce4..93af013 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -27,9 +27,9 @@
 import static java.lang.reflect.Modifier.isStatic;
 
 import android.platform.test.annotations.Presubmit;
-import android.provider.Settings.Global;
-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;
@@ -270,6 +270,7 @@
                     Settings.Global.GNSS_HAL_LOCATION_REQUEST_DURATION_MILLIS,
                     Settings.Global.GNSS_SATELLITE_BLACKLIST,
                     Settings.Global.GPRS_REGISTER_CHECK_PERIOD_MS,
+                    Settings.Global.HDMI_CEC_SWITCH_ENABLED,
                     Settings.Global.HDMI_CONTROL_AUTO_DEVICE_OFF_ENABLED,
                     Settings.Global.HDMI_CONTROL_AUTO_WAKEUP_ENABLED,
                     Settings.Global.HDMI_CONTROL_ENABLED,
@@ -477,9 +478,10 @@
                     Settings.Global.GLOBAL_SETTINGS_ANGLE_GL_DRIVER_ALL_ANGLE,
                     Settings.Global.GLOBAL_SETTINGS_ANGLE_GL_DRIVER_SELECTION_PKGS,
                     Settings.Global.GLOBAL_SETTINGS_ANGLE_GL_DRIVER_SELECTION_VALUES,
+                    Settings.Global.GUP_DEV_ALL_APPS,
                     Settings.Global.GUP_DEV_OPT_IN_APPS,
                     Settings.Global.GUP_DEV_OPT_OUT_APPS,
-                    Settings.Global.GUP_BLACK_LIST,
+                    Settings.Global.GUP_BLACKLIST,
                     Settings.Global.GPU_DEBUG_LAYER_APP,
                     Settings.Global.ENABLE_GNSS_RAW_MEAS_FULL_TRACKING,
                     Settings.Global.INSTALL_CARRIER_APP_NOTIFICATION_PERSISTENT,
@@ -554,8 +556,10 @@
                     Settings.Global.APPOP_HISTORY_PARAMETERS,
                     Settings.Global.APPOP_HISTORY_MODE,
                     Settings.Global.APPOP_HISTORY_INTERVAL_MULTIPLIER,
-                    Settings.Global.APPOP_HISTORY_BASE_INTERVAL_MILLIS);
-
+                    Settings.Global.APPOP_HISTORY_BASE_INTERVAL_MILLIS,
+                    Settings.Global.ENABLE_RADIO_BUG_DETECTION,
+                    Settings.Global.RADIO_BUG_WAKELOCK_TIMEOUT_COUNT_THRESHOLD,
+                    Settings.Global.RADIO_BUG_SYSTEM_ERROR_COUNT_THRESHOLD);
     private static final Set<String> BACKUP_BLACKLISTED_SECURE_SETTINGS =
              newHashSet(
                  Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE,
@@ -664,6 +668,7 @@
                  Settings.Secure.SMS_DEFAULT_APPLICATION,
                  Settings.Secure.SPELL_CHECKER_ENABLED,  // Intentionally removed in Q
                  Settings.Secure.TRUST_AGENTS_INITIALIZED,
+                 Settings.Secure.TV_APP_USES_NON_SYSTEM_INPUTS,
                  Settings.Secure.TV_INPUT_CUSTOM_LABELS,
                  Settings.Secure.TV_INPUT_HIDDEN_INPUTS,
                  Settings.Secure.TV_USER_SETUP_COMPLETE,
diff --git a/core/tests/coretests/src/android/provider/SettingsProviderTest.java b/core/tests/coretests/src/android/provider/SettingsProviderTest.java
index cb6f0e6..a5f5f67 100644
--- a/core/tests/coretests/src/android/provider/SettingsProviderTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsProviderTest.java
@@ -34,9 +34,10 @@
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.test.suitebuilder.annotation.SmallTest;
-import android.test.suitebuilder.annotation.Suppress;
+
+import androidx.test.filters.MediumTest;
+import androidx.test.filters.SmallTest;
+import androidx.test.filters.Suppress;
 
 import java.util.HashMap;
 import java.util.List;
diff --git a/core/tests/coretests/src/android/provider/SettingsValidatorsTest.java b/core/tests/coretests/src/android/provider/SettingsValidatorsTest.java
index b0d29bd..08f9de6 100644
--- a/core/tests/coretests/src/android/provider/SettingsValidatorsTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsValidatorsTest.java
@@ -22,8 +22,9 @@
 
 import android.platform.test.annotations.Presubmit;
 import android.provider.SettingsValidators.Validator;
-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/coretests/src/android/provider/SmsProviderTest.java b/core/tests/coretests/src/android/provider/SmsProviderTest.java
index af4d1a6..67ac8ea 100644
--- a/core/tests/coretests/src/android/provider/SmsProviderTest.java
+++ b/core/tests/coretests/src/android/provider/SmsProviderTest.java
@@ -22,8 +22,9 @@
 import android.net.Uri;
 import android.provider.Telephony.Sms;
 import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.test.suitebuilder.annotation.Suppress;
+
+import androidx.test.filters.LargeTest;
+import androidx.test.filters.Suppress;
 
 import java.util.GregorianCalendar;
 
diff --git a/core/tests/coretests/src/android/provider/TestFontsProvider.java b/core/tests/coretests/src/android/provider/TestFontsProvider.java
index 46906df..c3457fa 100644
--- a/core/tests/coretests/src/android/provider/TestFontsProvider.java
+++ b/core/tests/coretests/src/android/provider/TestFontsProvider.java
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package android.provider;
 
 import static android.os.ParcelFileDescriptor.MODE_READ_ONLY;
@@ -23,7 +24,6 @@
 import android.content.ContentValues;
 import android.database.Cursor;
 import android.database.MatrixCursor;
-import android.graphics.Typeface;
 import android.net.Uri;
 import android.os.ParcelFileDescriptor;
 
diff --git a/core/tests/coretests/src/android/security/keystore/recovery/KeyChainProtectionParamsTest.java b/core/tests/coretests/src/android/security/keystore/recovery/KeyChainProtectionParamsTest.java
index 0c9c4c1..ce0bf30 100644
--- a/core/tests/coretests/src/android/security/keystore/recovery/KeyChainProtectionParamsTest.java
+++ b/core/tests/coretests/src/android/security/keystore/recovery/KeyChainProtectionParamsTest.java
@@ -20,8 +20,9 @@
 import static org.junit.Assert.assertEquals;
 
 import android.os.Parcel;
-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/coretests/src/android/security/keystore/recovery/KeyChainSnapshotTest.java b/core/tests/coretests/src/android/security/keystore/recovery/KeyChainSnapshotTest.java
index 61ab152..2a962c2 100644
--- a/core/tests/coretests/src/android/security/keystore/recovery/KeyChainSnapshotTest.java
+++ b/core/tests/coretests/src/android/security/keystore/recovery/KeyChainSnapshotTest.java
@@ -20,8 +20,9 @@
 import static org.junit.Assert.assertEquals;
 
 import android.os.Parcel;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
 
 import com.google.common.collect.Lists;
 
diff --git a/core/tests/coretests/src/android/security/keystore/recovery/KeyDerivationParamsTest.java b/core/tests/coretests/src/android/security/keystore/recovery/KeyDerivationParamsTest.java
index b6af9bb..2b37b52 100644
--- a/core/tests/coretests/src/android/security/keystore/recovery/KeyDerivationParamsTest.java
+++ b/core/tests/coretests/src/android/security/keystore/recovery/KeyDerivationParamsTest.java
@@ -20,8 +20,9 @@
 import static org.junit.Assert.assertEquals;
 
 import android.os.Parcel;
-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/coretests/src/android/security/keystore/recovery/RecoveryCertPathTest.java b/core/tests/coretests/src/android/security/keystore/recovery/RecoveryCertPathTest.java
index dd8cd8d..3b8f715 100644
--- a/core/tests/coretests/src/android/security/keystore/recovery/RecoveryCertPathTest.java
+++ b/core/tests/coretests/src/android/security/keystore/recovery/RecoveryCertPathTest.java
@@ -20,8 +20,9 @@
 import static org.junit.Assert.fail;
 
 import android.os.Parcel;
-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/coretests/src/android/security/keystore/recovery/TrustedRootCertificatesTest.java b/core/tests/coretests/src/android/security/keystore/recovery/TrustedRootCertificatesTest.java
index a5a3ca9..2b15d73 100644
--- a/core/tests/coretests/src/android/security/keystore/recovery/TrustedRootCertificatesTest.java
+++ b/core/tests/coretests/src/android/security/keystore/recovery/TrustedRootCertificatesTest.java
@@ -20,8 +20,8 @@
 
 import static org.junit.Assert.assertTrue;
 
-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/coretests/src/android/security/keystore/recovery/WrappedApplicationKeyTest.java b/core/tests/coretests/src/android/security/keystore/recovery/WrappedApplicationKeyTest.java
index 15afbdd..8522e62 100644
--- a/core/tests/coretests/src/android/security/keystore/recovery/WrappedApplicationKeyTest.java
+++ b/core/tests/coretests/src/android/security/keystore/recovery/WrappedApplicationKeyTest.java
@@ -20,8 +20,9 @@
 import static org.junit.Assert.assertEquals;
 
 import android.os.Parcel;
-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.After;
 import org.junit.Before;
diff --git a/core/tests/coretests/src/android/security/keystore/recovery/X509CertificateParsingUtilsTest.java b/core/tests/coretests/src/android/security/keystore/recovery/X509CertificateParsingUtilsTest.java
index 7f0eb43..17486d5 100644
--- a/core/tests/coretests/src/android/security/keystore/recovery/X509CertificateParsingUtilsTest.java
+++ b/core/tests/coretests/src/android/security/keystore/recovery/X509CertificateParsingUtilsTest.java
@@ -21,8 +21,8 @@
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.fail;
 
-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/coretests/src/android/service/euicc/EuiccProfileInfoTest.java b/core/tests/coretests/src/android/service/euicc/EuiccProfileInfoTest.java
index e69d1e7..d00d052 100644
--- a/core/tests/coretests/src/android/service/euicc/EuiccProfileInfoTest.java
+++ b/core/tests/coretests/src/android/service/euicc/EuiccProfileInfoTest.java
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package android.service.euicc;
 
 import static org.junit.Assert.assertArrayEquals;
@@ -23,10 +24,11 @@
 
 import android.os.Parcel;
 import android.service.carrier.CarrierIdentifier;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
 import android.telephony.UiccAccessRule;
 
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
diff --git a/core/tests/coretests/src/android/service/notification/StatusBarNotificationTest.java b/core/tests/coretests/src/android/service/notification/StatusBarNotificationTest.java
new file mode 100644
index 0000000..4aa1000
--- /dev/null
+++ b/core/tests/coretests/src/android/service/notification/StatusBarNotificationTest.java
@@ -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.
+ */
+
+package android.service.notification;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertNull;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.app.ActivityManager;
+import android.app.Notification;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.metrics.LogMaker;
+import android.os.UserHandle;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class StatusBarNotificationTest {
+
+    private final Context mMockContext = mock(Context.class);
+    @Mock
+    private PackageManager mPm;
+
+    private static final String PKG = "com.example.o";
+    private static final int UID = 9583;
+    private static final int ID = 1;
+    private static final String TAG = "tag1";
+    private static final String CHANNEL_ID = "channel";
+    private static final String CHANNEL_ID_LONG =
+            "give_a_developer_a_string_argument_and_who_knows_what_they_will_pass_in_there";
+    private static final String GROUP_ID_1 = "group1";
+    private static final String GROUP_ID_2 = "group2";
+    private static final String GROUP_ID_LONG =
+            "0|com.foo.bar|g:content://com.foo.bar.ui/account%3A-0000000/account/";
+    private static final android.os.UserHandle USER =
+            UserHandle.of(ActivityManager.getCurrentUser());
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        when(mMockContext.getResources()).thenReturn(
+                InstrumentationRegistry.getContext().getResources());
+        when(mMockContext.getPackageManager()).thenReturn(mPm);
+        when(mMockContext.getApplicationInfo()).thenReturn(new ApplicationInfo());
+    }
+
+    @Test
+    public void testLogMaker() {
+        final LogMaker logMaker = getNotification(PKG, GROUP_ID_1, CHANNEL_ID).getLogMaker();
+
+        assertEquals(CHANNEL_ID,
+                (String) logMaker.getTaggedData(MetricsEvent.FIELD_NOTIFICATION_CHANNEL_ID));
+        assertEquals(PKG, logMaker.getPackageName());
+        assertEquals(ID, logMaker.getTaggedData(MetricsEvent.NOTIFICATION_ID));
+        assertEquals(TAG, logMaker.getTaggedData(MetricsEvent.NOTIFICATION_TAG));
+        assertEquals(GROUP_ID_1,
+                logMaker.getTaggedData(MetricsEvent.FIELD_NOTIFICATION_GROUP_ID));
+    }
+
+    @Test
+    public void testLogMakerNoChannel() {
+        final LogMaker logMaker = getNotification(PKG, GROUP_ID_1, null).getLogMaker();
+
+        assertNull(logMaker.getTaggedData(MetricsEvent.FIELD_NOTIFICATION_CHANNEL_ID));
+    }
+
+    @Test
+    public void testLogMakerLongChannel() {
+        final LogMaker logMaker = getNotification(PKG, null, CHANNEL_ID_LONG).getLogMaker();
+        final String loggedId = (String) logMaker
+                .getTaggedData(MetricsEvent.FIELD_NOTIFICATION_CHANNEL_ID);
+        assertEquals(StatusBarNotification.MAX_LOG_TAG_LENGTH, loggedId.length());
+        assertEquals(CHANNEL_ID_LONG.substring(0, 10), loggedId.substring(0, 10));
+    }
+
+    @Test
+    public void testLogMakerNoGroup() {
+        final LogMaker logMaker = getNotification(PKG, null, CHANNEL_ID).getLogMaker();
+
+        assertNull(
+                logMaker.getTaggedData(MetricsEvent.FIELD_NOTIFICATION_GROUP_ID));
+    }
+
+    @Test
+    public void testLogMakerLongGroup() {
+        final LogMaker logMaker = getNotification(PKG, GROUP_ID_LONG, CHANNEL_ID)
+                .getLogMaker();
+
+        final String loggedId = (String)
+                logMaker.getTaggedData(MetricsEvent.FIELD_NOTIFICATION_GROUP_ID);
+        assertEquals(StatusBarNotification.MAX_LOG_TAG_LENGTH, loggedId.length());
+        assertEquals(GROUP_ID_LONG.substring(0, 10), loggedId.substring(0, 10));
+    }
+
+    @Test
+    public void testLogMakerOverrideGroup() {
+        StatusBarNotification sbn = getNotification(PKG, GROUP_ID_1, CHANNEL_ID);
+        assertEquals(GROUP_ID_1,
+                sbn.getLogMaker().getTaggedData(MetricsEvent.FIELD_NOTIFICATION_GROUP_ID));
+
+        sbn.setOverrideGroupKey(GROUP_ID_2);
+        assertEquals(GROUP_ID_2,
+                sbn.getLogMaker().getTaggedData(MetricsEvent.FIELD_NOTIFICATION_GROUP_ID));
+
+        sbn.setOverrideGroupKey(null);
+        assertEquals(GROUP_ID_1,
+                sbn.getLogMaker().getTaggedData(MetricsEvent.FIELD_NOTIFICATION_GROUP_ID));
+    }
+
+    private StatusBarNotification getNotification(String pkg, String group, String channelId) {
+        final Notification.Builder builder = new Notification.Builder(mMockContext, channelId)
+                .setContentTitle("foo")
+                .setSmallIcon(android.R.drawable.sym_def_app_icon);
+
+        if (group != null) {
+            builder.setGroup(group);
+        }
+
+        Notification n = builder.build();
+        return new StatusBarNotification(
+                pkg, pkg, ID, TAG, UID, UID, n, USER, null, UID);
+    }
+
+}
diff --git a/core/tests/coretests/src/android/service/settings/suggestions/MockSuggestionService.java b/core/tests/coretests/src/android/service/settings/suggestions/MockSuggestionService.java
index ab541a1..9c8e868 100644
--- a/core/tests/coretests/src/android/service/settings/suggestions/MockSuggestionService.java
+++ b/core/tests/coretests/src/android/service/settings/suggestions/MockSuggestionService.java
@@ -16,16 +16,12 @@
 
 package android.service.settings.suggestions;
 
-import android.support.annotation.VisibleForTesting;
-
 import java.util.ArrayList;
 import java.util.List;
 
 public class MockSuggestionService extends SuggestionService {
 
-    @VisibleForTesting
     static boolean sOnSuggestionLaunchedCalled;
-    @VisibleForTesting
     static boolean sOnSuggestionDismissedCalled;
 
     public static void reset() {
diff --git a/core/tests/coretests/src/android/service/settings/suggestions/SuggestionServiceTest.java b/core/tests/coretests/src/android/service/settings/suggestions/SuggestionServiceTest.java
index dca8c09..64edda5 100644
--- a/core/tests/coretests/src/android/service/settings/suggestions/SuggestionServiceTest.java
+++ b/core/tests/coretests/src/android/service/settings/suggestions/SuggestionServiceTest.java
@@ -21,10 +21,11 @@
 import android.content.Intent;
 import android.os.IBinder;
 import android.os.RemoteException;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SmallTest;
-import android.support.test.rule.ServiceTestRule;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.rule.ServiceTestRule;
+import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.After;
 import org.junit.Before;
diff --git a/core/tests/coretests/src/android/service/settings/suggestions/SuggestionTest.java b/core/tests/coretests/src/android/service/settings/suggestions/SuggestionTest.java
index b0ec55d..8c47fcb 100644
--- a/core/tests/coretests/src/android/service/settings/suggestions/SuggestionTest.java
+++ b/core/tests/coretests/src/android/service/settings/suggestions/SuggestionTest.java
@@ -24,9 +24,10 @@
 import android.graphics.Bitmap;
 import android.graphics.drawable.Icon;
 import android.os.Parcel;
-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/coretests/src/android/text/AndroidCharacterTest.java b/core/tests/coretests/src/android/text/AndroidCharacterTest.java
index 0c7e730..1c5986a 100644
--- a/core/tests/coretests/src/android/text/AndroidCharacterTest.java
+++ b/core/tests/coretests/src/android/text/AndroidCharacterTest.java
@@ -19,7 +19,8 @@
 import static org.junit.Assert.assertArrayEquals;
 
 import android.platform.test.annotations.Presubmit;
-import android.support.test.filters.SmallTest;
+
+import androidx.test.filters.SmallTest;
 
 import org.junit.Test;
 
diff --git a/core/tests/coretests/src/android/text/BidiFormatterTest.java b/core/tests/coretests/src/android/text/BidiFormatterTest.java
index 1b936c7..312fb68 100644
--- a/core/tests/coretests/src/android/text/BidiFormatterTest.java
+++ b/core/tests/coretests/src/android/text/BidiFormatterTest.java
@@ -19,8 +19,9 @@
 import static org.junit.Assert.assertEquals;
 
 import android.platform.test.annotations.Presubmit;
-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/coretests/src/android/text/DynamicLayoutBlocksTest.java b/core/tests/coretests/src/android/text/DynamicLayoutBlocksTest.java
index c69f4f5..cca1ad3 100644
--- a/core/tests/coretests/src/android/text/DynamicLayoutBlocksTest.java
+++ b/core/tests/coretests/src/android/text/DynamicLayoutBlocksTest.java
@@ -22,8 +22,9 @@
 import static org.junit.Assert.assertTrue;
 
 import android.platform.test.annotations.Presubmit;
-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/coretests/src/android/text/DynamicLayoutTest.java b/core/tests/coretests/src/android/text/DynamicLayoutTest.java
index ea954f6..699243b 100644
--- a/core/tests/coretests/src/android/text/DynamicLayoutTest.java
+++ b/core/tests/coretests/src/android/text/DynamicLayoutTest.java
@@ -27,11 +27,12 @@
 import android.graphics.Canvas;
 import android.graphics.Paint;
 import android.platform.test.annotations.Presubmit;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
 import android.text.style.ReplacementSpan;
 import android.util.ArraySet;
 
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
diff --git a/core/tests/coretests/src/android/text/EmojiTest.java b/core/tests/coretests/src/android/text/EmojiTest.java
index 313f1b6..1948994 100644
--- a/core/tests/coretests/src/android/text/EmojiTest.java
+++ b/core/tests/coretests/src/android/text/EmojiTest.java
@@ -21,8 +21,9 @@
 
 import android.icu.lang.UCharacterDirection;
 import android.icu.text.Bidi;
-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/coretests/src/android/text/FontFallbackSetup.java b/core/tests/coretests/src/android/text/FontFallbackSetup.java
index 5592aac..cc51ec3 100644
--- a/core/tests/coretests/src/android/text/FontFallbackSetup.java
+++ b/core/tests/coretests/src/android/text/FontFallbackSetup.java
@@ -24,9 +24,10 @@
 import android.graphics.fonts.FontCustomizationParser;
 import android.graphics.fonts.FontFamily;
 import android.graphics.fonts.SystemFonts;
-import android.support.test.InstrumentationRegistry;
 import android.util.ArrayMap;
 
+import androidx.test.InstrumentationRegistry;
+
 import java.io.File;
 import java.io.FileOutputStream;
 import java.io.IOException;
diff --git a/core/tests/coretests/src/android/text/LayoutBidiCursorPathTest.java b/core/tests/coretests/src/android/text/LayoutBidiCursorPathTest.java
index 1208d7c..96e7fb9 100644
--- a/core/tests/coretests/src/android/text/LayoutBidiCursorPathTest.java
+++ b/core/tests/coretests/src/android/text/LayoutBidiCursorPathTest.java
@@ -22,12 +22,13 @@
 import android.graphics.Path;
 import android.graphics.Typeface;
 import android.platform.test.annotations.Presubmit;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
 import android.text.method.MetaKeyKeyListener;
 import android.view.KeyEvent;
 
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/text/LayoutTest.java b/core/tests/coretests/src/android/text/LayoutTest.java
index 97937a4..990161a 100644
--- a/core/tests/coretests/src/android/text/LayoutTest.java
+++ b/core/tests/coretests/src/android/text/LayoutTest.java
@@ -31,11 +31,12 @@
 import android.graphics.Rect;
 import android.graphics.RectF;
 import android.platform.test.annotations.Presubmit;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
 import android.text.Layout.Alignment;
 import android.text.style.StrikethroughSpan;
 
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/text/MeasuredParagraphTest.java b/core/tests/coretests/src/android/text/MeasuredParagraphTest.java
index a0dca2c..57bb434 100644
--- a/core/tests/coretests/src/android/text/MeasuredParagraphTest.java
+++ b/core/tests/coretests/src/android/text/MeasuredParagraphTest.java
@@ -23,9 +23,10 @@
 
 import android.content.Context;
 import android.graphics.Typeface;
-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.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/text/PackedIntVectorTest.java b/core/tests/coretests/src/android/text/PackedIntVectorTest.java
index d9dc6fc..ba15b92 100644
--- a/core/tests/coretests/src/android/text/PackedIntVectorTest.java
+++ b/core/tests/coretests/src/android/text/PackedIntVectorTest.java
@@ -19,8 +19,9 @@
 import static org.junit.Assert.assertEquals;
 
 import android.platform.test.annotations.Presubmit;
-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/coretests/src/android/text/SpannableStringBuilderTest.java b/core/tests/coretests/src/android/text/SpannableStringBuilderTest.java
index d0f2d46..91b8c6a 100644
--- a/core/tests/coretests/src/android/text/SpannableStringBuilderTest.java
+++ b/core/tests/coretests/src/android/text/SpannableStringBuilderTest.java
@@ -16,18 +16,18 @@
 
 package android.text;
 
-
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 
 import android.platform.test.annotations.Presubmit;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
 import android.text.style.BulletSpan;
 import android.text.style.QuoteSpan;
 import android.text.style.SubscriptSpan;
 import android.text.style.UnderlineSpan;
 
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
diff --git a/core/tests/coretests/src/android/text/SpannableStringNoCopyTest.java b/core/tests/coretests/src/android/text/SpannableStringNoCopyTest.java
index 6c05601..9149f7b 100644
--- a/core/tests/coretests/src/android/text/SpannableStringNoCopyTest.java
+++ b/core/tests/coretests/src/android/text/SpannableStringNoCopyTest.java
@@ -21,11 +21,12 @@
 import static org.junit.Assert.assertNotNull;
 
 import android.annotation.NonNull;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
 import android.text.style.QuoteSpan;
 import android.text.style.UnderlineSpan;
 
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
diff --git a/core/tests/coretests/src/android/text/SpannableTest.java b/core/tests/coretests/src/android/text/SpannableTest.java
index f368428..d248a1f 100644
--- a/core/tests/coretests/src/android/text/SpannableTest.java
+++ b/core/tests/coretests/src/android/text/SpannableTest.java
@@ -19,10 +19,11 @@
 import static org.junit.Assert.assertEquals;
 
 import android.platform.test.annotations.Presubmit;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
 import android.test.MoreAsserts;
 
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
diff --git a/core/tests/coretests/src/android/text/SpannedStringNoCopyTest.java b/core/tests/coretests/src/android/text/SpannedStringNoCopyTest.java
index 380e315..ca43733 100644
--- a/core/tests/coretests/src/android/text/SpannedStringNoCopyTest.java
+++ b/core/tests/coretests/src/android/text/SpannedStringNoCopyTest.java
@@ -21,11 +21,12 @@
 import static org.junit.Assert.assertNotNull;
 
 import android.annotation.NonNull;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
 import android.text.style.QuoteSpan;
 import android.text.style.UnderlineSpan;
 
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
diff --git a/core/tests/coretests/src/android/text/SpannedTest.java b/core/tests/coretests/src/android/text/SpannedTest.java
index 5737571..3ab0755 100644
--- a/core/tests/coretests/src/android/text/SpannedTest.java
+++ b/core/tests/coretests/src/android/text/SpannedTest.java
@@ -21,13 +21,14 @@
 import android.graphics.Typeface;
 import android.os.Parcel;
 import android.platform.test.annotations.Presubmit;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
 import android.text.style.CharacterStyle;
 import android.text.style.StyleSpan;
 import android.text.style.TextAppearanceSpan;
 import android.text.style.TypefaceSpan;
 
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
diff --git a/core/tests/coretests/src/android/text/StaticLayoutBidiTest.java b/core/tests/coretests/src/android/text/StaticLayoutBidiTest.java
index bf0d427..32370b3e 100644
--- a/core/tests/coretests/src/android/text/StaticLayoutBidiTest.java
+++ b/core/tests/coretests/src/android/text/StaticLayoutBidiTest.java
@@ -19,10 +19,11 @@
 import static org.junit.Assert.assertEquals;
 
 import android.platform.test.annotations.Presubmit;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
 import android.util.Log;
 
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
diff --git a/core/tests/coretests/src/android/text/StaticLayoutDirectionsTest.java b/core/tests/coretests/src/android/text/StaticLayoutDirectionsTest.java
index 5cf5426..4221ac2 100644
--- a/core/tests/coretests/src/android/text/StaticLayoutDirectionsTest.java
+++ b/core/tests/coretests/src/android/text/StaticLayoutDirectionsTest.java
@@ -19,11 +19,12 @@
 import static org.junit.Assert.fail;
 
 import android.platform.test.annotations.Presubmit;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
 import android.text.Layout.Directions;
 import android.text.StaticLayoutTest.LayoutBuilder;
 
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
diff --git a/core/tests/coretests/src/android/text/StaticLayoutTest.java b/core/tests/coretests/src/android/text/StaticLayoutTest.java
index 2521712..b1c896e 100644
--- a/core/tests/coretests/src/android/text/StaticLayoutTest.java
+++ b/core/tests/coretests/src/android/text/StaticLayoutTest.java
@@ -25,13 +25,14 @@
 import android.graphics.Paint.FontMetricsInt;
 import android.os.LocaleList;
 import android.platform.test.annotations.Presubmit;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
 import android.text.Layout.Alignment;
 import android.text.method.EditorState;
 import android.text.style.LocaleSpan;
 import android.util.Log;
 
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/text/StaticLayoutTextMeasuringTest.java b/core/tests/coretests/src/android/text/StaticLayoutTextMeasuringTest.java
index f6888e3..0d42326 100644
--- a/core/tests/coretests/src/android/text/StaticLayoutTextMeasuringTest.java
+++ b/core/tests/coretests/src/android/text/StaticLayoutTextMeasuringTest.java
@@ -19,10 +19,11 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
 import android.text.Layout.Alignment;
 
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/text/TextLayoutTest.java b/core/tests/coretests/src/android/text/TextLayoutTest.java
index 24020ce..15fbc9e 100644
--- a/core/tests/coretests/src/android/text/TextLayoutTest.java
+++ b/core/tests/coretests/src/android/text/TextLayoutTest.java
@@ -17,8 +17,9 @@
 package android.text;
 
 import android.platform.test.annotations.Presubmit;
-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.Before;
 import org.junit.Test;
diff --git a/core/tests/coretests/src/android/text/TextLineTest.java b/core/tests/coretests/src/android/text/TextLineTest.java
index 2fe882c..90ce305 100644
--- a/core/tests/coretests/src/android/text/TextLineTest.java
+++ b/core/tests/coretests/src/android/text/TextLineTest.java
@@ -25,14 +25,15 @@
 import android.graphics.Paint;
 import android.graphics.Typeface;
 import android.platform.test.annotations.Presubmit;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SmallTest;
-import android.support.test.filters.Suppress;
-import android.support.test.runner.AndroidJUnit4;
 import android.text.Layout.TabStops;
 import android.text.style.ReplacementSpan;
 import android.text.style.TabStopSpan;
 
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.filters.Suppress;
+import androidx.test.runner.AndroidJUnit4;
+
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
diff --git a/core/tests/coretests/src/android/text/TextUtilsTest.java b/core/tests/coretests/src/android/text/TextUtilsTest.java
index 72290bf..be6ef19 100644
--- a/core/tests/coretests/src/android/text/TextUtilsTest.java
+++ b/core/tests/coretests/src/android/text/TextUtilsTest.java
@@ -26,15 +26,16 @@
 
 import android.os.Parcel;
 import android.platform.test.annotations.Presubmit;
-import android.support.test.filters.LargeTest;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
 import android.test.MoreAsserts;
 import android.text.style.StyleSpan;
 import android.text.util.Rfc822Token;
 import android.text.util.Rfc822Tokenizer;
 import android.view.View;
 
+import androidx.test.filters.LargeTest;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
 import com.google.android.collect.Lists;
 
 import org.junit.Test;
diff --git a/core/tests/coretests/src/android/text/VariationParserTest.java b/core/tests/coretests/src/android/text/VariationParserTest.java
index fbd2412..0afe811 100644
--- a/core/tests/coretests/src/android/text/VariationParserTest.java
+++ b/core/tests/coretests/src/android/text/VariationParserTest.java
@@ -21,8 +21,9 @@
 
 import android.graphics.fonts.FontVariationAxis;
 import android.platform.test.annotations.Presubmit;
-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/coretests/src/android/text/format/DateFormatTest.java b/core/tests/coretests/src/android/text/format/DateFormatTest.java
index 9000ed0..5a0a84d 100644
--- a/core/tests/coretests/src/android/text/format/DateFormatTest.java
+++ b/core/tests/coretests/src/android/text/format/DateFormatTest.java
@@ -20,8 +20,9 @@
 import static org.junit.Assert.assertTrue;
 
 import android.platform.test.annotations.Presubmit;
-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/coretests/src/android/text/format/DateUtilsTest.java b/core/tests/coretests/src/android/text/format/DateUtilsTest.java
index 872b71a..381c051 100644
--- a/core/tests/coretests/src/android/text/format/DateUtilsTest.java
+++ b/core/tests/coretests/src/android/text/format/DateUtilsTest.java
@@ -22,8 +22,9 @@
 import android.content.res.Resources;
 import android.os.LocaleList;
 import android.platform.test.annotations.Presubmit;
-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.After;
 import org.junit.Before;
diff --git a/core/tests/coretests/src/android/text/format/FormatterTest.java b/core/tests/coretests/src/android/text/format/FormatterTest.java
index 82e4bff..068d047 100644
--- a/core/tests/coretests/src/android/text/format/FormatterTest.java
+++ b/core/tests/coretests/src/android/text/format/FormatterTest.java
@@ -25,11 +25,12 @@
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.platform.test.annotations.Presubmit;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
 import android.text.format.Formatter.BytesResult;
 
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
diff --git a/core/tests/coretests/src/android/text/format/TimeTest.java b/core/tests/coretests/src/android/text/format/TimeTest.java
index d563f2e..ac00411 100644
--- a/core/tests/coretests/src/android/text/format/TimeTest.java
+++ b/core/tests/coretests/src/android/text/format/TimeTest.java
@@ -21,12 +21,13 @@
 import static org.junit.Assert.fail;
 
 import android.platform.test.annotations.Presubmit;
-import android.support.test.filters.SmallTest;
-import android.support.test.filters.Suppress;
-import android.support.test.runner.AndroidJUnit4;
 import android.util.Log;
 import android.util.TimeFormatException;
 
+import androidx.test.filters.SmallTest;
+import androidx.test.filters.Suppress;
+import androidx.test.runner.AndroidJUnit4;
+
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
diff --git a/core/tests/coretests/src/android/text/method/BackspaceTest.java b/core/tests/coretests/src/android/text/method/BackspaceTest.java
index df4609f..ddae652 100644
--- a/core/tests/coretests/src/android/text/method/BackspaceTest.java
+++ b/core/tests/coretests/src/android/text/method/BackspaceTest.java
@@ -17,15 +17,16 @@
 package android.text.method;
 
 import android.platform.test.annotations.Presubmit;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
 import android.text.InputType;
 import android.util.KeyUtils;
 import android.view.KeyEvent;
 import android.widget.EditText;
 import android.widget.TextView.BufferType;
 
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/text/method/EditorState.java b/core/tests/coretests/src/android/text/method/EditorState.java
index 12bb8c8..4eff7a4 100644
--- a/core/tests/coretests/src/android/text/method/EditorState.java
+++ b/core/tests/coretests/src/android/text/method/EditorState.java
@@ -21,8 +21,6 @@
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
-import android.graphics.Canvas;
-import android.graphics.Paint;
 import android.text.Editable;
 import android.text.Spannable;
 import android.text.SpannableString;
diff --git a/core/tests/coretests/src/android/text/method/ForwardDeleteTest.java b/core/tests/coretests/src/android/text/method/ForwardDeleteTest.java
index 45a5010..45b63e0 100644
--- a/core/tests/coretests/src/android/text/method/ForwardDeleteTest.java
+++ b/core/tests/coretests/src/android/text/method/ForwardDeleteTest.java
@@ -17,15 +17,16 @@
 package android.text.method;
 
 import android.platform.test.annotations.Presubmit;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
 import android.text.InputType;
 import android.util.KeyUtils;
 import android.view.KeyEvent;
 import android.widget.EditText;
 import android.widget.TextView.BufferType;
 
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/text/method/WordIteratorTest.java b/core/tests/coretests/src/android/text/method/WordIteratorTest.java
index 6845863..cc345f5 100644
--- a/core/tests/coretests/src/android/text/method/WordIteratorTest.java
+++ b/core/tests/coretests/src/android/text/method/WordIteratorTest.java
@@ -22,8 +22,9 @@
 import static org.junit.Assert.fail;
 
 import android.platform.test.annotations.Presubmit;
-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/coretests/src/android/text/style/UnderlineSpanTest.java b/core/tests/coretests/src/android/text/style/UnderlineSpanTest.java
index 3d7e5ba..a0d2f85 100644
--- a/core/tests/coretests/src/android/text/style/UnderlineSpanTest.java
+++ b/core/tests/coretests/src/android/text/style/UnderlineSpanTest.java
@@ -16,17 +16,17 @@
 
 package android.text.style;
 
-
 import static org.junit.Assert.assertEquals;
 
 import android.platform.test.annotations.Presubmit;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
 import android.text.SpannableString;
 import android.text.Spanned;
 import android.text.StaticLayout;
 import android.text.TextPaint;
 
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
diff --git a/core/tests/coretests/src/android/text/util/LinkifyTest.java b/core/tests/coretests/src/android/text/util/LinkifyTest.java
index be3a0be..107ecd7 100644
--- a/core/tests/coretests/src/android/text/util/LinkifyTest.java
+++ b/core/tests/coretests/src/android/text/util/LinkifyTest.java
@@ -23,9 +23,6 @@
 import android.content.Context;
 import android.content.res.Configuration;
 import android.os.LocaleList;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
 import android.text.Spannable;
 import android.text.SpannableString;
 import android.text.method.LinkMovementMethod;
@@ -33,6 +30,10 @@
 import android.util.Patterns;
 import android.widget.TextView;
 
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
diff --git a/core/tests/coretests/src/android/transition/AutoTransitionTest.java b/core/tests/coretests/src/android/transition/AutoTransitionTest.java
index 834fb7a..deae967 100644
--- a/core/tests/coretests/src/android/transition/AutoTransitionTest.java
+++ b/core/tests/coretests/src/android/transition/AutoTransitionTest.java
@@ -16,16 +16,16 @@
 
 package android.transition;
 
-import android.support.test.runner.AndroidJUnit4;
-import android.test.suitebuilder.annotation.SmallTest;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
 @RunWith(AndroidJUnit4.class)
 public class AutoTransitionTest {
     @Test
diff --git a/core/tests/coretests/src/android/transition/FadeTransitionTest.java b/core/tests/coretests/src/android/transition/FadeTransitionTest.java
index 22365ba..2077d94 100644
--- a/core/tests/coretests/src/android/transition/FadeTransitionTest.java
+++ b/core/tests/coretests/src/android/transition/FadeTransitionTest.java
@@ -22,13 +22,14 @@
 import android.graphics.Bitmap;
 import android.graphics.drawable.BitmapDrawable;
 import android.test.ActivityInstrumentationTestCase2;
-import android.test.suitebuilder.annotation.SmallTest;
 import android.transition.Transition.TransitionListener;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.FrameLayout;
 import android.widget.ImageView;
 
+import androidx.test.filters.SmallTest;
+
 import com.android.frameworks.coretests.R;
 
 import java.util.concurrent.CountDownLatch;
diff --git a/core/tests/coretests/src/android/transition/SlideTransitionTest.java b/core/tests/coretests/src/android/transition/SlideTransitionTest.java
index 8b9ec74..046e49d 100644
--- a/core/tests/coretests/src/android/transition/SlideTransitionTest.java
+++ b/core/tests/coretests/src/android/transition/SlideTransitionTest.java
@@ -19,18 +19,18 @@
 import android.animation.AnimatorSetActivity;
 import android.app.Activity;
 import android.test.ActivityInstrumentationTestCase2;
-import android.test.suitebuilder.annotation.SmallTest;
 import android.view.Gravity;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.ViewTreeObserver;
 
+import androidx.test.filters.SmallTest;
+
 import com.android.frameworks.coretests.R;
 
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 
-
 public class SlideTransitionTest extends ActivityInstrumentationTestCase2<AnimatorSetActivity> {
 
     Activity mActivity;
diff --git a/core/tests/coretests/src/android/transition/TransitionTest.java b/core/tests/coretests/src/android/transition/TransitionTest.java
index 7e629f9..f59c406 100644
--- a/core/tests/coretests/src/android/transition/TransitionTest.java
+++ b/core/tests/coretests/src/android/transition/TransitionTest.java
@@ -19,7 +19,6 @@
 import android.animation.AnimatorSetActivity;
 import android.app.Activity;
 import android.graphics.Rect;
-import android.support.test.filters.LargeTest;
 import android.test.ActivityInstrumentationTestCase2;
 import android.transition.Transition.EpicenterCallback;
 import android.util.ArrayMap;
@@ -27,6 +26,8 @@
 import android.view.animation.AccelerateInterpolator;
 import android.widget.TextView;
 
+import androidx.test.filters.LargeTest;
+
 import com.android.frameworks.coretests.R;
 
 import java.lang.reflect.Field;
diff --git a/core/tests/coretests/src/android/util/ArrayMapTest.java b/core/tests/coretests/src/android/util/ArrayMapTest.java
index f0cc7f7..b212cf6 100644
--- a/core/tests/coretests/src/android/util/ArrayMapTest.java
+++ b/core/tests/coretests/src/android/util/ArrayMapTest.java
@@ -14,10 +14,10 @@
 
 package android.util;
 
-import android.support.test.filters.LargeTest;
-import android.util.ArrayMap;
+import androidx.test.filters.LargeTest;
 
 import junit.framework.TestCase;
+
 import org.junit.Test;
 
 import java.util.ConcurrentModificationException;
diff --git a/core/tests/coretests/src/android/util/Base64Test.java b/core/tests/coretests/src/android/util/Base64Test.java
index af608c3..15c51af 100644
--- a/core/tests/coretests/src/android/util/Base64Test.java
+++ b/core/tests/coretests/src/android/util/Base64Test.java
@@ -16,7 +16,7 @@
 
 package android.util;
 
-import android.support.test.filters.LargeTest;
+import androidx.test.filters.LargeTest;
 
 import junit.framework.TestCase;
 
diff --git a/core/tests/coretests/src/android/util/DataUnitTest.java b/core/tests/coretests/src/android/util/DataUnitTest.java
index 4eae8b4..ec296b7 100644
--- a/core/tests/coretests/src/android/util/DataUnitTest.java
+++ b/core/tests/coretests/src/android/util/DataUnitTest.java
@@ -16,7 +16,7 @@
 
 package android.util;
 
-import android.support.test.filters.SmallTest;
+import androidx.test.filters.SmallTest;
 
 import junit.framework.TestCase;
 
diff --git a/core/tests/coretests/src/android/util/DayOfMonthCursorTest.java b/core/tests/coretests/src/android/util/DayOfMonthCursorTest.java
index 4c5ad76..572e9b0 100644
--- a/core/tests/coretests/src/android/util/DayOfMonthCursorTest.java
+++ b/core/tests/coretests/src/android/util/DayOfMonthCursorTest.java
@@ -16,10 +16,11 @@
 
 package android.util;
 
+import androidx.test.filters.SmallTest;
+
 import junit.framework.TestCase;
 
 import java.util.Calendar;
-import android.test.suitebuilder.annotation.SmallTest;
 
 /**
  * Unit tests for {@link DayOfMonthCursor}.
diff --git a/core/tests/coretests/src/android/util/InternalSelectionView.java b/core/tests/coretests/src/android/util/InternalSelectionView.java
index a0fb0f1..4a1baef 100644
--- a/core/tests/coretests/src/android/util/InternalSelectionView.java
+++ b/core/tests/coretests/src/android/util/InternalSelectionView.java
@@ -16,19 +16,16 @@
 
 package android.util;
 
-import com.android.frameworks.coretests.R;
-
-import android.view.View;
-import android.view.KeyEvent;
 import android.content.Context;
 import android.content.res.TypedArray;
-import android.graphics.Paint;
 import android.graphics.Canvas;
-import android.graphics.Rect;
 import android.graphics.Color;
-import android.util.AttributeSet;
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.view.KeyEvent;
+import android.view.View;
 
-
+import com.android.frameworks.coretests.R;
 
 /**
  * A view that has a known number of selectable rows, and maintains a notion of which
diff --git a/core/tests/coretests/src/android/util/KeyUtils.java b/core/tests/coretests/src/android/util/KeyUtils.java
index 593f727..612643e 100644
--- a/core/tests/coretests/src/android/util/KeyUtils.java
+++ b/core/tests/coretests/src/android/util/KeyUtils.java
@@ -17,16 +17,10 @@
 package android.util;
 
 import android.app.Instrumentation;
-import android.os.SystemClock;
 import android.test.ActivityInstrumentationTestCase;
-import android.test.InstrumentationTestCase;
-import android.view.Gravity;
 import android.view.KeyCharacterMap;
 import android.view.KeyEvent;
-import android.view.MotionEvent;
-import android.view.View;
 import android.view.ViewConfiguration;
-import android.view.ViewGroup;
 
 /**
  * Reusable methods for generating key events.
diff --git a/core/tests/coretests/src/android/util/KeyValueListParserTest.java b/core/tests/coretests/src/android/util/KeyValueListParserTest.java
index 038f0b7..f65c4c7 100644
--- a/core/tests/coretests/src/android/util/KeyValueListParserTest.java
+++ b/core/tests/coretests/src/android/util/KeyValueListParserTest.java
@@ -19,8 +19,9 @@
 import static org.junit.Assert.assertEquals;
 
 import android.platform.test.annotations.Presubmit;
-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.Before;
 import org.junit.Test;
diff --git a/core/tests/coretests/src/android/util/ListUtil.java b/core/tests/coretests/src/android/util/ListUtil.java
index 2a7cb96..3748cf8 100644
--- a/core/tests/coretests/src/android/util/ListUtil.java
+++ b/core/tests/coretests/src/android/util/ListUtil.java
@@ -20,13 +20,11 @@
 import android.view.KeyEvent;
 import android.widget.ListView;
 
-
 /**
  * Various useful stuff for instrumentation testing listview.
  */
 public class ListUtil {
 
-
     private final ListView mListView;
     private final Instrumentation mInstrumentation;
 
diff --git a/core/tests/coretests/src/android/util/LocalLogTest.java b/core/tests/coretests/src/android/util/LocalLogTest.java
index 5144c85..6cdcb5e 100644
--- a/core/tests/coretests/src/android/util/LocalLogTest.java
+++ b/core/tests/coretests/src/android/util/LocalLogTest.java
@@ -16,7 +16,7 @@
 
 package android.util;
 
-import android.support.test.filters.LargeTest;
+import androidx.test.filters.LargeTest;
 
 import junit.framework.TestCase;
 
diff --git a/core/tests/coretests/src/android/util/LogNullabilityTest.java b/core/tests/coretests/src/android/util/LogNullabilityTest.java
index 3f4776d..370885d 100644
--- a/core/tests/coretests/src/android/util/LogNullabilityTest.java
+++ b/core/tests/coretests/src/android/util/LogNullabilityTest.java
@@ -19,8 +19,8 @@
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.fail;
 
-import android.support.test.runner.AndroidJUnit4;
-import android.test.suitebuilder.annotation.SmallTest;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/util/LogTest.java b/core/tests/coretests/src/android/util/LogTest.java
index 30c81b0..d783c12 100644
--- a/core/tests/coretests/src/android/util/LogTest.java
+++ b/core/tests/coretests/src/android/util/LogTest.java
@@ -16,13 +16,12 @@
 
 package android.util;
 
-import junit.framework.Assert;
-import junit.framework.TestCase;
-
 import android.os.SystemProperties;
 import android.test.PerformanceTestCase;
-import android.test.suitebuilder.annotation.Suppress;
-import android.util.Log;
+
+import androidx.test.filters.Suppress;
+
+import junit.framework.TestCase;
 
 //This is an empty TestCase.
 @Suppress
diff --git a/core/tests/coretests/src/android/util/LongSparseLongArrayTest.java b/core/tests/coretests/src/android/util/LongSparseLongArrayTest.java
index 3f03db9..a1d48e6 100644
--- a/core/tests/coretests/src/android/util/LongSparseLongArrayTest.java
+++ b/core/tests/coretests/src/android/util/LongSparseLongArrayTest.java
@@ -16,7 +16,7 @@
 
 package android.util;
 
-import android.support.test.filters.LargeTest;
+import androidx.test.filters.LargeTest;
 
 import junit.framework.TestCase;
 
diff --git a/core/tests/coretests/src/android/util/LruCacheTest.java b/core/tests/coretests/src/android/util/LruCacheTest.java
index 5a97158..1928bfd 100644
--- a/core/tests/coretests/src/android/util/LruCacheTest.java
+++ b/core/tests/coretests/src/android/util/LruCacheTest.java
@@ -16,12 +16,13 @@
 
 package android.util;
 
+import junit.framework.TestCase;
+
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
 import java.util.Map;
-import junit.framework.TestCase;
 
 public final class LruCacheTest extends TestCase {
     private int expectedCreateCount;
diff --git a/core/tests/coretests/src/android/util/MonthDisplayHelperTest.java b/core/tests/coretests/src/android/util/MonthDisplayHelperTest.java
index 55da665..30d5f77 100644
--- a/core/tests/coretests/src/android/util/MonthDisplayHelperTest.java
+++ b/core/tests/coretests/src/android/util/MonthDisplayHelperTest.java
@@ -16,12 +16,12 @@
 
 package android.util;
 
-import android.test.suitebuilder.annotation.SmallTest;
-
-import java.util.Calendar;
+import androidx.test.filters.SmallTest;
 
 import junit.framework.TestCase;
 
+import java.util.Calendar;
+
 /**
  * Unit tests for {@link MonthDisplayHelper}.
  */
diff --git a/core/tests/coretests/src/android/util/OrientationUtil.java b/core/tests/coretests/src/android/util/OrientationUtil.java
index ecdca5d..0361194 100644
--- a/core/tests/coretests/src/android/util/OrientationUtil.java
+++ b/core/tests/coretests/src/android/util/OrientationUtil.java
@@ -19,7 +19,6 @@
 import android.app.Activity;
 import android.app.Instrumentation;
 import android.content.pm.ActivityInfo;
-
 import android.test.ActivityInstrumentationTestCase2;
 
 import com.android.internal.util.Preconditions;
diff --git a/core/tests/coretests/src/android/util/PatternsTest.java b/core/tests/coretests/src/android/util/PatternsTest.java
index 360b875..6cea2f3 100644
--- a/core/tests/coretests/src/android/util/PatternsTest.java
+++ b/core/tests/coretests/src/android/util/PatternsTest.java
@@ -13,15 +13,16 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package android.util;
 
-import android.support.test.filters.SmallTest;
+import androidx.test.filters.SmallTest;
+
+import junit.framework.TestCase;
 
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
-import junit.framework.TestCase;
-
 public class PatternsTest extends TestCase {
 
     // Tests for Patterns.TOP_LEVEL_DOMAIN
diff --git a/core/tests/coretests/src/android/util/RecurrenceRuleTest.java b/core/tests/coretests/src/android/util/RecurrenceRuleTest.java
index 39d492d..caa1208 100644
--- a/core/tests/coretests/src/android/util/RecurrenceRuleTest.java
+++ b/core/tests/coretests/src/android/util/RecurrenceRuleTest.java
@@ -16,7 +16,7 @@
 
 package android.util;
 
-import android.support.test.filters.SmallTest;
+import androidx.test.filters.SmallTest;
 
 import junit.framework.TestCase;
 
diff --git a/core/tests/coretests/src/android/util/SparseLongArrayTest.java b/core/tests/coretests/src/android/util/SparseLongArrayTest.java
index 5a5e893..df2d752 100644
--- a/core/tests/coretests/src/android/util/SparseLongArrayTest.java
+++ b/core/tests/coretests/src/android/util/SparseLongArrayTest.java
@@ -19,9 +19,10 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 
-import android.support.annotation.NonNull;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
+import android.annotation.NonNull;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Before;
 import org.junit.Test;
diff --git a/core/tests/coretests/src/android/util/StateSetTest.java b/core/tests/coretests/src/android/util/StateSetTest.java
index e481ce0..b5d6270 100644
--- a/core/tests/coretests/src/android/util/StateSetTest.java
+++ b/core/tests/coretests/src/android/util/StateSetTest.java
@@ -16,8 +16,9 @@
 
 package android.util;
 
+import androidx.test.filters.SmallTest;
+
 import junit.framework.TestCase;
-import android.test.suitebuilder.annotation.SmallTest;
 
 /**
  * Tests for {@link StateSet}
diff --git a/core/tests/coretests/src/android/util/TimestampedValueTest.java b/core/tests/coretests/src/android/util/TimestampedValueTest.java
index 03b4abd..6e3ab79 100644
--- a/core/tests/coretests/src/android/util/TimestampedValueTest.java
+++ b/core/tests/coretests/src/android/util/TimestampedValueTest.java
@@ -21,7 +21,8 @@
 import static org.junit.Assert.fail;
 
 import android.os.Parcel;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/util/TimingsTraceLogTest.java b/core/tests/coretests/src/android/util/TimingsTraceLogTest.java
index 7bb4ab8..77d0552 100644
--- a/core/tests/coretests/src/android/util/TimingsTraceLogTest.java
+++ b/core/tests/coretests/src/android/util/TimingsTraceLogTest.java
@@ -19,8 +19,9 @@
 import static org.junit.Assert.assertTrue;
 
 import android.os.Trace;
-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;
@@ -28,7 +29,6 @@
 import java.util.ArrayList;
 import java.util.List;
 
-
 /**
  * Tests for {@link TimingsTraceLog}.
  * <p>Usage: bit FrameworksCoreTests:android.util.TimingsTraceLogTest
diff --git a/core/tests/coretests/src/android/util/TouchModeFlexibleAsserts.java b/core/tests/coretests/src/android/util/TouchModeFlexibleAsserts.java
index ca12a15..fc08235 100644
--- a/core/tests/coretests/src/android/util/TouchModeFlexibleAsserts.java
+++ b/core/tests/coretests/src/android/util/TouchModeFlexibleAsserts.java
@@ -16,12 +16,12 @@
 
 package android.util;
 
-import junit.framework.Assert;
-
 import android.test.InstrumentationTestCase;
 import android.test.TouchUtils;
 import android.view.View;
 
+import junit.framework.Assert;
+
 /**
  * When entering touch mode via touch, the tests can be flaky.  These asserts
  * are more flexible (allowing up to MAX_ATTEMPTS touches to enter touch mode via touch or
diff --git a/core/tests/coretests/src/android/view/BigCache.java b/core/tests/coretests/src/android/view/BigCache.java
index 2182176..6a1bcaa 100644
--- a/core/tests/coretests/src/android/view/BigCache.java
+++ b/core/tests/coretests/src/android/view/BigCache.java
@@ -16,16 +16,12 @@
 
 package android.view;
 
-import com.android.frameworks.coretests.R;
-
-import android.os.Bundle;
 import android.app.Activity;
+import android.os.Bundle;
 import android.widget.LinearLayout;
 import android.widget.ScrollView;
-import android.view.ViewGroup;
-import android.view.View;
-import android.view.Display;
-import android.view.ViewConfiguration;
+
+import com.android.frameworks.coretests.R;
 
 /**
  * This activity contains two Views, one as big as the screen, one much larger. The large one
diff --git a/core/tests/coretests/src/android/view/BigCacheTest.java b/core/tests/coretests/src/android/view/BigCacheTest.java
index 8c2c865..ed4b996 100644
--- a/core/tests/coretests/src/android/view/BigCacheTest.java
+++ b/core/tests/coretests/src/android/view/BigCacheTest.java
@@ -16,14 +16,12 @@
 
 package android.view;
 
-import android.view.BigCache;
-import com.android.frameworks.coretests.R;
-
-import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.view.View;
-import android.view.ViewConfiguration;
 import android.graphics.Bitmap;
+import android.test.ActivityInstrumentationTestCase;
+
+import androidx.test.filters.MediumTest;
+
+import com.android.frameworks.coretests.R;
 
 /**
  * Builds the drawing cache of two Views, one smaller than the maximum cache size,
diff --git a/core/tests/coretests/src/android/view/BitmapDrawable.java b/core/tests/coretests/src/android/view/BitmapDrawable.java
index f7bad84..42c176f 100644
--- a/core/tests/coretests/src/android/view/BitmapDrawable.java
+++ b/core/tests/coretests/src/android/view/BitmapDrawable.java
@@ -16,19 +16,10 @@
 
 package android.view;
 
-import com.android.frameworks.coretests.R;
-
 import android.app.Activity;
-import android.graphics.drawable.Drawable;
 import android.os.Bundle;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.widget.AbsoluteLayout;
-import android.widget.Button;
-import android.widget.FrameLayout;
-import android.widget.LinearLayout;
-import android.widget.RelativeLayout;
-import android.widget.TextView;
+
+import com.android.frameworks.coretests.R;
 
 public class BitmapDrawable extends Activity {
     @Override
diff --git a/core/tests/coretests/src/android/view/CreateViewTest.java b/core/tests/coretests/src/android/view/CreateViewTest.java
index 16656f6..520f83f 100644
--- a/core/tests/coretests/src/android/view/CreateViewTest.java
+++ b/core/tests/coretests/src/android/view/CreateViewTest.java
@@ -16,16 +16,17 @@
 
 package android.view;
 
+import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
+import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
+
 import android.content.Context;
 import android.test.AndroidTestCase;
 import android.test.PerformanceTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
-import android.view.View;
-import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
-import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
 import android.widget.LinearLayout;
 import android.widget.TextView;
 
+import androidx.test.filters.SmallTest;
+
 public class CreateViewTest extends AndroidTestCase implements PerformanceTestCase {
 
     public boolean isPerformanceOnly() {
diff --git a/core/tests/coretests/src/android/view/Disabled.java b/core/tests/coretests/src/android/view/Disabled.java
index d3c7470..ce90039 100644
--- a/core/tests/coretests/src/android/view/Disabled.java
+++ b/core/tests/coretests/src/android/view/Disabled.java
@@ -16,13 +16,12 @@
 
 package android.view;
 
-import com.android.frameworks.coretests.R;
-
-import android.os.Bundle;
-import android.widget.Button;
-import android.view.View;
-import android.view.View.OnClickListener;
 import android.app.Activity;
+import android.os.Bundle;
+import android.view.View.OnClickListener;
+import android.widget.Button;
+
+import com.android.frameworks.coretests.R;
 
 /**
  * Exercise View's disabled state.
diff --git a/core/tests/coretests/src/android/view/DisabledLongpressTest.java b/core/tests/coretests/src/android/view/DisabledLongpressTest.java
index faa0865..a9fec01 100644
--- a/core/tests/coretests/src/android/view/DisabledLongpressTest.java
+++ b/core/tests/coretests/src/android/view/DisabledLongpressTest.java
@@ -18,11 +18,12 @@
 
 import android.test.ActivityInstrumentationTestCase;
 import android.test.TouchUtils;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.util.KeyUtils;
 import android.view.View.OnLongClickListener;
 
+import androidx.test.filters.LargeTest;
+import androidx.test.filters.MediumTest;
+
 import com.android.frameworks.coretests.R;
 
 /**
diff --git a/core/tests/coretests/src/android/view/DisabledTest.java b/core/tests/coretests/src/android/view/DisabledTest.java
index 291a11c..8922e5f 100644
--- a/core/tests/coretests/src/android/view/DisabledTest.java
+++ b/core/tests/coretests/src/android/view/DisabledTest.java
@@ -18,12 +18,11 @@
 
 import android.test.ActivityInstrumentationTestCase;
 import android.test.TouchUtils;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.view.KeyEvent;
-import android.view.View;
 import android.widget.Button;
 
+import androidx.test.filters.LargeTest;
+import androidx.test.filters.MediumTest;
+
 import com.android.frameworks.coretests.R;
 
 /**
diff --git a/core/tests/coretests/src/android/view/DisplayCutoutTest.java b/core/tests/coretests/src/android/view/DisplayCutoutTest.java
index 8e4f2cd..dd50af877 100644
--- a/core/tests/coretests/src/android/view/DisplayCutoutTest.java
+++ b/core/tests/coretests/src/android/view/DisplayCutoutTest.java
@@ -33,17 +33,17 @@
 import android.graphics.Rect;
 import android.os.Parcel;
 import android.platform.test.annotations.Presubmit;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
 import android.view.DisplayCutout.ParcelableWrapper;
 
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
 import java.util.Arrays;
 import java.util.Collections;
 
-
 @RunWith(AndroidJUnit4.class)
 @SmallTest
 @Presubmit
diff --git a/core/tests/coretests/src/android/view/DrawableBgMinSize.java b/core/tests/coretests/src/android/view/DrawableBgMinSize.java
index 58bfb8a..ba9ba00 100644
--- a/core/tests/coretests/src/android/view/DrawableBgMinSize.java
+++ b/core/tests/coretests/src/android/view/DrawableBgMinSize.java
@@ -16,12 +16,9 @@
 
 package android.view;
 
-import com.android.frameworks.coretests.R;
-
 import android.app.Activity;
 import android.graphics.drawable.Drawable;
 import android.os.Bundle;
-import android.view.View;
 import android.view.View.OnClickListener;
 import android.widget.AbsoluteLayout;
 import android.widget.Button;
@@ -30,6 +27,8 @@
 import android.widget.RelativeLayout;
 import android.widget.TextView;
 
+import com.android.frameworks.coretests.R;
+
 /**
  * Views should obey their background {@link Drawable}'s minimum size
  * requirements ({@link Drawable#getMinimumHeight()} and
diff --git a/core/tests/coretests/src/android/view/DrawableBgMinSizeTest.java b/core/tests/coretests/src/android/view/DrawableBgMinSizeTest.java
index e705c87..1735e8c 100644
--- a/core/tests/coretests/src/android/view/DrawableBgMinSizeTest.java
+++ b/core/tests/coretests/src/android/view/DrawableBgMinSizeTest.java
@@ -16,14 +16,9 @@
 
 package android.view;
 
-import com.android.frameworks.coretests.R;
-import android.view.DrawableBgMinSize;
-import android.test.TouchUtils;
-import android.test.suitebuilder.annotation.MediumTest;
-
 import android.graphics.drawable.Drawable;
 import android.test.ActivityInstrumentationTestCase;
-import android.view.View;
+import android.test.TouchUtils;
 import android.widget.AbsoluteLayout;
 import android.widget.Button;
 import android.widget.FrameLayout;
@@ -31,6 +26,10 @@
 import android.widget.RelativeLayout;
 import android.widget.TextView;
 
+import androidx.test.filters.MediumTest;
+
+import com.android.frameworks.coretests.R;
+
 /**
  * {@link DrawableBgMinSize} exercises Views to obey their background drawable's
  * minimum sizes.
diff --git a/core/tests/coretests/src/android/view/FocusFinderTest.java b/core/tests/coretests/src/android/view/FocusFinderTest.java
index 2732a04..b35c64a 100644
--- a/core/tests/coretests/src/android/view/FocusFinderTest.java
+++ b/core/tests/coretests/src/android/view/FocusFinderTest.java
@@ -18,7 +18,8 @@
 
 import android.graphics.Rect;
 import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
+
+import androidx.test.filters.SmallTest;
 
 public class FocusFinderTest extends AndroidTestCase {
 
diff --git a/core/tests/coretests/src/android/view/GlobalFocusChange.java b/core/tests/coretests/src/android/view/GlobalFocusChange.java
index 041c0de..ee92b64 100644
--- a/core/tests/coretests/src/android/view/GlobalFocusChange.java
+++ b/core/tests/coretests/src/android/view/GlobalFocusChange.java
@@ -16,12 +16,10 @@
 
 package android.view;
 
-import com.android.frameworks.coretests.R;
-
 import android.app.Activity;
 import android.os.Bundle;
-import android.view.ViewTreeObserver;
-import android.view.View;
+
+import com.android.frameworks.coretests.R;
 
 public class GlobalFocusChange extends Activity implements ViewTreeObserver.OnGlobalFocusChangeListener {
     public View mOldFocus;
diff --git a/core/tests/coretests/src/android/view/GlobalFocusChangeTest.java b/core/tests/coretests/src/android/view/GlobalFocusChangeTest.java
index dab7b90..960654a 100644
--- a/core/tests/coretests/src/android/view/GlobalFocusChangeTest.java
+++ b/core/tests/coretests/src/android/view/GlobalFocusChangeTest.java
@@ -17,13 +17,13 @@
 package android.view;
 
 import android.test.ActivityInstrumentationTestCase;
-import android.test.FlakyTest;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.test.TouchUtils;
-import android.test.suitebuilder.annotation.Suppress;
-import android.view.View;
-import android.view.KeyEvent;
+
+import androidx.test.filters.FlakyTest;
+import androidx.test.filters.LargeTest;
+import androidx.test.filters.MediumTest;
+import androidx.test.filters.Suppress;
+
 import com.android.frameworks.coretests.R;
 
 @Suppress // Flaky
@@ -50,7 +50,7 @@
         super.tearDown();
     }
 
-    @FlakyTest(tolerance = 4)
+    @FlakyTest
     @LargeTest
     public void testFocusChange() throws Exception {
         sendKeys(KeyEvent.KEYCODE_DPAD_RIGHT);
@@ -62,7 +62,7 @@
         assertSame(mRight, mActivity.mNewFocus);        
     }
 
-    @FlakyTest(tolerance = 4)
+    @FlakyTest
     @MediumTest
     public void testEnterTouchMode() throws Exception {
         assertTrue(mLeft.isFocused());
@@ -73,7 +73,7 @@
         assertSame(null, mActivity.mNewFocus);        
     }
 
-    @FlakyTest(tolerance = 4)
+    @FlakyTest
     @MediumTest
     public void testLeaveTouchMode() throws Exception {
         assertTrue(mLeft.isFocused());
diff --git a/core/tests/coretests/src/android/view/HandlerActionQueueTest.java b/core/tests/coretests/src/android/view/HandlerActionQueueTest.java
index fd8f23a..bedb75b 100644
--- a/core/tests/coretests/src/android/view/HandlerActionQueueTest.java
+++ b/core/tests/coretests/src/android/view/HandlerActionQueueTest.java
@@ -17,7 +17,8 @@
 package android.view;
 
 import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
+
+import androidx.test.filters.SmallTest;
 
 public class HandlerActionQueueTest extends AndroidTestCase {
 
diff --git a/core/tests/coretests/src/android/view/Include.java b/core/tests/coretests/src/android/view/Include.java
index e90c484..4b6aefb 100644
--- a/core/tests/coretests/src/android/view/Include.java
+++ b/core/tests/coretests/src/android/view/Include.java
@@ -16,10 +16,10 @@
 
 package android.view;
 
-import com.android.frameworks.coretests.R;
-
-import android.os.Bundle;
 import android.app.Activity;
+import android.os.Bundle;
+
+import com.android.frameworks.coretests.R;
 
 /**
  * Exercise <include /> tag in XML files.
diff --git a/core/tests/coretests/src/android/view/IncludeTest.java b/core/tests/coretests/src/android/view/IncludeTest.java
index cdcfa3c..e490559 100644
--- a/core/tests/coretests/src/android/view/IncludeTest.java
+++ b/core/tests/coretests/src/android/view/IncludeTest.java
@@ -16,13 +16,11 @@
 
 package android.view;
 
-import android.view.Include;
-import com.android.frameworks.coretests.R;
-
 import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.view.View;
-import android.view.ViewGroup;
+
+import androidx.test.filters.MediumTest;
+
+import com.android.frameworks.coretests.R;
 
 public class IncludeTest extends ActivityInstrumentationTestCase<Include> {
     public IncludeTest() {
diff --git a/core/tests/coretests/src/android/view/InflateTest.java b/core/tests/coretests/src/android/view/InflateTest.java
index cb4f8e2..939d6ea 100644
--- a/core/tests/coretests/src/android/view/InflateTest.java
+++ b/core/tests/coretests/src/android/view/InflateTest.java
@@ -20,13 +20,11 @@
 import android.content.res.Resources;
 import android.test.AndroidTestCase;
 import android.test.PerformanceTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
 import android.util.AttributeSet;
-import android.view.LayoutInflater;
-import android.view.View;
-import com.android.frameworks.coretests.R;
 
-import java.util.Map;
+import androidx.test.filters.SmallTest;
+
+import com.android.frameworks.coretests.R;
 
 public class InflateTest extends AndroidTestCase implements PerformanceTestCase {
     private LayoutInflater mInflater;
diff --git a/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java b/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java
index d520f15..7f00ad9 100644
--- a/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java
+++ b/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java
@@ -18,7 +18,10 @@
 
 import static android.view.InsetsState.TYPE_NAVIGATION_BAR;
 import static android.view.InsetsState.TYPE_TOP_BAR;
+
 import static junit.framework.Assert.assertEquals;
+
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
 
 import android.graphics.Insets;
@@ -26,12 +29,13 @@
 import android.graphics.Rect;
 import android.graphics.RectF;
 import android.platform.test.annotations.Presubmit;
-import android.support.test.filters.FlakyTest;
-import android.support.test.runner.AndroidJUnit4;
 import android.util.SparseArray;
 import android.view.SurfaceControl.Transaction;
 import android.view.SyncRtSurfaceTransactionApplier.SurfaceParams;
 
+import androidx.test.filters.FlakyTest;
+import androidx.test.runner.AndroidJUnit4;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -83,7 +87,7 @@
         consumers.put(TYPE_NAVIGATION_BAR, navConsumer);
         mController = new InsetsAnimationControlImpl(consumers,
                 new Rect(0, 0, 500, 500), state, mMockListener, WindowInsets.Type.systemBars(),
-                () -> mMockTransactionApplier);
+                () -> mMockTransactionApplier, mock(InsetsController.class));
     }
 
     @Test
diff --git a/core/tests/coretests/src/android/view/InsetsControllerTest.java b/core/tests/coretests/src/android/view/InsetsControllerTest.java
index d3d274a..d447451 100644
--- a/core/tests/coretests/src/android/view/InsetsControllerTest.java
+++ b/core/tests/coretests/src/android/view/InsetsControllerTest.java
@@ -17,18 +17,20 @@
 package android.view;
 
 import static android.view.InsetsState.TYPE_TOP_BAR;
+
 import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.assertNull;
+
 import static org.mockito.Mockito.mock;
 
 import android.platform.test.annotations.Presubmit;
-import android.support.test.filters.FlakyTest;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.filters.FlakyTest;
+import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.mockito.Mockito;
 
 @Presubmit
 @FlakyTest(detail = "Promote once confirmed non-flaky")
diff --git a/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java b/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java
index 6d0f984..82cd213 100644
--- a/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java
+++ b/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java
@@ -17,6 +17,7 @@
 package android.view;
 
 import static android.view.InsetsState.TYPE_TOP_BAR;
+
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.reset;
@@ -24,10 +25,11 @@
 import static org.mockito.Mockito.verifyZeroInteractions;
 
 import android.platform.test.annotations.Presubmit;
-import android.support.test.filters.FlakyTest;
-import android.support.test.runner.AndroidJUnit4;
 import android.view.SurfaceControl.Transaction;
 
+import androidx.test.filters.FlakyTest;
+import androidx.test.runner.AndroidJUnit4;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/view/InsetsSourceTest.java b/core/tests/coretests/src/android/view/InsetsSourceTest.java
index ed472d2..98ab3e7 100644
--- a/core/tests/coretests/src/android/view/InsetsSourceTest.java
+++ b/core/tests/coretests/src/android/view/InsetsSourceTest.java
@@ -17,13 +17,15 @@
 package android.view;
 
 import static android.view.InsetsState.TYPE_NAVIGATION_BAR;
+
 import static junit.framework.Assert.assertEquals;
 
 import android.graphics.Insets;
 import android.graphics.Rect;
 import android.platform.test.annotations.Presubmit;
-import android.support.test.filters.FlakyTest;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.filters.FlakyTest;
+import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Before;
 import org.junit.Test;
diff --git a/core/tests/coretests/src/android/view/InsetsStateTest.java b/core/tests/coretests/src/android/view/InsetsStateTest.java
index 95af525..9857b7a 100644
--- a/core/tests/coretests/src/android/view/InsetsStateTest.java
+++ b/core/tests/coretests/src/android/view/InsetsStateTest.java
@@ -24,20 +24,23 @@
 import static android.view.InsetsState.TYPE_SIDE_BAR_2;
 import static android.view.InsetsState.TYPE_SIDE_BAR_3;
 import static android.view.InsetsState.TYPE_TOP_BAR;
+
 import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.assertFalse;
 import static junit.framework.Assert.assertTrue;
+
 import static org.junit.Assert.assertNotEquals;
 
 import android.graphics.Insets;
 import android.graphics.Rect;
 import android.os.Parcel;
 import android.platform.test.annotations.Presubmit;
-import android.support.test.filters.FlakyTest;
-import android.support.test.runner.AndroidJUnit4;
 import android.util.SparseIntArray;
 import android.view.WindowInsets.Type;
 
+import androidx.test.filters.FlakyTest;
+import androidx.test.runner.AndroidJUnit4;
+
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
diff --git a/core/tests/coretests/src/android/view/KeyEventTest.java b/core/tests/coretests/src/android/view/KeyEventTest.java
index b9d95e5..88d3bdb 100644
--- a/core/tests/coretests/src/android/view/KeyEventTest.java
+++ b/core/tests/coretests/src/android/view/KeyEventTest.java
@@ -20,8 +20,8 @@
 
 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/coretests/src/android/view/ListContextMenu.java b/core/tests/coretests/src/android/view/ListContextMenu.java
index 1b4ece6..e333b96 100644
--- a/core/tests/coretests/src/android/view/ListContextMenu.java
+++ b/core/tests/coretests/src/android/view/ListContextMenu.java
@@ -16,24 +16,17 @@
 
 package android.view;
 
-import com.android.frameworks.coretests.R;
-
 import android.app.ListActivity;
 import android.content.Context;
 import android.os.Bundle;
 import android.util.Log;
-import android.view.ContextMenu;
-import android.view.Menu;
-import android.view.MenuItem;
-import android.view.SubMenu;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.LayoutInflater;
 import android.view.ContextMenu.ContextMenuInfo;
 import android.widget.AdapterView;
 import android.widget.BaseAdapter;
 import android.widget.TextView;
 
+import com.android.frameworks.coretests.R;
+
 /**
  * Exercises context menus in lists
  */
diff --git a/core/tests/coretests/src/android/view/Longpress.java b/core/tests/coretests/src/android/view/Longpress.java
index e8e6f13..86fa9e5 100644
--- a/core/tests/coretests/src/android/view/Longpress.java
+++ b/core/tests/coretests/src/android/view/Longpress.java
@@ -16,11 +16,11 @@
 
 package android.view;
 
-import com.android.frameworks.coretests.R;
-
 import android.app.Activity;
 import android.os.Bundle;
 
+import com.android.frameworks.coretests.R;
+
 public class Longpress extends Activity {
     @Override
     protected void onCreate(Bundle savedInstanceState) {
diff --git a/core/tests/coretests/src/android/view/LongpressTest.java b/core/tests/coretests/src/android/view/LongpressTest.java
index d3d7589..4dfecd0 100644
--- a/core/tests/coretests/src/android/view/LongpressTest.java
+++ b/core/tests/coretests/src/android/view/LongpressTest.java
@@ -18,11 +18,12 @@
 
 import android.test.ActivityInstrumentationTestCase;
 import android.test.TouchUtils;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.util.KeyUtils;
 import android.view.View.OnLongClickListener;
 
+import androidx.test.filters.LargeTest;
+import androidx.test.filters.MediumTest;
+
 import com.android.frameworks.coretests.R;
 
 /**
diff --git a/core/tests/coretests/src/android/view/MenuTest.java b/core/tests/coretests/src/android/view/MenuTest.java
index 116b38a..794769cc 100644
--- a/core/tests/coretests/src/android/view/MenuTest.java
+++ b/core/tests/coretests/src/android/view/MenuTest.java
@@ -17,7 +17,8 @@
 package android.view;
 
 import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
+
+import androidx.test.filters.SmallTest;
 
 import com.android.frameworks.coretests.R;
 import com.android.internal.view.menu.MenuBuilder;
diff --git a/core/tests/coretests/src/android/view/Merge.java b/core/tests/coretests/src/android/view/Merge.java
index bdacd81..8020e4b 100644
--- a/core/tests/coretests/src/android/view/Merge.java
+++ b/core/tests/coretests/src/android/view/Merge.java
@@ -16,13 +16,11 @@
 
 package android.view;
 
-import com.android.frameworks.coretests.R;
-
-import android.os.Bundle;
 import android.app.Activity;
+import android.os.Bundle;
 import android.widget.LinearLayout;
-import android.view.ViewGroup;
-import android.view.LayoutInflater;
+
+import com.android.frameworks.coretests.R;
 
 /**
  * Exercise <merge /> tag in XML files.
diff --git a/core/tests/coretests/src/android/view/MergeTest.java b/core/tests/coretests/src/android/view/MergeTest.java
index acfee7e..eccc9cb 100644
--- a/core/tests/coretests/src/android/view/MergeTest.java
+++ b/core/tests/coretests/src/android/view/MergeTest.java
@@ -16,11 +16,9 @@
 
 package android.view;
 
-import android.view.Merge;
-
 import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.view.ViewGroup;
+
+import androidx.test.filters.MediumTest;
 
 public class MergeTest extends ActivityInstrumentationTestCase<Merge> {
     public MergeTest() {
diff --git a/core/tests/coretests/src/android/view/MotionEventTest.java b/core/tests/coretests/src/android/view/MotionEventTest.java
index 023526f..cadf37e 100644
--- a/core/tests/coretests/src/android/view/MotionEventTest.java
+++ b/core/tests/coretests/src/android/view/MotionEventTest.java
@@ -22,11 +22,12 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNull;
 
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
 import android.view.MotionEvent.PointerCoords;
 import android.view.MotionEvent.PointerProperties;
 
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
diff --git a/core/tests/coretests/src/android/view/MutateDrawable.java b/core/tests/coretests/src/android/view/MutateDrawable.java
index 39b5789..1ec525d 100644
--- a/core/tests/coretests/src/android/view/MutateDrawable.java
+++ b/core/tests/coretests/src/android/view/MutateDrawable.java
@@ -18,8 +18,9 @@
 
 import android.app.Activity;
 import android.os.Bundle;
-import android.widget.LinearLayout;
 import android.widget.Button;
+import android.widget.LinearLayout;
+
 import com.android.frameworks.coretests.R;
 
 public class MutateDrawable extends Activity {
diff --git a/core/tests/coretests/src/android/view/MutateDrawableTest.java b/core/tests/coretests/src/android/view/MutateDrawableTest.java
index 74e011d..d63ffb1 100644
--- a/core/tests/coretests/src/android/view/MutateDrawableTest.java
+++ b/core/tests/coretests/src/android/view/MutateDrawableTest.java
@@ -17,9 +17,8 @@
 package android.view;
 
 import android.test.ActivityInstrumentationTestCase2;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.view.View;
-import android.view.MutateDrawable;
+
+import androidx.test.filters.MediumTest;
 
 public class MutateDrawableTest extends ActivityInstrumentationTestCase2<MutateDrawable> {
     private View mFirstButton;
diff --git a/core/tests/coretests/src/android/view/PinchZoomAction.java b/core/tests/coretests/src/android/view/PinchZoomAction.java
index 97fe980..bec9b55 100644
--- a/core/tests/coretests/src/android/view/PinchZoomAction.java
+++ b/core/tests/coretests/src/android/view/PinchZoomAction.java
@@ -30,8 +30,6 @@
 import android.support.test.espresso.ViewAction;
 import android.support.test.espresso.action.Swiper;
 import android.support.test.espresso.util.HumanReadables;
-import android.view.MotionEvent;
-import android.view.View;
 
 import org.hamcrest.Matcher;
 
diff --git a/core/tests/coretests/src/android/view/PopupWindowVisibility.java b/core/tests/coretests/src/android/view/PopupWindowVisibility.java
index 85ce04f..e6430d3 100644
--- a/core/tests/coretests/src/android/view/PopupWindowVisibility.java
+++ b/core/tests/coretests/src/android/view/PopupWindowVisibility.java
@@ -18,7 +18,6 @@
 
 import android.app.Activity;
 import android.os.Bundle;
-import android.view.View;
 import android.view.View.OnClickListener;
 import android.widget.ArrayAdapter;
 import android.widget.AutoCompleteTextView;
diff --git a/core/tests/coretests/src/android/view/PreDrawListener.java b/core/tests/coretests/src/android/view/PreDrawListener.java
index 60bbee4..77bd36c 100644
--- a/core/tests/coretests/src/android/view/PreDrawListener.java
+++ b/core/tests/coretests/src/android/view/PreDrawListener.java
@@ -20,15 +20,12 @@
 import android.content.Context;
 import android.os.Bundle;
 import android.util.AttributeSet;
-import android.view.View;
-import android.view.ViewTreeObserver;
 import android.view.View.OnClickListener;
 import android.widget.Button;
 import android.widget.LinearLayout;
 
 import com.android.frameworks.coretests.R;
 
-
 /**
  * Tests views with popupWindows becoming invisible
  */
diff --git a/core/tests/coretests/src/android/view/RenderNodeAnimatorTest.java b/core/tests/coretests/src/android/view/RenderNodeAnimatorTest.java
index b52d98c..786c22b 100644
--- a/core/tests/coretests/src/android/view/RenderNodeAnimatorTest.java
+++ b/core/tests/coretests/src/android/view/RenderNodeAnimatorTest.java
@@ -21,10 +21,11 @@
 
 import android.app.Activity;
 import android.content.Context;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.annotation.UiThreadTest;
-import android.support.test.filters.MediumTest;
-import android.support.test.rule.ActivityTestRule;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.annotation.UiThreadTest;
+import androidx.test.filters.MediumTest;
+import androidx.test.rule.ActivityTestRule;
 
 import org.junit.Rule;
 import org.junit.Test;
diff --git a/core/tests/coretests/src/android/view/RunQueue.java b/core/tests/coretests/src/android/view/RunQueue.java
index 85dd32e..7544433 100644
--- a/core/tests/coretests/src/android/view/RunQueue.java
+++ b/core/tests/coretests/src/android/view/RunQueue.java
@@ -19,8 +19,7 @@
 import android.app.Activity;
 import android.os.Bundle;
 import android.widget.TextView;
-import android.view.View;
-import android.view.ViewTreeObserver;
+
 import com.android.frameworks.coretests.R;
 
 /**
diff --git a/core/tests/coretests/src/android/view/RunQueueTest.java b/core/tests/coretests/src/android/view/RunQueueTest.java
index d69860b..f42f590 100644
--- a/core/tests/coretests/src/android/view/RunQueueTest.java
+++ b/core/tests/coretests/src/android/view/RunQueueTest.java
@@ -17,7 +17,8 @@
 package android.view;
 
 import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
+
+import androidx.test.filters.MediumTest;
 
 public class RunQueueTest extends ActivityInstrumentationTestCase<RunQueue> {
     public RunQueueTest() {
diff --git a/core/tests/coretests/src/android/view/ScaleGesture.java b/core/tests/coretests/src/android/view/ScaleGesture.java
index a954a4a..235b224 100644
--- a/core/tests/coretests/src/android/view/ScaleGesture.java
+++ b/core/tests/coretests/src/android/view/ScaleGesture.java
@@ -16,16 +16,11 @@
 
 package android.view;
 
-import com.android.frameworks.coretests.R;
-
 import android.app.Activity;
 import android.os.Bundle;
-import android.util.Log;
-import android.util.TypedValue;
-import android.view.MotionEvent;
-import android.view.ScaleGestureDetector;
 import android.view.ScaleGestureDetector.SimpleOnScaleGestureListener;
-import android.widget.TextView;
+
+import com.android.frameworks.coretests.R;
 
 public class ScaleGesture extends Activity {
     private ScaleGestureDetector mScaleGestureDetector;
diff --git a/core/tests/coretests/src/android/view/ScaleGestureDetectorTest.java b/core/tests/coretests/src/android/view/ScaleGestureDetectorTest.java
index fba8eae..1990135 100644
--- a/core/tests/coretests/src/android/view/ScaleGestureDetectorTest.java
+++ b/core/tests/coretests/src/android/view/ScaleGestureDetectorTest.java
@@ -16,26 +16,22 @@
 
 package android.view;
 
+import static android.support.test.espresso.Espresso.onView;
+import static android.support.test.espresso.matcher.ViewMatchers.withId;
+
 import android.content.Context;
-import android.support.test.filters.LargeTest;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.runner.AndroidJUnit4;
 import android.test.ActivityInstrumentationTestCase2;
 import android.util.DisplayMetrics;
-import android.view.PinchZoomAction;
-import android.view.ScaleGesture;
-import android.view.WindowManager;
 import android.widget.TextView;
 
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.LargeTest;
+
 import com.android.frameworks.coretests.R;
 
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import static android.support.test.espresso.matcher.ViewMatchers.withId;
-import static android.support.test.espresso.Espresso.onView;
 
 @LargeTest
 public class ScaleGestureDetectorTest extends ActivityInstrumentationTestCase2<ScaleGesture> {
diff --git a/core/tests/coretests/src/android/view/SetTagsTest.java b/core/tests/coretests/src/android/view/SetTagsTest.java
index 373dce6..1699713 100644
--- a/core/tests/coretests/src/android/view/SetTagsTest.java
+++ b/core/tests/coretests/src/android/view/SetTagsTest.java
@@ -16,12 +16,13 @@
 
 package android.view;
 
-import com.android.frameworks.coretests.R;
-import android.test.suitebuilder.annotation.MediumTest;
-
 import android.test.ActivityInstrumentationTestCase2;
 import android.widget.Button;
 
+import androidx.test.filters.MediumTest;
+
+import com.android.frameworks.coretests.R;
+
 /**
  * Exercises {@link android.view.View}'s tags property.
  */
diff --git a/core/tests/coretests/src/android/view/StubbedView.java b/core/tests/coretests/src/android/view/StubbedView.java
index 612095c..c96b9b7 100644
--- a/core/tests/coretests/src/android/view/StubbedView.java
+++ b/core/tests/coretests/src/android/view/StubbedView.java
@@ -16,11 +16,10 @@
 
 package android.view;
 
-import com.android.frameworks.coretests.R;
-
-import android.os.Bundle;
 import android.app.Activity;
-import android.view.View;
+import android.os.Bundle;
+
+import com.android.frameworks.coretests.R;
 
 /**
  * Exercise <ViewStub /> tag in XML files.
diff --git a/core/tests/coretests/src/android/view/VelocityTest.java b/core/tests/coretests/src/android/view/VelocityTest.java
index 7f32208..c116f4d 100644
--- a/core/tests/coretests/src/android/view/VelocityTest.java
+++ b/core/tests/coretests/src/android/view/VelocityTest.java
@@ -16,16 +16,17 @@
 
 package android.view;
 
-import android.test.suitebuilder.annotation.Suppress;
-import junit.framework.Assert;
-
 import android.test.InstrumentationTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.view.animation.AccelerateInterpolator;
 import android.view.animation.DecelerateInterpolator;
 import android.view.animation.Interpolator;
 import android.view.animation.LinearInterpolator;
 
+import androidx.test.filters.MediumTest;
+import androidx.test.filters.Suppress;
+
+import junit.framework.Assert;
+
 /**
  * Exercises {@link android.view.VelocityTracker} to compute correct velocity.<br>
  * To launch this test, use :<br>
diff --git a/core/tests/coretests/src/android/view/ViewAttachTest.java b/core/tests/coretests/src/android/view/ViewAttachTest.java
index aa8f8d8..1a8dd99 100644
--- a/core/tests/coretests/src/android/view/ViewAttachTest.java
+++ b/core/tests/coretests/src/android/view/ViewAttachTest.java
@@ -16,16 +16,13 @@
 
 package android.view;
 
-import android.content.Context;
 import android.content.pm.ActivityInfo;
 import android.graphics.PixelFormat;
 import android.os.SystemClock;
-import android.support.test.filters.LargeTest;
 import android.test.ActivityInstrumentationTestCase2;
-import android.test.UiThreadTest;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.WindowManager;
+
+import androidx.test.annotation.UiThreadTest;
+import androidx.test.filters.LargeTest;
 
 import com.android.frameworks.coretests.R;
 
diff --git a/core/tests/coretests/src/android/view/ViewAttachTestActivity.java b/core/tests/coretests/src/android/view/ViewAttachTestActivity.java
index 59e25ae..bcbc813 100644
--- a/core/tests/coretests/src/android/view/ViewAttachTestActivity.java
+++ b/core/tests/coretests/src/android/view/ViewAttachTestActivity.java
@@ -16,11 +16,11 @@
 
 package android.view;
 
-import com.android.frameworks.coretests.R;
-
 import android.app.Activity;
 import android.os.Bundle;
 
+import com.android.frameworks.coretests.R;
+
 public class ViewAttachTestActivity extends Activity {
     public static final String TAG = "OnAttachedTest";
     @Override
diff --git a/core/tests/coretests/src/android/view/ViewAttachView.java b/core/tests/coretests/src/android/view/ViewAttachView.java
index 5af2d8f..2f3ff8f 100644
--- a/core/tests/coretests/src/android/view/ViewAttachView.java
+++ b/core/tests/coretests/src/android/view/ViewAttachView.java
@@ -22,7 +22,6 @@
 import android.os.SystemClock;
 import android.util.AttributeSet;
 import android.util.Log;
-import android.view.View;
 
 /**
  * A View that will throw a RuntimeException if onAttachedToWindow and
diff --git a/core/tests/coretests/src/android/view/ViewCaptureTest.java b/core/tests/coretests/src/android/view/ViewCaptureTest.java
index 4405934..218047c 100644
--- a/core/tests/coretests/src/android/view/ViewCaptureTest.java
+++ b/core/tests/coretests/src/android/view/ViewCaptureTest.java
@@ -21,14 +21,15 @@
 import android.app.Activity;
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
-import android.support.test.filters.SmallTest;
-import android.support.test.rule.ActivityTestRule;
-import android.support.test.runner.AndroidJUnit4;
 import android.util.SparseIntArray;
 import android.view.ViewDebug.CanvasProvider;
 import android.view.ViewDebug.HardwareCanvasProvider;
 import android.view.ViewDebug.SoftwareCanvasProvider;
 
+import androidx.test.filters.SmallTest;
+import androidx.test.rule.ActivityTestRule;
+import androidx.test.runner.AndroidJUnit4;
+
 import com.android.frameworks.coretests.R;
 
 import org.junit.Assert;
diff --git a/core/tests/coretests/src/android/view/ViewCaptureTestActivity.java b/core/tests/coretests/src/android/view/ViewCaptureTestActivity.java
index 20e3eb5..2f51c24 100644
--- a/core/tests/coretests/src/android/view/ViewCaptureTestActivity.java
+++ b/core/tests/coretests/src/android/view/ViewCaptureTestActivity.java
@@ -19,6 +19,7 @@
 import android.annotation.Nullable;
 import android.app.Activity;
 import android.os.Bundle;
+
 import com.android.frameworks.coretests.R;
 
 public class ViewCaptureTestActivity extends Activity {
diff --git a/core/tests/coretests/src/android/view/ViewGroupAttributesTest.java b/core/tests/coretests/src/android/view/ViewGroupAttributesTest.java
index b4ef0e7..c24cd35 100644
--- a/core/tests/coretests/src/android/view/ViewGroupAttributesTest.java
+++ b/core/tests/coretests/src/android/view/ViewGroupAttributesTest.java
@@ -18,7 +18,8 @@
 
 import android.content.Context;
 import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
+
+import androidx.test.filters.SmallTest;
 
 public class ViewGroupAttributesTest extends AndroidTestCase {
 
diff --git a/core/tests/coretests/src/android/view/ViewGroupChildren.java b/core/tests/coretests/src/android/view/ViewGroupChildren.java
index f39720b..d5d9879 100644
--- a/core/tests/coretests/src/android/view/ViewGroupChildren.java
+++ b/core/tests/coretests/src/android/view/ViewGroupChildren.java
@@ -16,12 +16,10 @@
 
 package android.view;
 
-import com.android.frameworks.coretests.R;
-
-import android.os.Bundle;
-import android.widget.Button;
-import android.view.View;
 import android.app.Activity;
+import android.os.Bundle;
+
+import com.android.frameworks.coretests.R;
 
 /**
  * Exercise ViewGroup's ability to add and remove children.
diff --git a/core/tests/coretests/src/android/view/ViewGroupChildrenTest.java b/core/tests/coretests/src/android/view/ViewGroupChildrenTest.java
index d1665ef..07cb2a1 100644
--- a/core/tests/coretests/src/android/view/ViewGroupChildrenTest.java
+++ b/core/tests/coretests/src/android/view/ViewGroupChildrenTest.java
@@ -16,18 +16,16 @@
 
 package android.view;
 
-import com.android.frameworks.coretests.R;
-import android.view.ViewGroupChildren;
-
 import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.test.ViewAsserts;
-import android.test.UiThreadTest;
-import android.view.View;
-import android.view.ViewGroup;
 import android.widget.LinearLayout;
 import android.widget.TextView;
 
+import androidx.test.annotation.UiThreadTest;
+import androidx.test.filters.MediumTest;
+
+import com.android.frameworks.coretests.R;
+
 /**
  * Exercises {@link android.view.ViewGroup}'s ability to add/remove children.
  */
diff --git a/core/tests/coretests/src/android/view/ViewGroupTransientViewTest.java b/core/tests/coretests/src/android/view/ViewGroupTransientViewTest.java
index 93ad41f0..54524b2 100644
--- a/core/tests/coretests/src/android/view/ViewGroupTransientViewTest.java
+++ b/core/tests/coretests/src/android/view/ViewGroupTransientViewTest.java
@@ -22,12 +22,13 @@
 import android.app.Activity;
 import android.content.Context;
 import android.graphics.Canvas;
-import android.support.test.annotation.UiThreadTest;
-import android.support.test.filters.MediumTest;
-import android.support.test.rule.ActivityTestRule;
-import android.support.test.runner.AndroidJUnit4;
 import android.widget.FrameLayout;
 
+import androidx.test.annotation.UiThreadTest;
+import androidx.test.filters.MediumTest;
+import androidx.test.rule.ActivityTestRule;
+import androidx.test.runner.AndroidJUnit4;
+
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
diff --git a/core/tests/coretests/src/android/view/ViewInvalidateTest.java b/core/tests/coretests/src/android/view/ViewInvalidateTest.java
index 115504b..c25a2deb 100644
--- a/core/tests/coretests/src/android/view/ViewInvalidateTest.java
+++ b/core/tests/coretests/src/android/view/ViewInvalidateTest.java
@@ -21,24 +21,30 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.app.Activity;
 import android.content.Context;
 import android.graphics.Rect;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.annotation.UiThreadTest;
-import android.support.test.filters.LargeTest;
-import android.support.test.rule.ActivityTestRule;
-import android.support.test.runner.AndroidJUnit4;
+import android.view.ViewTreeObserver.OnDrawListener;
 import android.widget.FrameLayout;
 
-import com.android.compatibility.common.util.WidgetTestUtils;
+import androidx.test.InstrumentationRegistry;
+import androidx.test.annotation.UiThreadTest;
+import androidx.test.filters.LargeTest;
+import androidx.test.rule.ActivityTestRule;
+import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.After;
+import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
 /**
  * Test of invalidates, drawing, and the flags that support them
  */
@@ -281,4 +287,42 @@
                 View.PFLAG_DRAWN);
         assertTrue(getViewRoot(mParent).mIsAnimating);
     }
+
+    /** Copied from cts/common/device-side/util. */
+    static class WidgetTestUtils {
+        public static void runOnMainAndDrawSync(@NonNull final ActivityTestRule activityTestRule,
+                @NonNull final View view, @Nullable final Runnable runner) throws Throwable {
+            final CountDownLatch latch = new CountDownLatch(1);
+
+            activityTestRule.runOnUiThread(() -> {
+                final OnDrawListener listener = new OnDrawListener() {
+                    @Override
+                    public void onDraw() {
+                        // posting so that the sync happens after the draw that's about to happen
+                        view.post(() -> {
+                            activityTestRule.getActivity().getWindow().getDecorView()
+                                    .getViewTreeObserver().removeOnDrawListener(this);
+                            latch.countDown();
+                        });
+                    }
+                };
+
+                activityTestRule.getActivity().getWindow().getDecorView()
+                        .getViewTreeObserver().addOnDrawListener(listener);
+
+                if (runner != null) {
+                    runner.run();
+                }
+                view.invalidate();
+            });
+
+            try {
+                Assert.assertTrue("Expected draw pass occurred within 5 seconds",
+                        latch.await(5, TimeUnit.SECONDS));
+            } catch (InterruptedException e) {
+                throw new RuntimeException(e);
+            }
+        }
+
+    }
 }
diff --git a/core/tests/coretests/src/android/view/ViewRootImplTest.java b/core/tests/coretests/src/android/view/ViewRootImplTest.java
index ca6d6cf..9a57847 100644
--- a/core/tests/coretests/src/android/view/ViewRootImplTest.java
+++ b/core/tests/coretests/src/android/view/ViewRootImplTest.java
@@ -23,9 +23,10 @@
 import android.graphics.Insets;
 import android.graphics.Rect;
 import android.platform.test.annotations.Presubmit;
-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/coretests/src/android/view/ViewStubTest.java b/core/tests/coretests/src/android/view/ViewStubTest.java
index ebd52a6..504a4ec 100644
--- a/core/tests/coretests/src/android/view/ViewStubTest.java
+++ b/core/tests/coretests/src/android/view/ViewStubTest.java
@@ -16,14 +16,12 @@
 
 package android.view;
 
-import android.view.StubbedView;
-import com.android.frameworks.coretests.R;
-
 import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.test.UiThreadTest;
-import android.view.View;
-import android.view.ViewStub;
+
+import androidx.test.annotation.UiThreadTest;
+import androidx.test.filters.MediumTest;
+
+import com.android.frameworks.coretests.R;
 
 public class ViewStubTest extends ActivityInstrumentationTestCase<StubbedView> {
     public ViewStubTest() {
diff --git a/core/tests/coretests/src/android/view/ViewTransientStateTest.java b/core/tests/coretests/src/android/view/ViewTransientStateTest.java
index 36ea01d..3f73b07 100644
--- a/core/tests/coretests/src/android/view/ViewTransientStateTest.java
+++ b/core/tests/coretests/src/android/view/ViewTransientStateTest.java
@@ -18,13 +18,12 @@
 
 import android.app.Activity;
 import android.test.ActivityInstrumentationTestCase;
-import android.test.UiThreadTest;
-import android.test.suitebuilder.annotation.MediumTest;
+
+import androidx.test.annotation.UiThreadTest;
+import androidx.test.filters.MediumTest;
 
 import com.android.frameworks.coretests.R;
 
-import static org.junit.Assert.assertFalse;
-
 /**
  * Exercise set View's transient state
  */
diff --git a/core/tests/coretests/src/android/view/Visibility.java b/core/tests/coretests/src/android/view/Visibility.java
index 031568c..8e3de4e 100644
--- a/core/tests/coretests/src/android/view/Visibility.java
+++ b/core/tests/coretests/src/android/view/Visibility.java
@@ -16,12 +16,11 @@
 
 package android.view;
 
-import com.android.frameworks.coretests.R;
-
+import android.app.Activity;
 import android.os.Bundle;
 import android.widget.Button;
-import android.view.View;
-import android.app.Activity;
+
+import com.android.frameworks.coretests.R;
 
 /**
  * Exercise View's ability to change their visibility: GONE, INVISIBLE and
diff --git a/core/tests/coretests/src/android/view/VisibilityCallback.java b/core/tests/coretests/src/android/view/VisibilityCallback.java
index f98a0a8..c965973 100644
--- a/core/tests/coretests/src/android/view/VisibilityCallback.java
+++ b/core/tests/coretests/src/android/view/VisibilityCallback.java
@@ -16,16 +16,15 @@
 
 package android.view;
 
+import android.app.Activity;
 import android.content.Context;
+import android.os.Bundle;
 import android.util.AttributeSet;
 import android.util.Log;
-import android.widget.TextView;
-import com.android.frameworks.coretests.R;
-
-import android.os.Bundle;
 import android.widget.Button;
-import android.view.View;
-import android.app.Activity;
+import android.widget.TextView;
+
+import com.android.frameworks.coretests.R;
 
 /**
  * Exercise View's ability to change their visibility: GONE, INVISIBLE and
diff --git a/core/tests/coretests/src/android/view/VisibilityCallbackTest.java b/core/tests/coretests/src/android/view/VisibilityCallbackTest.java
index ec956d2..d7c7b4c 100644
--- a/core/tests/coretests/src/android/view/VisibilityCallbackTest.java
+++ b/core/tests/coretests/src/android/view/VisibilityCallbackTest.java
@@ -17,12 +17,12 @@
 package android.view;
 
 import android.test.ActivityInstrumentationTestCase2;
-import android.test.UiThreadTest;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.view.View;
-import android.view.ViewGroup;
 import android.widget.Button;
 import android.widget.TextView;
+
+import androidx.test.annotation.UiThreadTest;
+import androidx.test.filters.MediumTest;
+
 import com.android.frameworks.coretests.R;
 
 /**
diff --git a/core/tests/coretests/src/android/view/VisibilityTest.java b/core/tests/coretests/src/android/view/VisibilityTest.java
index 29c1c8a..83a7702 100644
--- a/core/tests/coretests/src/android/view/VisibilityTest.java
+++ b/core/tests/coretests/src/android/view/VisibilityTest.java
@@ -20,11 +20,12 @@
 import static android.view.KeyEvent.KEYCODE_DPAD_LEFT;
 
 import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.widget.Button;
 import android.widget.TextView;
 
+import androidx.test.filters.LargeTest;
+import androidx.test.filters.MediumTest;
+
 import com.android.frameworks.coretests.R;
 
 /**
diff --git a/core/tests/coretests/src/android/view/WindowInsetsTest.java b/core/tests/coretests/src/android/view/WindowInsetsTest.java
index 1513a1a..15a96a1 100644
--- a/core/tests/coretests/src/android/view/WindowInsetsTest.java
+++ b/core/tests/coretests/src/android/view/WindowInsetsTest.java
@@ -17,18 +17,18 @@
 package android.view;
 
 import static android.view.WindowInsets.Type.ime;
-import static android.view.WindowInsets.Type.indexOf;
 import static android.view.WindowInsets.Type.sideBars;
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 
 import android.graphics.Insets;
 import android.graphics.Rect;
 import android.platform.test.annotations.Presubmit;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
 import android.view.WindowInsets.Builder;
-import android.view.WindowInsets.Type;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/view/ZeroSized.java b/core/tests/coretests/src/android/view/ZeroSized.java
index f2a6b3e..9e2dfcb 100644
--- a/core/tests/coretests/src/android/view/ZeroSized.java
+++ b/core/tests/coretests/src/android/view/ZeroSized.java
@@ -16,10 +16,10 @@
 
 package android.view;
 
-import com.android.frameworks.coretests.R;
-
-import android.os.Bundle;
 import android.app.Activity;
+import android.os.Bundle;
+
+import com.android.frameworks.coretests.R;
 
 /**
  * This activity contains Views with various widths and heights. The goal is to exercise the
diff --git a/core/tests/coretests/src/android/view/ZeroSizedTest.java b/core/tests/coretests/src/android/view/ZeroSizedTest.java
index 193fc98..946bf68 100644
--- a/core/tests/coretests/src/android/view/ZeroSizedTest.java
+++ b/core/tests/coretests/src/android/view/ZeroSizedTest.java
@@ -16,13 +16,12 @@
 
 package android.view;
 
-import android.view.ZeroSized;
-import com.android.frameworks.coretests.R;
-
-import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.view.View;
 import android.graphics.Bitmap;
+import android.test.ActivityInstrumentationTestCase;
+
+import androidx.test.filters.MediumTest;
+
+import com.android.frameworks.coretests.R;
 
 /**
  * Builds the drawing cache of Views of various dimension. The assumption is that
diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityCacheTest.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityCacheTest.java
index 7f675ff..a88968b 100644
--- a/core/tests/coretests/src/android/view/accessibility/AccessibilityCacheTest.java
+++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityCacheTest.java
@@ -28,10 +28,11 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
-import android.support.test.filters.LargeTest;
-import android.support.test.runner.AndroidJUnit4;
 import android.view.View;
 
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
+
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityEventTest.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityEventTest.java
index 3e03414..46c96c9 100644
--- a/core/tests/coretests/src/android/view/accessibility/AccessibilityEventTest.java
+++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityEventTest.java
@@ -21,7 +21,8 @@
 import static junit.framework.Assert.assertTrue;
 
 import android.os.Parcel;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityInteractionClientTest.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityInteractionClientTest.java
index 318d122..ab24f89 100644
--- a/core/tests/coretests/src/android/view/accessibility/AccessibilityInteractionClientTest.java
+++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityInteractionClientTest.java
@@ -22,10 +22,12 @@
 
 import android.os.Bundle;
 import android.os.RemoteException;
-import android.support.test.filters.LargeTest;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
 
 import libcore.util.EmptyArray;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java
index 506e544..0ed690c 100644
--- a/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java
+++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java
@@ -25,11 +25,12 @@
 import static org.junit.Assert.fail;
 
 import android.os.Parcel;
-import android.support.test.filters.LargeTest;
-import android.support.test.runner.AndroidJUnit4;
 import android.util.ArraySet;
 import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
 
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
+
 import com.android.internal.util.CollectionUtils;
 
 import org.junit.Test;
diff --git a/core/tests/coretests/src/android/view/accessibility/RecycleAccessibilityEventTest.java b/core/tests/coretests/src/android/view/accessibility/RecycleAccessibilityEventTest.java
index 4814c61..11f4e3c 100644
--- a/core/tests/coretests/src/android/view/accessibility/RecycleAccessibilityEventTest.java
+++ b/core/tests/coretests/src/android/view/accessibility/RecycleAccessibilityEventTest.java
@@ -14,7 +14,7 @@
 
 package android.view.accessibility;
 
-import android.test.suitebuilder.annotation.SmallTest;
+import androidx.test.filters.SmallTest;
 
 import junit.framework.TestCase;
 
diff --git a/core/tests/coretests/src/android/view/autofill/AutofillIdTest.java b/core/tests/coretests/src/android/view/autofill/AutofillIdTest.java
new file mode 100644
index 0000000..33bc593
--- /dev/null
+++ b/core/tests/coretests/src/android/view/autofill/AutofillIdTest.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.autofill;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.testng.Assert.assertThrows;
+
+import android.os.Parcel;
+import android.view.View;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class AutofillIdTest {
+
+    @Test
+    public void testNonVirtual() {
+        final AutofillId id = new AutofillId(42);
+        assertThat(id.getViewId()).isEqualTo(42);
+        assertThat(id.isVirtual()).isFalse();
+        assertThat(id.getVirtualChildId()).isEqualTo(View.NO_ID);
+
+        final AutofillId clone = cloneThroughParcel(id);
+        assertThat(clone.getViewId()).isEqualTo(42);
+        assertThat(clone.isVirtual()).isFalse();
+        assertThat(clone.getVirtualChildId()).isEqualTo(View.NO_ID);
+    }
+
+    @Test
+    public void testVirtual() {
+        final AutofillId id = new AutofillId(42, 108);
+        assertThat(id.getViewId()).isEqualTo(42);
+        assertThat(id.isVirtual()).isTrue();
+        assertThat(id.getVirtualChildId()).isEqualTo(108);
+
+        final AutofillId clone = cloneThroughParcel(id);
+        assertThat(clone.getViewId()).isEqualTo(42);
+        assertThat(clone.isVirtual()).isTrue();
+        assertThat(clone.getVirtualChildId()).isEqualTo(108);
+    }
+
+    @Test
+    public void testVirtual_parentObjectConstructor() {
+        assertThrows(NullPointerException.class, () -> new AutofillId(null, 108));
+
+        final AutofillId id = new AutofillId(new AutofillId(42), 108);
+        assertThat(id.getViewId()).isEqualTo(42);
+        assertThat(id.isVirtual()).isTrue();
+        assertThat(id.getVirtualChildId()).isEqualTo(108);
+
+        final AutofillId clone = cloneThroughParcel(id);
+        assertThat(clone.getViewId()).isEqualTo(42);
+        assertThat(clone.isVirtual()).isTrue();
+        assertThat(clone.getVirtualChildId()).isEqualTo(108);
+    }
+
+    @Test
+    public void testVirtual_withSession() {
+        final AutofillId id = new AutofillId(new AutofillId(42), 108, 666);
+        assertThat(id.getViewId()).isEqualTo(42);
+        assertThat(id.isVirtual()).isTrue();
+        assertThat(id.getVirtualChildId()).isEqualTo(108);
+        assertThat(id.getSessionId()).isEqualTo(666);
+
+        final AutofillId clone = cloneThroughParcel(id);
+        assertThat(clone.getViewId()).isEqualTo(42);
+        assertThat(clone.isVirtual()).isTrue();
+        assertThat(clone.getVirtualChildId()).isEqualTo(108);
+        assertThat(clone.getSessionId()).isEqualTo(666);
+    }
+
+    @Test
+    public void testEqualsHashCode() {
+        final AutofillId realId = new AutofillId(42);
+        final AutofillId realIdSame = new AutofillId(42);
+        assertThat(realId).isEqualTo(realIdSame);
+        assertThat(realIdSame).isEqualTo(realId);
+        assertThat(realId.hashCode()).isEqualTo(realIdSame.hashCode());
+
+        final AutofillId realIdDifferent = new AutofillId(108);
+        assertThat(realId).isNotEqualTo(realIdDifferent);
+        assertThat(realIdDifferent).isNotEqualTo(realId);
+
+        final AutofillId virtualId = new AutofillId(42, 1);
+        final AutofillId virtualIdSame = new AutofillId(42, 1);
+        assertThat(virtualId).isEqualTo(virtualIdSame);
+        assertThat(virtualIdSame).isEqualTo(virtualId);
+        assertThat(virtualId.hashCode()).isEqualTo(virtualIdSame.hashCode());
+        assertThat(virtualId).isNotEqualTo(realId);
+        assertThat(realId).isNotEqualTo(virtualId);
+
+        final AutofillId virtualIdDifferentChild = new AutofillId(42, 2);
+        assertThat(virtualIdDifferentChild).isNotEqualTo(virtualId);
+        assertThat(virtualId).isNotEqualTo(virtualIdDifferentChild);
+        assertThat(virtualIdDifferentChild).isNotEqualTo(realId);
+        assertThat(realId).isNotEqualTo(virtualIdDifferentChild);
+
+        final AutofillId virtualIdDifferentParent = new AutofillId(108, 1);
+        assertThat(virtualIdDifferentParent).isNotEqualTo(virtualId);
+        assertThat(virtualId).isNotEqualTo(virtualIdDifferentParent);
+        assertThat(virtualIdDifferentParent).isNotEqualTo(virtualIdDifferentChild);
+        assertThat(virtualIdDifferentChild).isNotEqualTo(virtualIdDifferentParent);
+
+        final AutofillId virtualIdDifferentSession = new AutofillId(new AutofillId(42), 1, 108);
+        assertThat(virtualIdDifferentSession).isNotEqualTo(virtualId);
+        assertThat(virtualId).isNotEqualTo(virtualIdDifferentSession);
+        assertThat(virtualIdDifferentSession).isNotEqualTo(realId);
+        assertThat(realId).isNotEqualTo(virtualIdDifferentSession);
+
+        final AutofillId sameVirtualIdDifferentSession = new AutofillId(new AutofillId(42), 1, 108);
+        assertThat(sameVirtualIdDifferentSession).isEqualTo(virtualIdDifferentSession);
+        assertThat(virtualIdDifferentSession).isEqualTo(sameVirtualIdDifferentSession);
+        assertThat(sameVirtualIdDifferentSession.hashCode())
+                .isEqualTo(virtualIdDifferentSession.hashCode());
+    }
+
+    private AutofillId cloneThroughParcel(AutofillId id) {
+        Parcel parcel = Parcel.obtain();
+
+        try {
+            // Write to parcel
+            parcel.setDataPosition(0); // Sanity / paranoid check
+            id.writeToParcel(parcel, 0);
+
+            // Read from parcel
+            parcel.setDataPosition(0);
+            AutofillId clone = AutofillId.CREATOR.createFromParcel(parcel);
+            assertThat(clone).isNotNull();
+            return clone;
+        } finally {
+            parcel.recycle();
+        }
+    }
+}
diff --git a/core/tests/coretests/src/android/view/contentcapture/ContentCaptureSessionTest.java b/core/tests/coretests/src/android/view/contentcapture/ContentCaptureSessionTest.java
index 59f3a4c..a2f0eba 100644
--- a/core/tests/coretests/src/android/view/contentcapture/ContentCaptureSessionTest.java
+++ b/core/tests/coretests/src/android/view/contentcapture/ContentCaptureSessionTest.java
@@ -13,17 +13,21 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package android.view.contentcapture;
 
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.testng.Assert.assertThrows;
 
+import android.view.View;
+import android.view.ViewStructure;
 import android.view.autofill.AutofillId;
+import android.view.contentcapture.ViewNode.ViewStructureImpl;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.mockito.Spy;
+import org.mockito.Mock;
 import org.mockito.junit.MockitoJUnitRunner;
 
 /**
@@ -35,34 +39,104 @@
 @RunWith(MockitoJUnitRunner.class)
 public class ContentCaptureSessionTest {
 
-    /**
-     * Uses a spy as ContentCaptureSession is abstract but (so far) we're testing its concrete
-     * methods.
-     */
-    @Spy
-    private ContentCaptureSession mMockSession;
+    private ContentCaptureSession mSession1 = new MyContentCaptureSession("111");
+
+    private ContentCaptureSession mSession2 = new MyContentCaptureSession("2222");
+
+    @Mock
+    private View mMockView;
 
     @Test
     public void testNewAutofillId_invalid() {
-        assertThrows(NullPointerException.class, () -> mMockSession.newAutofillId(null, 42));
+        assertThrows(NullPointerException.class, () -> mSession1.newAutofillId(null, 42));
         assertThrows(IllegalArgumentException.class,
-                () -> mMockSession.newAutofillId(new AutofillId(42, 42), 42));
+                () -> mSession1.newAutofillId(new AutofillId(42, 42), 42));
     }
 
     @Test
     public void testNewAutofillId_valid() {
         final AutofillId parentId = new AutofillId(42);
-        final AutofillId childId = mMockSession.newAutofillId(parentId, 108);
+        final AutofillId childId = mSession1.newAutofillId(parentId, 108);
         assertThat(childId.getViewId()).isEqualTo(42);
         assertThat(childId.getVirtualChildId()).isEqualTo(108);
-        // TODO(b/121197119): assert session id
+        assertThat(childId.getSessionId()).isEqualTo(mSession1.getIdAsInt());
+    }
+
+    @Test
+    public void testNewAutofillId_differentSessions() {
+        assertThat(mSession1.getIdAsInt()).isNotSameAs(mSession2.getIdAsInt()); //sanity check
+        final AutofillId parentId = new AutofillId(42);
+        final AutofillId childId1 = mSession1.newAutofillId(parentId, 108);
+        final AutofillId childId2 = mSession2.newAutofillId(parentId, 108);
+        assertThat(childId1).isNotEqualTo(childId2);
+        assertThat(childId2).isNotEqualTo(childId1);
     }
 
     @Test
     public void testNotifyXXX_null() {
-        assertThrows(NullPointerException.class, () -> mMockSession.notifyViewAppeared(null));
-        assertThrows(NullPointerException.class, () -> mMockSession.notifyViewDisappeared(null));
+        assertThrows(NullPointerException.class, () -> mSession1.notifyViewAppeared(null));
+        assertThrows(NullPointerException.class, () -> mSession1.notifyViewDisappeared(null));
         assertThrows(NullPointerException.class,
-                () -> mMockSession.notifyViewTextChanged(null, "whatever", 0));
+                () -> mSession1.notifyViewTextChanged(null, "whatever", 0));
+    }
+
+    @Test
+    public void testNewViewStructure() {
+        assertThat(mMockView.getAutofillId()).isNotNull(); // sanity check
+        final ViewStructure structure = mSession1.newViewStructure(mMockView);
+        assertThat(structure).isNotNull();
+        assertThat(structure.getAutofillId()).isEqualTo(mMockView.getAutofillId());
+    }
+
+    @Test
+    public void testNewVirtualViewStructure() {
+        final AutofillId parentId = new AutofillId(42);
+        final ViewStructure structure = mSession1.newVirtualViewStructure(parentId, 108);
+        assertThat(structure).isNotNull();
+        final AutofillId childId = mSession1.newAutofillId(parentId, 108);
+        assertThat(structure.getAutofillId()).isEqualTo(childId);
+    }
+
+    // Cannot use @Spy because we need to pass the session id on constructor
+    private class MyContentCaptureSession extends ContentCaptureSession {
+
+        private MyContentCaptureSession(String id) {
+            super(id);
+        }
+
+        @Override
+        MainContentCaptureSession getMainCaptureSession() {
+            throw new UnsupportedOperationException("should not have been called");
+        }
+
+        @Override
+        ContentCaptureSession newChild(ContentCaptureContext context) {
+            throw new UnsupportedOperationException("should not have been called");
+        }
+
+        @Override
+        void flush() {
+            throw new UnsupportedOperationException("should not have been called");
+        }
+
+        @Override
+        void onDestroy() {
+            throw new UnsupportedOperationException("should not have been called");
+        }
+
+        @Override
+        void internalNotifyViewAppeared(ViewStructureImpl node) {
+            throw new UnsupportedOperationException("should not have been called");
+        }
+
+        @Override
+        void internalNotifyViewDisappeared(AutofillId id) {
+            throw new UnsupportedOperationException("should not have been called");
+        }
+
+        @Override
+        void internalNotifyViewTextChanged(AutofillId id, CharSequence text, int flags) {
+            throw new UnsupportedOperationException("should not have been called");
+        }
     }
 }
diff --git a/core/tests/coretests/src/android/view/contentcapture/ViewNodeTest.java b/core/tests/coretests/src/android/view/contentcapture/ViewNodeTest.java
index 995946b..099f41d 100644
--- a/core/tests/coretests/src/android/view/contentcapture/ViewNodeTest.java
+++ b/core/tests/coretests/src/android/view/contentcapture/ViewNodeTest.java
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package android.view.contentcapture;
 
 import static com.google.common.truth.Truth.assertThat;
@@ -24,7 +25,6 @@
 import android.os.Bundle;
 import android.os.LocaleList;
 import android.os.Parcel;
-import android.support.test.InstrumentationRegistry;
 import android.view.View;
 import android.view.ViewStructure.HtmlInfo;
 import android.view.autofill.AutofillId;
@@ -32,6 +32,8 @@
 import android.view.contentcapture.ViewNode.ViewStructureImpl;
 import android.widget.FrameLayout;
 
+import androidx.test.InstrumentationRegistry;
+
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
@@ -110,10 +112,10 @@
     @Test
     public void testAutofillIdMethods_explicitIdsConstructor() {
         AutofillId initialParentId = new AutofillId(42);
-        ViewStructureImpl structure = new ViewStructureImpl(initialParentId, 108);
+        ViewStructureImpl structure = new ViewStructureImpl(initialParentId, 108, 666);
         ViewNode node = structure.getNode();
 
-        assertThat(node.getAutofillId()).isEqualTo(new AutofillId(initialParentId, 108));
+        assertThat(node.getAutofillId()).isEqualTo(new AutofillId(initialParentId, 108, 666));
         assertThat(node.getParentAutofillId()).isEqualTo(initialParentId);
 
         AutofillId newChildId = new AutofillId(108);
diff --git a/core/tests/coretests/src/android/view/inputmethod/CursorAnchorInfoTest.java b/core/tests/coretests/src/android/view/inputmethod/CursorAnchorInfoTest.java
index 0fd0136..ace6611 100644
--- a/core/tests/coretests/src/android/view/inputmethod/CursorAnchorInfoTest.java
+++ b/core/tests/coretests/src/android/view/inputmethod/CursorAnchorInfoTest.java
@@ -28,11 +28,12 @@
 import android.graphics.Matrix;
 import android.graphics.RectF;
 import android.os.Parcel;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
 import android.text.TextUtils;
 import android.view.inputmethod.CursorAnchorInfo.Builder;
 
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
diff --git a/core/tests/coretests/src/android/view/inputmethod/InputMethodInfoTest.java b/core/tests/coretests/src/android/view/inputmethod/InputMethodInfoTest.java
index 1b00e09..f24e232 100644
--- a/core/tests/coretests/src/android/view/inputmethod/InputMethodInfoTest.java
+++ b/core/tests/coretests/src/android/view/inputmethod/InputMethodInfoTest.java
@@ -26,9 +26,10 @@
 import android.content.pm.ServiceInfo;
 import android.os.Bundle;
 import android.os.Parcel;
-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 com.android.frameworks.coretests.R;
 
diff --git a/core/tests/coretests/src/android/view/inputmethod/InputMethodManagerTest.java b/core/tests/coretests/src/android/view/inputmethod/InputMethodManagerTest.java
index 55e5e36..9f259a8 100644
--- a/core/tests/coretests/src/android/view/inputmethod/InputMethodManagerTest.java
+++ b/core/tests/coretests/src/android/view/inputmethod/InputMethodManagerTest.java
@@ -20,11 +20,12 @@
 import static org.junit.Assert.assertNotNull;
 
 import android.content.Context;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
 import android.view.WindowManager;
 
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
diff --git a/core/tests/coretests/src/android/view/inputmethod/InputMethodSubtypeArrayTest.java b/core/tests/coretests/src/android/view/inputmethod/InputMethodSubtypeArrayTest.java
index 8df1848..e2fb46a 100644
--- a/core/tests/coretests/src/android/view/inputmethod/InputMethodSubtypeArrayTest.java
+++ b/core/tests/coretests/src/android/view/inputmethod/InputMethodSubtypeArrayTest.java
@@ -19,10 +19,11 @@
 import static org.junit.Assert.assertEquals;
 
 import android.os.Parcel;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
 import android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder;
 
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
diff --git a/core/tests/coretests/src/android/view/inputmethod/InputMethodSubtypeTest.java b/core/tests/coretests/src/android/view/inputmethod/InputMethodSubtypeTest.java
index c76359e..1e0e1235 100644
--- a/core/tests/coretests/src/android/view/inputmethod/InputMethodSubtypeTest.java
+++ b/core/tests/coretests/src/android/view/inputmethod/InputMethodSubtypeTest.java
@@ -23,10 +23,11 @@
 import static org.junit.Assert.assertTrue;
 
 import android.os.Parcel;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
 import android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder;
 
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
diff --git a/core/tests/coretests/src/android/view/inputmethod/SparseRectFArrayTest.java b/core/tests/coretests/src/android/view/inputmethod/SparseRectFArrayTest.java
index 8c96b58..453ad72 100644
--- a/core/tests/coretests/src/android/view/inputmethod/SparseRectFArrayTest.java
+++ b/core/tests/coretests/src/android/view/inputmethod/SparseRectFArrayTest.java
@@ -23,10 +23,11 @@
 
 import android.graphics.RectF;
 import android.os.Parcel;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
 import android.view.inputmethod.SparseRectFArray.SparseRectFArrayBuilder;
 
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
diff --git a/core/tests/coretests/src/android/view/menu/ContextMenuTest.java b/core/tests/coretests/src/android/view/menu/ContextMenuTest.java
index 657a7fc..ba85d76 100644
--- a/core/tests/coretests/src/android/view/menu/ContextMenuTest.java
+++ b/core/tests/coretests/src/android/view/menu/ContextMenuTest.java
@@ -18,7 +18,6 @@
 
 import android.content.Context;
 import android.graphics.Point;
-import android.support.test.filters.MediumTest;
 import android.test.ActivityInstrumentationTestCase;
 import android.util.PollingCheck;
 import android.view.Display;
@@ -26,6 +25,8 @@
 import android.view.WindowManager;
 import android.widget.espresso.ContextMenuUtils;
 
+import androidx.test.filters.MediumTest;
+
 @MediumTest
 public class ContextMenuTest extends ActivityInstrumentationTestCase<ContextMenuActivity> {
 
diff --git a/core/tests/coretests/src/android/view/menu/MenuLayout.java b/core/tests/coretests/src/android/view/menu/MenuLayout.java
index 356c948..33ee515 100644
--- a/core/tests/coretests/src/android/view/menu/MenuLayout.java
+++ b/core/tests/coretests/src/android/view/menu/MenuLayout.java
@@ -16,8 +16,6 @@
 
 package android.view.menu;
 
-import android.view.menu.MenuScenario.Params;
-
 import android.os.Bundle;
 import android.view.Menu;
 import android.widget.Button;
diff --git a/core/tests/coretests/src/android/view/menu/MenuLayoutLandscapeTest.java b/core/tests/coretests/src/android/view/menu/MenuLayoutLandscapeTest.java
index 8ed0d86..ff9f166 100644
--- a/core/tests/coretests/src/android/view/menu/MenuLayoutLandscapeTest.java
+++ b/core/tests/coretests/src/android/view/menu/MenuLayoutLandscapeTest.java
@@ -16,13 +16,10 @@
 
 package android.view.menu;
 
-import android.util.KeyUtils;
-import com.android.internal.view.menu.IconMenuView;
-import com.android.internal.view.menu.MenuBuilder;
-
-import android.content.pm.ActivityInfo;
-import android.support.test.filters.LargeTest;
 import android.test.ActivityInstrumentationTestCase;
+import android.util.KeyUtils;
+
+import androidx.test.filters.LargeTest;
 
 @LargeTest
 public class MenuLayoutLandscapeTest extends ActivityInstrumentationTestCase<MenuLayoutLandscape> {
diff --git a/core/tests/coretests/src/android/view/menu/MenuLayoutPortraitTest.java b/core/tests/coretests/src/android/view/menu/MenuLayoutPortraitTest.java
index ccf1264..360be53 100644
--- a/core/tests/coretests/src/android/view/menu/MenuLayoutPortraitTest.java
+++ b/core/tests/coretests/src/android/view/menu/MenuLayoutPortraitTest.java
@@ -16,11 +16,11 @@
 
 package android.view.menu;
 
-import android.content.pm.ActivityInfo;
-import android.support.test.filters.LargeTest;
 import android.test.ActivityInstrumentationTestCase;
 import android.util.KeyUtils;
 
+import androidx.test.filters.LargeTest;
+
 @LargeTest
 public class MenuLayoutPortraitTest extends ActivityInstrumentationTestCase<MenuLayoutPortrait> {
     private static final String LONG_TITLE = "Really really really really really really really really really really long title";
diff --git a/core/tests/coretests/src/android/view/menu/MenuWith1ItemTest.java b/core/tests/coretests/src/android/view/menu/MenuWith1ItemTest.java
index 82ad858..c18e361 100644
--- a/core/tests/coretests/src/android/view/menu/MenuWith1ItemTest.java
+++ b/core/tests/coretests/src/android/view/menu/MenuWith1ItemTest.java
@@ -16,17 +16,12 @@
 
 package android.view.menu;
 
-import android.view.menu.MenuWith1Item;
-import android.util.KeyUtils;
-import com.android.internal.view.menu.MenuBuilder;
-
-import android.test.suitebuilder.annotation.LargeTest;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.test.TouchUtils;
-
 import android.test.ActivityInstrumentationTestCase;
+import android.util.KeyUtils;
 import android.view.KeyEvent;
-import android.view.View;
+
+import androidx.test.filters.LargeTest;
+import androidx.test.filters.MediumTest;
 
 public class MenuWith1ItemTest extends ActivityInstrumentationTestCase<MenuWith1Item> {
     private MenuWith1Item mActivity;
diff --git a/core/tests/coretests/src/android/view/textclassifier/ActionsSuggestionsHelperTest.java b/core/tests/coretests/src/android/view/textclassifier/ActionsSuggestionsHelperTest.java
index 4a6c093..780e15a 100644
--- a/core/tests/coretests/src/android/view/textclassifier/ActionsSuggestionsHelperTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/ActionsSuggestionsHelperTest.java
@@ -22,8 +22,9 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import android.app.Person;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
 
 import com.google.android.textclassifier.ActionsSuggestionsModel;
 
diff --git a/core/tests/coretests/src/android/view/textclassifier/FakeContextBuilder.java b/core/tests/coretests/src/android/view/textclassifier/FakeContextBuilder.java
index 0180856..fef6583 100644
--- a/core/tests/coretests/src/android/view/textclassifier/FakeContextBuilder.java
+++ b/core/tests/coretests/src/android/view/textclassifier/FakeContextBuilder.java
@@ -21,6 +21,7 @@
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
+import android.annotation.Nullable;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.ContextWrapper;
@@ -29,9 +30,8 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
-import android.support.test.InstrumentationRegistry;
 
-import androidx.annotation.Nullable;
+import androidx.test.InstrumentationRegistry;
 
 import com.google.common.base.Preconditions;
 
diff --git a/core/tests/coretests/src/android/view/textclassifier/IntentFactoryTest.java b/core/tests/coretests/src/android/view/textclassifier/IntentFactoryTest.java
index aaadefb..3fc8e4c 100644
--- a/core/tests/coretests/src/android/view/textclassifier/IntentFactoryTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/IntentFactoryTest.java
@@ -13,14 +13,16 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package android.view.textclassifier;
 
 import static com.google.common.truth.Truth.assertThat;
 
 import android.content.Intent;
-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 com.google.android.textclassifier.AnnotatorModel;
 
diff --git a/core/tests/coretests/src/android/view/textclassifier/ModelFileManagerTest.java b/core/tests/coretests/src/android/view/textclassifier/ModelFileManagerTest.java
index 88d162b..74b8e3b 100644
--- a/core/tests/coretests/src/android/view/textclassifier/ModelFileManagerTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/ModelFileManagerTest.java
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package android.view.textclassifier;
 
 import static com.google.common.truth.Truth.assertThat;
@@ -20,9 +21,10 @@
 import static org.mockito.Mockito.when;
 
 import android.os.LocaleList;
-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.After;
 import org.junit.Before;
diff --git a/core/tests/coretests/src/android/view/textclassifier/SelectionEventTest.java b/core/tests/coretests/src/android/view/textclassifier/SelectionEventTest.java
index a3c6179..46e3a4c 100644
--- a/core/tests/coretests/src/android/view/textclassifier/SelectionEventTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/SelectionEventTest.java
@@ -19,8 +19,9 @@
 import static org.junit.Assert.assertEquals;
 
 import android.os.Parcel;
-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/coretests/src/android/view/textclassifier/TextClassificationConstantsTest.java b/core/tests/coretests/src/android/view/textclassifier/TextClassificationConstantsTest.java
index 54007fb..9662182 100644
--- a/core/tests/coretests/src/android/view/textclassifier/TextClassificationConstantsTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/TextClassificationConstantsTest.java
@@ -20,8 +20,8 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
-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/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java b/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java
index a3f69d9..4fcd51c 100644
--- a/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java
@@ -26,9 +26,10 @@
 import android.content.Intent;
 import android.os.LocaleList;
 import android.service.textclassifier.TextClassifierService;
-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/coretests/src/android/view/textclassifier/TextClassificationTest.java b/core/tests/coretests/src/android/view/textclassifier/TextClassificationTest.java
index aaf7312..99c959e 100644
--- a/core/tests/coretests/src/android/view/textclassifier/TextClassificationTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/TextClassificationTest.java
@@ -32,11 +32,12 @@
 import android.os.Bundle;
 import android.os.LocaleList;
 import android.os.Parcel;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
 import android.view.View;
 
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
diff --git a/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java b/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java
index 82eaf88..7009fb2 100644
--- a/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java
@@ -24,11 +24,12 @@
 import android.content.Context;
 import android.content.Intent;
 import android.os.LocaleList;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SmallTest;
 import android.text.Spannable;
 import android.text.SpannableString;
 
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+
 import org.hamcrest.BaseMatcher;
 import org.hamcrest.Description;
 import org.hamcrest.Matcher;
@@ -377,10 +378,10 @@
                         ConversationActions.Message.PERSON_USER_REMOTE)
                         .setText("Where are you?")
                         .build();
-        ConversationActions.TypeConfig typeConfig =
-                new ConversationActions.TypeConfig.Builder().includeTypesFromTextClassifier(false)
+        TextClassifier.EntityConfig typeConfig =
+                new TextClassifier.EntityConfig.Builder().includeTypesFromTextClassifier(false)
                         .setIncludedTypes(
-                                Collections.singletonList(ConversationActions.TYPE_TEXT_REPLY))
+                                Collections.singletonList(ConversationAction.TYPE_TEXT_REPLY))
                         .build();
         ConversationActions.Request request =
                 new ConversationActions.Request.Builder(Collections.singletonList(message))
@@ -391,10 +392,10 @@
         ConversationActions conversationActions = mClassifier.suggestConversationActions(request);
         assertTrue(conversationActions.getConversationActions().size() > 0);
         assertTrue(conversationActions.getConversationActions().size() == 1);
-        for (ConversationActions.ConversationAction conversationAction :
+        for (ConversationAction conversationAction :
                 conversationActions.getConversationActions()) {
             assertThat(conversationAction,
-                    isConversationAction(ConversationActions.TYPE_TEXT_REPLY));
+                    isConversationAction(ConversationAction.TYPE_TEXT_REPLY));
         }
     }*/
 
@@ -406,10 +407,10 @@
                         ConversationActions.Message.PERSON_USER_REMOTE)
                         .setText("Where are you?")
                         .build();
-        ConversationActions.TypeConfig typeConfig =
-                new ConversationActions.TypeConfig.Builder().includeTypesFromTextClassifier(false)
+        TextClassifier.EntityConfig typeConfig =
+                new TextClassifier.EntityConfig.Builder().includeTypesFromTextClassifier(false)
                         .setIncludedTypes(
-                                Collections.singletonList(ConversationActions.TYPE_TEXT_REPLY))
+                                Collections.singletonList(ConversationAction.TYPE_TEXT_REPLY))
                         .build();
         ConversationActions.Request request =
                 new ConversationActions.Request.Builder(Collections.singletonList(message))
@@ -418,10 +419,10 @@
 
         ConversationActions conversationActions = mClassifier.suggestConversationActions(request);
         assertTrue(conversationActions.getConversationActions().size() > 1);
-        for (ConversationActions.ConversationAction conversationAction :
+        for (ConversationAction conversationAction :
                 conversationActions.getConversationActions()) {
             assertThat(conversationAction,
-                    isConversationAction(ConversationActions.TYPE_TEXT_REPLY));
+                    isConversationAction(ConversationAction.TYPE_TEXT_REPLY));
         }
     }
 
@@ -524,20 +525,19 @@
         };
     }
 
-    private static Matcher<ConversationActions.ConversationAction> isConversationAction(
-            String actionType) {
-        return new BaseMatcher<ConversationActions.ConversationAction>() {
+    private static Matcher<ConversationAction> isConversationAction(String actionType) {
+        return new BaseMatcher<ConversationAction>() {
             @Override
             public boolean matches(Object o) {
-                if (!(o instanceof ConversationActions.ConversationAction)) {
+                if (!(o instanceof ConversationAction)) {
                     return false;
                 }
-                ConversationActions.ConversationAction conversationAction =
-                        (ConversationActions.ConversationAction) o;
+                ConversationAction conversationAction =
+                        (ConversationAction) o;
                 if (!actionType.equals(conversationAction.getType())) {
                     return false;
                 }
-                if (ConversationActions.TYPE_TEXT_REPLY.equals(actionType)) {
+                if (ConversationAction.TYPE_TEXT_REPLY.equals(actionType)) {
                     if (conversationAction.getTextReply() == null) {
                         return false;
                     }
diff --git a/core/tests/coretests/src/android/view/textclassifier/TextLanguageTest.java b/core/tests/coretests/src/android/view/textclassifier/TextLanguageTest.java
index 1dcaed6..d0d32e3 100644
--- a/core/tests/coretests/src/android/view/textclassifier/TextLanguageTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/TextLanguageTest.java
@@ -21,8 +21,9 @@
 import android.icu.util.ULocale;
 import android.os.Bundle;
 import android.os.Parcel;
-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/coretests/src/android/view/textclassifier/TextLinksTest.java b/core/tests/coretests/src/android/view/textclassifier/TextLinksTest.java
index f022d04..b9cc8f4 100644
--- a/core/tests/coretests/src/android/view/textclassifier/TextLinksTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/TextLinksTest.java
@@ -21,10 +21,11 @@
 import android.os.Bundle;
 import android.os.LocaleList;
 import android.os.Parcel;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
 import android.util.ArrayMap;
 
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
diff --git a/core/tests/coretests/src/android/view/textclassifier/TextSelectionTest.java b/core/tests/coretests/src/android/view/textclassifier/TextSelectionTest.java
index 2ea49f7..30cc4e8 100644
--- a/core/tests/coretests/src/android/view/textclassifier/TextSelectionTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/TextSelectionTest.java
@@ -21,8 +21,9 @@
 import android.os.Bundle;
 import android.os.LocaleList;
 import android.os.Parcel;
-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/coretests/src/android/view/textclassifier/logging/GenerateLinksLoggerTest.java b/core/tests/coretests/src/android/view/textclassifier/logging/GenerateLinksLoggerTest.java
index 8e4f02c..5e8e582 100644
--- a/core/tests/coretests/src/android/view/textclassifier/logging/GenerateLinksLoggerTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/logging/GenerateLinksLoggerTest.java
@@ -23,13 +23,14 @@
 import static org.mockito.Mockito.mock;
 
 import android.metrics.LogMaker;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
 import android.util.ArrayMap;
 import android.view.textclassifier.GenerateLinksLogger;
 import android.view.textclassifier.TextClassifier;
 import android.view.textclassifier.TextLinks;
 
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 
diff --git a/core/tests/coretests/src/android/view/textclassifier/logging/TextClassifierEventTronLoggerTest.java b/core/tests/coretests/src/android/view/textclassifier/logging/TextClassifierEventTronLoggerTest.java
index 344f79d..b1b7416 100644
--- a/core/tests/coretests/src/android/view/textclassifier/logging/TextClassifierEventTronLoggerTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/logging/TextClassifierEventTronLoggerTest.java
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package android.view.textclassifier.logging;
 
 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.ACTION_TEXT_SELECTION_SMART_SHARE;
@@ -24,13 +25,14 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import android.metrics.LogMaker;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
-import android.view.textclassifier.ConversationActions;
+import android.view.textclassifier.ConversationAction;
 import android.view.textclassifier.TextClassificationContext;
 import android.view.textclassifier.TextClassifierEvent;
 import android.view.textclassifier.TextClassifierEventTronLogger;
 
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
 import com.android.internal.logging.MetricsLogger;
 
 import org.junit.Before;
@@ -69,7 +71,7 @@
                 new TextClassifierEvent.Builder(
                         TextClassifierEvent.CATEGORY_CONVERSATION_ACTIONS,
                         TextClassifierEvent.TYPE_SMART_ACTION)
-                        .setEntityType(ConversationActions.TYPE_CALL_PHONE)
+                        .setEntityType(ConversationAction.TYPE_CALL_PHONE)
                         .setEventTime(EVENT_TIME)
                         .setEventContext(textClassificationContext)
                         .build();
@@ -84,7 +86,7 @@
         assertThat(logMaker.getType()).isEqualTo(
                 ACTION_TEXT_SELECTION_SMART_SHARE);
         assertThat(logMaker.getTaggedData(FIELD_SELECTION_ENTITY_TYPE))
-                .isEqualTo(ConversationActions.TYPE_CALL_PHONE);
+                .isEqualTo(ConversationAction.TYPE_CALL_PHONE);
         assertThat(logMaker.getTaggedData(FIELD_TEXT_CLASSIFIER_EVENT_TIME))
                 .isEqualTo(EVENT_TIME);
         assertThat(logMaker.getPackageName()).isEqualTo(PACKAGE_NAME);
diff --git a/core/tests/coretests/src/android/view/textservice/SpellCheckerSubtypeTest.java b/core/tests/coretests/src/android/view/textservice/SpellCheckerSubtypeTest.java
index 4a1c414..638d894 100644
--- a/core/tests/coretests/src/android/view/textservice/SpellCheckerSubtypeTest.java
+++ b/core/tests/coretests/src/android/view/textservice/SpellCheckerSubtypeTest.java
@@ -16,15 +16,16 @@
 
 package android.view.textservice;
 
+import static android.test.MoreAsserts.assertNotEqual;
+
 import android.os.Parcel;
 import android.test.InstrumentationTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
+
+import androidx.test.filters.SmallTest;
 
 import java.util.Arrays;
 import java.util.Locale;
 
-import static android.test.MoreAsserts.assertNotEqual;
-
 /**
  * TODO: Most of part can be, and probably should be, moved to CTS.
  */
diff --git a/core/tests/coretests/src/android/widget/AppWidgetHostViewTest.java b/core/tests/coretests/src/android/widget/AppWidgetHostViewTest.java
index 4f31c4e..6edc162 100644
--- a/core/tests/coretests/src/android/widget/AppWidgetHostViewTest.java
+++ b/core/tests/coretests/src/android/widget/AppWidgetHostViewTest.java
@@ -24,18 +24,14 @@
 import android.appwidget.AppWidgetHostView;
 import android.appwidget.AppWidgetManager;
 import android.content.Context;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
 import android.view.View;
 import android.view.ViewGroup.OnHierarchyChangeListener;
 
-import com.android.frameworks.coretests.R;
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
 
-import java.util.ArrayList;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.Executor;
-import java.util.concurrent.Future;
+import com.android.frameworks.coretests.R;
 
 import org.junit.Before;
 import org.junit.Rule;
@@ -43,6 +39,11 @@
 import org.junit.rules.ExpectedException;
 import org.junit.runner.RunWith;
 
+import java.util.ArrayList;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Executor;
+import java.util.concurrent.Future;
+
 /**
  * Tests for AppWidgetHostView
  */
diff --git a/core/tests/coretests/src/android/widget/AutoCompleteTextViewCallbacks.java b/core/tests/coretests/src/android/widget/AutoCompleteTextViewCallbacks.java
index 8e73b52..01e82a5 100644
--- a/core/tests/coretests/src/android/widget/AutoCompleteTextViewCallbacks.java
+++ b/core/tests/coretests/src/android/widget/AutoCompleteTextViewCallbacks.java
@@ -18,7 +18,8 @@
 
 import android.app.Instrumentation;
 import android.test.ActivityInstrumentationTestCase2;
-import android.test.FlakyTest;
+
+import androidx.test.filters.FlakyTest;
 
 // TODO: tests fail intermittently. Add back MediumTest annotation when fixed
 public class AutoCompleteTextViewCallbacks
@@ -32,7 +33,7 @@
 
     /** Test that the initial popup of the suggestions does not select anything.
      */
-    @FlakyTest(tolerance=3)
+    @FlakyTest
     public void testPopupNoSelection() throws Exception {
         AutoCompleteTextViewSimple theActivity = getActivity();
         AutoCompleteTextView textView = theActivity.getTextView();
@@ -57,7 +58,7 @@
     }
 
     /** Test that arrow-down into the popup calls the onSelected callback. */
-    @FlakyTest(tolerance=3)
+    @FlakyTest
     public void testPopupEnterSelection() throws Exception {
         final AutoCompleteTextViewSimple theActivity = getActivity();
         AutoCompleteTextView textView = theActivity.getTextView();
@@ -106,7 +107,7 @@
     }
 
     /** Test that arrow-up out of the popup calls the onNothingSelected callback */
-    @FlakyTest(tolerance=3)
+    @FlakyTest
     public void testPopupLeaveSelection() {
         final AutoCompleteTextViewSimple theActivity = getActivity();
         AutoCompleteTextView textView = theActivity.getTextView();
diff --git a/core/tests/coretests/src/android/widget/AutoCompleteTextViewPopup.java b/core/tests/coretests/src/android/widget/AutoCompleteTextViewPopup.java
index ee0abae..21e4184 100644
--- a/core/tests/coretests/src/android/widget/AutoCompleteTextViewPopup.java
+++ b/core/tests/coretests/src/android/widget/AutoCompleteTextViewPopup.java
@@ -18,8 +18,8 @@
 
 import android.app.Instrumentation;
 import android.test.ActivityInstrumentationTestCase2;
-import android.test.FlakyTest;
-import android.test.suitebuilder.annotation.MediumTest;
+
+import androidx.test.filters.FlakyTest;
 
 /**
  * A collection of tests on aspects of the AutoCompleteTextView's popup
@@ -41,7 +41,7 @@
     }
 
     /** Test that we can move the selection and it responds as expected */
-    @FlakyTest(tolerance=3)
+    @FlakyTest
     public void testPopupSetListSelection() throws Throwable {
         AutoCompleteTextViewSimple theActivity = getActivity();
         final AutoCompleteTextView textView = theActivity.getTextView();
@@ -73,7 +73,7 @@
     }
 
     /** Test that we can look at the selection as we move around */
-    @FlakyTest(tolerance=3)
+    @FlakyTest
     public void testPopupGetListSelection() throws Throwable {
         AutoCompleteTextViewSimple theActivity = getActivity();
         final AutoCompleteTextView textView = theActivity.getTextView();
@@ -100,7 +100,7 @@
     }
 
     /** Test that we can clear the selection */
-    @FlakyTest(tolerance=3)
+    @FlakyTest
     public void testPopupClearListSelection() throws Throwable {
         AutoCompleteTextViewSimple theActivity = getActivity();
         final AutoCompleteTextView textView = theActivity.getTextView();
@@ -133,7 +133,7 @@
     }
 
     /** Make sure we handle an empty adapter properly */
-    @FlakyTest(tolerance=3)
+    @FlakyTest
     public void testPopupNavigateNoAdapter() throws Throwable {
         AutoCompleteTextViewSimple theActivity = getActivity();
         final AutoCompleteTextView textView = theActivity.getTextView();
@@ -167,7 +167,7 @@
     }
 
     /** Test the show/hide behavior of the drop-down. */
-    @FlakyTest(tolerance=3)
+    @FlakyTest
     public void testPopupShow() throws Throwable {
         AutoCompleteTextViewSimple theActivity = getActivity();
         final AutoCompleteTextView textView = theActivity.getTextView();
diff --git a/core/tests/coretests/src/android/widget/AutoCompleteTextViewSimple.java b/core/tests/coretests/src/android/widget/AutoCompleteTextViewSimple.java
index b4e05aa..063bec5 100644
--- a/core/tests/coretests/src/android/widget/AutoCompleteTextViewSimple.java
+++ b/core/tests/coretests/src/android/widget/AutoCompleteTextViewSimple.java
@@ -16,8 +16,6 @@
 
 package android.widget;
 
-import com.android.frameworks.coretests.R;
-
 import android.app.Activity;
 import android.os.Bundle;
 import android.util.Log;
@@ -25,6 +23,8 @@
 import android.widget.AdapterView.OnItemClickListener;
 import android.widget.AdapterView.OnItemSelectedListener;
 
+import com.android.frameworks.coretests.R;
+
 public class AutoCompleteTextViewSimple extends Activity 
         implements OnItemClickListener, OnItemSelectedListener {
 
diff --git a/core/tests/coretests/src/android/widget/DatePickerActivity.java b/core/tests/coretests/src/android/widget/DatePickerActivity.java
index c3b25a1..9e455b2 100644
--- a/core/tests/coretests/src/android/widget/DatePickerActivity.java
+++ b/core/tests/coretests/src/android/widget/DatePickerActivity.java
@@ -18,6 +18,7 @@
 
 import android.app.Activity;
 import android.os.Bundle;
+
 import com.android.frameworks.coretests.R;
 
 /**
diff --git a/core/tests/coretests/src/android/widget/DatePickerFocusTest.java b/core/tests/coretests/src/android/widget/DatePickerFocusTest.java
index be85450..f067230 100644
--- a/core/tests/coretests/src/android/widget/DatePickerFocusTest.java
+++ b/core/tests/coretests/src/android/widget/DatePickerFocusTest.java
@@ -19,11 +19,12 @@
 import android.app.Activity;
 import android.app.Instrumentation;
 import android.os.SystemClock;
-import android.support.test.filters.LargeTest;
 import android.test.ActivityInstrumentationTestCase2;
 import android.view.KeyEvent;
 import android.view.View;
 
+import androidx.test.filters.LargeTest;
+
 import com.android.frameworks.coretests.R;
 
 /**
diff --git a/core/tests/coretests/src/android/widget/DateTimeViewTest.java b/core/tests/coretests/src/android/widget/DateTimeViewTest.java
index 40a6b7a..d0bd4b8 100644
--- a/core/tests/coretests/src/android/widget/DateTimeViewTest.java
+++ b/core/tests/coretests/src/android/widget/DateTimeViewTest.java
@@ -16,10 +16,10 @@
 
 package android.widget;
 
-import android.support.test.InstrumentationRegistry;
-import android.support.test.annotation.UiThreadTest;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
+import androidx.test.InstrumentationRegistry;
+import androidx.test.annotation.UiThreadTest;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/widget/EditorCursorTest.java b/core/tests/coretests/src/android/widget/EditorCursorTest.java
index 9186827b..e4f55df 100644
--- a/core/tests/coretests/src/android/widget/EditorCursorTest.java
+++ b/core/tests/coretests/src/android/widget/EditorCursorTest.java
@@ -31,10 +31,11 @@
 
 import android.app.Activity;
 import android.app.Instrumentation;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.MediumTest;
-import android.support.test.rule.ActivityTestRule;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.MediumTest;
+import androidx.test.rule.ActivityTestRule;
+import androidx.test.runner.AndroidJUnit4;
 
 import com.android.frameworks.coretests.R;
 
diff --git a/core/tests/coretests/src/android/widget/ListViewTest.java b/core/tests/coretests/src/android/widget/ListViewTest.java
index 449b696..254af2a 100644
--- a/core/tests/coretests/src/android/widget/ListViewTest.java
+++ b/core/tests/coretests/src/android/widget/ListViewTest.java
@@ -16,20 +16,19 @@
 
 package android.widget;
 
-import android.test.suitebuilder.annotation.Suppress;
-import com.google.android.collect.Lists;
-
-import junit.framework.Assert;
-
 import android.content.Context;
 import android.content.res.Resources;
 import android.test.InstrumentationTestCase;
 import android.test.mock.MockContext;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.view.View;
 import android.view.ViewGroup;
-import android.widget.ArrayAdapter;
-import android.widget.ListView;
+
+import androidx.test.filters.MediumTest;
+import androidx.test.filters.Suppress;
+
+import com.google.android.collect.Lists;
+
+import junit.framework.Assert;
 
 import java.util.List;
 
diff --git a/core/tests/coretests/src/android/widget/RadioGroupActivity.java b/core/tests/coretests/src/android/widget/RadioGroupActivity.java
index c87aa3a..dd3b30a 100644
--- a/core/tests/coretests/src/android/widget/RadioGroupActivity.java
+++ b/core/tests/coretests/src/android/widget/RadioGroupActivity.java
@@ -17,11 +17,11 @@
 
 package android.widget;
 
-import com.android.frameworks.coretests.R;
-
 import android.app.Activity;
 import android.os.Bundle;
 
+import com.android.frameworks.coretests.R;
+
 public class RadioGroupActivity extends Activity {
     @Override
     protected void onCreate(Bundle savedInstanceState) {
diff --git a/core/tests/coretests/src/android/widget/RadioGroupPreCheckedTest.java b/core/tests/coretests/src/android/widget/RadioGroupPreCheckedTest.java
index 1ab3628..18c1ea1 100644
--- a/core/tests/coretests/src/android/widget/RadioGroupPreCheckedTest.java
+++ b/core/tests/coretests/src/android/widget/RadioGroupPreCheckedTest.java
@@ -16,12 +16,13 @@
 
 package android.widget;
 
-import com.android.frameworks.coretests.R;
-
 import android.test.ActivityInstrumentationTestCase2;
 import android.test.TouchUtils;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.test.suitebuilder.annotation.MediumTest;
+
+import androidx.test.filters.LargeTest;
+import androidx.test.filters.MediumTest;
+
+import com.android.frameworks.coretests.R;
 
 /**
  * Exercises {@link android.widget.RadioGroup}'s check feature.
diff --git a/core/tests/coretests/src/android/widget/RemoteViewsAdapterTest.java b/core/tests/coretests/src/android/widget/RemoteViewsAdapterTest.java
index 06b860a..da53f6d 100644
--- a/core/tests/coretests/src/android/widget/RemoteViewsAdapterTest.java
+++ b/core/tests/coretests/src/android/widget/RemoteViewsAdapterTest.java
@@ -38,11 +38,12 @@
 import android.os.Handler;
 import android.os.Looper;
 import android.os.RemoteException;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
 import android.view.View;
 
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
 import com.android.frameworks.coretests.R;
 import com.android.internal.widget.IRemoteViewsFactory;
 
diff --git a/core/tests/coretests/src/android/widget/RemoteViewsTest.java b/core/tests/coretests/src/android/widget/RemoteViewsTest.java
index 36792bb..8cb7e1b 100644
--- a/core/tests/coretests/src/android/widget/RemoteViewsTest.java
+++ b/core/tests/coretests/src/android/widget/RemoteViewsTest.java
@@ -32,12 +32,13 @@
 import android.os.AsyncTask;
 import android.os.Binder;
 import android.os.Parcel;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
 import android.view.View;
 import android.view.ViewGroup;
 
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
 import com.android.frameworks.coretests.R;
 
 import org.junit.Before;
diff --git a/core/tests/coretests/src/android/widget/SelectionActionModeHelperTest.java b/core/tests/coretests/src/android/widget/SelectionActionModeHelperTest.java
index 2add221..74aad9a 100644
--- a/core/tests/coretests/src/android/widget/SelectionActionModeHelperTest.java
+++ b/core/tests/coretests/src/android/widget/SelectionActionModeHelperTest.java
@@ -22,7 +22,8 @@
 
 import android.graphics.PointF;
 import android.graphics.RectF;
-import android.support.test.filters.LargeTest;
+
+import androidx.test.filters.LargeTest;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/widget/SimpleCursorAdapterTest.java b/core/tests/coretests/src/android/widget/SimpleCursorAdapterTest.java
index 1731c08..a79b30d 100644
--- a/core/tests/coretests/src/android/widget/SimpleCursorAdapterTest.java
+++ b/core/tests/coretests/src/android/widget/SimpleCursorAdapterTest.java
@@ -16,14 +16,15 @@
 
 package android.widget;
 
-import android.test.suitebuilder.annotation.Suppress;
-import com.google.android.collect.Lists;
-
 import android.content.Context;
 import android.database.Cursor;
 import android.database.MatrixCursor;
 import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.filters.Suppress;
+
+import com.google.android.collect.Lists;
 
 import java.util.ArrayList;
 import java.util.Random;
diff --git a/core/tests/coretests/src/android/widget/SuggestionsPopupWindowTest.java b/core/tests/coretests/src/android/widget/SuggestionsPopupWindowTest.java
index eafe427..483270e 100644
--- a/core/tests/coretests/src/android/widget/SuggestionsPopupWindowTest.java
+++ b/core/tests/coretests/src/android/widget/SuggestionsPopupWindowTest.java
@@ -36,13 +36,13 @@
 import static android.widget.espresso.SuggestionsPopupwindowUtils.onSuggestionsPopup;
 import static android.widget.espresso.TextViewActions.clickOnTextAtIndex;
 import static android.widget.espresso.TextViewActions.longPressOnTextAtIndex;
+
 import static org.hamcrest.Matchers.is;
+
 import android.content.res.TypedArray;
 import android.support.test.espresso.NoMatchingViewException;
 import android.support.test.espresso.ViewAssertion;
 import android.test.ActivityInstrumentationTestCase2;
-import android.test.suitebuilder.annotation.SmallTest;
-import android.test.suitebuilder.annotation.Suppress;
 import android.text.Selection;
 import android.text.Spannable;
 import android.text.Spanned;
@@ -51,6 +51,8 @@
 import android.text.style.TextAppearanceSpan;
 import android.view.View;
 
+import androidx.test.filters.SmallTest;
+
 import com.android.frameworks.coretests.R;
 
 /**
diff --git a/core/tests/coretests/src/android/widget/TextViewActivityMouseTest.java b/core/tests/coretests/src/android/widget/TextViewActivityMouseTest.java
index ff4a7da..41fa08b 100644
--- a/core/tests/coretests/src/android/widget/TextViewActivityMouseTest.java
+++ b/core/tests/coretests/src/android/widget/TextViewActivityMouseTest.java
@@ -43,14 +43,15 @@
 import static android.widget.espresso.TextViewAssertions.hasSelection;
 
 import android.app.Activity;
-import android.support.test.filters.MediumTest;
-import android.support.test.filters.Suppress;
-import android.support.test.rule.ActivityTestRule;
-import android.support.test.runner.AndroidJUnit4;
 import android.view.MotionEvent;
 import android.view.textclassifier.TextClassificationManager;
 import android.view.textclassifier.TextClassifier;
 
+import androidx.test.filters.MediumTest;
+import androidx.test.filters.Suppress;
+import androidx.test.rule.ActivityTestRule;
+import androidx.test.runner.AndroidJUnit4;
+
 import com.android.frameworks.coretests.R;
 
 import org.junit.Before;
diff --git a/core/tests/coretests/src/android/widget/TextViewActivityTest.java b/core/tests/coretests/src/android/widget/TextViewActivityTest.java
index 90758ba..9d93421 100644
--- a/core/tests/coretests/src/android/widget/TextViewActivityTest.java
+++ b/core/tests/coretests/src/android/widget/TextViewActivityTest.java
@@ -27,10 +27,8 @@
 import static android.support.test.espresso.matcher.ViewMatchers.withText;
 import static android.widget.espresso.CustomViewActions.longPressAtRelativeCoordinates;
 import static android.widget.espresso.DragHandleUtils.onHandleView;
-import static android.widget.espresso.FloatingToolbarEspressoUtils
-        .assertFloatingToolbarContainsItem;
-import static android.widget.espresso.FloatingToolbarEspressoUtils
-        .assertFloatingToolbarDoesNotContainItem;
+import static android.widget.espresso.FloatingToolbarEspressoUtils.assertFloatingToolbarContainsItem;
+import static android.widget.espresso.FloatingToolbarEspressoUtils.assertFloatingToolbarDoesNotContainItem;
 import static android.widget.espresso.FloatingToolbarEspressoUtils.assertFloatingToolbarIsDisplayed;
 import static android.widget.espresso.FloatingToolbarEspressoUtils.assertFloatingToolbarItemIndex;
 import static android.widget.espresso.FloatingToolbarEspressoUtils.clickFloatingToolbarItem;
@@ -62,13 +60,8 @@
 import android.app.Instrumentation;
 import android.content.ClipData;
 import android.content.ClipboardManager;
-import android.support.test.InstrumentationRegistry;
 import android.support.test.espresso.action.EspressoKey;
-import android.support.test.filters.MediumTest;
-import android.support.test.rule.ActivityTestRule;
-import android.support.test.runner.AndroidJUnit4;
 import android.support.test.uiautomator.UiDevice;
-import android.test.suitebuilder.annotation.Suppress;
 import android.text.InputType;
 import android.text.Selection;
 import android.text.Spannable;
@@ -85,6 +78,12 @@
 import android.view.textclassifier.TextLinksParams;
 import android.widget.espresso.CustomViewActions.RelativeCoordinatesProvider;
 
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.MediumTest;
+import androidx.test.filters.Suppress;
+import androidx.test.rule.ActivityTestRule;
+import androidx.test.runner.AndroidJUnit4;
+
 import com.android.frameworks.coretests.R;
 
 import org.junit.Before;
diff --git a/core/tests/coretests/src/android/widget/TextViewFallbackLineSpacingTest.java b/core/tests/coretests/src/android/widget/TextViewFallbackLineSpacingTest.java
index e9d1d3e..113db9d 100644
--- a/core/tests/coretests/src/android/widget/TextViewFallbackLineSpacingTest.java
+++ b/core/tests/coretests/src/android/widget/TextViewFallbackLineSpacingTest.java
@@ -21,8 +21,6 @@
 import static org.junit.Assert.assertTrue;
 
 import android.app.Activity;
-import android.support.test.filters.MediumTest;
-import android.support.test.rule.ActivityTestRule;
 import android.text.DynamicLayout;
 import android.text.FontFallbackSetup;
 import android.text.Layout;
@@ -31,6 +29,9 @@
 import android.view.View;
 import android.widget.TextView.BufferType;
 
+import androidx.test.filters.MediumTest;
+import androidx.test.rule.ActivityTestRule;
+
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/widget/TextViewPerformanceTest.java b/core/tests/coretests/src/android/widget/TextViewPerformanceTest.java
index cf173fb..a769ea4 100644
--- a/core/tests/coretests/src/android/widget/TextViewPerformanceTest.java
+++ b/core/tests/coretests/src/android/widget/TextViewPerformanceTest.java
@@ -20,14 +20,15 @@
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.Paint;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.MediumTest;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
 import android.text.SpannedString;
 import android.view.View;
 import android.view.ViewGroup;
 
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.MediumTest;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/widget/TextViewTest.java b/core/tests/coretests/src/android/widget/TextViewTest.java
index 1c5610b..585360f 100644
--- a/core/tests/coretests/src/android/widget/TextViewTest.java
+++ b/core/tests/coretests/src/android/widget/TextViewTest.java
@@ -27,11 +27,6 @@
 import android.content.Intent;
 import android.graphics.Paint;
 import android.platform.test.annotations.Presubmit;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.annotation.UiThreadTest;
-import android.support.test.filters.MediumTest;
-import android.support.test.rule.ActivityTestRule;
-import android.support.test.runner.AndroidJUnit4;
 import android.text.GetChars;
 import android.text.Layout;
 import android.text.PrecomputedText;
@@ -40,6 +35,12 @@
 import android.view.View;
 import android.widget.TextView.BufferType;
 
+import androidx.test.InstrumentationRegistry;
+import androidx.test.annotation.UiThreadTest;
+import androidx.test.filters.MediumTest;
+import androidx.test.rule.ActivityTestRule;
+import androidx.test.runner.AndroidJUnit4;
+
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
diff --git a/core/tests/coretests/src/android/widget/focus/AdjacentVerticalRectLists.java b/core/tests/coretests/src/android/widget/focus/AdjacentVerticalRectLists.java
index 75da6fe..85a4509 100644
--- a/core/tests/coretests/src/android/widget/focus/AdjacentVerticalRectLists.java
+++ b/core/tests/coretests/src/android/widget/focus/AdjacentVerticalRectLists.java
@@ -16,12 +16,11 @@
 
 package android.widget.focus;
 
-import android.util.InternalSelectionView;
-
 import android.app.Activity;
 import android.os.Bundle;
-import android.widget.LinearLayout;
+import android.util.InternalSelectionView;
 import android.view.ViewGroup;
+import android.widget.LinearLayout;
 
 /**
  * {@link android.view.FocusFinder#findNextFocus(android.view.ViewGroup, android.view.View, int)}
diff --git a/core/tests/coretests/src/android/widget/focus/DescendantFocusability.java b/core/tests/coretests/src/android/widget/focus/DescendantFocusability.java
index f6b0520..fe6d3c8 100644
--- a/core/tests/coretests/src/android/widget/focus/DescendantFocusability.java
+++ b/core/tests/coretests/src/android/widget/focus/DescendantFocusability.java
@@ -16,13 +16,13 @@
 
 package android.widget.focus;
 
-import com.android.frameworks.coretests.R;
-
 import android.app.Activity;
 import android.os.Bundle;
 import android.view.ViewGroup;
 import android.widget.Button;
 
+import com.android.frameworks.coretests.R;
+
 public class DescendantFocusability extends Activity {
 
     public ViewGroup beforeDescendants;
diff --git a/core/tests/coretests/src/android/widget/focus/DescendantFocusabilityTest.java b/core/tests/coretests/src/android/widget/focus/DescendantFocusabilityTest.java
index 2af42ac..1c570df 100644
--- a/core/tests/coretests/src/android/widget/focus/DescendantFocusabilityTest.java
+++ b/core/tests/coretests/src/android/widget/focus/DescendantFocusabilityTest.java
@@ -16,15 +16,14 @@
 
 package android.widget.focus;
 
-import android.widget.focus.DescendantFocusability;
-
 import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.test.UiThreadTest;
 import android.test.TouchUtils;
 import android.view.ViewGroup;
 
+import androidx.test.annotation.UiThreadTest;
+import androidx.test.filters.LargeTest;
+import androidx.test.filters.MediumTest;
+
 public class DescendantFocusabilityTest extends ActivityInstrumentationTestCase<DescendantFocusability> {
 
     private DescendantFocusability a;
diff --git a/core/tests/coretests/src/android/widget/focus/FocusAfterRemoval.java b/core/tests/coretests/src/android/widget/focus/FocusAfterRemoval.java
index b3d5ec5..0cb80ce 100644
--- a/core/tests/coretests/src/android/widget/focus/FocusAfterRemoval.java
+++ b/core/tests/coretests/src/android/widget/focus/FocusAfterRemoval.java
@@ -16,13 +16,13 @@
 
 package android.widget.focus;
 
-import com.android.frameworks.coretests.R;
-
 import android.app.Activity;
 import android.os.Bundle;
-import android.widget.LinearLayout;
-import android.widget.Button;
 import android.view.View;
+import android.widget.Button;
+import android.widget.LinearLayout;
+
+import com.android.frameworks.coretests.R;
 
 /**
  * Exercises cases where elements of the UI are removed (and
diff --git a/core/tests/coretests/src/android/widget/focus/FocusAfterRemovalTest.java b/core/tests/coretests/src/android/widget/focus/FocusAfterRemovalTest.java
index a1b7bcb..6c46d08 100644
--- a/core/tests/coretests/src/android/widget/focus/FocusAfterRemovalTest.java
+++ b/core/tests/coretests/src/android/widget/focus/FocusAfterRemovalTest.java
@@ -16,15 +16,15 @@
 
 package android.widget.focus;
 
-import android.widget.focus.FocusAfterRemoval;
-import com.android.frameworks.coretests.R;
-
 import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.widget.Button;
-import android.widget.LinearLayout;
 import android.view.KeyEvent;
 import android.view.View;
+import android.widget.Button;
+import android.widget.LinearLayout;
+
+import androidx.test.filters.MediumTest;
+
+import com.android.frameworks.coretests.R;
 
 /**
  * {@link FocusAfterRemoval} is set up to exercise cases where the views that
diff --git a/core/tests/coretests/src/android/widget/focus/FocusChangeWithInterestingRectHintTest.java b/core/tests/coretests/src/android/widget/focus/FocusChangeWithInterestingRectHintTest.java
index 8f8f184..26dc233 100644
--- a/core/tests/coretests/src/android/widget/focus/FocusChangeWithInterestingRectHintTest.java
+++ b/core/tests/coretests/src/android/widget/focus/FocusChangeWithInterestingRectHintTest.java
@@ -16,14 +16,13 @@
 
 package android.widget.focus;
 
-import android.widget.focus.AdjacentVerticalRectLists;
-import android.util.InternalSelectionView;
-
 import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.test.suitebuilder.annotation.MediumTest;
+import android.util.InternalSelectionView;
 import android.view.KeyEvent;
 
+import androidx.test.filters.LargeTest;
+import androidx.test.filters.MediumTest;
+
 /**
  * {@link android.view.FocusFinder#findNextFocus(android.view.ViewGroup, android.view.View, int)}
  * and
diff --git a/core/tests/coretests/src/android/widget/focus/GoneParentFocusedChildTest.java b/core/tests/coretests/src/android/widget/focus/GoneParentFocusedChildTest.java
index dcbddef..b7974e3 100644
--- a/core/tests/coretests/src/android/widget/focus/GoneParentFocusedChildTest.java
+++ b/core/tests/coretests/src/android/widget/focus/GoneParentFocusedChildTest.java
@@ -17,10 +17,10 @@
 package android.widget.focus;
 
 import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.view.KeyEvent;
 import android.view.View;
-import android.widget.focus.GoneParentFocusedChild;
+
+import androidx.test.filters.MediumTest;
 
 /**
  * When a parent is GONE, key events shouldn't go to its children, even if they
diff --git a/core/tests/coretests/src/android/widget/focus/HorizontalFocusSearch.java b/core/tests/coretests/src/android/widget/focus/HorizontalFocusSearch.java
index 11cac1e..aab9119 100644
--- a/core/tests/coretests/src/android/widget/focus/HorizontalFocusSearch.java
+++ b/core/tests/coretests/src/android/widget/focus/HorizontalFocusSearch.java
@@ -17,12 +17,12 @@
 package android.widget.focus;
 
 import android.app.Activity;
-import android.widget.LinearLayout;
-import android.widget.Button;
-import android.widget.TextView;
+import android.content.Context;
 import android.os.Bundle;
 import android.view.ViewGroup;
-import android.content.Context;
+import android.widget.Button;
+import android.widget.LinearLayout;
+import android.widget.TextView;
 
 public class HorizontalFocusSearch extends Activity {
 
diff --git a/core/tests/coretests/src/android/widget/focus/HorizontalFocusSearchTest.java b/core/tests/coretests/src/android/widget/focus/HorizontalFocusSearchTest.java
index 43986ee..855a3219 100644
--- a/core/tests/coretests/src/android/widget/focus/HorizontalFocusSearchTest.java
+++ b/core/tests/coretests/src/android/widget/focus/HorizontalFocusSearchTest.java
@@ -16,18 +16,17 @@
 
 package android.widget.focus;
 
-import android.widget.focus.HorizontalFocusSearch;
-
-import android.support.test.filters.LargeTest;
-import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.Suppress;
-import android.widget.LinearLayout;
-import android.widget.Button;
-import android.view.View;
-
 import static android.widget.focus.VerticalFocusSearchTest.FocusSearchAlg;
 import static android.widget.focus.VerticalFocusSearchTest.NewFocusSearchAlg;
 
+import android.test.ActivityInstrumentationTestCase;
+import android.view.View;
+import android.widget.Button;
+import android.widget.LinearLayout;
+
+import androidx.test.filters.LargeTest;
+import androidx.test.filters.Suppress;
+
 /**
  * Tests that focus searching works on a horizontal linear layout of buttons of
  * various widths and vertical placements.
diff --git a/core/tests/coretests/src/android/widget/focus/LinearLayoutGrid.java b/core/tests/coretests/src/android/widget/focus/LinearLayoutGrid.java
index acd632f..ec234bc 100644
--- a/core/tests/coretests/src/android/widget/focus/LinearLayoutGrid.java
+++ b/core/tests/coretests/src/android/widget/focus/LinearLayoutGrid.java
@@ -21,6 +21,7 @@
 import android.view.ViewGroup;
 import android.widget.Button;
 import android.widget.LinearLayout;
+
 import com.android.frameworks.coretests.R;
 
 public class LinearLayoutGrid extends Activity {
diff --git a/core/tests/coretests/src/android/widget/focus/LinearLayoutGridTest.java b/core/tests/coretests/src/android/widget/focus/LinearLayoutGridTest.java
index 89cb8bb..c81317c 100644
--- a/core/tests/coretests/src/android/widget/focus/LinearLayoutGridTest.java
+++ b/core/tests/coretests/src/android/widget/focus/LinearLayoutGridTest.java
@@ -17,11 +17,11 @@
 package android.widget.focus;
 
 import android.test.SingleLaunchActivityTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.view.FocusFinder;
 import android.view.View;
 import android.view.ViewGroup;
-import android.widget.focus.LinearLayoutGrid;
+
+import androidx.test.filters.MediumTest;
 
 /**
  * Tests focus searching between buttons within a grid that are touching, for example,
diff --git a/core/tests/coretests/src/android/widget/focus/ListOfButtons.java b/core/tests/coretests/src/android/widget/focus/ListOfButtons.java
index 308861d..5663ed2 100644
--- a/core/tests/coretests/src/android/widget/focus/ListOfButtons.java
+++ b/core/tests/coretests/src/android/widget/focus/ListOfButtons.java
@@ -16,8 +16,6 @@
 
 package android.widget.focus;
 
-import com.android.frameworks.coretests.R;
-
 import android.app.ListActivity;
 import android.content.Context;
 import android.os.Bundle;
@@ -26,6 +24,8 @@
 import android.widget.ArrayAdapter;
 import android.widget.Button;
 
+import com.android.frameworks.coretests.R;
+
 /**
  * A layout with a ListView containing buttons.
  */
diff --git a/core/tests/coretests/src/android/widget/focus/ListOfButtonsTest.java b/core/tests/coretests/src/android/widget/focus/ListOfButtonsTest.java
index bec6f80..4cf4a3a 100644
--- a/core/tests/coretests/src/android/widget/focus/ListOfButtonsTest.java
+++ b/core/tests/coretests/src/android/widget/focus/ListOfButtonsTest.java
@@ -16,17 +16,17 @@
 
 package android.widget.focus;
 
-import android.test.suitebuilder.annotation.Suppress;
-import android.widget.focus.ListOfButtons;
-import com.android.frameworks.coretests.R;
-
 import android.test.ActivityInstrumentationTestCase2;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.widget.ListAdapter;
-import android.widget.Button;
-import android.widget.ListView;
 import android.view.KeyEvent;
 import android.view.View;
+import android.widget.Button;
+import android.widget.ListAdapter;
+import android.widget.ListView;
+
+import androidx.test.filters.MediumTest;
+import androidx.test.filters.Suppress;
+
+import com.android.frameworks.coretests.R;
 
 /**
  * Tests that focus works as expected when navigating into and out of
diff --git a/core/tests/coretests/src/android/widget/focus/ListOfEditTexts.java b/core/tests/coretests/src/android/widget/focus/ListOfEditTexts.java
index c2e7a26..936c999 100644
--- a/core/tests/coretests/src/android/widget/focus/ListOfEditTexts.java
+++ b/core/tests/coretests/src/android/widget/focus/ListOfEditTexts.java
@@ -21,7 +21,12 @@
 import android.os.Bundle;
 import android.view.View;
 import android.view.ViewGroup;
-import android.widget.*;
+import android.widget.ArrayAdapter;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.LinearLayout;
+import android.widget.ListView;
+
 import com.google.android.collect.Lists;
 
 import java.util.List;
diff --git a/core/tests/coretests/src/android/widget/focus/ListOfInternalSelectionViews.java b/core/tests/coretests/src/android/widget/focus/ListOfInternalSelectionViews.java
index 53b866c..73e4ea8 100644
--- a/core/tests/coretests/src/android/widget/focus/ListOfInternalSelectionViews.java
+++ b/core/tests/coretests/src/android/widget/focus/ListOfInternalSelectionViews.java
@@ -19,11 +19,11 @@
 import android.app.Activity;
 import android.graphics.Point;
 import android.os.Bundle;
+import android.util.InternalSelectionView;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.BaseAdapter;
 import android.widget.ListView;
-import android.util.InternalSelectionView;
 
 /**
  * A list of {@link InternalSelectionView}s paramatarized by the number of items,
diff --git a/core/tests/coretests/src/android/widget/focus/ListWithFooterViewAndNewLabels.java b/core/tests/coretests/src/android/widget/focus/ListWithFooterViewAndNewLabels.java
index b908201..8accf21 100644
--- a/core/tests/coretests/src/android/widget/focus/ListWithFooterViewAndNewLabels.java
+++ b/core/tests/coretests/src/android/widget/focus/ListWithFooterViewAndNewLabels.java
@@ -27,9 +27,10 @@
 import android.widget.Button;
 import android.widget.TextView;
 
-import com.google.android.collect.Lists;
 import com.android.frameworks.coretests.R;
 
+import com.google.android.collect.Lists;
+
 import java.util.List;
 
 public class ListWithFooterViewAndNewLabels extends ListActivity {
diff --git a/core/tests/coretests/src/android/widget/focus/ListWithFooterViewAndNewLabelsTest.java b/core/tests/coretests/src/android/widget/focus/ListWithFooterViewAndNewLabelsTest.java
index 57dbb78..d0fcde5 100644
--- a/core/tests/coretests/src/android/widget/focus/ListWithFooterViewAndNewLabelsTest.java
+++ b/core/tests/coretests/src/android/widget/focus/ListWithFooterViewAndNewLabelsTest.java
@@ -16,14 +16,13 @@
 
 package android.widget.focus;
 
-import android.widget.focus.ListWithFooterViewAndNewLabels;
-import com.android.frameworks.coretests.R;
-
 import android.test.ActivityInstrumentationTestCase;
 import android.widget.Button;
 import android.widget.ListAdapter;
 import android.widget.ListView;
 
+import com.android.frameworks.coretests.R;
+
 public class ListWithFooterViewAndNewLabelsTest
         extends ActivityInstrumentationTestCase<ListWithFooterViewAndNewLabels> {
 
diff --git a/core/tests/coretests/src/android/widget/focus/ListWithMailMessages.java b/core/tests/coretests/src/android/widget/focus/ListWithMailMessages.java
index 5c891f9..50a8614 100644
--- a/core/tests/coretests/src/android/widget/focus/ListWithMailMessages.java
+++ b/core/tests/coretests/src/android/widget/focus/ListWithMailMessages.java
@@ -16,19 +16,20 @@
 
 package android.widget.focus;
 
-import com.android.frameworks.coretests.R;
-import com.google.android.collect.Lists;
-
 import android.app.ListActivity;
+import android.content.Context;
 import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.webkit.WebView;
 import android.widget.ArrayAdapter;
 import android.widget.LinearLayout;
 import android.widget.TextView;
-import android.content.Context;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.LayoutInflater;
-import android.webkit.WebView;
+
+import com.android.frameworks.coretests.R;
+
+import com.google.android.collect.Lists;
 
 import java.util.List;
 
diff --git a/core/tests/coretests/src/android/widget/focus/RequestFocus.java b/core/tests/coretests/src/android/widget/focus/RequestFocus.java
index 4daf0b4..5042efd 100644
--- a/core/tests/coretests/src/android/widget/focus/RequestFocus.java
+++ b/core/tests/coretests/src/android/widget/focus/RequestFocus.java
@@ -16,13 +16,13 @@
 
 package android.widget.focus;
 
-import com.android.frameworks.coretests.R;
-
 import android.app.Activity;
 import android.os.Bundle;
 import android.os.Handler;
 import android.widget.Button;
 
+import com.android.frameworks.coretests.R;
+
 /**
  * Exercises cases where elements of the UI are requestFocus()ed.
  */
diff --git a/core/tests/coretests/src/android/widget/focus/RequestFocusTest.java b/core/tests/coretests/src/android/widget/focus/RequestFocusTest.java
index cdfa217..bc770c5 100644
--- a/core/tests/coretests/src/android/widget/focus/RequestFocusTest.java
+++ b/core/tests/coretests/src/android/widget/focus/RequestFocusTest.java
@@ -21,16 +21,18 @@
 
 import android.os.Handler;
 import android.test.ActivityInstrumentationTestCase2;
-import android.test.UiThreadTest;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.util.AndroidRuntimeException;
 import android.view.View;
 import android.view.View.OnFocusChangeListener;
 import android.view.ViewTreeObserver.OnGlobalFocusChangeListener;
 import android.widget.Button;
 
+import androidx.test.annotation.UiThreadTest;
+import androidx.test.filters.LargeTest;
+import androidx.test.filters.MediumTest;
+
 import com.android.frameworks.coretests.R;
+
 import org.mockito.InOrder;
 
 /**
diff --git a/core/tests/coretests/src/android/widget/focus/ScrollingThroughListOfFocusablesTest.java b/core/tests/coretests/src/android/widget/focus/ScrollingThroughListOfFocusablesTest.java
index 06cb75d..e6e76cc 100644
--- a/core/tests/coretests/src/android/widget/focus/ScrollingThroughListOfFocusablesTest.java
+++ b/core/tests/coretests/src/android/widget/focus/ScrollingThroughListOfFocusablesTest.java
@@ -18,12 +18,12 @@
 
 import android.graphics.Rect;
 import android.test.InstrumentationTestCase;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.util.InternalSelectionView;
 import android.view.KeyEvent;
 import android.widget.ListView;
 
+import androidx.test.filters.LargeTest;
+import androidx.test.filters.MediumTest;
 
 /**
  * TODO: extract base test case that launches {@link ListOfInternalSelectionViews} with
diff --git a/core/tests/coretests/src/android/widget/focus/VerticalFocusSearch.java b/core/tests/coretests/src/android/widget/focus/VerticalFocusSearch.java
index 4a809e0..2ca9f6d 100644
--- a/core/tests/coretests/src/android/widget/focus/VerticalFocusSearch.java
+++ b/core/tests/coretests/src/android/widget/focus/VerticalFocusSearch.java
@@ -17,13 +17,13 @@
 package android.widget.focus;
 
 import android.app.Activity;
+import android.content.Context;
 import android.os.Bundle;
-import android.widget.LinearLayout;
-import android.widget.Button;
-import android.widget.TextView;
 import android.view.Gravity;
 import android.view.ViewGroup;
-import android.content.Context;
+import android.widget.Button;
+import android.widget.LinearLayout;
+import android.widget.TextView;
 
 /**
  * Holds a few buttons of various sizes and horizontal placements in a
diff --git a/core/tests/coretests/src/android/widget/focus/VerticalFocusSearchTest.java b/core/tests/coretests/src/android/widget/focus/VerticalFocusSearchTest.java
index f01422e..319756c 100644
--- a/core/tests/coretests/src/android/widget/focus/VerticalFocusSearchTest.java
+++ b/core/tests/coretests/src/android/widget/focus/VerticalFocusSearchTest.java
@@ -16,17 +16,16 @@
 
 package android.widget.focus;
 
-import android.widget.focus.VerticalFocusSearch;
-
-import android.support.test.filters.LargeTest;
 import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.Suppress;
 import android.view.FocusFinder;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.Button;
 import android.widget.LinearLayout;
 
+import androidx.test.filters.LargeTest;
+import androidx.test.filters.Suppress;
+
 /**
  * Tests that focus searching works on a vertical linear layout of buttons of
  * various widths and horizontal placements.
diff --git a/core/tests/coretests/src/android/widget/gridview/GridDelete.java b/core/tests/coretests/src/android/widget/gridview/GridDelete.java
index 57ae8f39..b040f69 100644
--- a/core/tests/coretests/src/android/widget/gridview/GridDelete.java
+++ b/core/tests/coretests/src/android/widget/gridview/GridDelete.java
@@ -16,6 +16,7 @@
 
 package android.widget.gridview;
 
+import android.util.GridScenario;
 import android.view.KeyEvent;
 import android.view.View;
 import android.view.ViewGroup;
@@ -23,8 +24,6 @@
 import android.widget.GridView;
 import android.widget.ListAdapter;
 
-import android.util.GridScenario;
-
 import java.util.ArrayList;
 
 /**
diff --git a/core/tests/coretests/src/android/widget/gridview/GridInHorizontalTest.java b/core/tests/coretests/src/android/widget/gridview/GridInHorizontalTest.java
index 21ca655..5247009 100644
--- a/core/tests/coretests/src/android/widget/gridview/GridInHorizontalTest.java
+++ b/core/tests/coretests/src/android/widget/gridview/GridInHorizontalTest.java
@@ -17,10 +17,9 @@
 package android.widget.gridview;
 
 import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.widget.GridView;
 
-import android.widget.gridview.GridInHorizontal;
+import androidx.test.filters.MediumTest;
 
 public class GridInHorizontalTest extends ActivityInstrumentationTestCase<GridInHorizontal> {
 
diff --git a/core/tests/coretests/src/android/widget/gridview/GridInVerticalTest.java b/core/tests/coretests/src/android/widget/gridview/GridInVerticalTest.java
index a674db2..290f70e 100644
--- a/core/tests/coretests/src/android/widget/gridview/GridInVerticalTest.java
+++ b/core/tests/coretests/src/android/widget/gridview/GridInVerticalTest.java
@@ -17,10 +17,9 @@
 package android.widget.gridview;
 
 import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.widget.GridView;
 
-import android.widget.gridview.GridInVertical;
+import androidx.test.filters.MediumTest;
 
 public class GridInVerticalTest extends ActivityInstrumentationTestCase<GridInVertical> {
 
diff --git a/core/tests/coretests/src/android/widget/gridview/GridPaddingTest.java b/core/tests/coretests/src/android/widget/gridview/GridPaddingTest.java
index ecd4b1c..d2ae7cf 100644
--- a/core/tests/coretests/src/android/widget/gridview/GridPaddingTest.java
+++ b/core/tests/coretests/src/android/widget/gridview/GridPaddingTest.java
@@ -17,9 +17,10 @@
 package android.widget.gridview;
 
 import android.test.ActivityInstrumentationTestCase2;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.widget.GridView;
 
+import androidx.test.filters.MediumTest;
+
 public class GridPaddingTest extends ActivityInstrumentationTestCase2<GridPadding> {
     private GridView mGridView;
 
diff --git a/core/tests/coretests/src/android/widget/gridview/GridScrollListenerTest.java b/core/tests/coretests/src/android/widget/gridview/GridScrollListenerTest.java
index 53eeb48..466c55f 100644
--- a/core/tests/coretests/src/android/widget/gridview/GridScrollListenerTest.java
+++ b/core/tests/coretests/src/android/widget/gridview/GridScrollListenerTest.java
@@ -19,12 +19,13 @@
 import android.app.Instrumentation;
 import android.test.ActivityInstrumentationTestCase;
 import android.test.TouchUtils;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.view.KeyEvent;
 import android.widget.AbsListView;
 import android.widget.GridView;
 
+import androidx.test.filters.LargeTest;
+import androidx.test.filters.MediumTest;
+
 public class GridScrollListenerTest extends ActivityInstrumentationTestCase<GridScrollListener> implements
         AbsListView.OnScrollListener {
     private GridScrollListener mActivity;
diff --git a/core/tests/coretests/src/android/widget/gridview/GridSetSelectionBaseTest.java b/core/tests/coretests/src/android/widget/gridview/GridSetSelectionBaseTest.java
index 0e362b6..db4f2dc 100644
--- a/core/tests/coretests/src/android/widget/gridview/GridSetSelectionBaseTest.java
+++ b/core/tests/coretests/src/android/widget/gridview/GridSetSelectionBaseTest.java
@@ -16,13 +16,13 @@
 
 package android.widget.gridview;
 
-import android.util.GridScenario;
-
 import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.test.ViewAsserts;
+import android.util.GridScenario;
 import android.widget.GridView;
 
+import androidx.test.filters.MediumTest;
+
 public class GridSetSelectionBaseTest<T extends GridScenario> extends ActivityInstrumentationTestCase<T> {
     private T mActivity;
     private GridView mGridView;
diff --git a/core/tests/coretests/src/android/widget/gridview/GridSetSelectionManyTest.java b/core/tests/coretests/src/android/widget/gridview/GridSetSelectionManyTest.java
index 6739645..17a6044 100644
--- a/core/tests/coretests/src/android/widget/gridview/GridSetSelectionManyTest.java
+++ b/core/tests/coretests/src/android/widget/gridview/GridSetSelectionManyTest.java
@@ -16,8 +16,6 @@
 
 package android.widget.gridview;
 
-import android.widget.gridview.GridSetSelectionMany;
-
 public class GridSetSelectionManyTest extends GridSetSelectionBaseTest<GridSetSelectionMany> {
     public GridSetSelectionManyTest() {
         super(GridSetSelectionMany.class);
diff --git a/core/tests/coretests/src/android/widget/gridview/GridSetSelectionStackFromBottomManyTest.java b/core/tests/coretests/src/android/widget/gridview/GridSetSelectionStackFromBottomManyTest.java
index 46922b9..a71f732 100644
--- a/core/tests/coretests/src/android/widget/gridview/GridSetSelectionStackFromBottomManyTest.java
+++ b/core/tests/coretests/src/android/widget/gridview/GridSetSelectionStackFromBottomManyTest.java
@@ -16,8 +16,6 @@
 
 package android.widget.gridview;
 
-import android.widget.gridview.GridSetSelectionStackFromBottomMany;
-
 public class GridSetSelectionStackFromBottomManyTest extends GridSetSelectionBaseTest<GridSetSelectionStackFromBottomMany> {
     public GridSetSelectionStackFromBottomManyTest() {
         super(GridSetSelectionStackFromBottomMany.class);
diff --git a/core/tests/coretests/src/android/widget/gridview/GridSetSelectionStackFromBottomTest.java b/core/tests/coretests/src/android/widget/gridview/GridSetSelectionStackFromBottomTest.java
index 67dd6f1..4e6e8aa 100644
--- a/core/tests/coretests/src/android/widget/gridview/GridSetSelectionStackFromBottomTest.java
+++ b/core/tests/coretests/src/android/widget/gridview/GridSetSelectionStackFromBottomTest.java
@@ -16,8 +16,6 @@
 
 package android.widget.gridview;
 
-import android.widget.gridview.GridSetSelectionStackFromBottom;
-
 public class GridSetSelectionStackFromBottomTest extends GridSetSelectionBaseTest<GridSetSelectionStackFromBottom> {
     public GridSetSelectionStackFromBottomTest() {
         super(GridSetSelectionStackFromBottom.class);
diff --git a/core/tests/coretests/src/android/widget/gridview/GridSetSelectionTest.java b/core/tests/coretests/src/android/widget/gridview/GridSetSelectionTest.java
index 2127b3c..68802fe 100644
--- a/core/tests/coretests/src/android/widget/gridview/GridSetSelectionTest.java
+++ b/core/tests/coretests/src/android/widget/gridview/GridSetSelectionTest.java
@@ -16,8 +16,6 @@
 
 package android.widget.gridview;
 
-import android.widget.gridview.GridSetSelection;
-
 public class GridSetSelectionTest extends GridSetSelectionBaseTest<GridSetSelection> {
     public GridSetSelectionTest() {
         super(GridSetSelection.class);
diff --git a/core/tests/coretests/src/android/widget/gridview/GridSimple.java b/core/tests/coretests/src/android/widget/gridview/GridSimple.java
index 7c2c696..67bb751 100644
--- a/core/tests/coretests/src/android/widget/gridview/GridSimple.java
+++ b/core/tests/coretests/src/android/widget/gridview/GridSimple.java
@@ -18,12 +18,11 @@
 
 import android.graphics.drawable.PaintDrawable;
 import android.os.Bundle;
+import android.util.GridScenario;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.TextView;
 
-import android.util.GridScenario;
-
 public class GridSimple extends GridScenario {
     @Override
     protected void init(Params params) {
diff --git a/core/tests/coretests/src/android/widget/gridview/GridSingleColumnTest.java b/core/tests/coretests/src/android/widget/gridview/GridSingleColumnTest.java
index 3b2504e..0f67522 100644
--- a/core/tests/coretests/src/android/widget/gridview/GridSingleColumnTest.java
+++ b/core/tests/coretests/src/android/widget/gridview/GridSingleColumnTest.java
@@ -17,10 +17,9 @@
 package android.widget.gridview;
 
 import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.widget.GridView;
 
-import android.widget.gridview.GridSingleColumn;
+import androidx.test.filters.MediumTest;
 
 public class GridSingleColumnTest extends ActivityInstrumentationTestCase<GridSingleColumn> {
     private GridSingleColumn mActivity;
diff --git a/core/tests/coretests/src/android/widget/gridview/GridStackFromBottomManyTest.java b/core/tests/coretests/src/android/widget/gridview/GridStackFromBottomManyTest.java
index 640737e..a369616 100644
--- a/core/tests/coretests/src/android/widget/gridview/GridStackFromBottomManyTest.java
+++ b/core/tests/coretests/src/android/widget/gridview/GridStackFromBottomManyTest.java
@@ -16,11 +16,10 @@
 
 package android.widget.gridview;
 
-import android.widget.gridview.GridStackFromBottomMany;
-
-import android.test.suitebuilder.annotation.MediumTest;
-import android.widget.GridView;
 import android.test.ActivityInstrumentationTestCase;
+import android.widget.GridView;
+
+import androidx.test.filters.MediumTest;
 
 public class GridStackFromBottomManyTest extends ActivityInstrumentationTestCase<GridStackFromBottomMany> {
     private GridStackFromBottomMany mActivity;
diff --git a/core/tests/coretests/src/android/widget/gridview/GridStackFromBottomTest.java b/core/tests/coretests/src/android/widget/gridview/GridStackFromBottomTest.java
index 8fec241..da1b638 100644
--- a/core/tests/coretests/src/android/widget/gridview/GridStackFromBottomTest.java
+++ b/core/tests/coretests/src/android/widget/gridview/GridStackFromBottomTest.java
@@ -16,12 +16,11 @@
 
 package android.widget.gridview;
 
-import android.widget.gridview.GridStackFromBottom;
-
 import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.widget.GridView;
 
+import androidx.test.filters.MediumTest;
+
 public class GridStackFromBottomTest extends ActivityInstrumentationTestCase<GridStackFromBottom> {
     private GridStackFromBottom mActivity;
     private GridView mGridView;
diff --git a/core/tests/coretests/src/android/widget/gridview/GridThrasher.java b/core/tests/coretests/src/android/widget/gridview/GridThrasher.java
index ad89bb6..34c19c3 100644
--- a/core/tests/coretests/src/android/widget/gridview/GridThrasher.java
+++ b/core/tests/coretests/src/android/widget/gridview/GridThrasher.java
@@ -16,20 +16,20 @@
 
 package android.widget.gridview;
 
-import com.android.frameworks.coretests.R;
-
 import android.app.Activity;
 import android.content.Context;
 import android.os.Bundle;
 import android.os.Handler;
+import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
-import android.view.LayoutInflater;
 import android.widget.AdapterView;
 import android.widget.BaseAdapter;
 import android.widget.GridView;
 import android.widget.TextView;
 
+import com.android.frameworks.coretests.R;
+
 import java.util.Random;
 
 /**
diff --git a/core/tests/coretests/src/android/widget/gridview/touch/GridTouchSetSelectionTest.java b/core/tests/coretests/src/android/widget/gridview/touch/GridTouchSetSelectionTest.java
index ca789af..ab5fcfa 100644
--- a/core/tests/coretests/src/android/widget/gridview/touch/GridTouchSetSelectionTest.java
+++ b/core/tests/coretests/src/android/widget/gridview/touch/GridTouchSetSelectionTest.java
@@ -17,14 +17,14 @@
 package android.widget.gridview.touch;
 
 import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.test.TouchUtils;
 import android.view.View;
 import android.widget.GridView;
-
 import android.widget.gridview.GridSimple;
 
+import androidx.test.filters.LargeTest;
+import androidx.test.filters.MediumTest;
+
 /**
  * Tests setting the selection in touch mode
  */
diff --git a/core/tests/coretests/src/android/widget/gridview/touch/GridTouchStackFromBottomManyTest.java b/core/tests/coretests/src/android/widget/gridview/touch/GridTouchStackFromBottomManyTest.java
index 9a8d307..e312873 100644
--- a/core/tests/coretests/src/android/widget/gridview/touch/GridTouchStackFromBottomManyTest.java
+++ b/core/tests/coretests/src/android/widget/gridview/touch/GridTouchStackFromBottomManyTest.java
@@ -16,14 +16,14 @@
 
 package android.widget.gridview.touch;
 
-import android.test.suitebuilder.annotation.LargeTest;
-import android.test.suitebuilder.annotation.MediumTest;
+import android.test.ActivityInstrumentationTestCase;
 import android.test.TouchUtils;
+import android.view.View;
+import android.widget.GridView;
 import android.widget.gridview.GridStackFromBottomMany;
 
-import android.widget.GridView;
-import android.view.View;
-import android.test.ActivityInstrumentationTestCase;
+import androidx.test.filters.LargeTest;
+import androidx.test.filters.MediumTest;
 
 public class GridTouchStackFromBottomManyTest extends ActivityInstrumentationTestCase<GridStackFromBottomMany> {
     private GridStackFromBottomMany mActivity;
diff --git a/core/tests/coretests/src/android/widget/gridview/touch/GridTouchStackFromBottomTest.java b/core/tests/coretests/src/android/widget/gridview/touch/GridTouchStackFromBottomTest.java
index d8d4e43..c98c10a 100644
--- a/core/tests/coretests/src/android/widget/gridview/touch/GridTouchStackFromBottomTest.java
+++ b/core/tests/coretests/src/android/widget/gridview/touch/GridTouchStackFromBottomTest.java
@@ -16,13 +16,13 @@
 
 package android.widget.gridview.touch;
 
-import android.widget.gridview.GridStackFromBottom;
-import android.test.TouchUtils;
-import android.test.suitebuilder.annotation.MediumTest;
-
 import android.test.ActivityInstrumentationTestCase;
-import android.widget.GridView;
+import android.test.TouchUtils;
 import android.view.View;
+import android.widget.GridView;
+import android.widget.gridview.GridStackFromBottom;
+
+import androidx.test.filters.MediumTest;
 
 public class GridTouchStackFromBottomTest extends ActivityInstrumentationTestCase<GridStackFromBottom> {
     private GridStackFromBottom mActivity;
diff --git a/core/tests/coretests/src/android/widget/gridview/touch/GridTouchVerticalSpacingStackFromBottomTest.java b/core/tests/coretests/src/android/widget/gridview/touch/GridTouchVerticalSpacingStackFromBottomTest.java
index 55a66d9..0d3092c 100644
--- a/core/tests/coretests/src/android/widget/gridview/touch/GridTouchVerticalSpacingStackFromBottomTest.java
+++ b/core/tests/coretests/src/android/widget/gridview/touch/GridTouchVerticalSpacingStackFromBottomTest.java
@@ -18,16 +18,16 @@
 
 import android.content.Context;
 import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.test.TouchUtils;
 import android.view.Gravity;
 import android.view.View;
 import android.view.ViewConfiguration;
 import android.widget.GridView;
-
 import android.widget.gridview.GridVerticalSpacingStackFromBottom;
 
+import androidx.test.filters.LargeTest;
+import androidx.test.filters.MediumTest;
+
 public class GridTouchVerticalSpacingStackFromBottomTest extends ActivityInstrumentationTestCase<GridVerticalSpacingStackFromBottom> {
     private GridVerticalSpacingStackFromBottom mActivity;
     private GridView mGridView;
diff --git a/core/tests/coretests/src/android/widget/gridview/touch/GridTouchVerticalSpacingTest.java b/core/tests/coretests/src/android/widget/gridview/touch/GridTouchVerticalSpacingTest.java
index bae4ee7..e831e62 100644
--- a/core/tests/coretests/src/android/widget/gridview/touch/GridTouchVerticalSpacingTest.java
+++ b/core/tests/coretests/src/android/widget/gridview/touch/GridTouchVerticalSpacingTest.java
@@ -17,16 +17,16 @@
 package android.widget.gridview.touch;
 
 import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.test.TouchUtils;
 import android.view.Gravity;
 import android.view.View;
 import android.view.ViewConfiguration;
 import android.widget.GridView;
-
 import android.widget.gridview.GridVerticalSpacing;
 
+import androidx.test.filters.LargeTest;
+import androidx.test.filters.MediumTest;
+
 public class GridTouchVerticalSpacingTest extends ActivityInstrumentationTestCase<GridVerticalSpacing> {
     private GridVerticalSpacing mActivity;
     private GridView mGridView;
diff --git a/core/tests/coretests/src/android/widget/layout/frame/FrameLayoutGravity.java b/core/tests/coretests/src/android/widget/layout/frame/FrameLayoutGravity.java
index 9791e36..d159be8 100644
--- a/core/tests/coretests/src/android/widget/layout/frame/FrameLayoutGravity.java
+++ b/core/tests/coretests/src/android/widget/layout/frame/FrameLayoutGravity.java
@@ -16,11 +16,10 @@
 
 package android.widget.layout.frame;
 
-import com.android.frameworks.coretests.R;
-
 import android.app.Activity;
 import android.os.Bundle;
-import android.view.View;
+
+import com.android.frameworks.coretests.R;
 
 public class FrameLayoutGravity extends Activity {
     @Override
diff --git a/core/tests/coretests/src/android/widget/layout/frame/FrameLayoutGravityTest.java b/core/tests/coretests/src/android/widget/layout/frame/FrameLayoutGravityTest.java
index fe4e932..0cab660 100644
--- a/core/tests/coretests/src/android/widget/layout/frame/FrameLayoutGravityTest.java
+++ b/core/tests/coretests/src/android/widget/layout/frame/FrameLayoutGravityTest.java
@@ -16,12 +16,13 @@
 
 package android.widget.layout.frame;
 
-import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.test.ViewAsserts;
 import android.app.Activity;
+import android.test.ActivityInstrumentationTestCase;
+import android.test.ViewAsserts;
 import android.view.View;
-import android.widget.layout.frame.FrameLayoutGravity;
+
+import androidx.test.filters.MediumTest;
+
 import com.android.frameworks.coretests.R;
 
 public class FrameLayoutGravityTest extends ActivityInstrumentationTestCase<FrameLayoutGravity> {
diff --git a/core/tests/coretests/src/android/widget/layout/frame/FrameLayoutMargin.java b/core/tests/coretests/src/android/widget/layout/frame/FrameLayoutMargin.java
index 81b3ea1..8c54557 100644
--- a/core/tests/coretests/src/android/widget/layout/frame/FrameLayoutMargin.java
+++ b/core/tests/coretests/src/android/widget/layout/frame/FrameLayoutMargin.java
@@ -16,11 +16,10 @@
 
 package android.widget.layout.frame;
 
-import com.android.frameworks.coretests.R;
-
 import android.app.Activity;
 import android.os.Bundle;
-import android.view.View;
+
+import com.android.frameworks.coretests.R;
 
 public class FrameLayoutMargin extends Activity {
     @Override
diff --git a/core/tests/coretests/src/android/widget/layout/frame/FrameLayoutMarginTest.java b/core/tests/coretests/src/android/widget/layout/frame/FrameLayoutMarginTest.java
index c052d65..4973078 100644
--- a/core/tests/coretests/src/android/widget/layout/frame/FrameLayoutMarginTest.java
+++ b/core/tests/coretests/src/android/widget/layout/frame/FrameLayoutMarginTest.java
@@ -16,13 +16,14 @@
 
 package android.widget.layout.frame;
 
-import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.test.ViewAsserts;
 import android.app.Activity;
+import android.test.ActivityInstrumentationTestCase;
+import android.test.ViewAsserts;
 import android.view.View;
 import android.view.ViewGroup;
-import android.widget.layout.frame.FrameLayoutMargin;
+
+import androidx.test.filters.MediumTest;
+
 import com.android.frameworks.coretests.R;
 
 public class FrameLayoutMarginTest extends ActivityInstrumentationTestCase<FrameLayoutMargin> {
diff --git a/core/tests/coretests/src/android/widget/layout/linear/BaselineAlignmentCenterGravity.java b/core/tests/coretests/src/android/widget/layout/linear/BaselineAlignmentCenterGravity.java
index 766dd0a..79a34e5 100644
--- a/core/tests/coretests/src/android/widget/layout/linear/BaselineAlignmentCenterGravity.java
+++ b/core/tests/coretests/src/android/widget/layout/linear/BaselineAlignmentCenterGravity.java
@@ -16,11 +16,10 @@
 
 package android.widget.layout.linear;
 
-import com.android.frameworks.coretests.R;
-
 import android.app.Activity;
 import android.os.Bundle;
-import android.view.View;
+
+import com.android.frameworks.coretests.R;
 
 public class BaselineAlignmentCenterGravity extends Activity {
     @Override
diff --git a/core/tests/coretests/src/android/widget/layout/linear/BaselineAlignmentCenterGravityTest.java b/core/tests/coretests/src/android/widget/layout/linear/BaselineAlignmentCenterGravityTest.java
index 079e9d0..60b7c2f 100644
--- a/core/tests/coretests/src/android/widget/layout/linear/BaselineAlignmentCenterGravityTest.java
+++ b/core/tests/coretests/src/android/widget/layout/linear/BaselineAlignmentCenterGravityTest.java
@@ -18,13 +18,13 @@
 
 import android.app.Activity;
 import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.test.ViewAsserts;
 import android.view.View;
 import android.widget.Button;
 
+import androidx.test.filters.MediumTest;
+
 import com.android.frameworks.coretests.R;
-import android.widget.layout.linear.BaselineAlignmentCenterGravity;
 
 public class BaselineAlignmentCenterGravityTest extends ActivityInstrumentationTestCase<BaselineAlignmentCenterGravity> {
     private Button mButton1;
diff --git a/core/tests/coretests/src/android/widget/layout/linear/BaselineAlignmentSpinnerButton.java b/core/tests/coretests/src/android/widget/layout/linear/BaselineAlignmentSpinnerButton.java
index c3bfe3a..a429b75 100644
--- a/core/tests/coretests/src/android/widget/layout/linear/BaselineAlignmentSpinnerButton.java
+++ b/core/tests/coretests/src/android/widget/layout/linear/BaselineAlignmentSpinnerButton.java
@@ -18,12 +18,12 @@
 
 import android.app.Activity;
 import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.test.ViewAsserts;
 import android.view.View;
 
+import androidx.test.filters.MediumTest;
+
 import com.android.frameworks.coretests.R;
-import android.widget.layout.linear.HorizontalOrientationVerticalAlignment;
 
 public class BaselineAlignmentSpinnerButton extends ActivityInstrumentationTestCase<HorizontalOrientationVerticalAlignment> {
     private View mSpinner;
diff --git a/core/tests/coretests/src/android/widget/layout/linear/BaselineAlignmentZeroWidthAndWeight.java b/core/tests/coretests/src/android/widget/layout/linear/BaselineAlignmentZeroWidthAndWeight.java
index 5ed5e71..f33feb0 100644
--- a/core/tests/coretests/src/android/widget/layout/linear/BaselineAlignmentZeroWidthAndWeight.java
+++ b/core/tests/coretests/src/android/widget/layout/linear/BaselineAlignmentZeroWidthAndWeight.java
@@ -16,12 +16,12 @@
 
 package android.widget.layout.linear;
 
-import com.android.frameworks.coretests.R;
-
 import android.app.Activity;
 import android.os.Bundle;
 import android.view.View;
 
+import com.android.frameworks.coretests.R;
+
 public class BaselineAlignmentZeroWidthAndWeight extends Activity {
     @Override
     protected void onCreate(Bundle icicle) {
diff --git a/core/tests/coretests/src/android/widget/layout/linear/BaselineAlignmentZeroWidthAndWeightTest.java b/core/tests/coretests/src/android/widget/layout/linear/BaselineAlignmentZeroWidthAndWeightTest.java
index 2dd2bb8..443b0f7 100644
--- a/core/tests/coretests/src/android/widget/layout/linear/BaselineAlignmentZeroWidthAndWeightTest.java
+++ b/core/tests/coretests/src/android/widget/layout/linear/BaselineAlignmentZeroWidthAndWeightTest.java
@@ -16,16 +16,15 @@
 
 package android.widget.layout.linear;
 
-import com.android.frameworks.coretests.R;
-import android.widget.layout.linear.BaselineAlignmentZeroWidthAndWeight;
-import android.widget.layout.linear.ExceptionTextView;
-
 import android.app.Activity;
 import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.view.KeyEvent;
 import android.widget.Button;
 
+import androidx.test.filters.MediumTest;
+
+import com.android.frameworks.coretests.R;
+
 public class BaselineAlignmentZeroWidthAndWeightTest extends ActivityInstrumentationTestCase<BaselineAlignmentZeroWidthAndWeight> {
     private Button mShowButton;
 
diff --git a/core/tests/coretests/src/android/widget/layout/linear/BaselineButtons.java b/core/tests/coretests/src/android/widget/layout/linear/BaselineButtons.java
index c9ad831..9d91316 100644
--- a/core/tests/coretests/src/android/widget/layout/linear/BaselineButtons.java
+++ b/core/tests/coretests/src/android/widget/layout/linear/BaselineButtons.java
@@ -16,11 +16,10 @@
 
 package android.widget.layout.linear;
 
-import com.android.frameworks.coretests.R;
-
 import android.app.Activity;
 import android.os.Bundle;
-import android.view.View;
+
+import com.android.frameworks.coretests.R;
 
 public class BaselineButtons extends Activity {
     @Override
diff --git a/core/tests/coretests/src/android/widget/layout/linear/BaselineButtonsTest.java b/core/tests/coretests/src/android/widget/layout/linear/BaselineButtonsTest.java
index 6f1fc90..ac3c5d8 100644
--- a/core/tests/coretests/src/android/widget/layout/linear/BaselineButtonsTest.java
+++ b/core/tests/coretests/src/android/widget/layout/linear/BaselineButtonsTest.java
@@ -18,12 +18,12 @@
 
 import android.app.Activity;
 import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.view.View;
 import android.widget.ImageButton;
 
+import androidx.test.filters.MediumTest;
+
 import com.android.frameworks.coretests.R;
-import android.widget.layout.linear.BaselineButtons;
 
 public class BaselineButtonsTest extends ActivityInstrumentationTestCase<BaselineButtons> {
     private View mCurrentTime;
diff --git a/core/tests/coretests/src/android/widget/layout/linear/ExceptionTextView.java b/core/tests/coretests/src/android/widget/layout/linear/ExceptionTextView.java
index 6129b5b..c684501 100644
--- a/core/tests/coretests/src/android/widget/layout/linear/ExceptionTextView.java
+++ b/core/tests/coretests/src/android/widget/layout/linear/ExceptionTextView.java
@@ -21,7 +21,6 @@
 import android.util.AttributeSet;
 import android.widget.EditText;
 
-
 /**
  * A special EditText that sets {@link #isFailed()} to true as its internal makeNewLayout() method is called
  * with a width lower than 0. This is used to fail the unit test in
diff --git a/core/tests/coretests/src/android/widget/layout/linear/FillInWrap.java b/core/tests/coretests/src/android/widget/layout/linear/FillInWrap.java
index 50aa5b7..c8a5f34 100644
--- a/core/tests/coretests/src/android/widget/layout/linear/FillInWrap.java
+++ b/core/tests/coretests/src/android/widget/layout/linear/FillInWrap.java
@@ -16,12 +16,12 @@
 
 package android.widget.layout.linear;
 
-import com.android.frameworks.coretests.R;
-
 import android.app.Activity;
 import android.os.Bundle;
 import android.widget.TextView;
 
+import com.android.frameworks.coretests.R;
+
 public class FillInWrap extends Activity {
     @Override
     protected void onCreate(Bundle icicle) {
diff --git a/core/tests/coretests/src/android/widget/layout/linear/FillInWrapTest.java b/core/tests/coretests/src/android/widget/layout/linear/FillInWrapTest.java
index f161802..0e69efc 100644
--- a/core/tests/coretests/src/android/widget/layout/linear/FillInWrapTest.java
+++ b/core/tests/coretests/src/android/widget/layout/linear/FillInWrapTest.java
@@ -18,9 +18,10 @@
 
 import android.app.Activity;
 import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.view.View;
 
+import androidx.test.filters.MediumTest;
+
 import com.android.frameworks.coretests.R;
 
 public class FillInWrapTest extends ActivityInstrumentationTestCase<FillInWrap> {
diff --git a/core/tests/coretests/src/android/widget/layout/linear/HorizontalOrientationVerticalAlignment.java b/core/tests/coretests/src/android/widget/layout/linear/HorizontalOrientationVerticalAlignment.java
index 9f937a9..255f7b3 100644
--- a/core/tests/coretests/src/android/widget/layout/linear/HorizontalOrientationVerticalAlignment.java
+++ b/core/tests/coretests/src/android/widget/layout/linear/HorizontalOrientationVerticalAlignment.java
@@ -16,11 +16,10 @@
 
 package android.widget.layout.linear;
 
-import com.android.frameworks.coretests.R;
-
 import android.app.Activity;
 import android.os.Bundle;
-import android.view.View;
+
+import com.android.frameworks.coretests.R;
 
 public class HorizontalOrientationVerticalAlignment extends Activity {
     @Override
diff --git a/core/tests/coretests/src/android/widget/layout/linear/LLEditTextThenButton.java b/core/tests/coretests/src/android/widget/layout/linear/LLEditTextThenButton.java
index 83331ca..a0745dd 100644
--- a/core/tests/coretests/src/android/widget/layout/linear/LLEditTextThenButton.java
+++ b/core/tests/coretests/src/android/widget/layout/linear/LLEditTextThenButton.java
@@ -16,14 +16,14 @@
 
 package android.widget.layout.linear;
 
-import com.android.frameworks.coretests.R;
-
 import android.app.Activity;
 import android.os.Bundle;
 import android.widget.Button;
 import android.widget.EditText;
 import android.widget.LinearLayout;
 
+import com.android.frameworks.coretests.R;
+
 public class LLEditTextThenButton extends Activity {
     private EditText mEditText;
     private Button mButton;
diff --git a/core/tests/coretests/src/android/widget/layout/linear/LLOfButtons1.java b/core/tests/coretests/src/android/widget/layout/linear/LLOfButtons1.java
index ab2f060..1153062 100644
--- a/core/tests/coretests/src/android/widget/layout/linear/LLOfButtons1.java
+++ b/core/tests/coretests/src/android/widget/layout/linear/LLOfButtons1.java
@@ -18,10 +18,10 @@
 
 import android.app.Activity;
 import android.os.Bundle;
-import android.os.RemoteException;
 import android.view.View;
 import android.widget.Button;
 import android.widget.LinearLayout;
+
 import com.android.frameworks.coretests.R;
 
 /**
diff --git a/core/tests/coretests/src/android/widget/layout/linear/LLOfButtons2.java b/core/tests/coretests/src/android/widget/layout/linear/LLOfButtons2.java
index 77f564d..0aca699 100644
--- a/core/tests/coretests/src/android/widget/layout/linear/LLOfButtons2.java
+++ b/core/tests/coretests/src/android/widget/layout/linear/LLOfButtons2.java
@@ -22,5 +22,4 @@
  */
 public class LLOfButtons2  extends LLOfButtons1 {
 
-
 }
diff --git a/core/tests/coretests/src/android/widget/layout/linear/LinearLayoutEditTexts.java b/core/tests/coretests/src/android/widget/layout/linear/LinearLayoutEditTexts.java
index 90db788..5d37245 100644
--- a/core/tests/coretests/src/android/widget/layout/linear/LinearLayoutEditTexts.java
+++ b/core/tests/coretests/src/android/widget/layout/linear/LinearLayoutEditTexts.java
@@ -16,11 +16,11 @@
 
 package android.widget.layout.linear;
 
-import com.android.frameworks.coretests.R;
-
 import android.app.Activity;
 import android.os.Bundle;
 
+import com.android.frameworks.coretests.R;
+
 public class LinearLayoutEditTexts extends Activity {
     @Override
     protected void onCreate(Bundle savedInstanceState) {
diff --git a/core/tests/coretests/src/android/widget/layout/linear/LinearLayoutEditTextsTest.java b/core/tests/coretests/src/android/widget/layout/linear/LinearLayoutEditTextsTest.java
index d5998b7..966ed6d 100644
--- a/core/tests/coretests/src/android/widget/layout/linear/LinearLayoutEditTextsTest.java
+++ b/core/tests/coretests/src/android/widget/layout/linear/LinearLayoutEditTextsTest.java
@@ -16,13 +16,13 @@
 
 package android.widget.layout.linear;
 
-import android.widget.layout.linear.LinearLayoutEditTexts;
-import com.android.frameworks.coretests.R;
-
-import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.view.View;
 import android.app.Activity;
+import android.test.ActivityInstrumentationTestCase;
+import android.view.View;
+
+import androidx.test.filters.MediumTest;
+
+import com.android.frameworks.coretests.R;
 
 public class LinearLayoutEditTextsTest extends ActivityInstrumentationTestCase<LinearLayoutEditTexts> {
     private View mChild;
diff --git a/core/tests/coretests/src/android/widget/layout/linear/Weight.java b/core/tests/coretests/src/android/widget/layout/linear/Weight.java
index 20edd7c..3722a14f 100644
--- a/core/tests/coretests/src/android/widget/layout/linear/Weight.java
+++ b/core/tests/coretests/src/android/widget/layout/linear/Weight.java
@@ -16,11 +16,11 @@
 
 package android.widget.layout.linear;
 
-import com.android.frameworks.coretests.R;
-
 import android.app.Activity;
 import android.os.Bundle;
 
+import com.android.frameworks.coretests.R;
+
 public class Weight extends Activity {
     @Override
     protected void onCreate(Bundle icicle) {
diff --git a/core/tests/coretests/src/android/widget/layout/linear/WeightSum.java b/core/tests/coretests/src/android/widget/layout/linear/WeightSum.java
index 2e421da..144600b 100644
--- a/core/tests/coretests/src/android/widget/layout/linear/WeightSum.java
+++ b/core/tests/coretests/src/android/widget/layout/linear/WeightSum.java
@@ -16,11 +16,10 @@
 
 package android.widget.layout.linear;
 
-import com.android.frameworks.coretests.R;
-
 import android.app.Activity;
 import android.os.Bundle;
-import android.view.View;
+
+import com.android.frameworks.coretests.R;
 
 public class WeightSum extends Activity {
     @Override
diff --git a/core/tests/coretests/src/android/widget/layout/linear/WeightSumTest.java b/core/tests/coretests/src/android/widget/layout/linear/WeightSumTest.java
index f9a94ce..1d07db2 100644
--- a/core/tests/coretests/src/android/widget/layout/linear/WeightSumTest.java
+++ b/core/tests/coretests/src/android/widget/layout/linear/WeightSumTest.java
@@ -18,11 +18,11 @@
 
 import android.app.Activity;
 import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.view.View;
 
+import androidx.test.filters.MediumTest;
+
 import com.android.frameworks.coretests.R;
-import android.widget.layout.linear.WeightSum;
 
 public class WeightSumTest extends ActivityInstrumentationTestCase<WeightSum> {
     private View mChild;
diff --git a/core/tests/coretests/src/android/widget/layout/linear/WeightTest.java b/core/tests/coretests/src/android/widget/layout/linear/WeightTest.java
index 1c42e7c..db52495 100644
--- a/core/tests/coretests/src/android/widget/layout/linear/WeightTest.java
+++ b/core/tests/coretests/src/android/widget/layout/linear/WeightTest.java
@@ -18,13 +18,13 @@
 
 import android.app.Activity;
 import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.test.ViewAsserts;
-import android.test.suitebuilder.annotation.Suppress;
 import android.view.View;
 
+import androidx.test.filters.MediumTest;
+import androidx.test.filters.Suppress;
+
 import com.android.frameworks.coretests.R;
-import android.widget.layout.linear.Weight;
 
 @Suppress // Failing.
 public class WeightTest extends ActivityInstrumentationTestCase<Weight> {
diff --git a/core/tests/coretests/src/android/widget/layout/table/AddColumn.java b/core/tests/coretests/src/android/widget/layout/table/AddColumn.java
index 400c32c..b407941 100644
--- a/core/tests/coretests/src/android/widget/layout/table/AddColumn.java
+++ b/core/tests/coretests/src/android/widget/layout/table/AddColumn.java
@@ -16,8 +16,6 @@
 
 package android.widget.layout.table;
 
-import com.android.frameworks.coretests.R;
-
 import android.app.Activity;
 import android.os.Bundle;
 import android.view.View;
@@ -26,6 +24,8 @@
 import android.widget.TableRow;
 import android.widget.TextView;
 
+import com.android.frameworks.coretests.R;
+
 /**
  * This test adds an extra row with an extra column in the table.
  */
diff --git a/core/tests/coretests/src/android/widget/layout/table/AddColumnTest.java b/core/tests/coretests/src/android/widget/layout/table/AddColumnTest.java
index bfb4d17..08ae030 100644
--- a/core/tests/coretests/src/android/widget/layout/table/AddColumnTest.java
+++ b/core/tests/coretests/src/android/widget/layout/table/AddColumnTest.java
@@ -16,16 +16,16 @@
 
 package android.widget.layout.table;
 
-import android.widget.layout.table.AddColumn;
-import com.android.frameworks.coretests.R;
-
 import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.view.KeyEvent;
 import android.widget.Button;
 import android.widget.TableLayout;
 import android.widget.TableRow;
 
+import androidx.test.filters.MediumTest;
+
+import com.android.frameworks.coretests.R;
+
 /**
  * {@link android.widget.layout.table.AddColumn} is
  * setup to exercise the case of adding row programmatically in a table.
diff --git a/core/tests/coretests/src/android/widget/layout/table/CellSpan.java b/core/tests/coretests/src/android/widget/layout/table/CellSpan.java
index d91cf56..3102ac1 100644
--- a/core/tests/coretests/src/android/widget/layout/table/CellSpan.java
+++ b/core/tests/coretests/src/android/widget/layout/table/CellSpan.java
@@ -16,11 +16,11 @@
 
 package android.widget.layout.table;
 
-import com.android.frameworks.coretests.R;
-
 import android.app.Activity;
 import android.os.Bundle;
 
+import com.android.frameworks.coretests.R;
+
 /**
  * Exercise table layout with cells spanning.
  */
diff --git a/core/tests/coretests/src/android/widget/layout/table/CellSpanTest.java b/core/tests/coretests/src/android/widget/layout/table/CellSpanTest.java
index 331ec45..aa8e66c 100644
--- a/core/tests/coretests/src/android/widget/layout/table/CellSpanTest.java
+++ b/core/tests/coretests/src/android/widget/layout/table/CellSpanTest.java
@@ -16,13 +16,13 @@
 
 package android.widget.layout.table;
 
-import android.widget.layout.table.CellSpan;
-import com.android.frameworks.coretests.R;
-
 import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.view.View;
 
+import androidx.test.filters.MediumTest;
+
+import com.android.frameworks.coretests.R;
+
 /**
  * {@link android.widget.layout.table.CellSpan} is
  * setup to exercise tables in which cells use spanning.
diff --git a/core/tests/coretests/src/android/widget/layout/table/FixedWidth.java b/core/tests/coretests/src/android/widget/layout/table/FixedWidth.java
index 435815a..c587f74 100644
--- a/core/tests/coretests/src/android/widget/layout/table/FixedWidth.java
+++ b/core/tests/coretests/src/android/widget/layout/table/FixedWidth.java
@@ -16,11 +16,11 @@
 
 package android.widget.layout.table;
 
-import com.android.frameworks.coretests.R;
-
 import android.app.Activity;
 import android.os.Bundle;
 
+import com.android.frameworks.coretests.R;
+
 /**
  * Exercise table layout with cells having a fixed width and height.
  */
diff --git a/core/tests/coretests/src/android/widget/layout/table/FixedWidthTest.java b/core/tests/coretests/src/android/widget/layout/table/FixedWidthTest.java
index b20ec84..7d02453 100644
--- a/core/tests/coretests/src/android/widget/layout/table/FixedWidthTest.java
+++ b/core/tests/coretests/src/android/widget/layout/table/FixedWidthTest.java
@@ -16,13 +16,13 @@
 
 package android.widget.layout.table;
 
-import android.widget.layout.table.FixedWidth;
-import com.android.frameworks.coretests.R;
-
 import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.view.View;
 
+import androidx.test.filters.MediumTest;
+
+import com.android.frameworks.coretests.R;
+
 /**
  * {@link android.widget.layout.table.FixedWidth} is
  * setup to exercise tables in which cells use fixed width and height.
diff --git a/core/tests/coretests/src/android/widget/layout/table/HorizontalGravity.java b/core/tests/coretests/src/android/widget/layout/table/HorizontalGravity.java
index 1444f60..6f5148b 100644
--- a/core/tests/coretests/src/android/widget/layout/table/HorizontalGravity.java
+++ b/core/tests/coretests/src/android/widget/layout/table/HorizontalGravity.java
@@ -16,11 +16,11 @@
 
 package android.widget.layout.table;
 
-import com.android.frameworks.coretests.R;
-
 import android.app.Activity;
 import android.os.Bundle;
 
+import com.android.frameworks.coretests.R;
+
 /**
  * Exercise table layout with cells using a horizontal gravity.
  */
diff --git a/core/tests/coretests/src/android/widget/layout/table/HorizontalGravityTest.java b/core/tests/coretests/src/android/widget/layout/table/HorizontalGravityTest.java
index 964df82..73e8334 100644
--- a/core/tests/coretests/src/android/widget/layout/table/HorizontalGravityTest.java
+++ b/core/tests/coretests/src/android/widget/layout/table/HorizontalGravityTest.java
@@ -16,14 +16,14 @@
 
 package android.widget.layout.table;
 
-import android.widget.layout.table.HorizontalGravity;
-import com.android.frameworks.coretests.R;
-
 import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.test.ViewAsserts;
 import android.view.View;
 
+import androidx.test.filters.MediumTest;
+
+import com.android.frameworks.coretests.R;
+
 /**
  * {@link android.widget.layout.table.HorizontalGravity} is
  * setup to exercise tables in which cells use horizontal gravity.
diff --git a/core/tests/coretests/src/android/widget/layout/table/VerticalGravity.java b/core/tests/coretests/src/android/widget/layout/table/VerticalGravity.java
index 4fdb378..9055b32 100644
--- a/core/tests/coretests/src/android/widget/layout/table/VerticalGravity.java
+++ b/core/tests/coretests/src/android/widget/layout/table/VerticalGravity.java
@@ -16,11 +16,11 @@
 
 package android.widget.layout.table;
 
-import com.android.frameworks.coretests.R;
-
 import android.app.Activity;
 import android.os.Bundle;
 
+import com.android.frameworks.coretests.R;
+
 /**
  * Exercise table layout with cells using a vertical gravity.
  */
diff --git a/core/tests/coretests/src/android/widget/layout/table/VerticalGravityTest.java b/core/tests/coretests/src/android/widget/layout/table/VerticalGravityTest.java
index 1d6be3f..f14fa1c 100644
--- a/core/tests/coretests/src/android/widget/layout/table/VerticalGravityTest.java
+++ b/core/tests/coretests/src/android/widget/layout/table/VerticalGravityTest.java
@@ -16,15 +16,15 @@
 
 package android.widget.layout.table;
 
-import android.widget.layout.table.VerticalGravity;
-import com.android.frameworks.coretests.R;
-
 import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.test.suitebuilder.annotation.Suppress;
 import android.test.ViewAsserts;
 import android.view.View;
 
+import androidx.test.filters.MediumTest;
+import androidx.test.filters.Suppress;
+
+import com.android.frameworks.coretests.R;
+
 /**
  * {@link android.widget.layout.table.VerticalGravity} is
  * setup to exercise tables in which cells use vertical gravity.
diff --git a/core/tests/coretests/src/android/widget/layout/table/Weight.java b/core/tests/coretests/src/android/widget/layout/table/Weight.java
index 6d4d51d..5c08247 100644
--- a/core/tests/coretests/src/android/widget/layout/table/Weight.java
+++ b/core/tests/coretests/src/android/widget/layout/table/Weight.java
@@ -16,11 +16,11 @@
 
 package android.widget.layout.table;
 
-import com.android.frameworks.coretests.R;
-
 import android.app.Activity;
 import android.os.Bundle;
 
+import com.android.frameworks.coretests.R;
+
 /**
  * Exercise table layout with cells having a weight.
  */
diff --git a/core/tests/coretests/src/android/widget/layout/table/WeightTest.java b/core/tests/coretests/src/android/widget/layout/table/WeightTest.java
index b665573..fcf3de2 100644
--- a/core/tests/coretests/src/android/widget/layout/table/WeightTest.java
+++ b/core/tests/coretests/src/android/widget/layout/table/WeightTest.java
@@ -16,13 +16,13 @@
 
 package android.widget.layout.table;
 
-import android.widget.layout.table.Weight;
-import com.android.frameworks.coretests.R;
-
 import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.view.View;
 
+import androidx.test.filters.MediumTest;
+
+import com.android.frameworks.coretests.R;
+
 /**
  * {@link android.widget.layout.table.Weight} is
  * setup to exercise tables in which cells use a weight.
diff --git a/core/tests/coretests/src/android/widget/listview/ListBottomGravity.java b/core/tests/coretests/src/android/widget/listview/ListBottomGravity.java
index a386ebd..cd76d70 100644
--- a/core/tests/coretests/src/android/widget/listview/ListBottomGravity.java
+++ b/core/tests/coretests/src/android/widget/listview/ListBottomGravity.java
@@ -16,8 +16,6 @@
 
 package android.widget.listview;
 
-import android.view.Gravity;
-
 import android.util.ListScenario;
 
 /**
diff --git a/core/tests/coretests/src/android/widget/listview/ListBottomGravityMany.java b/core/tests/coretests/src/android/widget/listview/ListBottomGravityMany.java
index 519816c..e048e3e 100644
--- a/core/tests/coretests/src/android/widget/listview/ListBottomGravityMany.java
+++ b/core/tests/coretests/src/android/widget/listview/ListBottomGravityMany.java
@@ -16,8 +16,6 @@
 
 package android.widget.listview;
 
-import android.view.Gravity;
-
 import android.util.ListScenario;
 
 /**
diff --git a/core/tests/coretests/src/android/widget/listview/ListBottomGravityManyTest.java b/core/tests/coretests/src/android/widget/listview/ListBottomGravityManyTest.java
index e1171eb..bd8dbe4 100644
--- a/core/tests/coretests/src/android/widget/listview/ListBottomGravityManyTest.java
+++ b/core/tests/coretests/src/android/widget/listview/ListBottomGravityManyTest.java
@@ -17,10 +17,9 @@
 package android.widget.listview;
 
 import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.widget.ListView;
 
-import android.widget.listview.ListBottomGravityMany;
+import androidx.test.filters.MediumTest;
 
 public class ListBottomGravityManyTest extends ActivityInstrumentationTestCase<ListBottomGravityMany> {
     private ListBottomGravityMany mActivity;
diff --git a/core/tests/coretests/src/android/widget/listview/ListBottomGravityTest.java b/core/tests/coretests/src/android/widget/listview/ListBottomGravityTest.java
index c595f62..8da7358 100644
--- a/core/tests/coretests/src/android/widget/listview/ListBottomGravityTest.java
+++ b/core/tests/coretests/src/android/widget/listview/ListBottomGravityTest.java
@@ -17,10 +17,9 @@
 package android.widget.listview;
 
 import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.widget.ListView;
 
-import android.widget.listview.ListBottomGravity;
+import androidx.test.filters.MediumTest;
 
 public class ListBottomGravityTest extends ActivityInstrumentationTestCase<ListBottomGravity> {
     private ListBottomGravity mActivity;
diff --git a/core/tests/coretests/src/android/widget/listview/ListButtonsDiagonalAcrossItems.java b/core/tests/coretests/src/android/widget/listview/ListButtonsDiagonalAcrossItems.java
index bbed73c..8640741 100644
--- a/core/tests/coretests/src/android/widget/listview/ListButtonsDiagonalAcrossItems.java
+++ b/core/tests/coretests/src/android/widget/listview/ListButtonsDiagonalAcrossItems.java
@@ -16,10 +16,10 @@
 
 package android.widget.listview;
 
-import android.util.ListItemFactory;
 import static android.util.ListItemFactory.Slot;
-import android.util.ListScenario;
 
+import android.util.ListItemFactory;
+import android.util.ListScenario;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.Button;
diff --git a/core/tests/coretests/src/android/widget/listview/ListEmptyViewTest.java b/core/tests/coretests/src/android/widget/listview/ListEmptyViewTest.java
index 258d3ef..81d71a9 100644
--- a/core/tests/coretests/src/android/widget/listview/ListEmptyViewTest.java
+++ b/core/tests/coretests/src/android/widget/listview/ListEmptyViewTest.java
@@ -19,12 +19,13 @@
 import android.app.Instrumentation;
 import android.content.Intent;
 import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.view.KeyEvent;
 import android.view.View;
 import android.widget.ListView;
 
+import androidx.test.filters.LargeTest;
+import androidx.test.filters.MediumTest;
+
 public class ListEmptyViewTest extends ActivityInstrumentationTestCase<ListWithEmptyView> {
     private ListWithEmptyView mActivity;
     private ListView mListView;
diff --git a/core/tests/coretests/src/android/widget/listview/ListFilter.java b/core/tests/coretests/src/android/widget/listview/ListFilter.java
index c2ac90e..1cda717 100644
--- a/core/tests/coretests/src/android/widget/listview/ListFilter.java
+++ b/core/tests/coretests/src/android/widget/listview/ListFilter.java
@@ -25,7 +25,6 @@
 
 import com.android.frameworks.coretests.R;
 
-
 /**
  * Tests hiding and showing the list filter by hiding and showing an ancestor of the 
  * ListView
diff --git a/core/tests/coretests/src/android/widget/listview/ListFocusableTest.java b/core/tests/coretests/src/android/widget/listview/ListFocusableTest.java
index bf18a13..10b4a79 100644
--- a/core/tests/coretests/src/android/widget/listview/ListFocusableTest.java
+++ b/core/tests/coretests/src/android/widget/listview/ListFocusableTest.java
@@ -17,10 +17,11 @@
 package android.widget.listview;
 
 import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.widget.ListView;
-import android.widget.ListAdapter;
 import android.widget.ArrayAdapter;
+import android.widget.ListAdapter;
+import android.widget.ListView;
+
+import androidx.test.filters.MediumTest;
 
 public class ListFocusableTest extends ActivityInstrumentationTestCase<ListTopGravity> {
     private ListTopGravity mActivity;
diff --git a/core/tests/coretests/src/android/widget/listview/ListGetCheckItemIdsTest.java b/core/tests/coretests/src/android/widget/listview/ListGetCheckItemIdsTest.java
index 33d61a0..c691ed7 100644
--- a/core/tests/coretests/src/android/widget/listview/ListGetCheckItemIdsTest.java
+++ b/core/tests/coretests/src/android/widget/listview/ListGetCheckItemIdsTest.java
@@ -17,10 +17,11 @@
 package android.widget.listview;
 
 import android.test.ActivityInstrumentationTestCase2;
-import android.test.UiThreadTest;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.widget.ListView;
 
+import androidx.test.annotation.UiThreadTest;
+import androidx.test.filters.MediumTest;
+
 import java.util.Arrays;
 
 /**
diff --git a/core/tests/coretests/src/android/widget/listview/ListHeterogeneous.java b/core/tests/coretests/src/android/widget/listview/ListHeterogeneous.java
index 1f59c30..74eda3b 100644
--- a/core/tests/coretests/src/android/widget/listview/ListHeterogeneous.java
+++ b/core/tests/coretests/src/android/widget/listview/ListHeterogeneous.java
@@ -16,11 +16,10 @@
 
 package android.widget.listview;
 
-import android.view.View;
-import android.view.ViewGroup;
-
 import android.util.ListItemFactory;
 import android.util.ListScenario;
+import android.view.View;
+import android.view.ViewGroup;
 
 /**
  * List that has different view types
diff --git a/core/tests/coretests/src/android/widget/listview/ListHeterogeneousTest.java b/core/tests/coretests/src/android/widget/listview/ListHeterogeneousTest.java
index 01b39db..dbd58d7 100644
--- a/core/tests/coretests/src/android/widget/listview/ListHeterogeneousTest.java
+++ b/core/tests/coretests/src/android/widget/listview/ListHeterogeneousTest.java
@@ -18,12 +18,11 @@
 
 import android.app.Instrumentation;
 import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.view.KeyEvent;
 import android.widget.ListView;
 
-import android.widget.listview.ListHeterogeneous;
+import androidx.test.filters.LargeTest;
+import androidx.test.filters.MediumTest;
 
 public class ListHeterogeneousTest extends ActivityInstrumentationTestCase<ListHeterogeneous> {
     private ListHeterogeneous mActivity;
diff --git a/core/tests/coretests/src/android/widget/listview/ListHorizontalFocusWithinItemWins.java b/core/tests/coretests/src/android/widget/listview/ListHorizontalFocusWithinItemWins.java
index 2ff65de..e98de9c 100644
--- a/core/tests/coretests/src/android/widget/listview/ListHorizontalFocusWithinItemWins.java
+++ b/core/tests/coretests/src/android/widget/listview/ListHorizontalFocusWithinItemWins.java
@@ -16,11 +16,11 @@
 
 package android.widget.listview;
 
-import android.util.ListItemFactory;
 import static android.util.ListItemFactory.Slot;
-import android.util.ListScenario;
 
 import android.content.Context;
+import android.util.ListItemFactory;
+import android.util.ListScenario;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.Button;
diff --git a/core/tests/coretests/src/android/widget/listview/ListInHorizontal.java b/core/tests/coretests/src/android/widget/listview/ListInHorizontal.java
index a373a5b..6622c0d 100644
--- a/core/tests/coretests/src/android/widget/listview/ListInHorizontal.java
+++ b/core/tests/coretests/src/android/widget/listview/ListInHorizontal.java
@@ -18,10 +18,7 @@
 
 import android.app.Activity;
 import android.os.Bundle;
-import android.os.Handler;
 import android.widget.ArrayAdapter;
-import android.widget.GridView;
-import android.widget.TextView;
 import android.widget.ListView;
 
 import com.android.frameworks.coretests.R;
diff --git a/core/tests/coretests/src/android/widget/listview/ListInHorizontalTest.java b/core/tests/coretests/src/android/widget/listview/ListInHorizontalTest.java
index 3643f79..d8508ec 100644
--- a/core/tests/coretests/src/android/widget/listview/ListInHorizontalTest.java
+++ b/core/tests/coretests/src/android/widget/listview/ListInHorizontalTest.java
@@ -16,8 +16,6 @@
 
 package android.widget.listview;
 
-import android.widget.listview.ListInHorizontal;
-
 public class ListInHorizontalTest extends ListUnspecifiedMeasure<ListInHorizontal> {
     public ListInHorizontalTest() {
         super(ListInHorizontal.class);
diff --git a/core/tests/coretests/src/android/widget/listview/ListInVertical.java b/core/tests/coretests/src/android/widget/listview/ListInVertical.java
index 3b4885a..4ecf25d 100644
--- a/core/tests/coretests/src/android/widget/listview/ListInVertical.java
+++ b/core/tests/coretests/src/android/widget/listview/ListInVertical.java
@@ -18,10 +18,7 @@
 
 import android.app.Activity;
 import android.os.Bundle;
-import android.os.Handler;
 import android.widget.ArrayAdapter;
-import android.widget.GridView;
-import android.widget.TextView;
 import android.widget.ListView;
 
 import com.android.frameworks.coretests.R;
diff --git a/core/tests/coretests/src/android/widget/listview/ListInVerticalTest.java b/core/tests/coretests/src/android/widget/listview/ListInVerticalTest.java
index 8586429..5e385a3 100644
--- a/core/tests/coretests/src/android/widget/listview/ListInVerticalTest.java
+++ b/core/tests/coretests/src/android/widget/listview/ListInVerticalTest.java
@@ -16,8 +16,6 @@
 
 package android.widget.listview;
 
-import android.widget.listview.ListInVertical;
-
 public class ListInVerticalTest extends ListUnspecifiedMeasure<ListInVertical> {
     public ListInVerticalTest() {
         super(ListInVertical.class);
diff --git a/core/tests/coretests/src/android/widget/listview/ListInterleaveFocusables.java b/core/tests/coretests/src/android/widget/listview/ListInterleaveFocusables.java
index d5da28e..0ec7a24 100644
--- a/core/tests/coretests/src/android/widget/listview/ListInterleaveFocusables.java
+++ b/core/tests/coretests/src/android/widget/listview/ListInterleaveFocusables.java
@@ -16,13 +16,14 @@
 
 package android.widget.listview;
 
+import android.util.ListItemFactory;
+import android.util.ListScenario;
 import android.view.View;
- import android.view.ViewGroup;
- import com.google.android.collect.Sets;
- import android.util.ListScenario;
- import android.util.ListItemFactory;
+import android.view.ViewGroup;
 
- import java.util.Set;
+import com.google.android.collect.Sets;
+
+import java.util.Set;
 
 /**
  * List that interleaves focusable items.
diff --git a/core/tests/coretests/src/android/widget/listview/ListItemFocusableAboveUnfocusable.java b/core/tests/coretests/src/android/widget/listview/ListItemFocusableAboveUnfocusable.java
index f7c01b1..3159e53 100644
--- a/core/tests/coretests/src/android/widget/listview/ListItemFocusableAboveUnfocusable.java
+++ b/core/tests/coretests/src/android/widget/listview/ListItemFocusableAboveUnfocusable.java
@@ -16,12 +16,10 @@
 
 package android.widget.listview;
 
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.Button;
-import android.widget.TextView;
 import android.util.ListItemFactory;
 import android.util.ListScenario;
+import android.view.View;
+import android.view.ViewGroup;
 
 /**
  * A list where the items may befocusable, but the second item isn't actually focusabe.
diff --git a/core/tests/coretests/src/android/widget/listview/ListItemFocusablesClose.java b/core/tests/coretests/src/android/widget/listview/ListItemFocusablesClose.java
index b529b2e..861e2a91 100644
--- a/core/tests/coretests/src/android/widget/listview/ListItemFocusablesClose.java
+++ b/core/tests/coretests/src/android/widget/listview/ListItemFocusablesClose.java
@@ -16,8 +16,8 @@
 
 package android.widget.listview;
 
-import android.util.ListScenario;
 import android.util.ListItemFactory;
+import android.util.ListScenario;
 import android.view.View;
 import android.view.ViewGroup;
 
diff --git a/core/tests/coretests/src/android/widget/listview/ListItemFocusablesFarApart.java b/core/tests/coretests/src/android/widget/listview/ListItemFocusablesFarApart.java
index 59987ec..e9c9c1d 100644
--- a/core/tests/coretests/src/android/widget/listview/ListItemFocusablesFarApart.java
+++ b/core/tests/coretests/src/android/widget/listview/ListItemFocusablesFarApart.java
@@ -16,10 +16,10 @@
 
 package android.widget.listview;
 
-import android.view.View;
-import android.view.ViewGroup;
 import android.util.ListItemFactory;
 import android.util.ListScenario;
+import android.view.View;
+import android.view.ViewGroup;
 
 /**
  * A list where each item is tall with buttons that are farther apart than the screen
diff --git a/core/tests/coretests/src/android/widget/listview/ListItemISVAndButton.java b/core/tests/coretests/src/android/widget/listview/ListItemISVAndButton.java
index ea2c5f2..2a0e013 100644
--- a/core/tests/coretests/src/android/widget/listview/ListItemISVAndButton.java
+++ b/core/tests/coretests/src/android/widget/listview/ListItemISVAndButton.java
@@ -17,13 +17,13 @@
 package android.widget.listview;
 
 import android.content.Context;
+import android.util.InternalSelectionView;
+import android.util.ListScenario;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.Button;
 import android.widget.LinearLayout;
 import android.widget.TextView;
-import android.util.InternalSelectionView;
-import android.util.ListScenario;
 
 /**
  * Each item is an internal selection view, a button, and some filler
diff --git a/core/tests/coretests/src/android/widget/listview/ListItemRequestRectAboveThinFirstItemTest.java b/core/tests/coretests/src/android/widget/listview/ListItemRequestRectAboveThinFirstItemTest.java
index 73eb0a8..91ff06b 100644
--- a/core/tests/coretests/src/android/widget/listview/ListItemRequestRectAboveThinFirstItemTest.java
+++ b/core/tests/coretests/src/android/widget/listview/ListItemRequestRectAboveThinFirstItemTest.java
@@ -18,13 +18,13 @@
 
 import android.graphics.Rect;
 import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.test.suitebuilder.annotation.Suppress;
-import android.view.View;
 import android.view.KeyEvent;
+import android.view.View;
 import android.widget.ListView;
-import android.widget.listview.ListOfThinItems;
+
+import androidx.test.filters.LargeTest;
+import androidx.test.filters.MediumTest;
+import androidx.test.filters.Suppress;
 
 public class ListItemRequestRectAboveThinFirstItemTest
         extends ActivityInstrumentationTestCase<ListOfThinItems> {
diff --git a/core/tests/coretests/src/android/widget/listview/ListItemsExpandOnSelection.java b/core/tests/coretests/src/android/widget/listview/ListItemsExpandOnSelection.java
index a5fe17a..d80fd90 100644
--- a/core/tests/coretests/src/android/widget/listview/ListItemsExpandOnSelection.java
+++ b/core/tests/coretests/src/android/widget/listview/ListItemsExpandOnSelection.java
@@ -17,13 +17,12 @@
 package android.widget.listview;
 
 import android.content.Context;
+import android.util.ListScenario;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.AbsListView;
 import android.widget.TextView;
 
-import android.util.ListScenario;
-
 /**
  * A list where each item expands by 1.5 when selected.
  */
diff --git a/core/tests/coretests/src/android/widget/listview/ListManagedCursor.java b/core/tests/coretests/src/android/widget/listview/ListManagedCursor.java
index 12b5ef4..54f302c 100644
--- a/core/tests/coretests/src/android/widget/listview/ListManagedCursor.java
+++ b/core/tests/coretests/src/android/widget/listview/ListManagedCursor.java
@@ -20,14 +20,13 @@
 import android.content.Intent;
 import android.database.Cursor;
 import android.os.Bundle;
-import android.provider.Settings;
 import android.provider.Contacts.People;
+import android.provider.Settings;
 import android.view.View;
 import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemClickListener;
 import android.widget.ListAdapter;
 import android.widget.SimpleCursorAdapter;
-import android.widget.AdapterView.OnItemClickListener;
-
 
 public class ListManagedCursor extends ListActivity implements OnItemClickListener {
     
diff --git a/core/tests/coretests/src/android/widget/listview/ListManagedCursorTest.java b/core/tests/coretests/src/android/widget/listview/ListManagedCursorTest.java
index bc3776c..8e0b6fe 100644
--- a/core/tests/coretests/src/android/widget/listview/ListManagedCursorTest.java
+++ b/core/tests/coretests/src/android/widget/listview/ListManagedCursorTest.java
@@ -18,11 +18,12 @@
 
 import android.app.Instrumentation;
 import android.test.ActivityInstrumentationTestCase2;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.test.suitebuilder.annotation.MediumTest;
+import android.test.TouchUtils;
 import android.view.KeyEvent;
 import android.widget.ListView;
-import android.test.TouchUtils;
+
+import androidx.test.filters.LargeTest;
+import androidx.test.filters.MediumTest;
 
 /**
  * Tests restoring the scroll position in a list with a managed cursor.
diff --git a/core/tests/coretests/src/android/widget/listview/ListOfTouchables.java b/core/tests/coretests/src/android/widget/listview/ListOfTouchables.java
index 919ef69..70b9081 100644
--- a/core/tests/coretests/src/android/widget/listview/ListOfTouchables.java
+++ b/core/tests/coretests/src/android/widget/listview/ListOfTouchables.java
@@ -16,12 +16,11 @@
 
 package android.widget.listview;
 
+import android.util.ListScenario;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.Button;
 
-import android.util.ListScenario;
-
 /**
  * Each list item has two focusables that are close enough together that
  * it shouldn't require panning to move focus.
diff --git a/core/tests/coretests/src/android/widget/listview/ListRecyclerProfiling.java b/core/tests/coretests/src/android/widget/listview/ListRecyclerProfiling.java
index 76814fb..075712e 100644
--- a/core/tests/coretests/src/android/widget/listview/ListRecyclerProfiling.java
+++ b/core/tests/coretests/src/android/widget/listview/ListRecyclerProfiling.java
@@ -18,11 +18,11 @@
 
 import android.app.Activity;
 import android.os.Bundle;
-import android.widget.ArrayAdapter;
-import android.widget.ListView;
-import android.widget.ImageButton;
-import android.view.ViewDebug;
 import android.view.View;
+import android.view.ViewDebug;
+import android.widget.ArrayAdapter;
+import android.widget.ImageButton;
+import android.widget.ListView;
 
 import com.android.frameworks.coretests.R;
 
diff --git a/core/tests/coretests/src/android/widget/listview/ListRetainsFocusAcrossLayoutsTest.java b/core/tests/coretests/src/android/widget/listview/ListRetainsFocusAcrossLayoutsTest.java
index 896bd19..be14de8 100644
--- a/core/tests/coretests/src/android/widget/listview/ListRetainsFocusAcrossLayoutsTest.java
+++ b/core/tests/coretests/src/android/widget/listview/ListRetainsFocusAcrossLayoutsTest.java
@@ -16,12 +16,11 @@
 
 package android.widget.listview;
 
-import android.widget.listview.ListItemFocusablesClose;
-
 import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.view.KeyEvent;
 
+import androidx.test.filters.MediumTest;
+
 public class ListRetainsFocusAcrossLayoutsTest extends ActivityInstrumentationTestCase<ListItemFocusablesClose> {
 
     public ListRetainsFocusAcrossLayoutsTest() {
diff --git a/core/tests/coretests/src/android/widget/listview/ListScrollListenerTest.java b/core/tests/coretests/src/android/widget/listview/ListScrollListenerTest.java
index 7b29a66..28addd6 100644
--- a/core/tests/coretests/src/android/widget/listview/ListScrollListenerTest.java
+++ b/core/tests/coretests/src/android/widget/listview/ListScrollListenerTest.java
@@ -19,12 +19,13 @@
 import android.app.Instrumentation;
 import android.test.ActivityInstrumentationTestCase;
 import android.test.TouchUtils;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.view.KeyEvent;
 import android.widget.AbsListView;
 import android.widget.ListView;
 
+import androidx.test.filters.LargeTest;
+import androidx.test.filters.MediumTest;
+
 public class ListScrollListenerTest extends ActivityInstrumentationTestCase<ListScrollListener> implements
         AbsListView.OnScrollListener {
     private ListScrollListener mActivity;
diff --git a/core/tests/coretests/src/android/widget/listview/ListSetSelection.java b/core/tests/coretests/src/android/widget/listview/ListSetSelection.java
index 6c2e264..af8e899 100644
--- a/core/tests/coretests/src/android/widget/listview/ListSetSelection.java
+++ b/core/tests/coretests/src/android/widget/listview/ListSetSelection.java
@@ -16,12 +16,12 @@
 
 package android.widget.listview;
 
+import android.os.Bundle;
 import android.util.ListScenario;
 import android.view.KeyEvent;
 import android.view.View;
-import android.os.Bundle;
-import android.widget.LinearLayout;
 import android.widget.Button;
+import android.widget.LinearLayout;
 
 /**
  * List of 1,000 items used to test calls to setSelection() in touch mode.
diff --git a/core/tests/coretests/src/android/widget/listview/ListSetSelectionTest.java b/core/tests/coretests/src/android/widget/listview/ListSetSelectionTest.java
index 4cef164..2caca13 100644
--- a/core/tests/coretests/src/android/widget/listview/ListSetSelectionTest.java
+++ b/core/tests/coretests/src/android/widget/listview/ListSetSelectionTest.java
@@ -17,10 +17,11 @@
 package android.widget.listview;
 
 import android.test.ActivityInstrumentationTestCase2;
-import android.test.UiThreadTest;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.widget.ListView;
 
+import androidx.test.annotation.UiThreadTest;
+import androidx.test.filters.MediumTest;
+
 /**
  * Basic tests of setting & clearing the selection
  */
diff --git a/core/tests/coretests/src/android/widget/listview/ListSimple.java b/core/tests/coretests/src/android/widget/listview/ListSimple.java
index 6accae1..f53638e 100644
--- a/core/tests/coretests/src/android/widget/listview/ListSimple.java
+++ b/core/tests/coretests/src/android/widget/listview/ListSimple.java
@@ -16,11 +16,10 @@
 
 package android.widget.listview;
 
+import android.os.Bundle;
 import android.util.ListScenario;
-
 import android.view.View;
 import android.view.ViewGroup;
-import android.os.Bundle;
 import android.widget.TextView;
 
 public class ListSimple extends ListScenario {
diff --git a/core/tests/coretests/src/android/widget/listview/ListTakeFocusFromSide.java b/core/tests/coretests/src/android/widget/listview/ListTakeFocusFromSide.java
index 95f09f6..c4e9fe9 100644
--- a/core/tests/coretests/src/android/widget/listview/ListTakeFocusFromSide.java
+++ b/core/tests/coretests/src/android/widget/listview/ListTakeFocusFromSide.java
@@ -16,17 +16,17 @@
 
 package android.widget.listview;
 
-import com.android.frameworks.coretests.R;
-
 import android.app.ListActivity;
 import android.content.Context;
 import android.os.Bundle;
+import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
-import android.view.LayoutInflater;
 import android.widget.BaseAdapter;
 import android.widget.TextView;
 
+import com.android.frameworks.coretests.R;
+
 /**
  * Exercises moving focus into the list from the side
  */
diff --git a/core/tests/coretests/src/android/widget/listview/ListThrasher.java b/core/tests/coretests/src/android/widget/listview/ListThrasher.java
index 0237a95..d82f68d 100644
--- a/core/tests/coretests/src/android/widget/listview/ListThrasher.java
+++ b/core/tests/coretests/src/android/widget/listview/ListThrasher.java
@@ -16,19 +16,19 @@
 
 package android.widget.listview;
 
-import com.android.frameworks.coretests.R;
-
 import android.app.ListActivity;
 import android.content.Context;
 import android.os.Bundle;
 import android.os.Handler;
+import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
-import android.view.LayoutInflater;
 import android.widget.AdapterView;
 import android.widget.BaseAdapter;
 import android.widget.TextView;
 
+import com.android.frameworks.coretests.R;
+
 import java.util.Random;
 
 /**
diff --git a/core/tests/coretests/src/android/widget/listview/ListTopGravity.java b/core/tests/coretests/src/android/widget/listview/ListTopGravity.java
index 986cc57..31339e9 100644
--- a/core/tests/coretests/src/android/widget/listview/ListTopGravity.java
+++ b/core/tests/coretests/src/android/widget/listview/ListTopGravity.java
@@ -16,8 +16,6 @@
 
 package android.widget.listview;
 
-import android.view.Gravity;
-
 import android.util.ListScenario;
 
 /**
diff --git a/core/tests/coretests/src/android/widget/listview/ListUnspecifiedMeasure.java b/core/tests/coretests/src/android/widget/listview/ListUnspecifiedMeasure.java
index 199d069..0fc87ea 100644
--- a/core/tests/coretests/src/android/widget/listview/ListUnspecifiedMeasure.java
+++ b/core/tests/coretests/src/android/widget/listview/ListUnspecifiedMeasure.java
@@ -16,13 +16,14 @@
 
 package android.widget.listview;
 
-import com.android.frameworks.coretests.R;
-
-import android.test.ActivityInstrumentationTestCase;
 import android.app.Activity;
-import android.test.suitebuilder.annotation.MediumTest;
+import android.test.ActivityInstrumentationTestCase;
 import android.widget.ListView;
 
+import androidx.test.filters.MediumTest;
+
+import com.android.frameworks.coretests.R;
+
 public class ListUnspecifiedMeasure<T extends Activity> extends ActivityInstrumentationTestCase<T> {
     private T mActivity;
     private ListView mListView;
diff --git a/core/tests/coretests/src/android/widget/listview/ListViewHeightTest.java b/core/tests/coretests/src/android/widget/listview/ListViewHeightTest.java
index 5ab2757..63941f1 100644
--- a/core/tests/coretests/src/android/widget/listview/ListViewHeightTest.java
+++ b/core/tests/coretests/src/android/widget/listview/ListViewHeightTest.java
@@ -18,12 +18,12 @@
 
 import android.app.Instrumentation;
 import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.widget.Button;
 import android.widget.ListView;
 
+import androidx.test.filters.MediumTest;
+
 import com.android.frameworks.coretests.R;
-import android.widget.listview.ListViewHeight;
 
 public class ListViewHeightTest extends ActivityInstrumentationTestCase<ListViewHeight> {
     private ListViewHeight mActivity;
diff --git a/core/tests/coretests/src/android/widget/listview/ListWithDisappearingItemBug.java b/core/tests/coretests/src/android/widget/listview/ListWithDisappearingItemBug.java
index 348ea1b..10ba8b7 100644
--- a/core/tests/coretests/src/android/widget/listview/ListWithDisappearingItemBug.java
+++ b/core/tests/coretests/src/android/widget/listview/ListWithDisappearingItemBug.java
@@ -16,8 +16,6 @@
 
 package android.widget.listview;
 
-import com.android.frameworks.coretests.R;
-
 import android.app.ListActivity;
 import android.database.Cursor;
 import android.os.Bundle;
@@ -32,6 +30,8 @@
 import android.widget.SimpleCursorAdapter;
 import android.widget.Toast;
 
+import com.android.frameworks.coretests.R;
+
 /**
  * See 1080989. You need some contacts for this adapter.
  */
diff --git a/core/tests/coretests/src/android/widget/listview/ListWithEmptyView.java b/core/tests/coretests/src/android/widget/listview/ListWithEmptyView.java
index 74dd06c..52273fd 100644
--- a/core/tests/coretests/src/android/widget/listview/ListWithEmptyView.java
+++ b/core/tests/coretests/src/android/widget/listview/ListWithEmptyView.java
@@ -16,8 +16,6 @@
 
 package android.widget.listview;
 
-import com.android.frameworks.coretests.R;
-
 import android.app.ListActivity;
 import android.content.Context;
 import android.os.Bundle;
@@ -26,6 +24,8 @@
 import android.view.View;
 import android.widget.ArrayAdapter;
 
+import com.android.frameworks.coretests.R;
+
 
 /**
  * Tests using an empty view with a list */
diff --git a/core/tests/coretests/src/android/widget/listview/ListWithHeaders.java b/core/tests/coretests/src/android/widget/listview/ListWithHeaders.java
index aea091a..6030582 100644
--- a/core/tests/coretests/src/android/widget/listview/ListWithHeaders.java
+++ b/core/tests/coretests/src/android/widget/listview/ListWithHeaders.java
@@ -16,9 +16,8 @@
 
 package android.widget.listview;
 
-import android.util.ListScenario;
-
 import android.os.Bundle;
+import android.util.ListScenario;
 import android.widget.Button;
 import android.widget.ListAdapter;
 import android.widget.ListView;
diff --git a/core/tests/coretests/src/android/widget/listview/ListWithOnItemSelectedAction.java b/core/tests/coretests/src/android/widget/listview/ListWithOnItemSelectedAction.java
index 26e1d5d..13e770c 100644
--- a/core/tests/coretests/src/android/widget/listview/ListWithOnItemSelectedAction.java
+++ b/core/tests/coretests/src/android/widget/listview/ListWithOnItemSelectedAction.java
@@ -16,8 +16,8 @@
 
 package android.widget.listview;
 
-import android.widget.TextView;
 import android.util.ListScenario;
+import android.widget.TextView;
 
 /**
  * The header text view echos the value of the selected item by using (indirectly)
diff --git a/core/tests/coretests/src/android/widget/listview/arrowscroll/ListInterleaveFocusablesTest.java b/core/tests/coretests/src/android/widget/listview/arrowscroll/ListInterleaveFocusablesTest.java
index ec8ab7e..22c28c2 100644
--- a/core/tests/coretests/src/android/widget/listview/arrowscroll/ListInterleaveFocusablesTest.java
+++ b/core/tests/coretests/src/android/widget/listview/arrowscroll/ListInterleaveFocusablesTest.java
@@ -17,13 +17,14 @@
 package android.widget.listview.arrowscroll;
 
 import android.test.ActivityInstrumentationTestCase2;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.util.ListUtil;
 import android.view.KeyEvent;
 import android.view.View;
 import android.widget.ListView;
 import android.widget.listview.ListInterleaveFocusables;
 
+import androidx.test.filters.MediumTest;
+
 public class ListInterleaveFocusablesTest extends ActivityInstrumentationTestCase2<ListInterleaveFocusables> {
     private ListView mListView;
     private ListUtil mListUtil;
diff --git a/core/tests/coretests/src/android/widget/listview/arrowscroll/ListItemFocusableAboveUnfocusableTest.java b/core/tests/coretests/src/android/widget/listview/arrowscroll/ListItemFocusableAboveUnfocusableTest.java
index 82f48801..c645b7c 100644
--- a/core/tests/coretests/src/android/widget/listview/arrowscroll/ListItemFocusableAboveUnfocusableTest.java
+++ b/core/tests/coretests/src/android/widget/listview/arrowscroll/ListItemFocusableAboveUnfocusableTest.java
@@ -17,11 +17,12 @@
 package android.widget.listview.arrowscroll;
 
 import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.widget.ListView;
 import android.view.KeyEvent;
+import android.widget.ListView;
 import android.widget.listview.ListItemFocusableAboveUnfocusable;
 
+import androidx.test.filters.MediumTest;
+
 public class ListItemFocusableAboveUnfocusableTest extends ActivityInstrumentationTestCase<ListItemFocusableAboveUnfocusable> {
     private ListView mListView;
 
diff --git a/core/tests/coretests/src/android/widget/listview/arrowscroll/ListItemFocusablesCloseTest.java b/core/tests/coretests/src/android/widget/listview/arrowscroll/ListItemFocusablesCloseTest.java
index 3b30ebe..c7525b3 100644
--- a/core/tests/coretests/src/android/widget/listview/arrowscroll/ListItemFocusablesCloseTest.java
+++ b/core/tests/coretests/src/android/widget/listview/arrowscroll/ListItemFocusablesCloseTest.java
@@ -17,12 +17,13 @@
 package android.widget.listview.arrowscroll;
 
 import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.view.KeyEvent;
 import android.widget.LinearLayout;
 import android.widget.ListView;
 import android.widget.listview.ListItemFocusablesClose;
 
+import androidx.test.filters.MediumTest;
+
 public class ListItemFocusablesCloseTest extends ActivityInstrumentationTestCase<ListItemFocusablesClose> {
     private ListView mListView;
     private int mListTop;
diff --git a/core/tests/coretests/src/android/widget/listview/arrowscroll/ListItemFocusablesFarApartTest.java b/core/tests/coretests/src/android/widget/listview/arrowscroll/ListItemFocusablesFarApartTest.java
index 475930d..4bb2206 100644
--- a/core/tests/coretests/src/android/widget/listview/arrowscroll/ListItemFocusablesFarApartTest.java
+++ b/core/tests/coretests/src/android/widget/listview/arrowscroll/ListItemFocusablesFarApartTest.java
@@ -17,7 +17,6 @@
 package android.widget.listview.arrowscroll;
 
 import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.view.KeyEvent;
 import android.view.View;
 import android.view.ViewGroup;
@@ -25,6 +24,8 @@
 import android.widget.ListView;
 import android.widget.listview.ListItemFocusablesFarApart;
 
+import androidx.test.filters.MediumTest;
+
 public class ListItemFocusablesFarApartTest extends ActivityInstrumentationTestCase<ListItemFocusablesFarApart> {
     private ListView mListView;
     private int mListTop;
diff --git a/core/tests/coretests/src/android/widget/listview/arrowscroll/ListItemsExpandOnSelectionTest.java b/core/tests/coretests/src/android/widget/listview/arrowscroll/ListItemsExpandOnSelectionTest.java
index 91a1eba..7605291 100644
--- a/core/tests/coretests/src/android/widget/listview/arrowscroll/ListItemsExpandOnSelectionTest.java
+++ b/core/tests/coretests/src/android/widget/listview/arrowscroll/ListItemsExpandOnSelectionTest.java
@@ -17,13 +17,14 @@
 package android.widget.listview.arrowscroll;
 
 import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.test.suitebuilder.annotation.Suppress;
-import android.widget.ListView;
 import android.view.KeyEvent;
+import android.widget.ListView;
 import android.widget.listview.ListItemsExpandOnSelection;
 
+import androidx.test.filters.LargeTest;
+import androidx.test.filters.MediumTest;
+import androidx.test.filters.Suppress;
+
 public class ListItemsExpandOnSelectionTest extends ActivityInstrumentationTestCase<ListItemsExpandOnSelection> {
     private ListView mListView;
     private int mListTop;
diff --git a/core/tests/coretests/src/android/widget/listview/arrowscroll/ListLastItemPartiallyVisibleTest.java b/core/tests/coretests/src/android/widget/listview/arrowscroll/ListLastItemPartiallyVisibleTest.java
index 5bc121a..fdae483 100644
--- a/core/tests/coretests/src/android/widget/listview/arrowscroll/ListLastItemPartiallyVisibleTest.java
+++ b/core/tests/coretests/src/android/widget/listview/arrowscroll/ListLastItemPartiallyVisibleTest.java
@@ -17,12 +17,13 @@
 package android.widget.listview.arrowscroll;
 
 import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.widget.ListView;
-import android.view.View;
 import android.view.KeyEvent;
+import android.view.View;
+import android.widget.ListView;
 import android.widget.listview.ListLastItemPartiallyVisible;
 
+import androidx.test.filters.MediumTest;
+
 public class ListLastItemPartiallyVisibleTest extends ActivityInstrumentationTestCase<ListLastItemPartiallyVisible> {
     private ListView mListView;
     private int mListBottom;
diff --git a/core/tests/coretests/src/android/widget/listview/arrowscroll/ListOfItemsShorterThanScreenTest.java b/core/tests/coretests/src/android/widget/listview/arrowscroll/ListOfItemsShorterThanScreenTest.java
index bda71d0..d44b130 100644
--- a/core/tests/coretests/src/android/widget/listview/arrowscroll/ListOfItemsShorterThanScreenTest.java
+++ b/core/tests/coretests/src/android/widget/listview/arrowscroll/ListOfItemsShorterThanScreenTest.java
@@ -17,14 +17,15 @@
 package android.widget.listview.arrowscroll;
 
 import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.test.suitebuilder.annotation.Suppress;
 import android.view.KeyEvent;
 import android.view.View;
 import android.widget.ListView;
 import android.widget.TextView;
 import android.widget.listview.ListOfItemsShorterThanScreen;
 
+import androidx.test.filters.MediumTest;
+import androidx.test.filters.Suppress;
+
 public class ListOfItemsShorterThanScreenTest
         extends ActivityInstrumentationTestCase<ListOfItemsShorterThanScreen> {
     private ListView mListView;
diff --git a/core/tests/coretests/src/android/widget/listview/arrowscroll/ListOfItemsTallerThanScreenTest.java b/core/tests/coretests/src/android/widget/listview/arrowscroll/ListOfItemsTallerThanScreenTest.java
index 2135445..1decdad 100644
--- a/core/tests/coretests/src/android/widget/listview/arrowscroll/ListOfItemsTallerThanScreenTest.java
+++ b/core/tests/coretests/src/android/widget/listview/arrowscroll/ListOfItemsTallerThanScreenTest.java
@@ -17,13 +17,14 @@
 package android.widget.listview.arrowscroll;
 
 import android.test.ActivityInstrumentationTestCase2;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.test.suitebuilder.annotation.Suppress;
 import android.view.KeyEvent;
 import android.view.View;
 import android.widget.ListView;
 import android.widget.listview.ListOfItemsTallerThanScreen;
 
+import androidx.test.filters.MediumTest;
+import androidx.test.filters.Suppress;
+
 public class ListOfItemsTallerThanScreenTest
         extends ActivityInstrumentationTestCase2<ListOfItemsTallerThanScreen> {
 
diff --git a/core/tests/coretests/src/android/widget/listview/arrowscroll/ListOfShortShortTallShortShortTest.java b/core/tests/coretests/src/android/widget/listview/arrowscroll/ListOfShortShortTallShortShortTest.java
index ef70b5a..67808f1 100644
--- a/core/tests/coretests/src/android/widget/listview/arrowscroll/ListOfShortShortTallShortShortTest.java
+++ b/core/tests/coretests/src/android/widget/listview/arrowscroll/ListOfShortShortTallShortShortTest.java
@@ -17,13 +17,14 @@
 package android.widget.listview.arrowscroll;
 
 import android.test.ActivityInstrumentationTestCase2;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.test.suitebuilder.annotation.Suppress;
 import android.util.ListUtil;
 import android.view.KeyEvent;
 import android.widget.ListView;
 import android.widget.listview.ListOfShortShortTallShortShort;
 
+import androidx.test.filters.MediumTest;
+import androidx.test.filters.Suppress;
+
 public class ListOfShortShortTallShortShortTest extends ActivityInstrumentationTestCase2<ListOfShortShortTallShortShort> {
     private ListView mListView;
     private ListUtil mListUtil;
diff --git a/core/tests/coretests/src/android/widget/listview/arrowscroll/ListOfShortTallShortTest.java b/core/tests/coretests/src/android/widget/listview/arrowscroll/ListOfShortTallShortTest.java
index c958591..f9aa6dc 100644
--- a/core/tests/coretests/src/android/widget/listview/arrowscroll/ListOfShortTallShortTest.java
+++ b/core/tests/coretests/src/android/widget/listview/arrowscroll/ListOfShortTallShortTest.java
@@ -17,11 +17,12 @@
 package android.widget.listview.arrowscroll;
 
 import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.widget.ListView;
 import android.view.KeyEvent;
+import android.widget.ListView;
 import android.widget.listview.ListOfShortTallShort;
 
+import androidx.test.filters.MediumTest;
+
 public class ListOfShortTallShortTest extends ActivityInstrumentationTestCase<ListOfShortTallShort> {
     private ListView mListView;
 
diff --git a/core/tests/coretests/src/android/widget/listview/arrowscroll/ListOfThinItemsTest.java b/core/tests/coretests/src/android/widget/listview/arrowscroll/ListOfThinItemsTest.java
index c191d71..6bd1cb5 100644
--- a/core/tests/coretests/src/android/widget/listview/arrowscroll/ListOfThinItemsTest.java
+++ b/core/tests/coretests/src/android/widget/listview/arrowscroll/ListOfThinItemsTest.java
@@ -17,14 +17,15 @@
 package android.widget.listview.arrowscroll;
 
 import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.test.suitebuilder.annotation.Suppress;
 import android.view.KeyEvent;
 import android.view.View;
 import android.widget.ListView;
 import android.widget.listview.ListOfThinItems;
 
+import androidx.test.filters.LargeTest;
+import androidx.test.filters.MediumTest;
+import androidx.test.filters.Suppress;
+
 public class ListOfThinItemsTest extends ActivityInstrumentationTestCase<ListOfThinItems> {
     private ListView mListView;
 
diff --git a/core/tests/coretests/src/android/widget/listview/arrowscroll/ListWithFirstScreenUnSelectableTest.java b/core/tests/coretests/src/android/widget/listview/arrowscroll/ListWithFirstScreenUnSelectableTest.java
index 9a8e634..60bc115 100644
--- a/core/tests/coretests/src/android/widget/listview/arrowscroll/ListWithFirstScreenUnSelectableTest.java
+++ b/core/tests/coretests/src/android/widget/listview/arrowscroll/ListWithFirstScreenUnSelectableTest.java
@@ -16,12 +16,13 @@
 
 package android.widget.listview.arrowscroll;
 
-import android.support.test.filters.LargeTest;
 import android.test.ActivityInstrumentationTestCase2;
 import android.view.KeyEvent;
+import android.widget.AdapterView;
 import android.widget.ListView;
 import android.widget.listview.ListWithFirstScreenUnSelectable;
-import android.widget.AdapterView;
+
+import androidx.test.filters.LargeTest;
 
 @LargeTest
 public class ListWithFirstScreenUnSelectableTest
diff --git a/core/tests/coretests/src/android/widget/listview/arrowscroll/ListWithNoFadingEdgeTest.java b/core/tests/coretests/src/android/widget/listview/arrowscroll/ListWithNoFadingEdgeTest.java
index 56ca009..a60054e 100644
--- a/core/tests/coretests/src/android/widget/listview/arrowscroll/ListWithNoFadingEdgeTest.java
+++ b/core/tests/coretests/src/android/widget/listview/arrowscroll/ListWithNoFadingEdgeTest.java
@@ -17,12 +17,13 @@
 package android.widget.listview.arrowscroll;
 
 import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.view.KeyEvent;
 import android.widget.ListView;
 import android.widget.listview.ListWithNoFadingEdge;
 
+import androidx.test.filters.LargeTest;
+import androidx.test.filters.MediumTest;
+
 public class ListWithNoFadingEdgeTest extends ActivityInstrumentationTestCase<ListWithNoFadingEdge> {
 
     private ListView mListView;
diff --git a/core/tests/coretests/src/android/widget/listview/arrowscroll/ListWithOffScreenNextSelectableTest.java b/core/tests/coretests/src/android/widget/listview/arrowscroll/ListWithOffScreenNextSelectableTest.java
index cf319d1..80e70c6 100644
--- a/core/tests/coretests/src/android/widget/listview/arrowscroll/ListWithOffScreenNextSelectableTest.java
+++ b/core/tests/coretests/src/android/widget/listview/arrowscroll/ListWithOffScreenNextSelectableTest.java
@@ -17,14 +17,15 @@
 package android.widget.listview.arrowscroll;
 
 import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.test.suitebuilder.annotation.Suppress;
 import android.view.KeyEvent;
 import android.view.View;
 import android.widget.ListView;
 import android.widget.TextView;
 import android.widget.listview.ListWithOffScreenNextSelectable;
 
+import androidx.test.filters.MediumTest;
+import androidx.test.filters.Suppress;
+
 @Suppress // Failing.
 public class ListWithOffScreenNextSelectableTest
         extends ActivityInstrumentationTestCase<ListWithOffScreenNextSelectable> {
diff --git a/core/tests/coretests/src/android/widget/listview/arrowscroll/ListWithOnItemSelectedActionTest.java b/core/tests/coretests/src/android/widget/listview/arrowscroll/ListWithOnItemSelectedActionTest.java
index feea9b2..819b739 100644
--- a/core/tests/coretests/src/android/widget/listview/arrowscroll/ListWithOnItemSelectedActionTest.java
+++ b/core/tests/coretests/src/android/widget/listview/arrowscroll/ListWithOnItemSelectedActionTest.java
@@ -17,12 +17,13 @@
 package android.widget.listview.arrowscroll;
 
 import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
+import android.view.KeyEvent;
 import android.widget.ListView;
 import android.widget.TextView;
-import android.view.KeyEvent;
 import android.widget.listview.ListWithOnItemSelectedAction;
 
+import androidx.test.filters.MediumTest;
+
 public class ListWithOnItemSelectedActionTest extends ActivityInstrumentationTestCase<ListWithOnItemSelectedAction> {
     private ListView mListView;
 
diff --git a/core/tests/coretests/src/android/widget/listview/arrowscroll/ListWithScreenOfNoSelectablesTest.java b/core/tests/coretests/src/android/widget/listview/arrowscroll/ListWithScreenOfNoSelectablesTest.java
index 211c8c8..9bbdc2a 100644
--- a/core/tests/coretests/src/android/widget/listview/arrowscroll/ListWithScreenOfNoSelectablesTest.java
+++ b/core/tests/coretests/src/android/widget/listview/arrowscroll/ListWithScreenOfNoSelectablesTest.java
@@ -17,13 +17,14 @@
 package android.widget.listview.arrowscroll;
 
 import android.test.ActivityInstrumentationTestCase2;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.test.suitebuilder.annotation.Suppress;
 import android.view.KeyEvent;
 import android.view.View;
 import android.widget.ListView;
 import android.widget.listview.ListWithScreenOfNoSelectables;
 
+import androidx.test.filters.MediumTest;
+import androidx.test.filters.Suppress;
+
 public class ListWithScreenOfNoSelectablesTest extends ActivityInstrumentationTestCase2<ListWithScreenOfNoSelectables> {
 
     private ListView mListView;
diff --git a/core/tests/coretests/src/android/widget/listview/arrowscroll/ListWithSeparatorsTest.java b/core/tests/coretests/src/android/widget/listview/arrowscroll/ListWithSeparatorsTest.java
index 42058f0..21fa51d 100644
--- a/core/tests/coretests/src/android/widget/listview/arrowscroll/ListWithSeparatorsTest.java
+++ b/core/tests/coretests/src/android/widget/listview/arrowscroll/ListWithSeparatorsTest.java
@@ -17,11 +17,12 @@
 package android.widget.listview.arrowscroll;
 
 import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.widget.ListView;
 import android.view.KeyEvent;
+import android.widget.ListView;
 import android.widget.listview.ListWithSeparators;
 
+import androidx.test.filters.MediumTest;
+
 public class ListWithSeparatorsTest extends ActivityInstrumentationTestCase<ListWithSeparators> {
     private ListWithSeparators mActivity;
     private ListView mListView;
diff --git a/core/tests/coretests/src/android/widget/listview/focus/AdjacentListsWithAdjacentISVsInsideTest.java b/core/tests/coretests/src/android/widget/listview/focus/AdjacentListsWithAdjacentISVsInsideTest.java
index 6a7466b..e9baabf 100644
--- a/core/tests/coretests/src/android/widget/listview/focus/AdjacentListsWithAdjacentISVsInsideTest.java
+++ b/core/tests/coretests/src/android/widget/listview/focus/AdjacentListsWithAdjacentISVsInsideTest.java
@@ -16,14 +16,14 @@
 
 package android.widget.listview.focus;
 
-import android.test.suitebuilder.annotation.Suppress;
-import android.widget.listview.AdjacentListsWithAdjacentISVsInside;
-import android.util.InternalSelectionView;
-
 import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
+import android.util.InternalSelectionView;
 import android.view.KeyEvent;
 import android.widget.ListView;
+import android.widget.listview.AdjacentListsWithAdjacentISVsInside;
+
+import androidx.test.filters.MediumTest;
+import androidx.test.filters.Suppress;
 
 public class AdjacentListsWithAdjacentISVsInsideTest extends ActivityInstrumentationTestCase<AdjacentListsWithAdjacentISVsInside> {
 
diff --git a/core/tests/coretests/src/android/widget/listview/focus/ListButtonsDiagonalAcrossItemsTest.java b/core/tests/coretests/src/android/widget/listview/focus/ListButtonsDiagonalAcrossItemsTest.java
index 5540d65..3fbdacc 100644
--- a/core/tests/coretests/src/android/widget/listview/focus/ListButtonsDiagonalAcrossItemsTest.java
+++ b/core/tests/coretests/src/android/widget/listview/focus/ListButtonsDiagonalAcrossItemsTest.java
@@ -16,15 +16,15 @@
 
 package android.widget.listview.focus;
 
-import android.widget.listview.ListButtonsDiagonalAcrossItems;
-
 import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.view.FocusFinder;
 import android.view.KeyEvent;
 import android.view.View;
 import android.widget.Button;
 import android.widget.ListView;
+import android.widget.listview.ListButtonsDiagonalAcrossItems;
+
+import androidx.test.filters.MediumTest;
 
 /**
  * Test that ListView will override default behavior of focus searching to
diff --git a/core/tests/coretests/src/android/widget/listview/focus/ListHorizontalFocusWithinItemWinsTest.java b/core/tests/coretests/src/android/widget/listview/focus/ListHorizontalFocusWithinItemWinsTest.java
index edc60b5..5448952 100644
--- a/core/tests/coretests/src/android/widget/listview/focus/ListHorizontalFocusWithinItemWinsTest.java
+++ b/core/tests/coretests/src/android/widget/listview/focus/ListHorizontalFocusWithinItemWinsTest.java
@@ -17,12 +17,13 @@
 package android.widget.listview.focus;
 
 import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.view.KeyEvent;
 import android.widget.Button;
 import android.widget.ListView;
 import android.widget.listview.ListHorizontalFocusWithinItemWins;
 
+import androidx.test.filters.MediumTest;
+
 public class ListHorizontalFocusWithinItemWinsTest extends ActivityInstrumentationTestCase<ListHorizontalFocusWithinItemWins> {
 
     private ListView mListView;
diff --git a/core/tests/coretests/src/android/widget/listview/focus/ListWithEditTextHeaderTest.java b/core/tests/coretests/src/android/widget/listview/focus/ListWithEditTextHeaderTest.java
index b449b61..0b17aaf 100644
--- a/core/tests/coretests/src/android/widget/listview/focus/ListWithEditTextHeaderTest.java
+++ b/core/tests/coretests/src/android/widget/listview/focus/ListWithEditTextHeaderTest.java
@@ -17,17 +17,18 @@
 package android.widget.listview.focus;
 
 import android.test.ActivityInstrumentationTestCase2;
-import android.test.FlakyTest;
 import android.test.TouchUtils;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.test.suitebuilder.annotation.Suppress;
 import android.view.KeyEvent;
 import android.view.View;
 import android.widget.AbsListView;
 import android.widget.ListView;
 import android.widget.listview.ListWithEditTextHeader;
 
+import androidx.test.filters.FlakyTest;
+import androidx.test.filters.LargeTest;
+import androidx.test.filters.MediumTest;
+import androidx.test.filters.Suppress;
+
 public class ListWithEditTextHeaderTest extends ActivityInstrumentationTestCase2<ListWithEditTextHeader> {
     private ListView mListView;
 
@@ -49,7 +50,7 @@
         assertTrue("header does not have focus", mListView.getChildAt(0).isFocused());
     }
 
-    @FlakyTest(tolerance=2)
+    @FlakyTest
     @LargeTest
     public void testClickingHeaderKeepsFocus() {
         TouchUtils.clickView(this, mListView.getChildAt(0));
diff --git a/core/tests/coretests/src/android/widget/listview/touch/ListGetSelectedViewTest.java b/core/tests/coretests/src/android/widget/listview/touch/ListGetSelectedViewTest.java
index 28f899e..262d0f4 100644
--- a/core/tests/coretests/src/android/widget/listview/touch/ListGetSelectedViewTest.java
+++ b/core/tests/coretests/src/android/widget/listview/touch/ListGetSelectedViewTest.java
@@ -17,14 +17,14 @@
 package android.widget.listview.touch;
 
 import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.test.TouchUtils;
-import android.widget.ListView;
 import android.view.View;
-
+import android.widget.ListView;
 import android.widget.listview.ListGetSelectedView;
 
+import androidx.test.filters.LargeTest;
+import androidx.test.filters.MediumTest;
+
 /**
  * This test is made to check that getSelectedView() will return
  * null in touch mode.
diff --git a/core/tests/coretests/src/android/widget/listview/touch/ListOfTouchablesTest.java b/core/tests/coretests/src/android/widget/listview/touch/ListOfTouchablesTest.java
index ffa9a5e..48e8c5e 100644
--- a/core/tests/coretests/src/android/widget/listview/touch/ListOfTouchablesTest.java
+++ b/core/tests/coretests/src/android/widget/listview/touch/ListOfTouchablesTest.java
@@ -17,15 +17,14 @@
 package android.widget.listview.touch;
 
 import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.test.suitebuilder.annotation.MediumTest;
+import android.test.TouchUtils;
 import android.view.Gravity;
 import android.view.View;
 import android.view.ViewConfiguration;
 import android.widget.ListView;
-
 import android.widget.listview.ListOfTouchables;
-import android.test.TouchUtils;
+
+import androidx.test.filters.MediumTest;
 
 /**
  * Touch tests for a list where all of the items fit on the screen.
diff --git a/core/tests/coretests/src/android/widget/listview/touch/ListSetSelectionTest.java b/core/tests/coretests/src/android/widget/listview/touch/ListSetSelectionTest.java
index aed513a..716683a 100644
--- a/core/tests/coretests/src/android/widget/listview/touch/ListSetSelectionTest.java
+++ b/core/tests/coretests/src/android/widget/listview/touch/ListSetSelectionTest.java
@@ -17,15 +17,15 @@
 package android.widget.listview.touch;
 
 import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.test.TouchUtils;
-import android.test.suitebuilder.annotation.Suppress;
 import android.view.View;
 import android.widget.ListView;
-
 import android.widget.listview.ListSimple;
 
+import androidx.test.filters.LargeTest;
+import androidx.test.filters.MediumTest;
+import androidx.test.filters.Suppress;
+
 /**
  * Tests setting the selection in touch mode
  */
diff --git a/core/tests/coretests/src/android/widget/listview/touch/ListTouchBottomGravityManyTest.java b/core/tests/coretests/src/android/widget/listview/touch/ListTouchBottomGravityManyTest.java
index 7daf64e..6a1f076 100644
--- a/core/tests/coretests/src/android/widget/listview/touch/ListTouchBottomGravityManyTest.java
+++ b/core/tests/coretests/src/android/widget/listview/touch/ListTouchBottomGravityManyTest.java
@@ -17,16 +17,16 @@
 package android.widget.listview.touch;
 
 import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.test.TouchUtils;
 import android.view.Gravity;
 import android.view.View;
 import android.view.ViewConfiguration;
 import android.widget.ListView;
-
 import android.widget.listview.ListBottomGravityMany;
 
+import androidx.test.filters.LargeTest;
+import androidx.test.filters.MediumTest;
+
 /**
  * Touch tests for a list where all of the items do not fit on the screen, and the list 
  * stacks from the bottom.
diff --git a/core/tests/coretests/src/android/widget/listview/touch/ListTouchBottomGravityTest.java b/core/tests/coretests/src/android/widget/listview/touch/ListTouchBottomGravityTest.java
index 4086cf0..89498d66 100644
--- a/core/tests/coretests/src/android/widget/listview/touch/ListTouchBottomGravityTest.java
+++ b/core/tests/coretests/src/android/widget/listview/touch/ListTouchBottomGravityTest.java
@@ -17,13 +17,13 @@
 package android.widget.listview.touch;
 
 import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.test.TouchUtils;
 import android.view.View;
 import android.widget.ListView;
-
 import android.widget.listview.ListBottomGravity;
 
+import androidx.test.filters.MediumTest;
+
 /**
  * Touch tests for a list where all of the items fit on the screen, and the list 
  * stacks from the bottom.
diff --git a/core/tests/coretests/src/android/widget/listview/touch/ListTouchManyTest.java b/core/tests/coretests/src/android/widget/listview/touch/ListTouchManyTest.java
index 30d56ca..c7f2371 100644
--- a/core/tests/coretests/src/android/widget/listview/touch/ListTouchManyTest.java
+++ b/core/tests/coretests/src/android/widget/listview/touch/ListTouchManyTest.java
@@ -17,16 +17,16 @@
 package android.widget.listview.touch;
 
 import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.test.TouchUtils;
 import android.view.Gravity;
 import android.view.View;
 import android.view.ViewConfiguration;
 import android.widget.ListView;
-
 import android.widget.listview.ListTopGravityMany;
 
+import androidx.test.filters.LargeTest;
+import androidx.test.filters.MediumTest;
+
 /**
  * Touch tests for a list where all of the items do not fit on the screen.
  */
diff --git a/core/tests/coretests/src/android/widget/listview/touch/ListTouchTest.java b/core/tests/coretests/src/android/widget/listview/touch/ListTouchTest.java
index 5b064b3..8a557ab 100644
--- a/core/tests/coretests/src/android/widget/listview/touch/ListTouchTest.java
+++ b/core/tests/coretests/src/android/widget/listview/touch/ListTouchTest.java
@@ -17,13 +17,13 @@
 package android.widget.listview.touch;
 
 import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.test.TouchUtils;
 import android.view.View;
 import android.widget.ListView;
-
 import android.widget.listview.ListTopGravity;
 
+import androidx.test.filters.MediumTest;
+
 /**
  * Touch tests for a list where all of the items fit on the screen.
  */
diff --git a/core/tests/coretests/src/android/widget/scroll/ButtonAboveTallInternalSelectionView.java b/core/tests/coretests/src/android/widget/scroll/ButtonAboveTallInternalSelectionView.java
index a7f5c05..a30985b 100644
--- a/core/tests/coretests/src/android/widget/scroll/ButtonAboveTallInternalSelectionView.java
+++ b/core/tests/coretests/src/android/widget/scroll/ButtonAboveTallInternalSelectionView.java
@@ -18,7 +18,6 @@
 
 import android.util.InternalSelectionView;
 import android.util.ScrollViewScenario;
-
 import android.widget.Button;
 
 /**
diff --git a/core/tests/coretests/src/android/widget/scroll/ButtonAboveTallInternalSelectionViewTest.java b/core/tests/coretests/src/android/widget/scroll/ButtonAboveTallInternalSelectionViewTest.java
index 8123228..825aa1a 100644
--- a/core/tests/coretests/src/android/widget/scroll/ButtonAboveTallInternalSelectionViewTest.java
+++ b/core/tests/coretests/src/android/widget/scroll/ButtonAboveTallInternalSelectionViewTest.java
@@ -16,14 +16,13 @@
 
 package android.widget.scroll;
 
-import android.test.suitebuilder.annotation.Suppress;
-import android.widget.scroll.ButtonAboveTallInternalSelectionView;
-import android.util.InternalSelectionView;
-
 import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
+import android.util.InternalSelectionView;
 import android.view.KeyEvent;
 
+import androidx.test.filters.MediumTest;
+import androidx.test.filters.Suppress;
+
 @Suppress // Failing.
 public class ButtonAboveTallInternalSelectionViewTest extends
         ActivityInstrumentationTestCase<ButtonAboveTallInternalSelectionView> {
diff --git a/core/tests/coretests/src/android/widget/scroll/ButtonsWithTallTextViewInBetween.java b/core/tests/coretests/src/android/widget/scroll/ButtonsWithTallTextViewInBetween.java
index 3d5f86d..47d36dd 100644
--- a/core/tests/coretests/src/android/widget/scroll/ButtonsWithTallTextViewInBetween.java
+++ b/core/tests/coretests/src/android/widget/scroll/ButtonsWithTallTextViewInBetween.java
@@ -17,7 +17,6 @@
 package android.widget.scroll;
 
 import android.util.ScrollViewScenario;
-
 import android.widget.Button;
 import android.widget.LinearLayout;
 import android.widget.TextView;
diff --git a/core/tests/coretests/src/android/widget/scroll/RequestRectangleVisible.java b/core/tests/coretests/src/android/widget/scroll/RequestRectangleVisible.java
index afc275f..dba07a0 100644
--- a/core/tests/coretests/src/android/widget/scroll/RequestRectangleVisible.java
+++ b/core/tests/coretests/src/android/widget/scroll/RequestRectangleVisible.java
@@ -16,15 +16,15 @@
 
 package android.widget.scroll;
 
-import com.android.frameworks.coretests.R;
-
 import android.app.Activity;
-import android.os.Bundle;
 import android.graphics.Rect;
+import android.os.Bundle;
 import android.view.View;
 import android.widget.ScrollView;
 import android.widget.TextView;
 
+import com.android.frameworks.coretests.R;
+
 /**
  * A screen with some scenarios that exercise {@link ScrollView}'s implementation
  * of {@link android.view.ViewGroup#requestChildRectangleOnScreen}:
diff --git a/core/tests/coretests/src/android/widget/scroll/RequestRectangleVisibleTest.java b/core/tests/coretests/src/android/widget/scroll/RequestRectangleVisibleTest.java
index f8abdb2..7c3df91 100644
--- a/core/tests/coretests/src/android/widget/scroll/RequestRectangleVisibleTest.java
+++ b/core/tests/coretests/src/android/widget/scroll/RequestRectangleVisibleTest.java
@@ -16,19 +16,19 @@
 
 package android.widget.scroll;
 
-import android.test.suitebuilder.annotation.Suppress;
-import android.widget.scroll.RequestRectangleVisible;
-import com.android.frameworks.coretests.R;
-
 import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.test.ViewAsserts;
+import android.view.KeyEvent;
+import android.view.View;
 import android.widget.Button;
 import android.widget.ScrollView;
 import android.widget.TextView;
-import android.view.View;
-import android.view.KeyEvent;
+
+import androidx.test.filters.LargeTest;
+import androidx.test.filters.MediumTest;
+import androidx.test.filters.Suppress;
+
+import com.android.frameworks.coretests.R;
 
 /**
  * {@link RequestRectangleVisible} is set up to exercise the cases of moving a
diff --git a/core/tests/coretests/src/android/widget/scroll/RequestRectangleVisibleWithInternalScroll.java b/core/tests/coretests/src/android/widget/scroll/RequestRectangleVisibleWithInternalScroll.java
index 731b25a..105686a 100644
--- a/core/tests/coretests/src/android/widget/scroll/RequestRectangleVisibleWithInternalScroll.java
+++ b/core/tests/coretests/src/android/widget/scroll/RequestRectangleVisibleWithInternalScroll.java
@@ -16,15 +16,14 @@
 
 package android.widget.scroll;
 
-import com.android.frameworks.coretests.R;
-
 import android.app.Activity;
-import android.os.Bundle;
-import android.widget.EditText;
-import android.widget.TextView;
-import android.widget.Button;
-import android.view.View;
 import android.graphics.Rect;
+import android.os.Bundle;
+import android.view.View;
+import android.widget.Button;
+import android.widget.TextView;
+
+import com.android.frameworks.coretests.R;
 
 public class RequestRectangleVisibleWithInternalScroll extends Activity {
 
diff --git a/core/tests/coretests/src/android/widget/scroll/RequestRectangleVisibleWithInternalScrollTest.java b/core/tests/coretests/src/android/widget/scroll/RequestRectangleVisibleWithInternalScrollTest.java
index 5e9b520..1a221ff 100644
--- a/core/tests/coretests/src/android/widget/scroll/RequestRectangleVisibleWithInternalScrollTest.java
+++ b/core/tests/coretests/src/android/widget/scroll/RequestRectangleVisibleWithInternalScrollTest.java
@@ -16,16 +16,17 @@
 
 package android.widget.scroll;
 
-import com.android.frameworks.coretests.R;
-
 import android.test.ActivityInstrumentationTestCase;
 import android.test.ViewAsserts;
-import android.test.suitebuilder.annotation.Suppress;
 import android.view.KeyEvent;
 import android.widget.Button;
 import android.widget.ScrollView;
 import android.widget.TextView;
 
+import androidx.test.filters.Suppress;
+
+import com.android.frameworks.coretests.R;
+
 /**
  * This is suppressed because {@link TextView#scrollBy} isn't working.
  */
diff --git a/core/tests/coretests/src/android/widget/scroll/ScrollViewButtonsAndLabels.java b/core/tests/coretests/src/android/widget/scroll/ScrollViewButtonsAndLabels.java
index 027ea0f..92a3152 100644
--- a/core/tests/coretests/src/android/widget/scroll/ScrollViewButtonsAndLabels.java
+++ b/core/tests/coretests/src/android/widget/scroll/ScrollViewButtonsAndLabels.java
@@ -16,15 +16,14 @@
 
 package android.widget.scroll;
 
-import com.android.frameworks.coretests.R;
-
 import android.app.Activity;
 import android.os.Bundle;
+import android.widget.Button;
 import android.widget.LinearLayout;
 import android.widget.ScrollView;
 import android.widget.TextView;
-import android.widget.Button;
 
+import com.android.frameworks.coretests.R;
 
 /**
  * Basic scroll view example
diff --git a/core/tests/coretests/src/android/widget/scroll/ScrollViewButtonsAndLabelsTest.java b/core/tests/coretests/src/android/widget/scroll/ScrollViewButtonsAndLabelsTest.java
index 3fd17ee..8d71f84 100644
--- a/core/tests/coretests/src/android/widget/scroll/ScrollViewButtonsAndLabelsTest.java
+++ b/core/tests/coretests/src/android/widget/scroll/ScrollViewButtonsAndLabelsTest.java
@@ -17,14 +17,14 @@
 package android.widget.scroll;
 
 import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.test.suitebuilder.annotation.Suppress;
 import android.view.KeyEvent;
 import android.widget.Button;
 import android.widget.LinearLayout;
 import android.widget.ScrollView;
 
+import androidx.test.filters.LargeTest;
+import androidx.test.filters.MediumTest;
+import androidx.test.filters.Suppress;
 
 public class ScrollViewButtonsAndLabelsTest
         extends ActivityInstrumentationTestCase<ScrollViewButtonsAndLabels> {
diff --git a/core/tests/coretests/src/android/widget/scroll/ShortButtons.java b/core/tests/coretests/src/android/widget/scroll/ShortButtons.java
index 3a0f29a..90ede7d 100644
--- a/core/tests/coretests/src/android/widget/scroll/ShortButtons.java
+++ b/core/tests/coretests/src/android/widget/scroll/ShortButtons.java
@@ -17,7 +17,6 @@
 package android.widget.scroll;
 
 import android.util.ScrollViewScenario;
-
 import android.widget.Button;
 import android.widget.LinearLayout;
 
diff --git a/core/tests/coretests/src/android/widget/scroll/arrowscroll/ButtonsWithTallTextViewInBetweenTest.java b/core/tests/coretests/src/android/widget/scroll/arrowscroll/ButtonsWithTallTextViewInBetweenTest.java
index 56d7ed2..04f5ac8 100644
--- a/core/tests/coretests/src/android/widget/scroll/arrowscroll/ButtonsWithTallTextViewInBetweenTest.java
+++ b/core/tests/coretests/src/android/widget/scroll/arrowscroll/ButtonsWithTallTextViewInBetweenTest.java
@@ -16,16 +16,16 @@
 
 package android.widget.scroll.arrowscroll;
 
-import android.widget.scroll.ButtonsWithTallTextViewInBetween;
-
 import android.graphics.Rect;
 import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.view.KeyEvent;
 import android.view.View;
 import android.widget.Button;
 import android.widget.ScrollView;
 import android.widget.TextView;
+import android.widget.scroll.ButtonsWithTallTextViewInBetween;
+
+import androidx.test.filters.MediumTest;
 
 public class ButtonsWithTallTextViewInBetweenTest
         extends ActivityInstrumentationTestCase<ButtonsWithTallTextViewInBetween> {
diff --git a/core/tests/coretests/src/android/widget/scroll/arrowscroll/MultiPageTextWithPaddingTest.java b/core/tests/coretests/src/android/widget/scroll/arrowscroll/MultiPageTextWithPaddingTest.java
index 6ce4c15..2f45098 100644
--- a/core/tests/coretests/src/android/widget/scroll/arrowscroll/MultiPageTextWithPaddingTest.java
+++ b/core/tests/coretests/src/android/widget/scroll/arrowscroll/MultiPageTextWithPaddingTest.java
@@ -16,14 +16,14 @@
 
 package android.widget.scroll.arrowscroll;
 
-import android.test.suitebuilder.annotation.Suppress;
-import android.widget.scroll.arrowscroll.MultiPageTextWithPadding;
 import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.view.KeyEvent;
-import android.widget.TextView;
 import android.widget.ScrollView;
+import android.widget.TextView;
+
+import androidx.test.filters.LargeTest;
+import androidx.test.filters.MediumTest;
+import androidx.test.filters.Suppress;
 
 @Suppress // Flaky
 public class MultiPageTextWithPaddingTest extends
diff --git a/core/tests/coretests/src/android/widget/scroll/arrowscroll/ShortButtonsTest.java b/core/tests/coretests/src/android/widget/scroll/arrowscroll/ShortButtonsTest.java
index 267d8ee..a2928cb 100644
--- a/core/tests/coretests/src/android/widget/scroll/arrowscroll/ShortButtonsTest.java
+++ b/core/tests/coretests/src/android/widget/scroll/arrowscroll/ShortButtonsTest.java
@@ -16,15 +16,15 @@
 
 package android.widget.scroll.arrowscroll;
 
-import android.widget.scroll.ShortButtons;
-
 import android.graphics.Rect;
 import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.view.KeyEvent;
 import android.widget.Button;
 import android.widget.ScrollView;
+import android.widget.scroll.ShortButtons;
+
+import androidx.test.filters.LargeTest;
+import androidx.test.filters.MediumTest;
 
 public class ShortButtonsTest extends ActivityInstrumentationTestCase<ShortButtons> {
 
diff --git a/core/tests/coretests/src/android/widget/scroll/arrowscroll/TallTextAboveButtonTest.java b/core/tests/coretests/src/android/widget/scroll/arrowscroll/TallTextAboveButtonTest.java
index 5351839..0681081 100644
--- a/core/tests/coretests/src/android/widget/scroll/arrowscroll/TallTextAboveButtonTest.java
+++ b/core/tests/coretests/src/android/widget/scroll/arrowscroll/TallTextAboveButtonTest.java
@@ -16,13 +16,13 @@
 
 package android.widget.scroll.arrowscroll;
 
-import android.widget.scroll.TallTextAboveButton;
-
 import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.view.KeyEvent;
 import android.widget.ScrollView;
 import android.widget.TextView;
+import android.widget.scroll.TallTextAboveButton;
+
+import androidx.test.filters.MediumTest;
 
 public class TallTextAboveButtonTest extends ActivityInstrumentationTestCase<TallTextAboveButton> {
     private ScrollView mScrollView;
diff --git a/core/tests/coretests/src/android/widget/touchmode/ChangeTouchModeTest.java b/core/tests/coretests/src/android/widget/touchmode/ChangeTouchModeTest.java
index 449c95c..aa20c9b 100644
--- a/core/tests/coretests/src/android/widget/touchmode/ChangeTouchModeTest.java
+++ b/core/tests/coretests/src/android/widget/touchmode/ChangeTouchModeTest.java
@@ -16,16 +16,17 @@
 
 package android.widget.touchmode;
 
-import android.widget.layout.linear.LLOfButtons1;
-import android.widget.layout.linear.LLOfButtons2;
 import static android.util.TouchModeFlexibleAsserts.assertInTouchModeAfterClick;
-import static android.util.TouchModeFlexibleAsserts.assertNotInTouchModeAfterKey;
 import static android.util.TouchModeFlexibleAsserts.assertInTouchModeAfterTap;
+import static android.util.TouchModeFlexibleAsserts.assertNotInTouchModeAfterKey;
 
 import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.view.KeyEvent;
+import android.widget.layout.linear.LLOfButtons1;
+import android.widget.layout.linear.LLOfButtons2;
+
+import androidx.test.filters.LargeTest;
+import androidx.test.filters.MediumTest;
 
 /**
  * Tests that the touch mode changes from various events, and that the state
diff --git a/core/tests/coretests/src/android/widget/touchmode/FocusableInTouchModeClickTest.java b/core/tests/coretests/src/android/widget/touchmode/FocusableInTouchModeClickTest.java
index 691b25a..97c982f 100644
--- a/core/tests/coretests/src/android/widget/touchmode/FocusableInTouchModeClickTest.java
+++ b/core/tests/coretests/src/android/widget/touchmode/FocusableInTouchModeClickTest.java
@@ -16,12 +16,12 @@
 
 package android.widget.touchmode;
 
-import android.widget.layout.linear.LLOfTwoFocusableInTouchMode;
-
 import android.test.ActivityInstrumentationTestCase2;
 import android.test.TouchUtils;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.test.suitebuilder.annotation.MediumTest;
+import android.widget.layout.linear.LLOfTwoFocusableInTouchMode;
+
+import androidx.test.filters.LargeTest;
+import androidx.test.filters.MediumTest;
 
 public class FocusableInTouchModeClickTest extends ActivityInstrumentationTestCase2<LLOfTwoFocusableInTouchMode> {
 
diff --git a/core/tests/coretests/src/android/widget/touchmode/StartInTouchWithViewInFocusTest.java b/core/tests/coretests/src/android/widget/touchmode/StartInTouchWithViewInFocusTest.java
index 5339188..88aa9ca 100644
--- a/core/tests/coretests/src/android/widget/touchmode/StartInTouchWithViewInFocusTest.java
+++ b/core/tests/coretests/src/android/widget/touchmode/StartInTouchWithViewInFocusTest.java
@@ -16,14 +16,15 @@
 
 package android.widget.touchmode;
 
-import android.widget.layout.linear.LLEditTextThenButton;
 import static android.util.TouchModeFlexibleAsserts.assertNotInTouchModeAfterKey;
 
 import android.test.ActivityInstrumentationTestCase2;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.view.KeyEvent;
 import android.widget.Button;
 import android.widget.EditText;
+import android.widget.layout.linear.LLEditTextThenButton;
+
+import androidx.test.filters.MediumTest;
 
 public class StartInTouchWithViewInFocusTest extends 
         ActivityInstrumentationTestCase2<LLEditTextThenButton> {
diff --git a/core/tests/coretests/src/android/widget/touchmode/TouchModeFocusChangeTest.java b/core/tests/coretests/src/android/widget/touchmode/TouchModeFocusChangeTest.java
index 5a6110c..f192f68 100644
--- a/core/tests/coretests/src/android/widget/touchmode/TouchModeFocusChangeTest.java
+++ b/core/tests/coretests/src/android/widget/touchmode/TouchModeFocusChangeTest.java
@@ -21,11 +21,11 @@
 import static android.util.TouchModeFlexibleAsserts.assertNotInTouchModeAfterKey;
 
 import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.view.KeyEvent;
 import android.widget.Button;
 import android.widget.layout.linear.LLOfButtons1;
 
+import androidx.test.filters.MediumTest;
 
 /**
  * Make sure focus isn't kept by buttons when entering touch mode.
diff --git a/core/tests/coretests/src/android/widget/touchmode/TouchModeFocusableTest.java b/core/tests/coretests/src/android/widget/touchmode/TouchModeFocusableTest.java
index 3ddeef0..87f33a4 100644
--- a/core/tests/coretests/src/android/widget/touchmode/TouchModeFocusableTest.java
+++ b/core/tests/coretests/src/android/widget/touchmode/TouchModeFocusableTest.java
@@ -20,12 +20,13 @@
 import static android.util.TouchModeFlexibleAsserts.assertInTouchModeAfterTap;
 
 import android.test.ActivityInstrumentationTestCase;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.widget.Button;
 import android.widget.EditText;
 import android.widget.layout.linear.LLEditTextThenButton;
 
+import androidx.test.filters.LargeTest;
+import androidx.test.filters.MediumTest;
+
 /**
  * Some views, like edit texts, can keep and gain focus even when in touch mode.
  */
diff --git a/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java b/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java
index a302657..aadfcbc 100644
--- a/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java
+++ b/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java
@@ -57,7 +57,6 @@
 import android.provider.Settings;
 import android.speech.tts.TextToSpeech;
 import android.speech.tts.Voice;
-import android.support.test.runner.AndroidJUnit4;
 import android.test.mock.MockContentResolver;
 import android.text.TextUtils;
 import android.view.Window;
@@ -66,6 +65,8 @@
 import android.view.accessibility.IAccessibilityManager;
 import android.widget.Toast;
 
+import androidx.test.runner.AndroidJUnit4;
+
 import com.android.internal.R;
 import com.android.internal.accessibility.AccessibilityShortcutController.FrameworkObjectProvider;
 import com.android.internal.util.test.FakeSettingsProvider;
diff --git a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
index 1859378..aaa624e 100644
--- a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
@@ -16,6 +16,30 @@
 
 package com.android.internal.app;
 
+import static android.support.test.espresso.Espresso.onView;
+import static android.support.test.espresso.action.ViewActions.click;
+import static android.support.test.espresso.assertion.ViewAssertions.matches;
+import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
+import static android.support.test.espresso.matcher.ViewMatchers.withId;
+import static android.support.test.espresso.matcher.ViewMatchers.withText;
+
+import static com.android.internal.app.ChooserWrapperActivity.sOverrides;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.usage.UsageStatsManager;
+import android.content.Intent;
+import android.content.pm.ResolveInfo;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.rule.ActivityTestRule;
+import androidx.test.runner.AndroidJUnit4;
+
 import com.android.internal.R;
 import com.android.internal.app.ResolverActivity.ResolvedComponentInfo;
 
@@ -25,32 +49,8 @@
 import org.junit.runner.RunWith;
 import org.mockito.Mockito;
 
-import android.app.usage.UsageStats;
-import android.app.usage.UsageStatsManager;
-import android.content.Intent;
-import android.content.pm.ResolveInfo;
-import android.os.UserHandle;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.rule.ActivityTestRule;
-import android.support.test.runner.AndroidJUnit4;
-
 import java.util.ArrayList;
 import java.util.List;
-import java.util.Map;
-
-import static android.support.test.espresso.Espresso.onView;
-import static android.support.test.espresso.action.ViewActions.click;
-import static android.support.test.espresso.assertion.ViewAssertions.matches;
-import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
-import static android.support.test.espresso.matcher.ViewMatchers.withId;
-import static android.support.test.espresso.matcher.ViewMatchers.withText;
-import static com.android.internal.app.ChooserWrapperActivity.sOverrides;
-import static org.hamcrest.CoreMatchers.is;
-import static org.hamcrest.CoreMatchers.not;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.mockito.Mockito.when;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.times;
 
 /**
  * Chooser activity instrumentation tests
diff --git a/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java b/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java
index c446f3c..60529f6 100644
--- a/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java
+++ b/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java
@@ -16,14 +16,14 @@
 
 package com.android.internal.app;
 
+import static org.mockito.Mockito.mock;
+
 import android.app.usage.UsageStatsManager;
 import android.content.Context;
 import android.content.pm.PackageManager;
 
 import java.util.function.Function;
 
-import static org.mockito.Mockito.mock;
-
 public class ChooserWrapperActivity extends ChooserActivity {
     /*
      * Simple wrapper around chooser activity to be able to initiate it under test
diff --git a/core/tests/coretests/src/com/android/internal/app/IntentForwarderActivityTest.java b/core/tests/coretests/src/com/android/internal/app/IntentForwarderActivityTest.java
index cfb6bdd..9b13af2 100644
--- a/core/tests/coretests/src/com/android/internal/app/IntentForwarderActivityTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/IntentForwarderActivityTest.java
@@ -46,9 +46,10 @@
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.os.UserManager;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.rule.ActivityTestRule;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.rule.ActivityTestRule;
+import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Before;
 import org.junit.Rule;
diff --git a/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java b/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java
index 404c99c..fe2fb85 100644
--- a/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java
@@ -16,6 +16,29 @@
 
 package com.android.internal.app;
 
+import static android.support.test.espresso.Espresso.onView;
+import static android.support.test.espresso.action.ViewActions.click;
+import static android.support.test.espresso.assertion.ViewAssertions.matches;
+import static android.support.test.espresso.matcher.ViewMatchers.isEnabled;
+import static android.support.test.espresso.matcher.ViewMatchers.withId;
+import static android.support.test.espresso.matcher.ViewMatchers.withText;
+
+import static com.android.internal.app.ResolverWrapperActivity.sOverrides;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.mockito.Mockito.when;
+
+import android.content.Intent;
+import android.content.pm.ResolveInfo;
+import android.view.View;
+import android.widget.RelativeLayout;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.rule.ActivityTestRule;
+import androidx.test.runner.AndroidJUnit4;
+
 import com.android.internal.R;
 import com.android.internal.app.ResolverActivity.ResolvedComponentInfo;
 import com.android.internal.widget.ResolverDrawerLayout;
@@ -26,37 +49,8 @@
 import org.junit.runner.RunWith;
 import org.mockito.Mockito;
 
-import android.app.usage.UsageStats;
-import android.app.usage.UsageStatsManager;
-import android.content.Intent;
-import android.content.pm.ResolveInfo;
-import android.os.UserHandle;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.rule.ActivityTestRule;
-import android.support.test.runner.AndroidJUnit4;
-import android.view.View;
-import android.widget.RelativeLayout;
-
 import java.util.ArrayList;
 import java.util.List;
-import java.util.Map;
-
-import static android.os.SystemClock.sleep;
-import static android.support.test.espresso.Espresso.onView;
-import static android.support.test.espresso.action.ViewActions.click;
-import static android.support.test.espresso.assertion.ViewAssertions.matches;
-import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
-import static android.support.test.espresso.matcher.ViewMatchers.isEnabled;
-import static android.support.test.espresso.matcher.ViewMatchers.withId;
-import static android.support.test.espresso.matcher.ViewMatchers.withText;
-import static com.android.internal.app.ResolverWrapperActivity.sOverrides;
-import static org.hamcrest.CoreMatchers.is;
-import static org.hamcrest.CoreMatchers.not;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
 
 /**
  * Resolver activity instrumentation tests
diff --git a/core/tests/coretests/src/com/android/internal/app/ResolverDataProvider.java b/core/tests/coretests/src/com/android/internal/app/ResolverDataProvider.java
index c710b9a..850b466 100644
--- a/core/tests/coretests/src/com/android/internal/app/ResolverDataProvider.java
+++ b/core/tests/coretests/src/com/android/internal/app/ResolverDataProvider.java
@@ -23,10 +23,6 @@
 import android.content.pm.ResolveInfo;
 import android.os.UserHandle;
 
-import java.util.HashMap;
-import java.util.Map;
-import java.util.concurrent.atomic.AtomicInteger;
-
 /**
  * Utility class used by resolver tests to create mock data
  */
diff --git a/core/tests/coretests/src/com/android/internal/app/ResolverListControllerTest.java b/core/tests/coretests/src/com/android/internal/app/ResolverListControllerTest.java
index 284ab60..fcec00e 100644
--- a/core/tests/coretests/src/com/android/internal/app/ResolverListControllerTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/ResolverListControllerTest.java
@@ -25,34 +25,31 @@
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.when;
 
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import org.mockito.invocation.InvocationOnMock;
-import org.mockito.Mock;
-import org.mockito.Mockito;
-import org.mockito.MockitoAnnotations;
-import org.mockito.stubbing.Answer;
-
 import android.app.usage.IUsageStatsManager;
 import android.app.usage.UsageStats;
 import android.app.usage.UsageStatsManager;
 import android.content.Context;
+import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.content.pm.ParceledListSlice;
 import android.content.pm.ResolveInfo;
 import android.content.res.Configuration;
 import android.content.res.Resources;
-import android.content.Intent;
 import android.os.RemoteException;
 import android.os.UserHandle;
-import android.support.test.runner.AndroidJUnit4;
 import android.util.ArrayMap;
 
-import java.io.File;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+
 import java.util.ArrayList;
-import java.util.HashMap;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
diff --git a/core/tests/coretests/src/com/android/internal/app/ResolverWrapperActivity.java b/core/tests/coretests/src/com/android/internal/app/ResolverWrapperActivity.java
index 163211e..83f6bc2 100644
--- a/core/tests/coretests/src/com/android/internal/app/ResolverWrapperActivity.java
+++ b/core/tests/coretests/src/com/android/internal/app/ResolverWrapperActivity.java
@@ -16,17 +16,12 @@
 
 package com.android.internal.app;
 
-import android.app.usage.UsageStatsManager;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.pm.PackageManager;
-import android.os.RemoteException;
-
-import java.util.function.Function;
-
 import static org.mockito.Mockito.mock;
 
+import android.app.usage.UsageStatsManager;
+import android.content.pm.PackageManager;
+
+import java.util.function.Function;
 
 /*
  * Simple wrapper around chooser activity to be able to initiate it under test
diff --git a/core/tests/coretests/src/com/android/internal/app/WindowDecorActionBarTest.java b/core/tests/coretests/src/com/android/internal/app/WindowDecorActionBarTest.java
index 472958a..87ad124 100644
--- a/core/tests/coretests/src/com/android/internal/app/WindowDecorActionBarTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/WindowDecorActionBarTest.java
@@ -17,12 +17,13 @@
 package com.android.internal.app;
 
 import android.test.ActivityInstrumentationTestCase2;
-import android.test.UiThreadTest;
-import android.test.suitebuilder.annotation.SmallTest;
 import android.view.ActionMode;
 import android.view.Menu;
 import android.view.MenuItem;
 
+import androidx.test.annotation.UiThreadTest;
+import androidx.test.filters.SmallTest;
+
 /**
  * Tests for {@link WindowDecorActionBar}.
  */
diff --git a/core/tests/coretests/src/com/android/internal/app/procstats/SparseMappingTableTest.java b/core/tests/coretests/src/com/android/internal/app/procstats/SparseMappingTableTest.java
index 115af5e..f7fea3b 100644
--- a/core/tests/coretests/src/com/android/internal/app/procstats/SparseMappingTableTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/procstats/SparseMappingTableTest.java
@@ -16,20 +16,14 @@
 
 package com.android.internal.app.procstats;
 
-import java.io.PrintWriter;
-import java.io.StringWriter;
-import java.util.ArrayList;
-
-import android.os.BatteryStats;
 import android.os.Parcel;
-import android.test.suitebuilder.annotation.SmallTest;
 import android.util.Log;
 
+import androidx.test.filters.SmallTest;
+
 import junit.framework.Assert;
 import junit.framework.TestCase;
 
-import org.mockito.Mockito;
-
 /**
  * Provides test cases for SparseMappingTable.
  */
diff --git a/core/tests/coretests/src/com/android/internal/inputmethod/InputMethodDebugTest.java b/core/tests/coretests/src/com/android/internal/inputmethod/InputMethodDebugTest.java
index 629f7b6..222e494 100644
--- a/core/tests/coretests/src/com/android/internal/inputmethod/InputMethodDebugTest.java
+++ b/core/tests/coretests/src/com/android/internal/inputmethod/InputMethodDebugTest.java
@@ -18,10 +18,11 @@
 
 import static org.junit.Assert.assertEquals;
 
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
 import android.view.WindowManager.LayoutParams;
 
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
diff --git a/core/tests/coretests/src/com/android/internal/inputmethod/SubtypeLocaleUtilsTest.java b/core/tests/coretests/src/com/android/internal/inputmethod/SubtypeLocaleUtilsTest.java
index 8179328..ba63908 100644
--- a/core/tests/coretests/src/com/android/internal/inputmethod/SubtypeLocaleUtilsTest.java
+++ b/core/tests/coretests/src/com/android/internal/inputmethod/SubtypeLocaleUtilsTest.java
@@ -18,8 +18,8 @@
 
 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/coretests/src/com/android/internal/os/BatteryStatsBackgroundStatsTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsBackgroundStatsTest.java
index 992b46f..b687801 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsBackgroundStatsTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsBackgroundStatsTest.java
@@ -13,6 +13,7 @@
  * License for the specific language governing permissions and limitations under
  * the License.
  */
+
 package com.android.internal.os;
 
 import static android.os.BatteryStats.STATS_SINCE_CHARGED;
@@ -20,10 +21,11 @@
 import android.app.ActivityManager;
 import android.os.BatteryStats;
 import android.os.WorkSource;
-import android.support.test.filters.SmallTest;
 import android.util.ArrayMap;
 import android.view.Display;
 
+import androidx.test.filters.SmallTest;
+
 import junit.framework.TestCase;
 
 /**
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsCounterTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsCounterTest.java
index 08f8dd1..37f818a 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsCounterTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsCounterTest.java
@@ -13,11 +13,13 @@
  * License for the specific language governing permissions and limitations under
  * the License.
  */
+
 package com.android.internal.os;
 
 import android.os.BatteryStats;
 import android.os.Parcel;
-import android.support.test.filters.SmallTest;
+
+import androidx.test.filters.SmallTest;
 
 import junit.framework.TestCase;
 
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsCpuTimesTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsCpuTimesTest.java
index ee8d508..0179ead 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsCpuTimesTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsCpuTimesTest.java
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package com.android.internal.os;
 
 import static android.os.BatteryStats.STATS_SINCE_CHARGED;
@@ -36,11 +37,12 @@
 
 import android.os.BatteryStats;
 import android.os.UserHandle;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
 import android.util.SparseLongArray;
 import android.view.Display;
 
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
 import com.android.internal.util.ArrayUtils;
 
 import org.junit.Before;
@@ -64,7 +66,7 @@
  * Install: adb install -r \
  * ${ANDROID_PRODUCT_OUT}/data/app/FrameworksCoreTests/FrameworksCoreTests.apk
  * Run: adb shell am instrument -e class com.android.internal.os.BatteryStatsCpuTimesTest -w \
- * com.android.frameworks.coretests/android.support.test.runner.AndroidJUnitRunner
+ * com.android.frameworks.coretests/androidx.test.runner.AndroidJUnitRunner
  *
  * or
  *
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsDualTimerTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsDualTimerTest.java
index 3a5a9f5..efb8710 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsDualTimerTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsDualTimerTest.java
@@ -13,10 +13,12 @@
  * License for the specific language governing permissions and limitations under
  * the License.
  */
+
 package com.android.internal.os;
 
 import android.os.BatteryStats;
-import android.support.test.filters.SmallTest;
+
+import androidx.test.filters.SmallTest;
 
 import junit.framework.TestCase;
 
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsDurationTimerTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsDurationTimerTest.java
index 19dab79..a42286f 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsDurationTimerTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsDurationTimerTest.java
@@ -13,11 +13,13 @@
  * License for the specific language governing permissions and limitations under
  * the License.
  */
+
 package com.android.internal.os;
 
 import android.os.BatteryStats;
 import android.os.Parcel;
-import android.support.test.filters.SmallTest;
+
+import androidx.test.filters.SmallTest;
 
 import junit.framework.TestCase;
 
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsHelperTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsHelperTest.java
index 7467114..355601c 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsHelperTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsHelperTest.java
@@ -17,7 +17,6 @@
 
 package com.android.internal.os;
 
-
 import static android.os.BatteryStats.Uid.PROCESS_STATE_TOP;
 
 import static com.google.common.truth.Truth.assertThat;
@@ -37,12 +36,13 @@
 import android.content.pm.PackageManager;
 import android.os.BatteryStats;
 import android.os.Process;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
 import android.text.format.DateUtils;
 import android.util.StatsLog;
 
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
 import junit.framework.TestCase;
 
 import org.junit.Before;
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsHistoryTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsHistoryTest.java
index 10a3189..cc0ddb7 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsHistoryTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsHistoryTest.java
@@ -13,18 +13,18 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package com.android.internal.os;
 
+import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
-import static org.junit.Assert.assertEquals;
-
 import android.content.Context;
 import android.os.Parcel;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.LargeTest;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Before;
 import org.junit.Test;
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsImplTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsImplTest.java
index 613de45..dc93675 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsImplTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsImplTest.java
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package com.android.internal.os;
 
 import static android.os.BatteryStats.STATS_SINCE_CHARGED;
@@ -31,12 +32,13 @@
 import static org.mockito.Mockito.when;
 
 import android.os.BatteryStats;
-import android.support.test.filters.LargeTest;
-import android.support.test.runner.AndroidJUnit4;
 import android.util.SparseArray;
 import android.util.SparseIntArray;
 import android.view.Display;
 
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
+
 import com.android.internal.util.ArrayUtils;
 
 import org.junit.Before;
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java
index 8cbe5d6..3e33273 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java
@@ -13,6 +13,7 @@
  * License for the specific language governing permissions and limitations under
  * the License.
  */
+
 package com.android.internal.os;
 
 import static android.os.BatteryStats.STATS_CURRENT;
@@ -20,21 +21,20 @@
 import static android.os.BatteryStats.WAKE_TYPE_PARTIAL;
 
 import android.app.ActivityManager;
-import android.os.BatteryManager;
 import android.os.BatteryStats;
 import android.os.BatteryStats.HistoryItem;
 import android.os.BatteryStats.Uid.Sensor;
 import android.os.WorkSource;
-import android.support.test.filters.SmallTest;
 import android.view.Display;
 
+import androidx.test.filters.SmallTest;
+
 import com.android.internal.os.BatteryStatsImpl.DualTimer;
 import com.android.internal.os.BatteryStatsImpl.Uid;
+
 import junit.framework.TestCase;
 
-import java.util.ArrayList;
 import java.util.HashMap;
-import java.util.List;
 import java.util.Map;
 
 /**
@@ -47,7 +47,7 @@
  * Install: adb install -r \
  *      ${ANDROID_PRODUCT_OUT}/data/app/FrameworksCoreTests/FrameworksCoreTests.apk
  * Run: adb shell am instrument -e class com.android.internal.os.BatteryStatsNoteTest -w \
- *      com.android.frameworks.coretests/android.support.test.runner.AndroidJUnitRunner
+ *      com.android.frameworks.coretests/androidx.test.runner.AndroidJUnitRunner
  */
 public class BatteryStatsNoteTest extends TestCase {
 
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsSamplingTimerTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsSamplingTimerTest.java
index 251ceb0..61d20df 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsSamplingTimerTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsSamplingTimerTest.java
@@ -13,11 +13,13 @@
  * License for the specific language governing permissions and limitations under
  * the License.
  */
+
 package com.android.internal.os;
 
 import android.os.BatteryStats;
 import android.os.Parcel;
-import android.support.test.filters.SmallTest;
+
+import androidx.test.filters.SmallTest;
 
 import junit.framework.TestCase;
 
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsSensorTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsSensorTest.java
index a751f90..b851f0a 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsSensorTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsSensorTest.java
@@ -13,13 +13,15 @@
  * License for the specific language governing permissions and limitations under
  * the License.
  */
+
 package com.android.internal.os;
 
 import android.app.ActivityManager;
 import android.os.BatteryStats;
-import android.support.test.filters.SmallTest;
 import android.view.Display;
 
+import androidx.test.filters.SmallTest;
+
 import junit.framework.TestCase;
 
 /**
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsServTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsServTest.java
index 5fd8225..b9995c4 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsServTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsServTest.java
@@ -18,7 +18,8 @@
 
 import android.os.BatteryStats;
 import android.os.Parcel;
-import android.support.test.filters.SmallTest;
+
+import androidx.test.filters.SmallTest;
 
 import junit.framework.Assert;
 import junit.framework.TestCase;
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsStopwatchTimerTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsStopwatchTimerTest.java
index 015314e..f76f316 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsStopwatchTimerTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsStopwatchTimerTest.java
@@ -13,10 +13,12 @@
  * License for the specific language governing permissions and limitations under
  * the License.
  */
+
 package com.android.internal.os;
 
 import android.os.BatteryStats;
-import android.support.test.filters.SmallTest;
+
+import androidx.test.filters.SmallTest;
 
 import junit.framework.TestCase;
 
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java
index 225515e..d69e1d1 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package com.android.internal.os;
 
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsTimeBaseTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsTimeBaseTest.java
index 3190d9e..bce8b40 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsTimeBaseTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsTimeBaseTest.java
@@ -16,19 +16,20 @@
 
 package com.android.internal.os;
 
-import java.io.PrintWriter;
-import java.io.StringWriter;
-
 import android.os.BatteryStats;
 import android.os.Parcel;
-import android.support.test.filters.SmallTest;
 import android.util.Log;
 
+import androidx.test.filters.SmallTest;
+
 import junit.framework.Assert;
 import junit.framework.TestCase;
 
 import org.mockito.Mockito;
 
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
 /**
  * Provides test cases for android.os.BatteryStats.
  */
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsTimerTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsTimerTest.java
index 98d0f7f..87dc2f3 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsTimerTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsTimerTest.java
@@ -18,16 +18,17 @@
 
 import android.os.BatteryStats;
 import android.os.Parcel;
-import android.support.test.filters.SmallTest;
 import android.util.StringBuilderPrinter;
 
-import junit.framework.Assert;
-import junit.framework.TestCase;
+import androidx.test.filters.SmallTest;
 
 import com.android.internal.os.BatteryStatsImpl.Clocks;
 import com.android.internal.os.BatteryStatsImpl.TimeBase;
 import com.android.internal.os.BatteryStatsImpl.Timer;
 
+import junit.framework.Assert;
+import junit.framework.TestCase;
+
 /**
  * Provides test cases for android.os.BatteryStats.
  */
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsUidTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsUidTest.java
index a7e75a2..4df3190 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsUidTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsUidTest.java
@@ -16,22 +16,10 @@
 
 package com.android.internal.os;
 
-import java.io.PrintWriter;
-import java.io.StringWriter;
-import java.util.ArrayList;
+import androidx.test.filters.SmallTest;
 
-import android.os.BatteryStats;
-import android.os.Parcel;
-import android.test.suitebuilder.annotation.SmallTest;
-import android.util.Log;
-
-import junit.framework.Assert;
 import junit.framework.TestCase;
 
-import com.android.internal.os.BatteryStatsImpl;
-
-import org.mockito.Mockito;
-
 /**
  * Provides test cases for android.os.BatteryStats.
  */
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsUserLifecycleTests.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsUserLifecycleTests.java
index 450473d..e7a1bca 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsUserLifecycleTests.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsUserLifecycleTests.java
@@ -28,12 +28,13 @@
 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 android.support.test.uiautomator.UiDevice;
 import android.util.ArraySet;
 
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
+
 import org.junit.After;
 import org.junit.Before;
 import org.junit.BeforeClass;
diff --git a/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java b/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java
index dc3a12f..1d35143 100644
--- a/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java
@@ -22,11 +22,12 @@
 import android.os.Binder;
 import android.os.SystemClock;
 import android.platform.test.annotations.Presubmit;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
 import android.util.ArrayMap;
 import android.util.SparseArray;
 
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
 import com.android.internal.os.BinderInternal.CallSession;
 
 import org.junit.Assert;
diff --git a/core/tests/coretests/src/com/android/internal/os/BstatsCpuTimesValidationTest.java b/core/tests/coretests/src/com/android/internal/os/BstatsCpuTimesValidationTest.java
index 42c9139..01515bd 100644
--- a/core/tests/coretests/src/com/android/internal/os/BstatsCpuTimesValidationTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BstatsCpuTimesValidationTest.java
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package com.android.internal.os;
 
 import static android.os.BatteryStats.UID_TIMES_TYPE_ALL;
@@ -33,9 +34,6 @@
 import static junit.framework.Assert.assertTrue;
 import static junit.framework.Assert.fail;
 
-import com.android.frameworks.coretests.aidl.ICmdCallback;
-import com.android.frameworks.coretests.aidl.ICmdReceiver;
-
 import android.app.ActivityManager;
 import android.app.KeyguardManager;
 import android.content.ComponentName;
@@ -52,14 +50,18 @@
 import android.os.Process;
 import android.os.SystemClock;
 import android.provider.Settings;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.LargeTest;
-import android.support.test.runner.AndroidJUnit4;
 import android.support.test.uiautomator.UiDevice;
 import android.text.TextUtils;
 import android.util.DebugUtils;
 import android.util.Log;
 
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.frameworks.coretests.aidl.ICmdCallback;
+import com.android.frameworks.coretests.aidl.ICmdReceiver;
+
 import org.junit.AfterClass;
 import org.junit.BeforeClass;
 import org.junit.Rule;
diff --git a/core/tests/coretests/src/com/android/internal/os/DebugTest.java b/core/tests/coretests/src/com/android/internal/os/DebugTest.java
index efb78d7..2a8a857 100644
--- a/core/tests/coretests/src/com/android/internal/os/DebugTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/DebugTest.java
@@ -18,7 +18,8 @@
 
 import android.os.Debug;
 
-import android.test.suitebuilder.annotation.SmallTest;
+import androidx.test.filters.SmallTest;
+
 import junit.framework.TestCase;
 
 @SmallTest
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelCpuProcReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelCpuProcReaderTest.java
index 8360126..a25a7489 100644
--- a/core/tests/coretests/src/com/android/internal/os/KernelCpuProcReaderTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/KernelCpuProcReaderTest.java
@@ -24,9 +24,10 @@
 import android.content.Context;
 import android.os.FileUtils;
 import android.os.SystemClock;
-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.After;
 import org.junit.Before;
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelCpuProcStringReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelCpuProcStringReaderTest.java
index 2663f2b..7a31605 100644
--- a/core/tests/coretests/src/com/android/internal/os/KernelCpuProcStringReaderTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/KernelCpuProcStringReaderTest.java
@@ -24,9 +24,10 @@
 import android.content.Context;
 import android.os.FileUtils;
 import android.os.SystemClock;
-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.After;
 import org.junit.Before;
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderTest.java
index b242a34..0c56b8a 100644
--- a/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderTest.java
@@ -23,9 +23,10 @@
 
 import android.content.Context;
 import android.os.FileUtils;
-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.After;
 import org.junit.Before;
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelCpuUidActiveTimeReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelCpuUidActiveTimeReaderTest.java
index adafda0..1b13a99 100644
--- a/core/tests/coretests/src/com/android/internal/os/KernelCpuUidActiveTimeReaderTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/KernelCpuUidActiveTimeReaderTest.java
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package com.android.internal.os;
 
 import static org.junit.Assert.assertEquals;
@@ -20,11 +21,12 @@
 
 import android.content.Context;
 import android.os.FileUtils;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
 import android.util.SparseLongArray;
 
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
 import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidActiveTimeReader;
 
 import org.junit.After;
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelCpuUidClusterTimeReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelCpuUidClusterTimeReaderTest.java
index ad20d84..2ea80da 100644
--- a/core/tests/coretests/src/com/android/internal/os/KernelCpuUidClusterTimeReaderTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/KernelCpuUidClusterTimeReaderTest.java
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package com.android.internal.os;
 
 import static org.junit.Assert.assertArrayEquals;
@@ -22,11 +23,12 @@
 
 import android.content.Context;
 import android.os.FileUtils;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
 import android.util.SparseArray;
 
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
 import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidClusterTimeReader;
 
 import org.junit.After;
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelCpuUidFreqTimeReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelCpuUidFreqTimeReaderTest.java
index 1d3a98a..0b6fed3 100644
--- a/core/tests/coretests/src/com/android/internal/os/KernelCpuUidFreqTimeReaderTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/KernelCpuUidFreqTimeReaderTest.java
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package com.android.internal.os;
 
 import static org.junit.Assert.assertArrayEquals;
@@ -24,11 +25,12 @@
 
 import android.content.Context;
 import android.os.FileUtils;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
 import android.util.SparseArray;
 
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
 import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidFreqTimeReader;
 
 import org.junit.After;
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelCpuUidUserSysTimeReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelCpuUidUserSysTimeReaderTest.java
index 9b4512b..8f81ea2 100644
--- a/core/tests/coretests/src/com/android/internal/os/KernelCpuUidUserSysTimeReaderTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/KernelCpuUidUserSysTimeReaderTest.java
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package com.android.internal.os;
 
 import static org.junit.Assert.assertArrayEquals;
@@ -23,11 +24,12 @@
 import android.content.Context;
 import android.os.FileUtils;
 import android.os.SystemClock;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
 import android.util.SparseArray;
 
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
 import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidUserSysTimeReader;
 
 import org.junit.After;
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelMemoryBandwidthStatsTest.java b/core/tests/coretests/src/com/android/internal/os/KernelMemoryBandwidthStatsTest.java
index 32317ee..60dac85 100644
--- a/core/tests/coretests/src/com/android/internal/os/KernelMemoryBandwidthStatsTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/KernelMemoryBandwidthStatsTest.java
@@ -1,11 +1,27 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
 package com.android.internal.os;
 
-import android.support.test.filters.SmallTest;
 import android.util.LongSparseLongArray;
 
+import androidx.test.filters.SmallTest;
+
 import junit.framework.TestCase;
 
-import org.junit.Assert;
 import org.mockito.Mockito;
 
 import java.io.BufferedReader;
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelSingleUidTimeReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelSingleUidTimeReaderTest.java
index 29227f9..479e19e 100644
--- a/core/tests/coretests/src/com/android/internal/os/KernelSingleUidTimeReaderTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/KernelSingleUidTimeReaderTest.java
@@ -21,10 +21,11 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
 import android.util.SparseArray;
 
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
 import com.android.internal.os.KernelSingleUidTimeReader.Injector;
 
 import org.junit.Before;
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelUidCpuActiveTimeReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelUidCpuActiveTimeReaderTest.java
index 28570e8..12f6c18 100644
--- a/core/tests/coretests/src/com/android/internal/os/KernelUidCpuActiveTimeReaderTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/KernelUidCpuActiveTimeReaderTest.java
@@ -20,8 +20,8 @@
 import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.when;
 
-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.Before;
 import org.junit.Test;
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelUidCpuClusterTimeReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelUidCpuClusterTimeReaderTest.java
index 85dce02..532f337 100644
--- a/core/tests/coretests/src/com/android/internal/os/KernelUidCpuClusterTimeReaderTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/KernelUidCpuClusterTimeReaderTest.java
@@ -21,10 +21,11 @@
 import static org.junit.Assert.assertNotNull;
 import static org.mockito.Mockito.when;
 
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
 import android.util.SparseArray;
 
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelUidCpuFreqTimeReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelUidCpuFreqTimeReaderTest.java
index 67c4e61..6d2980b 100644
--- a/core/tests/coretests/src/com/android/internal/os/KernelUidCpuFreqTimeReaderTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/KernelUidCpuFreqTimeReaderTest.java
@@ -24,10 +24,11 @@
 import static org.mockito.Mockito.verifyZeroInteractions;
 import static org.mockito.Mockito.when;
 
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
 import android.util.SparseArray;
 
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -53,7 +54,7 @@
  * Install: adb install -r \
  * ${ANDROID_PRODUCT_OUT}/data/app/FrameworksCoreTests/FrameworksCoreTests.apk
  * Run: adb shell am instrument -e class com.android.internal.os.KernelUidCpuFreqTimeReaderTest -w \
- * com.android.frameworks.coretests/android.support.test.runner.AndroidJUnitRunner
+ * com.android.frameworks.coretests/androidx.test.runner.AndroidJUnitRunner
  *
  * or
  *
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelWakelockReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelWakelockReaderTest.java
index 4e4bb3507..78b6843 100644
--- a/core/tests/coretests/src/com/android/internal/os/KernelWakelockReaderTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/KernelWakelockReaderTest.java
@@ -13,9 +13,10 @@
  * License for the specific language governing permissions and limitations under
  * the License.
  */
+
 package com.android.internal.os;
 
-import android.support.test.filters.SmallTest;
+import androidx.test.filters.SmallTest;
 
 import junit.framework.TestCase;
 
diff --git a/core/tests/coretests/src/com/android/internal/os/LoggingPrintStreamTest.java b/core/tests/coretests/src/com/android/internal/os/LoggingPrintStreamTest.java
index fe62764..cb8a62c 100644
--- a/core/tests/coretests/src/com/android/internal/os/LoggingPrintStreamTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/LoggingPrintStreamTest.java
@@ -16,7 +16,9 @@
 
 package com.android.internal.os;
 
-import android.test.suitebuilder.annotation.Suppress;
+import androidx.test.filters.Suppress;
+
+import junit.framework.TestCase;
 
 import java.io.PrintWriter;
 import java.io.StringWriter;
@@ -25,8 +27,6 @@
 import java.util.Collections;
 import java.util.List;
 
-import junit.framework.TestCase;
-
 // this test causes a IllegalAccessError: superclass not accessible
 @Suppress
 public class LoggingPrintStreamTest extends TestCase {
diff --git a/core/tests/coretests/src/com/android/internal/os/LongSamplingCounterArrayTest.java b/core/tests/coretests/src/com/android/internal/os/LongSamplingCounterArrayTest.java
index 37b4e41a..0516bb7 100644
--- a/core/tests/coretests/src/com/android/internal/os/LongSamplingCounterArrayTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/LongSamplingCounterArrayTest.java
@@ -30,8 +30,9 @@
 import static org.mockito.Mockito.when;
 
 import android.os.Parcel;
-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.Before;
 import org.junit.Test;
@@ -55,7 +56,7 @@
  * Install: adb install -r \
  *     ${ANDROID_PRODUCT_OUT}/data/app/FrameworksCoreTests/FrameworksCoreTests.apk
  * Run: adb shell am instrument -e class com.android.internal.os.LongSamplingCounterArrayTest -w \
- *     com.android.frameworks.coretests/android.support.test.runner.AndroidJUnitRunner
+ *     com.android.frameworks.coretests/androidx.test.runner.AndroidJUnitRunner
  */
 @SmallTest
 @RunWith(AndroidJUnit4.class)
diff --git a/core/tests/coretests/src/com/android/internal/os/LongSamplingCounterTest.java b/core/tests/coretests/src/com/android/internal/os/LongSamplingCounterTest.java
index 853bf8a..d2f5735 100644
--- a/core/tests/coretests/src/com/android/internal/os/LongSamplingCounterTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/LongSamplingCounterTest.java
@@ -28,8 +28,9 @@
 import static org.mockito.Mockito.when;
 
 import android.os.Parcel;
-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.Before;
 import org.junit.Test;
diff --git a/core/tests/coretests/src/com/android/internal/os/LooperStatsTest.java b/core/tests/coretests/src/com/android/internal/os/LooperStatsTest.java
index b65c1e6..2c597b1 100644
--- a/core/tests/coretests/src/com/android/internal/os/LooperStatsTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/LooperStatsTest.java
@@ -23,8 +23,9 @@
 import android.os.Looper;
 import android.os.Message;
 import android.platform.test.annotations.Presubmit;
-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.After;
 import org.junit.Assert;
diff --git a/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java b/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java
index b68f6b1..c18445e 100644
--- a/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java
+++ b/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java
@@ -21,6 +21,7 @@
 import android.util.SparseIntArray;
 
 import com.android.internal.location.gnssmetrics.GnssMetrics;
+
 import java.util.ArrayList;
 import java.util.Queue;
 import java.util.concurrent.Future;
diff --git a/core/tests/coretests/src/com/android/internal/os/PowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/PowerCalculatorTest.java
index 14d62e0..c592ab6 100644
--- a/core/tests/coretests/src/com/android/internal/os/PowerCalculatorTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/PowerCalculatorTest.java
@@ -17,13 +17,13 @@
 
 package com.android.internal.os;
 
-
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
 import android.os.BatteryStats;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
 
 import junit.framework.TestCase;
 
diff --git a/core/tests/coretests/src/com/android/internal/os/PowerProfileTest.java b/core/tests/coretests/src/com/android/internal/os/PowerProfileTest.java
index 2853c96..5862368 100644
--- a/core/tests/coretests/src/com/android/internal/os/PowerProfileTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/PowerProfileTest.java
@@ -17,8 +17,8 @@
 
 package com.android.internal.os;
 
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SmallTest;
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
 
 import junit.framework.TestCase;
 
diff --git a/core/tests/coretests/src/com/android/internal/os/ProcStatsUtilTest.java b/core/tests/coretests/src/com/android/internal/os/ProcStatsUtilTest.java
index 489e164..e97caf8 100644
--- a/core/tests/coretests/src/com/android/internal/os/ProcStatsUtilTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/ProcStatsUtilTest.java
@@ -20,9 +20,10 @@
 
 import android.content.Context;
 import android.os.FileUtils;
-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.After;
 import org.junit.Before;
diff --git a/core/tests/coretests/src/com/android/internal/os/ProcTimeInStateReaderTest.java b/core/tests/coretests/src/com/android/internal/os/ProcTimeInStateReaderTest.java
index 2893066..9db3f8a 100644
--- a/core/tests/coretests/src/com/android/internal/os/ProcTimeInStateReaderTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/ProcTimeInStateReaderTest.java
@@ -22,9 +22,10 @@
 
 import android.content.Context;
 import android.os.FileUtils;
-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.After;
 import org.junit.Before;
diff --git a/core/tests/coretests/src/com/android/internal/os/StoragedUidIoStatsReaderTest.java b/core/tests/coretests/src/com/android/internal/os/StoragedUidIoStatsReaderTest.java
index c051a1c..85eafc5 100644
--- a/core/tests/coretests/src/com/android/internal/os/StoragedUidIoStatsReaderTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/StoragedUidIoStatsReaderTest.java
@@ -15,15 +15,17 @@
  */
 
 package com.android.internal.os;
+
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.verifyZeroInteractions;
 
 import android.content.Context;
 import android.os.FileUtils;
-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.After;
 import org.junit.Before;
@@ -36,7 +38,6 @@
 import java.io.File;
 import java.nio.file.Files;
 
-
 /**
  * Test class for {@link StoragedUidIoStatsReader}.
  *
diff --git a/core/tests/coretests/src/com/android/internal/policy/PhoneWindowActionModeTest.java b/core/tests/coretests/src/com/android/internal/policy/PhoneWindowActionModeTest.java
index 2a24881..8e0c1fe 100644
--- a/core/tests/coretests/src/com/android/internal/policy/PhoneWindowActionModeTest.java
+++ b/core/tests/coretests/src/com/android/internal/policy/PhoneWindowActionModeTest.java
@@ -17,8 +17,6 @@
 package com.android.internal.policy;
 
 import android.test.ActivityInstrumentationTestCase2;
-import android.test.UiThreadTest;
-import android.test.suitebuilder.annotation.SmallTest;
 import android.view.ActionMode;
 import android.view.ActionMode.Callback;
 import android.view.KeyEvent;
@@ -32,7 +30,8 @@
 import android.view.WindowManager.LayoutParams;
 import android.view.accessibility.AccessibilityEvent;
 
-import java.util.List;
+import androidx.test.annotation.UiThreadTest;
+import androidx.test.filters.SmallTest;
 
 /**
  * Tests {@link PhoneWindow}'s {@link ActionMode} related methods.
diff --git a/core/tests/coretests/src/com/android/internal/policy/PhoneWindowTest.java b/core/tests/coretests/src/com/android/internal/policy/PhoneWindowTest.java
index d4b8566..6c2d630 100644
--- a/core/tests/coretests/src/com/android/internal/policy/PhoneWindowTest.java
+++ b/core/tests/coretests/src/com/android/internal/policy/PhoneWindowTest.java
@@ -28,12 +28,13 @@
 import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
 import android.platform.test.annotations.Presubmit;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
 import android.view.ActionMode;
 import android.view.ContextThemeWrapper;
 
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
 import com.android.frameworks.coretests.R;
 
 import org.junit.Before;
diff --git a/core/tests/coretests/src/com/android/internal/util/ArrayUtilsTest.java b/core/tests/coretests/src/com/android/internal/util/ArrayUtilsTest.java
index b3897ce..d026735 100644
--- a/core/tests/coretests/src/com/android/internal/util/ArrayUtilsTest.java
+++ b/core/tests/coretests/src/com/android/internal/util/ArrayUtilsTest.java
@@ -13,9 +13,11 @@
  * License for the specific language governing permissions and limitations under
  * the License.
  */
+
 package com.android.internal.util;
 
-import android.test.suitebuilder.annotation.SmallTest;
+import androidx.test.filters.SmallTest;
+
 import junit.framework.TestCase;
 
 import java.util.ArrayList;
diff --git a/core/tests/coretests/src/com/android/internal/util/DumpUtilsTest.java b/core/tests/coretests/src/com/android/internal/util/DumpUtilsTest.java
index a44b860..4716312 100644
--- a/core/tests/coretests/src/com/android/internal/util/DumpUtilsTest.java
+++ b/core/tests/coretests/src/com/android/internal/util/DumpUtilsTest.java
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package com.android.internal.util;
 
 import static com.android.internal.util.DumpUtils.CRITICAL_SECTION_COMPONENTS;
diff --git a/core/tests/coretests/src/com/android/internal/util/LineBreakBufferedWriterTest.java b/core/tests/coretests/src/com/android/internal/util/LineBreakBufferedWriterTest.java
index 4845c4e..b2a2265 100644
--- a/core/tests/coretests/src/com/android/internal/util/LineBreakBufferedWriterTest.java
+++ b/core/tests/coretests/src/com/android/internal/util/LineBreakBufferedWriterTest.java
@@ -19,11 +19,8 @@
 import junit.framework.TestCase;
 
 import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.PrintWriter;
 import java.io.Writer;
 import java.util.ArrayList;
-import java.util.LinkedList;
 import java.util.List;
 
 /**
diff --git a/core/tests/coretests/src/com/android/internal/util/ParseUtilsTest.java b/core/tests/coretests/src/com/android/internal/util/ParseUtilsTest.java
index f00c48c..867152e 100644
--- a/core/tests/coretests/src/com/android/internal/util/ParseUtilsTest.java
+++ b/core/tests/coretests/src/com/android/internal/util/ParseUtilsTest.java
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package com.android.internal.util;
 
 import junit.framework.TestCase;
diff --git a/core/tests/coretests/src/android/util/TokenBucketTest.java b/core/tests/coretests/src/com/android/internal/util/TokenBucketTest.java
similarity index 90%
rename from core/tests/coretests/src/android/util/TokenBucketTest.java
rename to core/tests/coretests/src/com/android/internal/util/TokenBucketTest.java
index f7ac20c..6c50bce 100644
--- a/core/tests/coretests/src/android/util/TokenBucketTest.java
+++ b/core/tests/coretests/src/com/android/internal/util/TokenBucketTest.java
@@ -18,6 +18,7 @@
 
 import android.os.SystemClock;
 import android.text.format.DateUtils;
+
 import junit.framework.TestCase;
 
 public class TokenBucketTest extends TestCase {
@@ -54,9 +55,9 @@
 
         drain(new TokenBucket(FILL_DELTA_VERY_LONG, 10, 100), 10);
 
-        drain(new TokenBucket((int)DateUtils.MINUTE_IN_MILLIS, 50), 50);
-        drain(new TokenBucket((int)DateUtils.HOUR_IN_MILLIS, 10), 10);
-        drain(new TokenBucket((int)DateUtils.DAY_IN_MILLIS, 200), 200);
+        drain(new TokenBucket((int) DateUtils.MINUTE_IN_MILLIS, 50), 50);
+        drain(new TokenBucket((int) DateUtils.HOUR_IN_MILLIS, 10), 10);
+        drain(new TokenBucket((int) DateUtils.DAY_IN_MILLIS, 200), 200);
     }
 
     public void testReset() {
@@ -163,16 +164,16 @@
 
     void assertDuration(long expected, long elapsed) {
         String msg = String.format(
-            "expected elapsed time at least %d ms, but was %d ms", expected, elapsed);
+                "expected elapsed time at least %d ms, but was %d ms", expected, elapsed);
         elapsed += 1; // one millisecond extra guard
         assertTrue(msg, elapsed >= expected);
     }
 
-    void assertThrow(Fn fn)     {
-      try {
-          fn.call();
-          fail("expected n exception to be thrown.");
-      } catch (Throwable t) {}
+    void assertThrow(Fn fn) {
+        try {
+            fn.call();
+            fail("expected n exception to be thrown.");
+        } catch (Throwable t) { }
     }
 
     interface Fn { void call(); }
diff --git a/core/tests/coretests/src/com/android/internal/widget/ActionBarContainerTest.java b/core/tests/coretests/src/com/android/internal/widget/ActionBarContainerTest.java
index 912b7ec..b372366 100644
--- a/core/tests/coretests/src/com/android/internal/widget/ActionBarContainerTest.java
+++ b/core/tests/coretests/src/com/android/internal/widget/ActionBarContainerTest.java
@@ -18,11 +18,12 @@
 
 import android.content.Context;
 import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
 import android.view.ActionMode;
 import android.view.View;
 import android.view.ViewGroup;
 
+import androidx.test.filters.SmallTest;
+
 /**
  * Tests for {@link ActionBarContainer}.
  */
diff --git a/core/tests/coretests/src/com/android/internal/widget/ActionBarOverlayLayoutTest.java b/core/tests/coretests/src/com/android/internal/widget/ActionBarOverlayLayoutTest.java
index d782c0c..d10f173 100644
--- a/core/tests/coretests/src/com/android/internal/widget/ActionBarOverlayLayoutTest.java
+++ b/core/tests/coretests/src/com/android/internal/widget/ActionBarOverlayLayoutTest.java
@@ -29,9 +29,6 @@
 import android.content.Context;
 import android.graphics.Insets;
 import android.graphics.Rect;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
 import android.view.DisplayCutout;
 import android.view.View;
 import android.view.View.OnApplyWindowInsetsListener;
@@ -40,6 +37,10 @@
 import android.widget.FrameLayout;
 import android.widget.Toolbar;
 
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/com/android/internal/widget/BackgroundFallbackTest.java b/core/tests/coretests/src/com/android/internal/widget/BackgroundFallbackTest.java
index e21143d..0fada06 100644
--- a/core/tests/coretests/src/com/android/internal/widget/BackgroundFallbackTest.java
+++ b/core/tests/coretests/src/com/android/internal/widget/BackgroundFallbackTest.java
@@ -32,10 +32,11 @@
 import android.graphics.Color;
 import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
-import android.support.test.InstrumentationRegistry;
 import android.view.ViewGroup;
 import android.widget.FrameLayout;
 
+import androidx.test.InstrumentationRegistry;
+
 import org.junit.Before;
 import org.junit.Test;
 
diff --git a/core/tests/coretests/src/com/android/internal/widget/ImageFloatingTextViewTest.java b/core/tests/coretests/src/com/android/internal/widget/ImageFloatingTextViewTest.java
index 1806b22..2e0dbb4 100644
--- a/core/tests/coretests/src/com/android/internal/widget/ImageFloatingTextViewTest.java
+++ b/core/tests/coretests/src/com/android/internal/widget/ImageFloatingTextViewTest.java
@@ -19,12 +19,13 @@
 import static org.junit.Assert.assertTrue;
 
 import android.content.Context;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SmallTest;
 import android.text.Layout;
 import android.view.View.MeasureSpec;
 import android.widget.TextView;
 
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+
 import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
diff --git a/core/tests/coretests/src/com/android/internal/widget/LockPatternUtilsTest.java b/core/tests/coretests/src/com/android/internal/widget/LockPatternUtilsTest.java
index f73950a..6167c4b 100644
--- a/core/tests/coretests/src/com/android/internal/widget/LockPatternUtilsTest.java
+++ b/core/tests/coretests/src/com/android/internal/widget/LockPatternUtilsTest.java
@@ -20,8 +20,9 @@
 import static org.junit.Assert.assertTrue;
 
 import android.os.UserHandle;
-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/coretests/src/com/android/internal/widget/MessagingLinearLayoutTest.java b/core/tests/coretests/src/com/android/internal/widget/MessagingLinearLayoutTest.java
index 41082b7..6af7c88 100644
--- a/core/tests/coretests/src/com/android/internal/widget/MessagingLinearLayoutTest.java
+++ b/core/tests/coretests/src/com/android/internal/widget/MessagingLinearLayoutTest.java
@@ -21,12 +21,12 @@
 import static org.junit.Assert.assertTrue;
 
 import android.content.Context;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SmallTest;
-import android.text.Layout;
 import android.view.LayoutInflater;
 import android.view.View.MeasureSpec;
 
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+
 import com.android.frameworks.coretests.R;
 
 import org.junit.Before;
diff --git a/core/tests/hdmitests/src/android/hardware/hdmi/HdmiAudioSystemClientTest.java b/core/tests/hdmitests/src/android/hardware/hdmi/HdmiAudioSystemClientTest.java
index 0dd9d09..28a8afe 100644
--- a/core/tests/hdmitests/src/android/hardware/hdmi/HdmiAudioSystemClientTest.java
+++ b/core/tests/hdmitests/src/android/hardware/hdmi/HdmiAudioSystemClientTest.java
@@ -318,6 +318,27 @@
             mMaxVolume = maxVolume;
             mIsMute = isMute;
         }
+
+        @Override
+        public void setSystemAudioModeOnForAudioOnlySource() {
+        }
+
+        @Override
+        public int getPhysicalAddress() {
+            return 0x0000;
+        }
+
+        @Override
+        public void powerOffRemoteDevice(int logicalAddress, int powerStatus) {
+        }
+
+        @Override
+        public void powerOnRemoteDevice(int logicalAddress, int powerStatus) {
+        }
+
+        @Override
+        public void askRemoteDeviceToBecomeActiveSource(int physicalAddress) {
+        }
     }
 
 }
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyTestServicesTests2/Android.mk b/core/tests/hosttests/test-apps/MultiDexLegacyTestServicesTests2/Android.mk
index f3d98a8..afbcd46 100644
--- a/core/tests/hosttests/test-apps/MultiDexLegacyTestServicesTests2/Android.mk
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyTestServicesTests2/Android.mk
@@ -23,7 +23,7 @@
 LOCAL_PACKAGE_NAME := MultiDexLegacyTestServicesTests2
 
 LOCAL_JAVA_LIBRARIES := android-support-multidex
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
+LOCAL_STATIC_JAVA_LIBRARIES := androidx.test.rules
 
 LOCAL_SDK_VERSION := 9
 
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyTestServicesTests2/AndroidManifest.xml b/core/tests/hosttests/test-apps/MultiDexLegacyTestServicesTests2/AndroidManifest.xml
index 0ab2959..01285e7 100644
--- a/core/tests/hosttests/test-apps/MultiDexLegacyTestServicesTests2/AndroidManifest.xml
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyTestServicesTests2/AndroidManifest.xml
@@ -7,7 +7,7 @@
     <uses-sdk android:minSdkVersion="9" />
     <uses-permission android:name="android.permission.KILL_BACKGROUND_PROCESSES"/>
     <instrumentation
-        android:name="android.support.test.runner.AndroidJUnitRunner"
+        android:name="androidx.test.runner.AndroidJUnitRunner"
         android:targetPackage="com.android.framework.multidexlegacytestservices" />
 
     <application
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyTestServicesTests2/src/com/android/framework/multidexlegacytestservices/test2/ServicesTests.java b/core/tests/hosttests/test-apps/MultiDexLegacyTestServicesTests2/src/com/android/framework/multidexlegacytestservices/test2/ServicesTests.java
index 900f203..f2c72f0 100644
--- a/core/tests/hosttests/test-apps/MultiDexLegacyTestServicesTests2/src/com/android/framework/multidexlegacytestservices/test2/ServicesTests.java
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyTestServicesTests2/src/com/android/framework/multidexlegacytestservices/test2/ServicesTests.java
@@ -19,22 +19,26 @@
 import android.app.ActivityManager;
 import android.content.Context;
 import android.content.Intent;
-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 junit.framework.Assert;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
 import java.io.File;
 import java.io.FileFilter;
 import java.io.IOException;
 import java.io.RandomAccessFile;
 import java.util.concurrent.TimeoutException;
-import junit.framework.Assert;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
 
 /**
  * Run the tests with: <code>adb shell am instrument -w
- * com.android.framework.multidexlegacytestservices.test2/android.support.test.runner.AndroidJUnitRunner
+ * com.android.framework.multidexlegacytestservices.test2/androidx.test.runner.AndroidJUnitRunner
  * </code>
  */
 @RunWith(AndroidJUnit4.class)
diff --git a/core/tests/overlaytests/device/Android.mk b/core/tests/overlaytests/device/Android.mk
index 680ebeb..5630749 100644
--- a/core/tests/overlaytests/device/Android.mk
+++ b/core/tests/overlaytests/device/Android.mk
@@ -19,7 +19,7 @@
 LOCAL_MODULE_TAGS := tests
 LOCAL_PACKAGE_NAME := OverlayDeviceTests
 LOCAL_PRIVATE_PLATFORM_APIS := true
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
+LOCAL_STATIC_JAVA_LIBRARIES := androidx.test.rules
 LOCAL_COMPATIBILITY_SUITE := device-tests
 LOCAL_TARGET_REQUIRED_MODULES := \
     OverlayDeviceTests_AppOverlayOne \
diff --git a/core/tests/overlaytests/device/AndroidManifest.xml b/core/tests/overlaytests/device/AndroidManifest.xml
index d14fdf5..4881636 100644
--- a/core/tests/overlaytests/device/AndroidManifest.xml
+++ b/core/tests/overlaytests/device/AndroidManifest.xml
@@ -23,7 +23,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.overlaytest"
         android:label="Runtime resource overlay tests" />
 </manifest>
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 5a86885..91fcdbb 100644
--- a/core/tests/overlaytests/device/src/com/android/overlaytest/OverlayBaseTest.java
+++ b/core/tests/overlaytests/device/src/com/android/overlaytest/OverlayBaseTest.java
@@ -25,10 +25,11 @@
 import android.content.res.XmlResourceParser;
 import android.os.LocaleList;
 import android.os.ParcelFileDescriptor;
-import android.support.test.InstrumentationRegistry;
 import android.util.AttributeSet;
 import android.util.Xml;
 
+import androidx.test.InstrumentationRegistry;
+
 import org.junit.Before;
 import org.junit.Ignore;
 import org.junit.Test;
diff --git a/core/tests/overlaytests/device/src/com/android/overlaytest/WithMultipleOverlaysTest.java b/core/tests/overlaytests/device/src/com/android/overlaytest/WithMultipleOverlaysTest.java
index f35e511..cd3ed9d 100644
--- a/core/tests/overlaytests/device/src/com/android/overlaytest/WithMultipleOverlaysTest.java
+++ b/core/tests/overlaytests/device/src/com/android/overlaytest/WithMultipleOverlaysTest.java
@@ -16,7 +16,7 @@
 
 package com.android.overlaytest;
 
-import android.support.test.filters.MediumTest;
+import androidx.test.filters.MediumTest;
 
 import org.junit.BeforeClass;
 import org.junit.runner.RunWith;
diff --git a/core/tests/overlaytests/device/src/com/android/overlaytest/WithOverlayTest.java b/core/tests/overlaytests/device/src/com/android/overlaytest/WithOverlayTest.java
index 037449f..c0d4281 100644
--- a/core/tests/overlaytests/device/src/com/android/overlaytest/WithOverlayTest.java
+++ b/core/tests/overlaytests/device/src/com/android/overlaytest/WithOverlayTest.java
@@ -16,7 +16,7 @@
 
 package com.android.overlaytest;
 
-import android.support.test.filters.MediumTest;
+import androidx.test.filters.MediumTest;
 
 import org.junit.BeforeClass;
 import org.junit.runner.RunWith;
diff --git a/core/tests/overlaytests/device/src/com/android/overlaytest/WithoutOverlayTest.java b/core/tests/overlaytests/device/src/com/android/overlaytest/WithoutOverlayTest.java
index f657b5c..33c7b25 100644
--- a/core/tests/overlaytests/device/src/com/android/overlaytest/WithoutOverlayTest.java
+++ b/core/tests/overlaytests/device/src/com/android/overlaytest/WithoutOverlayTest.java
@@ -16,7 +16,7 @@
 
 package com.android.overlaytest;
 
-import android.support.test.filters.MediumTest;
+import androidx.test.filters.MediumTest;
 
 import org.junit.BeforeClass;
 import org.junit.runner.RunWith;
diff --git a/data/etc/Android.bp b/data/etc/Android.bp
index 25dabad..035ee10 100644
--- a/data/etc/Android.bp
+++ b/data/etc/Android.bp
@@ -42,6 +42,38 @@
 }
 
 prebuilt_etc {
+    name: "privapp_whitelist_com.android.carrierconfig",
+    product_specific: true,
+    sub_dir: "permissions",
+    src: "com.android.carrierconfig.xml",
+    filename_from_src: true,
+}
+
+prebuilt_etc {
+    name: "privapp_whitelist_com.android.contacts",
+    product_specific: true,
+    sub_dir: "permissions",
+    src: "com.android.contacts.xml",
+    filename_from_src: true,
+}
+
+prebuilt_etc {
+    name: "privapp_whitelist_com.android.launcher3",
+    product_specific: true,
+    sub_dir: "permissions",
+    src: "com.android.launcher3.xml",
+    filename_from_src: true,
+}
+
+prebuilt_etc {
+    name: "privapp_whitelist_com.android.provision",
+    product_specific: true,
+    sub_dir: "permissions",
+    src: "com.android.provision.xml",
+    filename_from_src: true,
+}
+
+prebuilt_etc {
     name: "privapp_whitelist_com.android.settings",
     product_specific: true,
     sub_dir: "permissions",
@@ -50,6 +82,22 @@
 }
 
 prebuilt_etc {
+    name: "privapp_whitelist_com.android.settings.intelligence",
+    product_specific: true,
+    sub_dir: "permissions",
+    src: "com.android.settings.intelligence.xml",
+    filename_from_src: true,
+}
+
+prebuilt_etc {
+    name: "privapp_whitelist_com.android.storagemanager",
+    product_specific: true,
+    sub_dir: "permissions",
+    src: "com.android.storagemanager.xml",
+    filename_from_src: true,
+}
+
+prebuilt_etc {
     name: "privapp_whitelist_com.android.systemui",
     product_specific: true,
     sub_dir: "permissions",
diff --git a/data/etc/com.android.carrierconfig.xml b/data/etc/com.android.carrierconfig.xml
new file mode 100644
index 0000000..17efb03
--- /dev/null
+++ b/data/etc/com.android.carrierconfig.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2019 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+<permissions>
+    <privapp-permissions package="com.android.carrierconfig">
+        <permission name="android.permission.READ_PRIVILEGED_PHONE_STATE"/>
+    </privapp-permissions>
+</permissions>
diff --git a/data/etc/com.android.contacts.xml b/data/etc/com.android.contacts.xml
new file mode 100644
index 0000000..78eae40
--- /dev/null
+++ b/data/etc/com.android.contacts.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2019 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+<permissions>
+    <privapp-permissions package="com.android.contacts">
+        <permission name="android.permission.GET_ACCOUNTS_PRIVILEGED"/>
+        <permission name="com.android.voicemail.permission.READ_VOICEMAIL"/>
+    </privapp-permissions>
+</permissions>
diff --git a/data/etc/com.android.launcher3.xml b/data/etc/com.android.launcher3.xml
new file mode 100644
index 0000000..337e153
--- /dev/null
+++ b/data/etc/com.android.launcher3.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2019 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+<permissions>
+    <privapp-permissions package="com.android.launcher3">
+        <permission name="android.permission.BIND_APPWIDGET"/>
+        <permission name="android.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS"/>
+        <permission name="android.permission.GET_ACCOUNTS_PRIVILEGED"/>
+    </privapp-permissions>
+</permissions>
diff --git a/data/etc/com.android.provision.xml b/data/etc/com.android.provision.xml
new file mode 100644
index 0000000..05404ef
--- /dev/null
+++ b/data/etc/com.android.provision.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2019 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+<permissions>
+    <privapp-permissions package="com.android.provision">
+        <permission name="android.permission.WRITE_SECURE_SETTINGS"/>
+    </privapp-permissions>
+</permissions>
diff --git a/data/etc/com.android.settings.intelligence.xml b/data/etc/com.android.settings.intelligence.xml
new file mode 100644
index 0000000..6ca30c1
--- /dev/null
+++ b/data/etc/com.android.settings.intelligence.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2019 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+<permissions>
+    <privapp-permissions package="com.android.settings.intelligence">
+        <permission name="android.permission.MANAGE_FINGERPRINT"/>
+        <permission name="android.permission.MODIFY_PHONE_STATE"/>
+        <permission name="android.permission.READ_SEARCH_INDEXABLES"/>
+        <permission name="android.permission.WRITE_SETTINGS_HOMEPAGE_DATA"/>
+    </privapp-permissions>
+</permissions>
diff --git a/data/etc/com.android.storagemanager.xml b/data/etc/com.android.storagemanager.xml
new file mode 100644
index 0000000..e85a82c
--- /dev/null
+++ b/data/etc/com.android.storagemanager.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2019 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+<permissions>
+    <privapp-permissions package="com.android.storagemanager">
+        <permission name="android.permission.DELETE_PACKAGES"/>
+        <permission name="android.permission.INTERACT_ACROSS_USERS"/>
+        <permission name="android.permission.MANAGE_USERS"/>
+        <permission name="android.permission.PACKAGE_USAGE_STATS"/>
+        <permission name="android.permission.USE_RESERVED_DISK"/>
+        <permission name="android.permission.WRITE_SECURE_SETTINGS"/>
+    </privapp-permissions>
+</permissions>
diff --git a/data/etc/com.android.systemui.xml b/data/etc/com.android.systemui.xml
index 221708b..3562a8f 100644
--- a/data/etc/com.android.systemui.xml
+++ b/data/etc/com.android.systemui.xml
@@ -44,6 +44,7 @@
         <permission name="android.permission.READ_PRIVILEGED_PHONE_STATE"/>
         <permission name="android.permission.REAL_GET_TASKS"/>
         <permission name="android.permission.RECEIVE_MEDIA_RESOURCE_USAGE"/>
+        <permission name="android.permission.START_ACTIVITIES_FROM_BACKGROUND" />
         <permission name="android.permission.START_ACTIVITY_AS_CALLER"/>
         <permission name="android.permission.START_TASKS_FROM_RECENTS"/>
         <permission name="android.permission.STATUS_BAR"/>
diff --git a/data/etc/platform.xml b/data/etc/platform.xml
index 4a2db0a..fb43e41 100644
--- a/data/etc/platform.xml
+++ b/data/etc/platform.xml
@@ -223,12 +223,12 @@
          code to link against. -->
 
     <library name="android.test.base"
-            file="/system/framework/android.test.base.impl.jar" />
+            file="/system/framework/android.test.base.jar" />
     <library name="android.test.mock"
-            file="/system/framework/android.test.mock.impl.jar"
+            file="/system/framework/android.test.mock.jar"
             dependency="android.test.base" />
     <library name="android.test.runner"
-            file="/system/framework/android.test.runner.impl.jar"
+            file="/system/framework/android.test.runner.jar"
             dependency="android.test.base:android.test.mock" />
 
     <!-- In BOOT_JARS historically, and now added to legacy applications. -->
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index fbe613d..597d14a 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -33,10 +33,6 @@
         <permission name="android.permission.CRYPT_KEEPER"/>
     </privapp-permissions>
 
-    <privapp-permissions package="com.android.carrierconfig">
-        <permission name="android.permission.READ_PRIVILEGED_PHONE_STATE"/>
-    </privapp-permissions>
-
     <privapp-permissions package="com.android.cellbroadcastreceiver">
         <permission name="android.permission.INTERACT_ACROSS_USERS"/>
         <permission name="android.permission.MANAGE_USERS"/>
@@ -45,11 +41,6 @@
         <permission name="android.permission.RECEIVE_EMERGENCY_BROADCAST"/>
     </privapp-permissions>
 
-    <privapp-permissions package="com.android.contacts">
-        <permission name="android.permission.GET_ACCOUNTS_PRIVILEGED"/>
-        <permission name="com.android.voicemail.permission.READ_VOICEMAIL"/>
-    </privapp-permissions>
-
     <privapp-permissions package="com.android.defcontainer">
         <permission name="android.permission.ACCESS_CACHE_FILESYSTEM"/>
         <permission name="android.permission.ALLOCATE_AGGRESSIVE"/>
@@ -79,12 +70,6 @@
         <permission name="android.permission.WRITE_MEDIA_STORAGE"/>
     </privapp-permissions>
 
-    <privapp-permissions package="com.android.launcher3">
-        <permission name="android.permission.BIND_APPWIDGET"/>
-        <permission name="android.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS"/>
-        <permission name="android.permission.GET_ACCOUNTS_PRIVILEGED"/>
-    </privapp-permissions>
-
     <privapp-permissions package="com.android.location.fused">
         <permission name="android.permission.INSTALL_LOCATION_PROVIDER"/>
     </privapp-permissions>
@@ -238,10 +223,6 @@
         <permission name="android.permission.USE_RESERVED_DISK"/>
     </privapp-permissions>
 
-    <privapp-permissions package="com.android.provision">
-        <permission name="android.permission.WRITE_SECURE_SETTINGS"/>
-    </privapp-permissions>
-
     <privapp-permissions package="com.android.mainline.networkstack">
         <permission name="android.permission.ACCESS_NETWORK_CONDITIONS"/>
         <permission name="android.permission.CHANGE_BACKGROUND_DATA_SETTING"/>
@@ -280,13 +261,6 @@
         <permission name="android.permission.SUBSTITUTE_NOTIFICATION_APP_NAME"/>
     </privapp-permissions>
 
-    <privapp-permissions package="com.android.settings.intelligence">
-        <permission name="android.permission.MANAGE_FINGERPRINT"/>
-        <permission name="android.permission.MODIFY_PHONE_STATE"/>
-        <permission name="android.permission.READ_SEARCH_INDEXABLES"/>
-        <permission name="android.permission.WRITE_SETTINGS_HOMEPAGE_DATA"/>
-    </privapp-permissions>
-
     <privapp-permissions package="com.android.sharedstoragebackup">
         <permission name="android.permission.WRITE_MEDIA_STORAGE"/>
     </privapp-permissions>
@@ -342,6 +316,7 @@
         <permission name="android.permission.SET_TIME"/>
         <permission name="android.permission.SET_TIME_ZONE"/>
         <permission name="android.permission.SIGNAL_PERSISTENT_PROCESSES"/>
+        <permission name="android.permission.START_ACTIVITIES_FROM_BACKGROUND" />
         <permission name="android.permission.START_TASKS_FROM_RECENTS" />
         <permission name="android.permission.STOP_APP_SWITCHES"/>
         <permission name="android.permission.SUBSTITUTE_NOTIFICATION_APP_NAME"/>
@@ -356,15 +331,6 @@
         <permission name="android.permission.INTENT_FILTER_VERIFICATION_AGENT"/>
     </privapp-permissions>
 
-    <privapp-permissions package="com.android.storagemanager">
-        <permission name="android.permission.DELETE_PACKAGES"/>
-        <permission name="android.permission.INTERACT_ACROSS_USERS"/>
-        <permission name="android.permission.MANAGE_USERS"/>
-        <permission name="android.permission.PACKAGE_USAGE_STATS"/>
-        <permission name="android.permission.USE_RESERVED_DISK"/>
-        <permission name="android.permission.WRITE_SECURE_SETTINGS"/>
-    </privapp-permissions>
-
     <privapp-permissions package="com.android.tv">
         <permission name="android.permission.CHANGE_HDMI_CEC_ACTIVE_SOURCE"/>
         <permission name="android.permission.DVB_DEVICE"/>
diff --git a/graphics/java/android/graphics/BaseCanvas.java b/graphics/java/android/graphics/BaseCanvas.java
index ca9dc47..65aaba1 100644
--- a/graphics/java/android/graphics/BaseCanvas.java
+++ b/graphics/java/android/graphics/BaseCanvas.java
@@ -22,6 +22,7 @@
 import android.annotation.Size;
 import android.annotation.UnsupportedAppUsage;
 import android.graphics.Canvas.VertexMode;
+import android.graphics.text.MeasuredText;
 import android.text.GraphicsOperations;
 import android.text.MeasuredParagraph;
 import android.text.PrecomputedText;
@@ -554,14 +555,12 @@
                     final int paraStart = pt.getParagraphStart(paraIndex);
                     final MeasuredParagraph mp = pt.getMeasuredParagraph(paraIndex);
                     // Only support the text in the same paragraph.
-                    nDrawTextRun(mNativeCanvasWrapper,
-                            mp.getChars(),
-                            start - paraStart,
-                            end - start,
-                            contextStart - paraStart,
-                            contextEnd - contextStart,
-                            x, y, isRtl, paint.getNativeInstance(),
-                            mp.getMeasuredText().getNativePtr());
+                    drawTextRun(mp.getMeasuredText(),
+                                start - paraStart,
+                                end - paraStart,
+                                contextStart - paraStart,
+                                contextEnd - paraStart,
+                                x, y, isRtl, paint);
                     return;
                 }
             }
@@ -576,6 +575,14 @@
         }
     }
 
+    public void drawTextRun(@NonNull MeasuredText measuredText, int start, int end,
+            int contextStart, int contextEnd, float x, float y, boolean isRtl,
+            @NonNull Paint paint) {
+        nDrawTextRun(mNativeCanvasWrapper, measuredText.getChars(), start, end - start,
+                contextStart, contextEnd - contextStart, x, y, isRtl, paint.getNativeInstance(),
+                measuredText.getNativePtr());
+    }
+
     public void drawVertices(@NonNull VertexMode mode, int vertexCount, @NonNull float[] verts,
             int vertOffset, @Nullable float[] texs, int texOffset, @Nullable int[] colors,
             int colorOffset, @Nullable short[] indices, int indexOffset, int indexCount,
diff --git a/graphics/java/android/graphics/BaseRecordingCanvas.java b/graphics/java/android/graphics/BaseRecordingCanvas.java
index 901c211..4f60935 100644
--- a/graphics/java/android/graphics/BaseRecordingCanvas.java
+++ b/graphics/java/android/graphics/BaseRecordingCanvas.java
@@ -20,6 +20,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.Size;
+import android.graphics.text.MeasuredText;
 import android.text.GraphicsOperations;
 import android.text.MeasuredParagraph;
 import android.text.PrecomputedText;
@@ -522,14 +523,12 @@
                     final int paraStart = pt.getParagraphStart(paraIndex);
                     final MeasuredParagraph mp = pt.getMeasuredParagraph(paraIndex);
                     // Only support if the target is in the same paragraph.
-                    nDrawTextRun(mNativeCanvasWrapper,
-                            mp.getChars(),
+                    drawTextRun(mp.getMeasuredText(),
                             start - paraStart,
-                            end - start,
+                            end - paraStart,
                             contextStart - paraStart,
-                            contextEnd - contextStart,
-                            x, y, isRtl, paint.getNativeInstance(),
-                            mp.getMeasuredText().getNativePtr());
+                            contextEnd - paraStart,
+                            x, y, isRtl, paint);
                     return;
                 }
             }
@@ -545,6 +544,15 @@
     }
 
     @Override
+    public void drawTextRun(@NonNull MeasuredText measuredText, int start, int end,
+            int contextStart, int contextEnd, float x, float y, boolean isRtl,
+            @NonNull Paint paint) {
+        nDrawTextRun(mNativeCanvasWrapper, measuredText.getChars(), start, end - start,
+                contextStart, contextEnd - contextStart, x, y, isRtl, paint.getNativeInstance(),
+                measuredText.getNativePtr());
+    }
+
+    @Override
     public final void drawVertices(@NonNull VertexMode mode, int vertexCount,
             @NonNull float[] verts, int vertOffset, @Nullable float[] texs, int texOffset,
             @Nullable int[] colors, int colorOffset, @Nullable short[] indices, int indexOffset,
diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java
index 790b37e..30f0bfa 100644
--- a/graphics/java/android/graphics/Bitmap.java
+++ b/graphics/java/android/graphics/Bitmap.java
@@ -18,9 +18,11 @@
 
 import android.annotation.CheckResult;
 import android.annotation.ColorInt;
+import android.annotation.ColorLong;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.Size;
+import android.annotation.TestApi;
 import android.annotation.UnsupportedAppUsage;
 import android.annotation.WorkerThread;
 import android.content.res.ResourcesImpl;
@@ -1780,6 +1782,30 @@
     }
 
     /**
+     * Fills the bitmap's pixels with the specified {@link Color}.
+     *
+     * @throws IllegalStateException if the bitmap is not mutable.
+     * @throws IllegalArgumentException if the color space encoded in the long
+     *                                  is invalid or unknown.
+     *
+     * @hide pending API approval
+     */
+    @TestApi
+    public void eraseColor(@ColorLong long c) {
+        checkRecycled("Can't erase a recycled bitmap");
+        if (!isMutable()) {
+            throw new IllegalStateException("cannot erase immutable bitmaps");
+        }
+
+        ColorSpace cs = Color.colorSpace(c);
+        float r = Color.red(c);
+        float g = Color.green(c);
+        float b = Color.blue(c);
+        float a = Color.alpha(c);
+        nativeErase(mNativePtr, cs, r, g, b, a);
+    }
+
+    /**
      * Returns the {@link Color} at the specified location. Throws an exception
      * if x or y are out of bounds (negative or >= to the width or height
      * respectively). The returned color is a non-premultiplied ARGB value in
@@ -2123,6 +2149,8 @@
                                             int quality, OutputStream stream,
                                             byte[] tempStorage);
     private static native void nativeErase(long nativeBitmap, int color);
+    private static native void nativeErase(long nativeBitmap, ColorSpace cs,
+                                           float r, float g, float b, float a);
     private static native int nativeRowBytes(long nativeBitmap);
     private static native int nativeConfig(long nativeBitmap);
 
diff --git a/graphics/java/android/graphics/Canvas.java b/graphics/java/android/graphics/Canvas.java
index 63a806e..8c1bae2 100644
--- a/graphics/java/android/graphics/Canvas.java
+++ b/graphics/java/android/graphics/Canvas.java
@@ -22,6 +22,7 @@
 import android.annotation.Nullable;
 import android.annotation.Size;
 import android.annotation.UnsupportedAppUsage;
+import android.graphics.text.MeasuredText;
 import android.os.Build;
 
 import dalvik.annotation.optimization.CriticalNative;
@@ -2122,7 +2123,8 @@
      * the text next to it.
      * <p>
      * All text outside the range {@code contextStart..contextEnd} is ignored. The text between
-     * {@code start} and {@code end} will be laid out and drawn.
+     * {@code start} and {@code end} will be laid out and drawn. The context range is useful for
+     * contextual shaping, e.g. Kerning, Arabic contextural form.
      * <p>
      * The direction of the run is explicitly specified by {@code isRtl}. Thus, this method is
      * suitable only for runs of a single direction. Alignment of the text is as determined by the
@@ -2151,6 +2153,31 @@
     }
 
     /**
+     * Draw a run of text, all in a single direction, with optional context for complex text
+     * shaping.
+     * <p>
+     * See {@link #drawTextRun(CharSequence, int, int, int, int, float, float, boolean, Paint)} for
+     * more details. This method uses a {@link MeasuredText} rather than CharSequence to represent
+     * the string.
+     *
+     * @param text the text to render
+     * @param start the start of the text to render. Data before this position can be used for
+     *            shaping context.
+     * @param end the end of the text to render. Data at or after this position can be used for
+     *            shaping context.
+     * @param contextStart the index of the start of the shaping context
+     * @param contextEnd the index of the end of the shaping context
+     * @param x the x position at which to draw the text
+     * @param y the y position at which to draw the text
+     * @param isRtl whether the run is in RTL direction
+     * @param paint the paint
+     */
+    public void drawTextRun(@NonNull MeasuredText text, int start, int end, int contextStart,
+            int contextEnd, float x, float y, boolean isRtl, @NonNull Paint paint) {
+        super.drawTextRun(text, start, end, contextStart, contextEnd, x, y, isRtl, paint);
+    }
+
+    /**
      * Draw the array of vertices, interpreted as triangles (based on mode). The verts array is
      * required, and specifies the x,y pairs for each vertex. If texs is non-null, then it is used
      * to specify the coordinate in shader coordinates to use at each vertex (the paint must have a
diff --git a/graphics/java/android/graphics/ColorSpace.java b/graphics/java/android/graphics/ColorSpace.java
index f0efb58..9fa70a5 100644
--- a/graphics/java/android/graphics/ColorSpace.java
+++ b/graphics/java/android/graphics/ColorSpace.java
@@ -984,11 +984,12 @@
      *         {@link Named#SRGB sRGB} primaries.
      *     </li>
      *     <li>
-     *         Its white point is withing 1e-3 of the CIE standard
+     *         Its white point is within 1e-3 of the CIE standard
      *         illuminant {@link #ILLUMINANT_D65 D65}.
      *     </li>
      *     <li>Its opto-electronic transfer function is not linear.</li>
      *     <li>Its electro-optical transfer function is not linear.</li>
+     *     <li>Its transfer functions yield values within 1e-3 of {@link Named#SRGB}.</li>
      *     <li>Its range is \([0..1]\).</li>
      * </ul>
      * <p>This method always returns true for {@link Named#SRGB}.</p>
@@ -1660,10 +1661,12 @@
      * @param rhs 3x3 matrix, as a non-null array of 9 floats
      * @return A new array of 9 floats containing the result of the multiplication
      *         of rhs by lhs
+     *
+     * @hide
      */
     @NonNull
     @Size(9)
-    private static float[] mul3x3(@NonNull @Size(9) float[] lhs, @NonNull @Size(9) float[] rhs) {
+    public static float[] mul3x3(@NonNull @Size(9) float[] lhs, @NonNull @Size(9) float[] rhs) {
         float[] r = new float[9];
         r[0] = lhs[0] * rhs[0] + lhs[3] * rhs[1] + lhs[6] * rhs[2];
         r[1] = lhs[1] * rhs[0] + lhs[4] * rhs[1] + lhs[7] * rhs[2];
@@ -3145,19 +3148,35 @@
                 float max,
                 @IntRange(from = MIN_ID, to = MAX_ID) int id) {
             if (id == 0) return true;
-            if (!compare(primaries, SRGB_PRIMARIES)) {
+            if (!ColorSpace.compare(primaries, SRGB_PRIMARIES)) {
                 return false;
             }
-            if (!compare(whitePoint, ILLUMINANT_D65)) {
+            if (!ColorSpace.compare(whitePoint, ILLUMINANT_D65)) {
                 return false;
             }
-            if (OETF.applyAsDouble(0.5) < 0.5001) return false;
-            if (EOTF.applyAsDouble(0.5) > 0.5001) return false;
+
             if (min != 0.0f) return false;
             if (max != 1.0f) return false;
+
+            // We would have already returned true if this was SRGB itself, so
+            // it is safe to reference it here.
+            ColorSpace.Rgb srgb = (ColorSpace.Rgb) get(Named.SRGB);
+
+            for (double x = 0.0; x <= 1.0; x += 1 / 255.0) {
+                if (!compare(x, OETF, srgb.mOetf)) return false;
+                if (!compare(x, EOTF, srgb.mEotf)) return false;
+            }
+
             return true;
         }
 
+        private static boolean compare(double point, @NonNull DoubleUnaryOperator a,
+                @NonNull DoubleUnaryOperator b) {
+            double rA = a.applyAsDouble(point);
+            double rB = b.applyAsDouble(point);
+            return Math.abs(rA - rB) <= 1e-3;
+        }
+
         /**
          * Computes whether the specified CIE xyY or XYZ primaries (with Y set to 1) form
          * a wide color gamut. A color gamut is considered wide if its area is &gt; 90%
diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java
index 3342fd2..cbb780d 100644
--- a/graphics/java/android/graphics/Paint.java
+++ b/graphics/java/android/graphics/Paint.java
@@ -17,12 +17,14 @@
 package android.graphics;
 
 import android.annotation.ColorInt;
+import android.annotation.ColorLong;
 import android.annotation.IntDef;
 import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.Px;
 import android.annotation.Size;
+import android.annotation.TestApi;
 import android.annotation.UnsupportedAppUsage;
 import android.graphics.fonts.FontVariationAxis;
 import android.os.Build;
@@ -972,6 +974,31 @@
     }
 
     /**
+     * Set the paint's color with a {@link ColorLong}. Note that the color is
+     * a long with an encoded {@link ColorSpace} as well as alpha and r,g,b.
+     * These values are not premultiplied, meaning that alpha can be any value,
+     * regardless of the values of r,g,b. See the {@link Color} class for more
+     * details.
+     *
+     * @param color The new color (including alpha and {@link ColorSpace})
+     *      to set in the paint.
+     * @throws IllegalArgumentException if the color space encoded in the long
+     *      is invalid or unknown.
+     *
+     * @hide pending API approval
+     */
+    @TestApi
+    public void setColor(@ColorLong long color) {
+        ColorSpace cs = Color.colorSpace(color);
+        float r = Color.red(color);
+        float g = Color.green(color);
+        float b = Color.blue(color);
+        float a = Color.alpha(color);
+
+        nSetColor(mNativePaint, cs, r, g, b, a);
+    }
+
+    /**
      * Helper to getColor() that just returns the color's alpha value. This is
      * the same as calling getColor() >>> 24. It always returns a value between
      * 0 (completely transparent) and 255 (completely opaque).
@@ -1370,12 +1397,45 @@
      * The alpha of the shadow will be the paint's alpha if the shadow color is
      * opaque, or the alpha from the shadow color if not.
      */
-    public void setShadowLayer(float radius, float dx, float dy, int shadowColor) {
-      mShadowLayerRadius = radius;
-      mShadowLayerDx = dx;
-      mShadowLayerDy = dy;
-      mShadowLayerColor = shadowColor;
-      nSetShadowLayer(mNativePaint, radius, dx, dy, shadowColor);
+    public void setShadowLayer(float radius, float dx, float dy, @ColorInt int shadowColor) {
+        mShadowLayerRadius = radius;
+        mShadowLayerDx = dx;
+        mShadowLayerDy = dy;
+        mShadowLayerColor = shadowColor;
+        // FIXME: Share a single native method with the ColorLong version.
+        nSetShadowLayer(mNativePaint, radius, dx, dy, shadowColor);
+    }
+
+    /**
+     * This draws a shadow layer below the main layer, with the specified
+     * offset and color, and blur radius. If radius is 0, then the shadow
+     * layer is removed.
+     * <p>
+     * Can be used to create a blurred shadow underneath text. Support for use
+     * with other drawing operations is constrained to the software rendering
+     * pipeline.
+     * <p>
+     * The alpha of the shadow will be the paint's alpha if the shadow color is
+     * opaque, or the alpha from the shadow color if not.
+     *
+     * @throws IllegalArgumentException if the color space encoded in the long
+     *      is invalid or unknown.
+     *
+     * @hide pending API approval
+     */
+    @TestApi
+    public void setShadowLayer(float radius, float dx, float dy, @ColorLong long shadowColor) {
+        ColorSpace cs = Color.colorSpace(shadowColor);
+        float r = Color.red(shadowColor);
+        float g = Color.green(shadowColor);
+        float b = Color.blue(shadowColor);
+        float a = Color.alpha(shadowColor);
+        nSetShadowLayer(mNativePaint, radius, dx, dy, cs, r, g, b, a);
+
+        mShadowLayerRadius = radius;
+        mShadowLayerDx = dx;
+        mShadowLayerDy = dy;
+        mShadowLayerColor = Color.toArgb(shadowColor);
     }
 
     /**
@@ -2906,6 +2966,11 @@
             int contextStart, int contextEnd, boolean isRtl, int offset);
     private static native int nGetOffsetForAdvance(long paintPtr, char[] text, int start, int end,
             int contextStart, int contextEnd, boolean isRtl, float advance);
+    private static native void nSetColor(long paintPtr, ColorSpace cs,
+            float r, float g, float b, float a);
+    private static native void nSetShadowLayer(long paintPtr,
+            float radius, float dx, float dy, ColorSpace cs,
+            float r, float g, float b, float a);
 
 
     // ---------------- @FastNative ------------------------
@@ -2961,7 +3026,7 @@
             int mMinikinLocaleListId);
     @CriticalNative
     private static native void nSetShadowLayer(long paintPtr,
-            float radius, float dx, float dy, int color);
+            float radius, float dx, float dy, @ColorInt int color);
     @CriticalNative
     private static native boolean nHasShadowLayer(long paintPtr);
     @CriticalNative
diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp
index ad9ec02..3c35d9b 100644
--- a/libs/androidfw/AssetManager2.cpp
+++ b/libs/androidfw/AssetManager2.cpp
@@ -20,8 +20,9 @@
 
 #include <algorithm>
 #include <iterator>
-#include <set>
 #include <map>
+#include <set>
+#include <sstream>
 
 #include "android-base/logging.h"
 #include "android-base/stringprintf.h"
@@ -372,6 +373,9 @@
   uint32_t best_offset = 0u;
   uint32_t type_flags = 0u;
 
+  Resolution::Step::Type resolution_type;
+  std::vector<Resolution::Step> resolution_steps;
+
   // If desired_config is the same as the set configuration, then we can use our filtered list
   // and we don't need to match the configurations, since they already matched.
   const bool use_fast_path = desired_config == &configuration_;
@@ -403,8 +407,8 @@
     // If the package is an overlay, then even configurations that are the same MUST be chosen.
     const bool package_is_overlay = loaded_package->IsOverlay();
 
-    const FilteredConfigGroup& filtered_group = loaded_package_impl.filtered_configs_[type_idx];
     if (use_fast_path) {
+      const FilteredConfigGroup& filtered_group = loaded_package_impl.filtered_configs_[type_idx];
       const std::vector<ResTable_config>& candidate_configs = filtered_group.configurations;
       const size_t type_count = candidate_configs.size();
       for (uint32_t i = 0; i < type_count; i++) {
@@ -412,21 +416,34 @@
 
         // We can skip calling ResTable_config::match() because we know that all candidate
         // configurations that do NOT match have been filtered-out.
-        if ((best_config == nullptr || this_config.isBetterThan(*best_config, desired_config)) ||
-            (package_is_overlay && this_config.compare(*best_config) == 0)) {
-          // The configuration matches and is better than the previous selection.
-          // Find the entry value if it exists for this configuration.
-          const ResTable_type* type_chunk = filtered_group.types[i];
-          const uint32_t offset = LoadedPackage::GetEntryOffset(type_chunk, local_entry_idx);
-          if (offset == ResTable_type::NO_ENTRY) {
-            continue;
-          }
+        if (best_config == nullptr) {
+          resolution_type = Resolution::Step::Type::INITIAL;
+        } else if (this_config.isBetterThan(*best_config, desired_config)) {
+          resolution_type = Resolution::Step::Type::BETTER_MATCH;
+        } else if (package_is_overlay && this_config.compare(*best_config) == 0) {
+          resolution_type = Resolution::Step::Type::OVERLAID;
+        } else {
+          continue;
+        }
 
-          best_cookie = cookie;
-          best_package = loaded_package;
-          best_type = type_chunk;
-          best_config = &this_config;
-          best_offset = offset;
+        // The configuration matches and is better than the previous selection.
+        // Find the entry value if it exists for this configuration.
+        const ResTable_type* type = filtered_group.types[i];
+        const uint32_t offset = LoadedPackage::GetEntryOffset(type, local_entry_idx);
+        if (offset == ResTable_type::NO_ENTRY) {
+          continue;
+        }
+
+        best_cookie = cookie;
+        best_package = loaded_package;
+        best_type = type;
+        best_config = &this_config;
+        best_offset = offset;
+
+        if (resource_resolution_logging_enabled_) {
+          resolution_steps.push_back(Resolution::Step{resolution_type,
+                                                      this_config.toString(),
+                                                      &loaded_package->GetPackageName()});
         }
       }
     } else {
@@ -440,23 +457,38 @@
         ResTable_config this_config;
         this_config.copyFromDtoH((*iter)->config);
 
-        if (this_config.match(*desired_config)) {
-          if ((best_config == nullptr || this_config.isBetterThan(*best_config, desired_config)) ||
-              (package_is_overlay && this_config.compare(*best_config) == 0)) {
-            // The configuration matches and is better than the previous selection.
-            // Find the entry value if it exists for this configuration.
-            const uint32_t offset = LoadedPackage::GetEntryOffset(*iter, local_entry_idx);
-            if (offset == ResTable_type::NO_ENTRY) {
-              continue;
-            }
+        if (!this_config.match(*desired_config)) {
+          continue;
+        }
 
-            best_cookie = cookie;
-            best_package = loaded_package;
-            best_type = *iter;
-            best_config_copy = this_config;
-            best_config = &best_config_copy;
-            best_offset = offset;
-          }
+        if (best_config == nullptr) {
+          resolution_type = Resolution::Step::Type::INITIAL;
+        } else if (this_config.isBetterThan(*best_config, desired_config)) {
+          resolution_type = Resolution::Step::Type::BETTER_MATCH;
+        } else if (package_is_overlay && this_config.compare(*best_config) == 0) {
+          resolution_type = Resolution::Step::Type::OVERLAID;
+        } else {
+          continue;
+        }
+
+        // The configuration matches and is better than the previous selection.
+        // Find the entry value if it exists for this configuration.
+        const uint32_t offset = LoadedPackage::GetEntryOffset(*iter, local_entry_idx);
+        if (offset == ResTable_type::NO_ENTRY) {
+          continue;
+        }
+
+        best_cookie = cookie;
+        best_package = loaded_package;
+        best_type = *iter;
+        best_config_copy = this_config;
+        best_config = &best_config_copy;
+        best_offset = offset;
+
+        if (resource_resolution_logging_enabled_) {
+          resolution_steps.push_back(Resolution::Step{resolution_type,
+                                                      this_config.toString(),
+                                                      &loaded_package->GetPackageName()});
         }
       }
     }
@@ -478,9 +510,95 @@
   out_entry->entry_string_ref =
       StringPoolRef(best_package->GetKeyStringPool(), best_entry->key.index);
   out_entry->dynamic_ref_table = &package_group.dynamic_ref_table;
+
+  if (resource_resolution_logging_enabled_) {
+    last_resolution.resid = resid;
+    last_resolution.cookie = best_cookie;
+    last_resolution.steps = resolution_steps;
+
+    // Cache only the type/entry refs since that's all that's needed to build name
+    last_resolution.type_string_ref =
+        StringPoolRef(best_package->GetTypeStringPool(), best_type->id - 1);
+    last_resolution.entry_string_ref =
+        StringPoolRef(best_package->GetKeyStringPool(), best_entry->key.index);
+  }
+
   return best_cookie;
 }
 
+void AssetManager2::SetResourceResolutionLoggingEnabled(bool enabled) {
+  resource_resolution_logging_enabled_ = enabled;
+
+  if (!enabled) {
+    last_resolution.cookie = kInvalidCookie;
+    last_resolution.resid = 0;
+    last_resolution.steps.clear();
+    last_resolution.type_string_ref = StringPoolRef();
+    last_resolution.entry_string_ref = StringPoolRef();
+  }
+}
+
+std::string AssetManager2::GetLastResourceResolution() const {
+  if (!resource_resolution_logging_enabled_) {
+    LOG(ERROR) << "Must enable resource resolution logging before getting path.";
+    return std::string();
+  }
+
+  auto cookie = last_resolution.cookie;
+  if (cookie == kInvalidCookie) {
+    LOG(ERROR) << "AssetManager hasn't resolved a resource to read resolution path.";
+    return std::string();
+  }
+
+  uint32_t resid = last_resolution.resid;
+  std::vector<Resolution::Step>& steps = last_resolution.steps;
+
+  ResourceName resource_name;
+  std::string resource_name_string;
+
+  const LoadedPackage* package =
+      apk_assets_[cookie]->GetLoadedArsc()->GetPackageById(get_package_id(resid));
+
+  if (package != nullptr) {
+    ToResourceName(last_resolution.type_string_ref,
+                   last_resolution.entry_string_ref,
+                   package,
+                   &resource_name);
+    resource_name_string = ToFormattedResourceString(&resource_name);
+  }
+
+  std::stringstream log_stream;
+  log_stream << base::StringPrintf("Resolution for 0x%08x ", resid)
+            << resource_name_string
+            << "\n\tFor config -"
+            << configuration_.toString();
+
+  std::string prefix;
+  for (Resolution::Step step : steps) {
+    switch (step.type) {
+      case Resolution::Step::Type::INITIAL:
+        prefix = "Found initial";
+        break;
+      case Resolution::Step::Type::BETTER_MATCH:
+        prefix = "Found better";
+        break;
+      case Resolution::Step::Type::OVERLAID:
+        prefix = "Overlaid";
+        break;
+    }
+
+    if (!prefix.empty()) {
+      log_stream << "\n\t" << prefix << ": " << *step.package_name;
+
+      if (!step.config_name.isEmpty()) {
+        log_stream << " -" << step.config_name;
+      }
+    }
+  }
+
+  return log_stream.str();
+}
+
 bool AssetManager2::GetResourceName(uint32_t resid, ResourceName* out_name) const {
   FindEntryResult entry;
   ApkAssetsCookie cookie =
@@ -495,27 +613,10 @@
     return false;
   }
 
-  out_name->package = package->GetPackageName().data();
-  out_name->package_len = package->GetPackageName().size();
-
-  out_name->type = entry.type_string_ref.string8(&out_name->type_len);
-  out_name->type16 = nullptr;
-  if (out_name->type == nullptr) {
-    out_name->type16 = entry.type_string_ref.string16(&out_name->type_len);
-    if (out_name->type16 == nullptr) {
-      return false;
-    }
-  }
-
-  out_name->entry = entry.entry_string_ref.string8(&out_name->entry_len);
-  out_name->entry16 = nullptr;
-  if (out_name->entry == nullptr) {
-    out_name->entry16 = entry.entry_string_ref.string16(&out_name->entry_len);
-    if (out_name->entry16 == nullptr) {
-      return false;
-    }
-  }
-  return true;
+  return ToResourceName(entry.type_string_ref,
+                        entry.entry_string_ref,
+                        package,
+                        out_name);
 }
 
 bool AssetManager2::GetResourceFlags(uint32_t resid, uint32_t* out_flags) const {
diff --git a/libs/androidfw/AttributeResolution.cpp b/libs/androidfw/AttributeResolution.cpp
index 57e3491..18d74ef 100644
--- a/libs/androidfw/AttributeResolution.cpp
+++ b/libs/androidfw/AttributeResolution.cpp
@@ -51,7 +51,7 @@
 class BagAttributeFinder
     : public BackTrackingAttributeFinder<BagAttributeFinder, const ResolvedBag::Entry*> {
  public:
-  BagAttributeFinder(const ResolvedBag* bag)
+  explicit BagAttributeFinder(const ResolvedBag* bag)
       : BackTrackingAttributeFinder(bag != nullptr ? bag->entries : nullptr,
                                     bag != nullptr ? bag->entries + bag->entry_count : nullptr) {
   }
@@ -286,6 +286,7 @@
     value.dataType = Res_value::TYPE_NULL;
     value.data = Res_value::DATA_NULL_UNDEFINED;
     config.density = 0;
+    uint32_t source_style_resid = 0;
 
     // Try to find a value for this attribute...  we prioritize values
     // coming from, first XML attributes, then XML style, then default
@@ -309,6 +310,7 @@
         cookie = entry->cookie;
         type_set_flags = style_flags;
         value = entry->value;
+        source_style_resid = entry->style;
         if (kDebugStyles) {
           ALOGI("-> From style: type=0x%x, data=0x%08x, style=0x%08x", value.dataType, value.data,
               entry->style);
@@ -325,8 +327,10 @@
         type_set_flags = def_style_flags;
         value = entry->value;
         if (kDebugStyles) {
-          ALOGI("-> From def style: type=0x%x, data=0x%08x", value.dataType, value.data);
+          ALOGI("-> From def style: type=0x%x, data=0x%08x, style=0x%08x", value.dataType, value.data,
+              entry->style);
         }
+        source_style_resid = entry->style;
       }
     }
 
@@ -382,6 +386,7 @@
     out_values[STYLE_RESOURCE_ID] = resid;
     out_values[STYLE_CHANGING_CONFIGURATIONS] = type_set_flags;
     out_values[STYLE_DENSITY] = config.density;
+    out_values[SYTLE_SOURCE_STYLE] = source_style_resid;
 
     if (value.dataType != Res_value::TYPE_NULL || value.data == Res_value::DATA_NULL_EMPTY) {
       indices_idx++;
diff --git a/libs/androidfw/CursorWindow.cpp b/libs/androidfw/CursorWindow.cpp
index 5694115..a99e77f 100644
--- a/libs/androidfw/CursorWindow.cpp
+++ b/libs/androidfw/CursorWindow.cpp
@@ -94,7 +94,7 @@
         if (size < 0) {
             result = UNKNOWN_ERROR;
         } else {
-            int dupAshmemFd = ::dup(ashmemFd);
+            int dupAshmemFd = ::fcntl(ashmemFd, F_DUPFD_CLOEXEC, 0);
             if (dupAshmemFd < 0) {
                 result = -errno;
             } else {
diff --git a/libs/androidfw/LoadedArsc.cpp b/libs/androidfw/LoadedArsc.cpp
index 5a26780..70ce9bc 100644
--- a/libs/androidfw/LoadedArsc.cpp
+++ b/libs/androidfw/LoadedArsc.cpp
@@ -593,7 +593,12 @@
           return {};
         }
 
-        // Iterate over the overlayable policy chunks
+        std::string name;
+        util::ReadUtf16StringFromDevice(header->name, arraysize(header->name), &name);
+        std::string actor;
+        util::ReadUtf16StringFromDevice(header->actor, arraysize(header->actor), &actor);
+
+        // Iterate over the overlayable policy chunks contained within the overlayable chunk data
         ChunkIterator overlayable_iter(child_chunk.data_ptr(), child_chunk.data_size());
         while (overlayable_iter.HasNext()) {
           const Chunk overlayable_child_chunk = overlayable_iter.Next();
@@ -613,7 +618,7 @@
                 return {};
               }
 
-              // Retrieve all the ids belonging to this policy
+              // Retrieve all the resource ids belonging to this policy chunk
               std::unordered_set<uint32_t> ids;
               const auto ids_begin =
                   reinterpret_cast<const ResTable_ref*>(overlayable_child_chunk.data_ptr());
@@ -622,8 +627,10 @@
                 ids.insert(dtohl(id_iter->ident));
               }
 
-              // Add the pairing of overlayable properties to resource ids to the package
+              // Add the pairing of overlayable properties and resource ids to the package
               OverlayableInfo overlayable_info{};
+              overlayable_info.name = name;
+              overlayable_info.actor = actor;
               overlayable_info.policy_flags = policy_header->policy_flags;
               loaded_package->overlayable_infos_.push_back(std::make_pair(overlayable_info, ids));
               break;
@@ -636,7 +643,7 @@
         }
 
         if (overlayable_iter.HadError()) {
-          LOG(ERROR) << StringPrintf("Error parsing RES_TABLE_OVERLAYABLE_POLICY_TYPE: %s",
+          LOG(ERROR) << StringPrintf("Error parsing RES_TABLE_OVERLAYABLE_TYPE: %s",
                                      overlayable_iter.GetLastError().c_str());
           if (overlayable_iter.HadFatalError()) {
             return {};
diff --git a/libs/androidfw/ResourceUtils.cpp b/libs/androidfw/ResourceUtils.cpp
index d63feb01..645984d 100644
--- a/libs/androidfw/ResourceUtils.cpp
+++ b/libs/androidfw/ResourceUtils.cpp
@@ -48,4 +48,65 @@
          !(has_type_separator && out_type->empty());
 }
 
+bool ToResourceName(StringPoolRef& type_string_ref,
+                    StringPoolRef& entry_string_ref,
+                    const LoadedPackage* package,
+                    AssetManager2::ResourceName* out_name) {
+  out_name->package = package->GetPackageName().data();
+  out_name->package_len = package->GetPackageName().size();
+
+  out_name->type = type_string_ref.string8(&out_name->type_len);
+  out_name->type16 = nullptr;
+  if (out_name->type == nullptr) {
+    out_name->type16 = type_string_ref.string16(&out_name->type_len);
+    if (out_name->type16 == nullptr) {
+      return false;
+    }
+  }
+
+  out_name->entry = entry_string_ref.string8(&out_name->entry_len);
+  out_name->entry16 = nullptr;
+  if (out_name->entry == nullptr) {
+    out_name->entry16 = entry_string_ref.string16(&out_name->entry_len);
+    if (out_name->entry16 == nullptr) {
+      return false;
+    }
+  }
+
+  return true;
+}
+
+std::string ToFormattedResourceString(AssetManager2::ResourceName* resource_name) {
+  std::string result;
+  if (resource_name->package != nullptr) {
+    result.append(resource_name->package, resource_name->package_len);
+  }
+
+  if (resource_name->type != nullptr || resource_name->type16 != nullptr) {
+    if (!result.empty()) {
+      result += ":";
+    }
+
+    if (resource_name->type != nullptr) {
+      result.append(resource_name->type, resource_name->type_len);
+    } else {
+      result += util::Utf16ToUtf8(StringPiece16(resource_name->type16, resource_name->type_len));
+    }
+  }
+
+  if (resource_name->entry != nullptr || resource_name->entry16 != nullptr) {
+    if (!result.empty()) {
+      result += "/";
+    }
+
+    if (resource_name->entry != nullptr) {
+      result.append(resource_name->entry, resource_name->entry_len);
+    } else {
+      result += util::Utf16ToUtf8(StringPiece16(resource_name->entry16, resource_name->entry_len));
+    }
+  }
+
+  return result;
+}
+
 }  // namespace android
diff --git a/libs/androidfw/ZipUtils.cpp b/libs/androidfw/ZipUtils.cpp
index 5d243da..5be2105 100644
--- a/libs/androidfw/ZipUtils.cpp
+++ b/libs/androidfw/ZipUtils.cpp
@@ -37,7 +37,7 @@
 // TODO: This can go away once the only remaining usage in aapt goes away.
 class FileReader : public zip_archive::Reader {
   public:
-    FileReader(FILE* fp) : Reader(), mFp(fp), mCurrentOffset(0) {
+    explicit FileReader(FILE* fp) : Reader(), mFp(fp), mCurrentOffset(0) {
     }
 
     bool ReadAtOffset(uint8_t* buf, size_t len, uint32_t offset) const {
diff --git a/libs/androidfw/include/androidfw/AssetDir.h b/libs/androidfw/include/androidfw/AssetDir.h
index 7aef02d..ce6e066 100644
--- a/libs/androidfw/include/androidfw/AssetDir.h
+++ b/libs/androidfw/include/androidfw/AssetDir.h
@@ -78,7 +78,7 @@
     class FileInfo {
     public:
         FileInfo(void) {}
-        FileInfo(const String8& path)      // useful for e.g. svect.indexOf
+        explicit FileInfo(const String8& path)      // useful for e.g. svect.indexOf
             : mFileName(path), mFileType(kFileTypeUnknown)
             {}
         ~FileInfo(void) {}
diff --git a/libs/androidfw/include/androidfw/AssetManager2.h b/libs/androidfw/include/androidfw/AssetManager2.h
index 0d49298..f29769b 100644
--- a/libs/androidfw/include/androidfw/AssetManager2.h
+++ b/libs/androidfw/include/androidfw/AssetManager2.h
@@ -229,6 +229,14 @@
                                    ResTable_config* in_out_selected_config, uint32_t* in_out_flags,
                                    uint32_t* out_last_reference) const;
 
+  // Enables or disables resource resolution logging. Clears stored steps when
+  // disabled.
+  void SetResourceResolutionLoggingEnabled(bool enabled);
+
+  // Returns formatted log of last resource resolution path, or empty if no
+  // resource has been resolved yet.
+  std::string GetLastResourceResolution() const;
+
   // Retrieves the best matching bag/map resource with ID `resid`.
   // This method will resolve all parent references for this bag and merge keys with the child.
   // To iterate over the keys, use the following idiom:
@@ -346,6 +354,48 @@
   // Cached set of bags. These are cached because they can inherit keys from parent bags,
   // which involves some calculation.
   std::unordered_map<uint32_t, util::unique_cptr<ResolvedBag>> cached_bags_;
+
+  // Whether or not to save resource resolution steps
+  bool resource_resolution_logging_enabled_ = false;
+
+  struct Resolution {
+
+    struct Step {
+
+      enum class Type {
+        INITIAL,
+        BETTER_MATCH,
+        OVERLAID
+      };
+
+      // Marks what kind of override this step was.
+      Type type;
+
+      // Built name of configuration for this step.
+      String8 config_name;
+
+      // Marks the package name of the better resource found in this step.
+      const std::string* package_name;
+    };
+
+    // Last resolved resource ID.
+    uint32_t resid;
+
+    // Last resolved resource result cookie.
+    ApkAssetsCookie cookie = kInvalidCookie;
+
+    // Last resolved resource type.
+    StringPoolRef type_string_ref;
+
+    // Last resolved resource entry.
+    StringPoolRef entry_string_ref;
+
+    // Steps taken to resolve last resource.
+    std::vector<Step> steps;
+  };
+
+  // Record of the last resolved resource's resolution path.
+  mutable Resolution last_resolution;
 };
 
 class Theme {
diff --git a/libs/androidfw/include/androidfw/AttributeResolution.h b/libs/androidfw/include/androidfw/AttributeResolution.h
index 35ef98d..c88004c 100644
--- a/libs/androidfw/include/androidfw/AttributeResolution.h
+++ b/libs/androidfw/include/androidfw/AttributeResolution.h
@@ -23,15 +23,17 @@
 namespace android {
 
 // Offsets into the outValues array populated by the methods below. outValues is a uint32_t
-// array, but each logical element takes up 6 uint32_t-sized physical elements.
+// array, but each logical element takes up 7 uint32_t-sized physical elements.
+// Keep these in sync with android.content.res.TypedArray java class
 enum {
-  STYLE_NUM_ENTRIES = 6,
+  STYLE_NUM_ENTRIES = 7,
   STYLE_TYPE = 0,
   STYLE_DATA = 1,
   STYLE_ASSET_COOKIE = 2,
   STYLE_RESOURCE_ID = 3,
   STYLE_CHANGING_CONFIGURATIONS = 4,
-  STYLE_DENSITY = 5
+  STYLE_DENSITY = 5,
+  SYTLE_SOURCE_STYLE = 6
 };
 
 // These are all variations of the same method. They each perform the exact same operation,
diff --git a/libs/androidfw/include/androidfw/BackupHelpers.h b/libs/androidfw/include/androidfw/BackupHelpers.h
index fc1ad47..2da247b 100644
--- a/libs/androidfw/include/androidfw/BackupHelpers.h
+++ b/libs/androidfw/include/androidfw/BackupHelpers.h
@@ -67,7 +67,7 @@
 class BackupDataWriter
 {
 public:
-    BackupDataWriter(int fd);
+    explicit BackupDataWriter(int fd);
     // does not close fd
     ~BackupDataWriter();
 
@@ -104,7 +104,7 @@
 class BackupDataReader
 {
 public:
-    BackupDataReader(int fd);
+    explicit BackupDataReader(int fd);
     // does not close fd
     ~BackupDataReader();
 
diff --git a/libs/androidfw/include/androidfw/ConfigDescription.h b/libs/androidfw/include/androidfw/ConfigDescription.h
index 29424c4..6fa089a 100644
--- a/libs/androidfw/include/androidfw/ConfigDescription.h
+++ b/libs/androidfw/include/androidfw/ConfigDescription.h
@@ -82,7 +82,7 @@
   static void ApplyVersionForCompatibility(ConfigDescription* config);
 
   ConfigDescription();
-  ConfigDescription(const android::ResTable_config& o);  // NOLINT(implicit)
+  ConfigDescription(const android::ResTable_config& o);  // NOLINT(google-explicit-constructor)
   ConfigDescription(const ConfigDescription& o);
   ConfigDescription(ConfigDescription&& o) noexcept;
 
diff --git a/libs/androidfw/include/androidfw/DisplayEventDispatcher.h b/libs/androidfw/include/androidfw/DisplayEventDispatcher.h
index e1dfb94..bf35aa3 100644
--- a/libs/androidfw/include/androidfw/DisplayEventDispatcher.h
+++ b/libs/androidfw/include/androidfw/DisplayEventDispatcher.h
@@ -22,7 +22,7 @@
 
 class DisplayEventDispatcher : public LooperCallback {
 public:
-    DisplayEventDispatcher(const sp<Looper>& looper,
+    explicit DisplayEventDispatcher(const sp<Looper>& looper,
             ISurfaceComposer::VsyncSource vsyncSource = ISurfaceComposer::eVsyncSourceApp);
 
     status_t initialize();
diff --git a/libs/androidfw/include/androidfw/LoadedArsc.h b/libs/androidfw/include/androidfw/LoadedArsc.h
index 8c5c3b7..be62f30 100644
--- a/libs/androidfw/include/androidfw/LoadedArsc.h
+++ b/libs/androidfw/include/androidfw/LoadedArsc.h
@@ -78,6 +78,8 @@
 using TypeSpecPtr = util::unique_cptr<TypeSpec>;
 
 struct OverlayableInfo {
+  std::string name;
+  std::string actor;
   uint32_t policy_flags;
 };
 
diff --git a/libs/androidfw/include/androidfw/ResourceTypes.h b/libs/androidfw/include/androidfw/ResourceTypes.h
index cf2d8fb..6b9ebd3 100644
--- a/libs/androidfw/include/androidfw/ResourceTypes.h
+++ b/libs/androidfw/include/androidfw/ResourceTypes.h
@@ -693,7 +693,7 @@
 class ResXMLParser
 {
 public:
-    ResXMLParser(const ResXMLTree& tree);
+    explicit ResXMLParser(const ResXMLTree& tree);
 
     enum event_code_t {
         BAD_DOCUMENT = -1,
@@ -806,7 +806,7 @@
      * The tree stores a clone of the specified DynamicRefTable, so any changes to the original
      * DynamicRefTable will not affect this tree after instantiation.
      **/
-    ResXMLTree(const DynamicRefTable* dynamicRefTable);
+    explicit ResXMLTree(const DynamicRefTable* dynamicRefTable);
     ResXMLTree();
     ~ResXMLTree();
 
@@ -1611,6 +1611,12 @@
 struct ResTable_overlayable_header
 {
   struct ResChunk_header header;
+
+  // The name of the overlayable set of resources that overlays target.
+  uint16_t name[256];
+
+ // The component responsible for enabling and disabling overlays targeting this chunk.
+  uint16_t actor[256];
 };
 
 /**
@@ -1844,7 +1850,7 @@
 
     class Theme {
     public:
-        Theme(const ResTable& table);
+        explicit Theme(const ResTable& table);
         ~Theme();
 
         inline const ResTable& getResTable() const { return mTable; }
diff --git a/libs/androidfw/include/androidfw/ResourceUtils.h b/libs/androidfw/include/androidfw/ResourceUtils.h
index d94779b..eb6eb8e 100644
--- a/libs/androidfw/include/androidfw/ResourceUtils.h
+++ b/libs/androidfw/include/androidfw/ResourceUtils.h
@@ -17,6 +17,7 @@
 #ifndef ANDROIDFW_RESOURCEUTILS_H
 #define ANDROIDFW_RESOURCEUTILS_H
 
+#include "androidfw/AssetManager2.h"
 #include "androidfw/StringPiece.h"
 
 namespace android {
@@ -27,6 +28,17 @@
 bool ExtractResourceName(const StringPiece& str, StringPiece* out_package, StringPiece* out_type,
                          StringPiece* out_entry);
 
+// Convert a type_string_ref, entry_string_ref, and package
+// to AssetManager2::ResourceName. Useful for getting
+// resource name without re-running AssetManager2::FindEntry searches.
+bool ToResourceName(StringPoolRef& type_string_ref,
+                    StringPoolRef& entry_string_ref,
+                    const LoadedPackage* package,
+                    AssetManager2::ResourceName* out_name);
+
+// Formats a ResourceName to "package:type/entry_name".
+std::string ToFormattedResourceString(AssetManager2::ResourceName* resource_name);
+
 inline uint32_t fix_package_id(uint32_t resid, uint8_t package_id) {
   return (resid & 0x00ffffffu) | (static_cast<uint32_t>(package_id) << 24);
 }
diff --git a/libs/androidfw/include/androidfw/StringPiece.h b/libs/androidfw/include/androidfw/StringPiece.h
index a33865f..921877d 100644
--- a/libs/androidfw/include/androidfw/StringPiece.h
+++ b/libs/androidfw/include/androidfw/StringPiece.h
@@ -52,8 +52,8 @@
 
   BasicStringPiece();
   BasicStringPiece(const BasicStringPiece<TChar>& str);
-  BasicStringPiece(const std::basic_string<TChar>& str);  // NOLINT(implicit)
-  BasicStringPiece(const TChar* str);                     // NOLINT(implicit)
+  BasicStringPiece(const std::basic_string<TChar>& str);  // NOLINT(google-explicit-constructor)
+  BasicStringPiece(const TChar* str);                     // NOLINT(google-explicit-constructor)
   BasicStringPiece(const TChar* str, size_t len);
 
   BasicStringPiece<TChar>& operator=(const BasicStringPiece<TChar>& rhs);
diff --git a/libs/androidfw/include/androidfw/TypeWrappers.h b/libs/androidfw/include/androidfw/TypeWrappers.h
index 5cfe54e5..fb2fad6 100644
--- a/libs/androidfw/include/androidfw/TypeWrappers.h
+++ b/libs/androidfw/include/androidfw/TypeWrappers.h
@@ -23,7 +23,7 @@
 namespace android {
 
 struct TypeVariant {
-    TypeVariant(const ResTable_type* data);
+    explicit TypeVariant(const ResTable_type* data);
 
     class iterator {
     public:
diff --git a/libs/androidfw/include/androidfw/Util.h b/libs/androidfw/include/androidfw/Util.h
index 10d088e..aa1466f 100644
--- a/libs/androidfw/include/androidfw/Util.h
+++ b/libs/androidfw/include/androidfw/Util.h
@@ -46,7 +46,7 @@
   using pointer = typename std::add_pointer<T>::type;
 
   constexpr unique_cptr() : ptr_(nullptr) {}
-  constexpr unique_cptr(std::nullptr_t) : ptr_(nullptr) {}
+  constexpr explicit unique_cptr(std::nullptr_t) : ptr_(nullptr) {}
   explicit unique_cptr(pointer ptr) : ptr_(ptr) {}
   unique_cptr(unique_cptr&& o) noexcept : ptr_(o.ptr_) { o.ptr_ = nullptr; }
 
diff --git a/libs/androidfw/tests/AssetManager2_test.cpp b/libs/androidfw/tests/AssetManager2_test.cpp
index 5449a54..105dcd2 100644
--- a/libs/androidfw/tests/AssetManager2_test.cpp
+++ b/libs/androidfw/tests/AssetManager2_test.cpp
@@ -586,4 +586,111 @@
   EXPECT_THAT(asset_dir->getFileType(2), Eq(FileType::kFileTypeDirectory));
 }
 
+TEST_F(AssetManager2Test, GetLastPathWithoutEnablingReturnsEmpty) {
+  ResTable_config desired_config;
+
+  AssetManager2 assetmanager;
+  assetmanager.SetConfiguration(desired_config);
+  assetmanager.SetApkAssets({basic_assets_.get()});
+  assetmanager.SetResourceResolutionLoggingEnabled(false);
+
+  Res_value value;
+  ResTable_config selected_config;
+  uint32_t flags;
+
+  ApkAssetsCookie cookie =
+      assetmanager.GetResource(basic::R::string::test1, false /*may_be_bag*/,
+                               0 /*density_override*/, &value, &selected_config, &flags);
+  ASSERT_NE(kInvalidCookie, cookie);
+
+  auto result = assetmanager.GetLastResourceResolution();
+  EXPECT_EQ("", result);
+}
+
+TEST_F(AssetManager2Test, GetLastPathWithoutResolutionReturnsEmpty) {
+  ResTable_config desired_config;
+
+  AssetManager2 assetmanager;
+  assetmanager.SetConfiguration(desired_config);
+  assetmanager.SetApkAssets({basic_assets_.get()});
+
+  auto result = assetmanager.GetLastResourceResolution();
+  EXPECT_EQ("", result);
+}
+
+TEST_F(AssetManager2Test, GetLastPathWithSingleApkAssets) {
+  ResTable_config desired_config;
+  memset(&desired_config, 0, sizeof(desired_config));
+  desired_config.language[0] = 'd';
+  desired_config.language[1] = 'e';
+
+  AssetManager2 assetmanager;
+  assetmanager.SetResourceResolutionLoggingEnabled(true);
+  assetmanager.SetConfiguration(desired_config);
+  assetmanager.SetApkAssets({basic_assets_.get()});
+
+  Res_value value;
+  ResTable_config selected_config;
+  uint32_t flags;
+
+  ApkAssetsCookie cookie =
+      assetmanager.GetResource(basic::R::string::test1, false /*may_be_bag*/,
+                               0 /*density_override*/, &value, &selected_config, &flags);
+  ASSERT_NE(kInvalidCookie, cookie);
+
+  auto result = assetmanager.GetLastResourceResolution();
+  EXPECT_EQ("Resolution for 0x7f030000 com.android.basic:string/test1\n\tFor config -de\n\tFound initial: com.android.basic", result);
+}
+
+TEST_F(AssetManager2Test, GetLastPathWithMultipleApkAssets) {
+  ResTable_config desired_config;
+  memset(&desired_config, 0, sizeof(desired_config));
+  desired_config.language[0] = 'd';
+  desired_config.language[1] = 'e';
+
+  AssetManager2 assetmanager;
+  assetmanager.SetResourceResolutionLoggingEnabled(true);
+  assetmanager.SetConfiguration(desired_config);
+  assetmanager.SetApkAssets({basic_assets_.get(), basic_de_fr_assets_.get()});
+
+  Res_value value = Res_value();
+  ResTable_config selected_config;
+  uint32_t flags;
+
+  ApkAssetsCookie cookie =
+      assetmanager.GetResource(basic::R::string::test1, false /*may_be_bag*/,
+                               0 /*density_override*/, &value, &selected_config, &flags);
+  ASSERT_NE(kInvalidCookie, cookie);
+
+  auto result = assetmanager.GetLastResourceResolution();
+  EXPECT_EQ("Resolution for 0x7f030000 com.android.basic:string/test1\n\tFor config -de\n\tFound initial: com.android.basic\n\tFound better: com.android.basic -de", result);
+}
+
+TEST_F(AssetManager2Test, GetLastPathAfterDisablingReturnsEmpty) {
+  ResTable_config desired_config;
+  memset(&desired_config, 0, sizeof(desired_config));
+
+  AssetManager2 assetmanager;
+  assetmanager.SetResourceResolutionLoggingEnabled(true);
+  assetmanager.SetConfiguration(desired_config);
+  assetmanager.SetApkAssets({basic_assets_.get()});
+
+  Res_value value = Res_value();
+  ResTable_config selected_config;
+  uint32_t flags;
+
+  ApkAssetsCookie cookie =
+      assetmanager.GetResource(basic::R::string::test1, false /*may_be_bag*/,
+                               0 /*density_override*/, &value, &selected_config, &flags);
+  ASSERT_NE(kInvalidCookie, cookie);
+
+  auto resultEnabled = assetmanager.GetLastResourceResolution();
+  ASSERT_NE("", resultEnabled);
+
+  assetmanager.SetResourceResolutionLoggingEnabled(false);
+
+  auto resultDisabled = assetmanager.GetLastResourceResolution();
+  EXPECT_EQ("", resultDisabled);
+}
+
 }  // namespace android
diff --git a/libs/androidfw/tests/LoadedArsc_test.cpp b/libs/androidfw/tests/LoadedArsc_test.cpp
index 22d587a..2e386a0 100644
--- a/libs/androidfw/tests/LoadedArsc_test.cpp
+++ b/libs/androidfw/tests/LoadedArsc_test.cpp
@@ -294,22 +294,30 @@
 
   info = package->GetOverlayableInfo(overlayable::R::string::overlayable1);
   ASSERT_THAT(info, NotNull());
+  EXPECT_THAT(info->name, Eq("OverlayableResources1"));
+  EXPECT_THAT(info->actor, Eq("overlay://theme"));
   EXPECT_THAT(info->policy_flags, Eq(ResTable_overlayable_policy_header::POLICY_PUBLIC));
 
   info = package->GetOverlayableInfo(overlayable::R::string::overlayable2);
   ASSERT_THAT(info, NotNull());
+  EXPECT_THAT(info->name, Eq("OverlayableResources1"));
+  EXPECT_THAT(info->actor, Eq("overlay://theme"));
   EXPECT_THAT(info->policy_flags,
               Eq(ResTable_overlayable_policy_header::POLICY_SYSTEM_PARTITION
                  | ResTable_overlayable_policy_header::POLICY_PRODUCT_PARTITION));
 
   info = package->GetOverlayableInfo(overlayable::R::string::overlayable3);
   ASSERT_THAT(info, NotNull());
+  EXPECT_THAT(info->name, Eq("OverlayableResources2"));
+  EXPECT_THAT(info->actor, Eq("overlay://com.android.overlayable"));
   EXPECT_THAT(info->policy_flags,
               Eq(ResTable_overlayable_policy_header::POLICY_VENDOR_PARTITION
                  | ResTable_overlayable_policy_header::POLICY_PRODUCT_SERVICES_PARTITION
                  | ResTable_overlayable_policy_header::POLICY_PRODUCT_PARTITION));
 
   info = package->GetOverlayableInfo(overlayable::R::string::overlayable4);
+  EXPECT_THAT(info->name, Eq("OverlayableResources1"));
+  EXPECT_THAT(info->actor, Eq("overlay://theme"));
   ASSERT_THAT(info, NotNull());
   EXPECT_THAT(info->policy_flags, Eq(ResTable_overlayable_policy_header::POLICY_PUBLIC));
 }
diff --git a/libs/androidfw/tests/data/overlayable/overlayable.apk b/libs/androidfw/tests/data/overlayable/overlayable.apk
index 85ab4be..8634747 100644
--- a/libs/androidfw/tests/data/overlayable/overlayable.apk
+++ b/libs/androidfw/tests/data/overlayable/overlayable.apk
Binary files differ
diff --git a/libs/androidfw/tests/data/overlayable/res/values/overlayable.xml b/libs/androidfw/tests/data/overlayable/res/values/overlayable.xml
index 11aa735..dba7b08 100644
--- a/libs/androidfw/tests/data/overlayable/res/values/overlayable.xml
+++ b/libs/androidfw/tests/data/overlayable/res/values/overlayable.xml
@@ -15,7 +15,7 @@
 -->
 
 <resources>
-<overlayable>
+<overlayable name="OverlayableResources1" actor="overlay://theme">
     <!-- Any overlay can overlay the value of @string/overlayable1 -->
     <item type="string" name="overlayable1" />
 
@@ -31,9 +31,9 @@
     </policy>
 </overlayable>
 
-<overlayable>
+<overlayable name="OverlayableResources2" actor="overlay://com.android.overlayable">
     <!-- Any overlay on the product_services, vendor, or product partition can overlay the value of
-   @string/overlayable3 -->
+        @string/overlayable3 -->
     <policy type="product_services|vendor|product">
         <item type="string" name="overlayable3" />
     </policy>
diff --git a/libs/hwui/DeviceInfo.cpp b/libs/hwui/DeviceInfo.cpp
index ed167e5..56b1885 100644
--- a/libs/hwui/DeviceInfo.cpp
+++ b/libs/hwui/DeviceInfo.cpp
@@ -79,8 +79,7 @@
     switch (wcgDataspace) {
         case ui::Dataspace::DISPLAY_P3:
             *colorGamut = SkColorSpace::Gamut::kDCIP3_D65_Gamut;
-            *colorSpace = SkColorSpace::MakeRGB(SkColorSpace::kSRGB_RenderTargetGamma,
-                                                SkColorSpace::Gamut::kDCIP3_D65_Gamut);
+            *colorSpace = SkColorSpace::MakeRGB(SkNamedTransferFn::kSRGB, SkNamedGamut::kDCIP3);
             break;
         case ui::Dataspace::V0_SCRGB:
             *colorGamut = SkColorSpace::Gamut::kSRGB_Gamut;
diff --git a/libs/hwui/WebViewFunctorManager.cpp b/libs/hwui/WebViewFunctorManager.cpp
index 9170d6d..68541b4 100644
--- a/libs/hwui/WebViewFunctorManager.cpp
+++ b/libs/hwui/WebViewFunctorManager.cpp
@@ -86,6 +86,26 @@
     mCallbacks.gles.draw(mFunctor, mData, drawInfo);
 }
 
+void WebViewFunctor::initVk(const VkFunctorInitParams& params) {
+    ATRACE_NAME("WebViewFunctor::initVk");
+    if (!mHasContext) {
+        mHasContext = true;
+    } else {
+        return;
+    }
+    mCallbacks.vk.initialize(mFunctor, mData, params);
+}
+
+void WebViewFunctor::drawVk(const VkFunctorDrawParams& params) {
+    ATRACE_NAME("WebViewFunctor::drawVk");
+    mCallbacks.vk.draw(mFunctor, mData, params);
+}
+
+void WebViewFunctor::postDrawVk() {
+    ATRACE_NAME("WebViewFunctor::postDrawVk");
+    mCallbacks.vk.postDraw(mFunctor, mData);
+}
+
 void WebViewFunctor::destroyContext() {
     if (mHasContext) {
         mHasContext = false;
diff --git a/libs/hwui/WebViewFunctorManager.h b/libs/hwui/WebViewFunctorManager.h
index 1719ce7..2846cb1 100644
--- a/libs/hwui/WebViewFunctorManager.h
+++ b/libs/hwui/WebViewFunctorManager.h
@@ -42,6 +42,12 @@
 
         void drawGl(const DrawGlInfo& drawInfo) const { mReference.drawGl(drawInfo); }
 
+        void initVk(const VkFunctorInitParams& params) { mReference.initVk(params); }
+
+        void drawVk(const VkFunctorDrawParams& params) { mReference.drawVk(params); }
+
+        void postDrawVk() { mReference.postDrawVk(); }
+
     private:
         friend class WebViewFunctor;
 
@@ -53,6 +59,9 @@
     int id() const { return mFunctor; }
     void sync(const WebViewSyncData& syncData) const;
     void drawGl(const DrawGlInfo& drawInfo);
+    void initVk(const VkFunctorInitParams& params);
+    void drawVk(const VkFunctorDrawParams& params);
+    void postDrawVk();
     void destroyContext();
 
     sp<Handle> createHandle() {
diff --git a/libs/hwui/pipeline/skia/ShaderCache.cpp b/libs/hwui/pipeline/skia/ShaderCache.cpp
index 562a3b2..1661905 100644
--- a/libs/hwui/pipeline/skia/ShaderCache.cpp
+++ b/libs/hwui/pipeline/skia/ShaderCache.cpp
@@ -23,6 +23,7 @@
 #include "FileBlobCache.h"
 #include "Properties.h"
 #include "utils/TraceUtils.h"
+#include <GrContext.h>
 
 namespace android {
 namespace uirenderer {
@@ -168,6 +169,24 @@
     const void* value = data.data();
 
     BlobCache* bc = getBlobCacheLocked();
+    if (mInStoreVkPipelineInProgress) {
+        if (mOldPipelineCacheSize == -1) {
+            // Record the initial pipeline cache size stored in the file.
+            mOldPipelineCacheSize = bc->get(key.data(), keySize, nullptr, 0);
+        }
+        if (mNewPipelineCacheSize != -1 && mNewPipelineCacheSize == valueSize) {
+            // There has not been change in pipeline cache size. Stop trying to save.
+            mTryToStorePipelineCache = false;
+            return;
+        }
+        mNewPipelineCacheSize = valueSize;
+    } else {
+        mCacheDirty = true;
+        // If there are new shaders compiled, we probably have new pipeline state too.
+        // Store pipeline cache on the next flush.
+        mNewPipelineCacheSize = -1;
+        mTryToStorePipelineCache = true;
+    }
     bc->set(key.data(), keySize, value, valueSize);
 
     if (!mSavePending && mDeferredSaveDelay > 0) {
@@ -175,12 +194,31 @@
         std::thread deferredSaveThread([this]() {
             sleep(mDeferredSaveDelay);
             std::lock_guard<std::mutex> lock(mMutex);
-            saveToDiskLocked();
+            // Store file on disk if there a new shader or Vulkan pipeline cache size changed.
+            if (mCacheDirty || mNewPipelineCacheSize != mOldPipelineCacheSize) {
+                saveToDiskLocked();
+                mOldPipelineCacheSize = mNewPipelineCacheSize;
+                mTryToStorePipelineCache = false;
+                mCacheDirty = false;
+            }
         });
         deferredSaveThread.detach();
     }
 }
 
+void ShaderCache::onVkFrameFlushed(GrContext* context) {
+    {
+        std::lock_guard<std::mutex> lock(mMutex);
+
+        if (!mInitialized || !mTryToStorePipelineCache) {
+            return;
+        }
+    }
+    mInStoreVkPipelineInProgress = true;
+    context->storeVkPipelineCacheData();
+    mInStoreVkPipelineInProgress = false;
+}
+
 } /* namespace skiapipeline */
 } /* namespace uirenderer */
 } /* namespace android */
diff --git a/libs/hwui/pipeline/skia/ShaderCache.h b/libs/hwui/pipeline/skia/ShaderCache.h
index d41aadb..0898017 100644
--- a/libs/hwui/pipeline/skia/ShaderCache.h
+++ b/libs/hwui/pipeline/skia/ShaderCache.h
@@ -75,6 +75,13 @@
      */
     void store(const SkData& key, const SkData& data) override;
 
+    /**
+     * "onVkFrameFlushed" tries to store Vulkan pipeline cache state.
+     * Pipeline cache is saved on disk only if the size of the data has changed or there was
+     * a new shader compiled.
+     */
+    void onVkFrameFlushed(GrContext* context);
+
 private:
     // Creation and (the lack of) destruction is handled internally.
     ShaderCache();
@@ -167,6 +174,33 @@
     mutable std::mutex mMutex;
 
     /**
+     *  If set to "true", the next call to onVkFrameFlushed, will invoke
+     * GrCanvas::storeVkPipelineCacheData. This does not guarantee that data will be stored on disk.
+     */
+    bool mTryToStorePipelineCache = true;
+
+    /**
+     * This flag is used by "ShaderCache::store" to distinguish between shader data and
+     * Vulkan pipeline data.
+     */
+    bool mInStoreVkPipelineInProgress = false;
+
+    /**
+     *  "mNewPipelineCacheSize" has the size of the new Vulkan pipeline cache data. It is used
+     *  to prevent unnecessary disk writes, if the pipeline cache size has not changed.
+     */
+    size_t mNewPipelineCacheSize = -1;
+    /**
+     *  "mOldPipelineCacheSize" has the size of the Vulkan pipeline cache data stored on disk.
+     */
+    size_t mOldPipelineCacheSize = -1;
+
+    /**
+     *  "mCacheDirty" is true when there is new shader cache data, which is not saved to disk.
+     */
+    bool mCacheDirty = false;
+
+    /**
      * "sCache" is the singleton ShaderCache object.
      */
     static ShaderCache sCache;
diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
index 4338b1c..e6e6b0e 100644
--- a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
@@ -96,7 +96,7 @@
 
     SkASSERT(mRenderThread.getGrContext() != nullptr);
     sk_sp<SkSurface> surface(SkSurface::MakeFromBackendRenderTarget(
-            mRenderThread.getGrContext(), backendRT, kBottomLeft_GrSurfaceOrigin, colorType,
+            mRenderThread.getGrContext(), backendRT, this->getSurfaceOrigin(), colorType,
             mSurfaceColorSpace, &props));
 
     SkiaPipeline::updateLighting(lightGeometry, lightInfo);
diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h
index 47991069..6692922 100644
--- a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h
+++ b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h
@@ -39,6 +39,7 @@
               const Rect& contentDrawBounds, bool opaque, const LightInfo& lightInfo,
               const std::vector<sp<RenderNode> >& renderNodes,
               FrameInfoVisualizer* profiler) override;
+    GrSurfaceOrigin getSurfaceOrigin() override { return kBottomLeft_GrSurfaceOrigin; }
     bool swapBuffers(const renderthread::Frame& frame, bool drew, const SkRect& screenDirty,
                      FrameInfo* currentFrameInfo, bool* requireSwap) override;
     DeferredLayerUpdater* createTextureLayer() override;
diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.cpp b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
index 2e7850d..d7faaf7 100644
--- a/libs/hwui/pipeline/skia/SkiaPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
@@ -174,7 +174,8 @@
         SkSurfaceProps props(0, kUnknown_SkPixelGeometry);
         SkASSERT(mRenderThread.getGrContext() != nullptr);
         node->setLayerSurface(SkSurface::MakeRenderTarget(mRenderThread.getGrContext(),
-                                                          SkBudgeted::kYes, info, 0, &props));
+                                                          SkBudgeted::kYes, info, 0,
+                                                          this->getSurfaceOrigin(), &props));
         if (node->getLayerSurface()) {
             // update the transform in window of the layer to reset its origin wrt light source
             // position
diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.h b/libs/hwui/pipeline/skia/SkiaPipeline.h
index ff87313..94a699b 100644
--- a/libs/hwui/pipeline/skia/SkiaPipeline.h
+++ b/libs/hwui/pipeline/skia/SkiaPipeline.h
@@ -48,7 +48,7 @@
     bool createOrUpdateLayer(RenderNode* node, const DamageAccumulator& damageAccumulator,
                              ErrorHandler* errorHandler) override;
 
-    SkColorType getSurfaceColorType() const { return mSurfaceColorType; }
+    SkColorType getSurfaceColorType() const override { return mSurfaceColorType; }
     sk_sp<SkColorSpace> getSurfaceColorSpace() override { return mSurfaceColorSpace; }
 
     void renderFrame(const LayerUpdateQueue& layers, const SkRect& clip,
diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
index 6eefed9..d54275f 100644
--- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
+++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
@@ -125,8 +125,6 @@
                                              uirenderer::GlFunctorLifecycleListener* listener) {
     FunctorDrawable* functorDrawable;
     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());
     } else {
diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
index 1d3a244..b9aae98 100644
--- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
@@ -23,6 +23,7 @@
 #include "VkInteropFunctorDrawable.h"
 #include "renderstate/RenderState.h"
 #include "renderthread/Frame.h"
+#include "ShaderCache.h"
 
 #include <SkSurface.h>
 #include <SkTypes.h>
@@ -73,6 +74,7 @@
     }
     SkiaPipeline::updateLighting(lightGeometry, lightInfo);
     renderFrame(*layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, backBuffer);
+    ShaderCache::get().onVkFrameFlushed(mRenderThread.getGrContext());
     layerUpdateQueue->clear();
 
     // Draw visual debugging features
diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h
index 53ffc44..9343076 100644
--- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h
+++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h
@@ -35,6 +35,7 @@
               const Rect& contentDrawBounds, bool opaque, const LightInfo& lightInfo,
               const std::vector<sp<RenderNode> >& renderNodes,
               FrameInfoVisualizer* profiler) override;
+    GrSurfaceOrigin getSurfaceOrigin() override { return kTopLeft_GrSurfaceOrigin; }
     bool swapBuffers(const renderthread::Frame& frame, bool drew, const SkRect& screenDirty,
                      FrameInfo* currentFrameInfo, bool* requireSwap) override;
     DeferredLayerUpdater* createTextureLayer() override;
diff --git a/libs/hwui/pipeline/skia/VkFunctorDrawable.cpp b/libs/hwui/pipeline/skia/VkFunctorDrawable.cpp
index 156f74a..2f8d381 100644
--- a/libs/hwui/pipeline/skia/VkFunctorDrawable.cpp
+++ b/libs/hwui/pipeline/skia/VkFunctorDrawable.cpp
@@ -17,6 +17,8 @@
 #include "VkFunctorDrawable.h"
 #include <private/hwui/DrawVkInfo.h>
 
+#include "renderthread/VulkanManager.h"
+#include "renderthread/RenderThread.h"
 #include <GrBackendDrawableInfo.h>
 #include <SkImage.h>
 #include <utils/Color.h>
@@ -31,34 +33,58 @@
 namespace uirenderer {
 namespace skiapipeline {
 
-VkFunctorDrawHandler::VkFunctorDrawHandler(Functor* functor) : INHERITED(), mFunctor(functor) {}
+VkFunctorDrawHandler::VkFunctorDrawHandler(sp<WebViewFunctor::Handle> functor_handle,
+                                           const SkMatrix& matrix, const SkIRect& clip,
+                                           const SkImageInfo& image_info)
+        : INHERITED()
+        , mFunctorHandle(functor_handle)
+        , mMatrix(matrix)
+        , mClip(clip)
+        , mImageInfo(image_info) {}
 
 VkFunctorDrawHandler::~VkFunctorDrawHandler() {
-    // TODO(cblume) Fill in the DrawVkInfo parameters.
-    (*mFunctor)(DrawVkInfo::kModePostComposite, nullptr);
+    mFunctorHandle->postDrawVk();
 }
 
 void VkFunctorDrawHandler::draw(const GrBackendDrawableInfo& info) {
     ATRACE_CALL();
+    if (!renderthread::RenderThread::isCurrent())
+        LOG_ALWAYS_FATAL("VkFunctorDrawHandler::draw not called on render thread");
 
     GrVkDrawableInfo vulkan_info;
     if (!info.getVkDrawableInfo(&vulkan_info)) {
         return;
     }
+    renderthread::VulkanManager& vk_manager =
+            renderthread::RenderThread::getInstance().vulkanManager();
+    mFunctorHandle->initVk(vk_manager.getVkFunctorInitParams());
 
-    DrawVkInfo draw_vk_info;
-    // TODO(cblume) Fill in the rest of the parameters and test the actual call.
-    draw_vk_info.isLayer = true;
+    SkMatrix44 mat4(mMatrix);
+    VkFunctorDrawParams params{
+      .width = mImageInfo.width(),
+      .height = mImageInfo.height(),
+      .is_layer = false,  // TODO(boliu): Populate is_layer.
+      .color_space_ptr = mImageInfo.colorSpace(),
+      .clip_left = mClip.fLeft,
+      .clip_top = mClip.fTop,
+      .clip_right = mClip.fRight,
+      .clip_bottom = mClip.fBottom,
+    };
+    mat4.asColMajorf(&params.transform[0]);
+    params.secondary_command_buffer = vulkan_info.fSecondaryCommandBuffer;
+    params.color_attachment_index = vulkan_info.fColorAttachmentIndex;
+    params.compatible_render_pass = vulkan_info.fCompatibleRenderPass;
+    params.format = vulkan_info.fFormat;
 
-    (*mFunctor)(DrawVkInfo::kModeComposite, &draw_vk_info);
+    mFunctorHandle->drawVk(params);
+
+    vulkan_info.fDrawBounds->offset.x = mClip.fLeft;
+    vulkan_info.fDrawBounds->offset.y = mClip.fTop;
+    vulkan_info.fDrawBounds->extent.width = mClip.fRight - mClip.fLeft;
+    vulkan_info.fDrawBounds->extent.height = mClip.fBottom - mClip.fTop;
 }
 
 VkFunctorDrawable::~VkFunctorDrawable() {
-    if (auto lp = std::get_if<LegacyFunctor>(&mAnyFunctor)) {
-        if (lp->listener) {
-            lp->listener->onGlFunctorReleased(lp->functor);
-        }
-    }
 }
 
 void VkFunctorDrawable::onDraw(SkCanvas* /*canvas*/) {
@@ -67,16 +93,17 @@
 }
 
 std::unique_ptr<FunctorDrawable::GpuDrawHandler> VkFunctorDrawable::onSnapGpuDrawHandler(
-        GrBackendApi backendApi, const SkMatrix& matrix) {
+        GrBackendApi backendApi, const SkMatrix& matrix, const SkIRect& clip,
+        const SkImageInfo& image_info) {
     if (backendApi != GrBackendApi::kVulkan) {
         return nullptr;
     }
     std::unique_ptr<VkFunctorDrawHandler> draw;
     if (mAnyFunctor.index() == 0) {
-        LOG_ALWAYS_FATAL("Not implemented");
-        return nullptr;
+        return std::make_unique<VkFunctorDrawHandler>(std::get<0>(mAnyFunctor).handle, matrix, clip,
+                                                      image_info);
     } else {
-        return std::make_unique<VkFunctorDrawHandler>(std::get<1>(mAnyFunctor).functor);
+        LOG_ALWAYS_FATAL("Not implemented");
     }
 }
 
diff --git a/libs/hwui/pipeline/skia/VkFunctorDrawable.h b/libs/hwui/pipeline/skia/VkFunctorDrawable.h
index d6fefc1..1a53c8f 100644
--- a/libs/hwui/pipeline/skia/VkFunctorDrawable.h
+++ b/libs/hwui/pipeline/skia/VkFunctorDrawable.h
@@ -32,15 +32,18 @@
  */
 class VkFunctorDrawHandler : public FunctorDrawable::GpuDrawHandler {
 public:
-    explicit VkFunctorDrawHandler(Functor* functor);
+    VkFunctorDrawHandler(sp<WebViewFunctor::Handle> functor_handle, const SkMatrix& matrix,
+                         const SkIRect& clip, const SkImageInfo& image_info);
     ~VkFunctorDrawHandler() override;
 
     void draw(const GrBackendDrawableInfo& info) override;
 
 private:
     typedef GpuDrawHandler INHERITED;
-
-    Functor* mFunctor;
+    sp<WebViewFunctor::Handle> mFunctorHandle;
+    const SkMatrix mMatrix;
+    const SkIRect mClip;
+    const SkImageInfo mImageInfo;
 };
 
 /**
@@ -57,7 +60,8 @@
     // SkDrawable functions:
     void onDraw(SkCanvas* canvas) override;
     std::unique_ptr<FunctorDrawable::GpuDrawHandler> onSnapGpuDrawHandler(
-            GrBackendApi backendApi, const SkMatrix& matrix) override;
+            GrBackendApi backendApi, const SkMatrix& matrix, const SkIRect& clip,
+            const SkImageInfo& image_info) override;
 };
 
 }  // namespace skiapipeline
diff --git a/libs/hwui/private/hwui/DrawVkInfo.h b/libs/hwui/private/hwui/DrawVkInfo.h
index b2351fc..abc4dbf 100644
--- a/libs/hwui/private/hwui/DrawVkInfo.h
+++ b/libs/hwui/private/hwui/DrawVkInfo.h
@@ -17,89 +17,61 @@
 #ifndef ANDROID_HWUI_DRAW_VK_INFO_H
 #define ANDROID_HWUI_DRAW_VK_INFO_H
 
+#include <SkColorSpace.h>
 #include <vulkan/vulkan.h>
 
 namespace android {
 namespace uirenderer {
 
-/**
- * Structure used by VulkanRenderer::callDrawVKFunction() to pass and receive data from Vulkan
- * functors.
- */
-struct DrawVkInfo {
-    // Input: current width/height of destination surface
-    int width;
-    int height;
+struct VkFunctorInitParams {
+  VkInstance instance;
+  VkPhysicalDevice physical_device;
+  VkDevice device;
+  VkQueue queue;
+  uint32_t graphics_queue_index;
+  uint32_t instance_version;
+  const char* const* enabled_instance_extension_names;
+  uint32_t enabled_instance_extension_names_length;
+  const char* const* enabled_device_extension_names;
+  uint32_t enabled_device_extension_names_length;
+  const VkPhysicalDeviceFeatures2* device_features_2;
+};
 
-    // Input: is the render target an FBO
-    bool isLayer;
+struct VkFunctorDrawParams {
+  // Input: current width/height of destination surface.
+  int width;
+  int height;
 
-    // Input: current transform matrix, in OpenGL format
-    float transform[16];
+  // Input: is the render target a FBO
+  bool is_layer;
 
-    // Input: WebView should do its main compositing draws into this. It cannot do anything that
-    // would require stopping the render pass.
-    VkCommandBuffer secondaryCommandBuffer;
+  // Input: current transform matrix
+  float transform[16];
 
-    // Input: The main color attachment index where secondaryCommandBuffer will eventually be
-    // submitted.
-    uint32_t colorAttachmentIndex;
+  // Input WebView should do its main compositing draws into this. It cannot do
+  // anything that would require stopping the render pass.
+  VkCommandBuffer secondary_command_buffer;
 
-    // Input: A render pass which will be compatible to the one which the secondaryCommandBuffer
-    // will be submitted into.
-    VkRenderPass compatibleRenderPass;
+  // Input: The main color attachment index where secondary_command_buffer will
+  // eventually be submitted.
+  uint32_t color_attachment_index;
 
-    // Input: Format of the destination surface.
-    VkFormat format;
+  // Input: A render pass which will be compatible to the one which the
+  // secondary_command_buffer will be submitted into.
+  VkRenderPass compatible_render_pass;
 
-    // Input: Color space transfer params
-    float g;
-    float a;
-    float b;
-    float c;
-    float d;
-    float e;
-    float f;
+  // Input: Format of the destination surface.
+  VkFormat format;
 
-    // Input: Color space transformation from linear RGB to D50-adapted XYZ
-    float colorSpaceTransform[9];
+  // Input: Color space.
+  const SkColorSpace* color_space_ptr;
 
-    // Input: current clip rect
-    int clipLeft;
-    int clipTop;
-    int clipRight;
-    int clipBottom;
-
-    /**
-     * Values used as the "what" parameter of the functor.
-     */
-    enum Mode {
-        // Called once at WebView start
-        kModeInit,
-        // Called when things need to be re-created
-        kModeReInit,
-        // Notifies the app that the composite functor will be called soon. This allows WebView to
-        // begin work early.
-        kModePreComposite,
-        // Do the actual composite work
-        kModeComposite,
-        // This allows WebView to begin using the previously submitted objects in future work.
-        kModePostComposite,
-        // Invoked every time the UI thread pushes over a frame to the render thread and the owning
-        // view has a dirty display list*. This is a signal to sync any data that needs to be
-        // shared between the UI thread and the render thread. During this time the UI thread is
-        // blocked.
-        kModeSync
-    };
-
-    /**
-     * Values used by Vulkan functors to tell the framework what to do next.
-     */
-    enum Status {
-        // The functor is done
-        kStatusDone = 0x0,
-    };
-};  // struct DrawVkInfo
+  // Input: current clip rect
+  int clip_left;
+  int clip_top;
+  int clip_right;
+  int clip_bottom;
+};
 
 }  // namespace uirenderer
 }  // namespace android
diff --git a/libs/hwui/private/hwui/WebViewFunctor.h b/libs/hwui/private/hwui/WebViewFunctor.h
index da3d06a..96da947 100644
--- a/libs/hwui/private/hwui/WebViewFunctor.h
+++ b/libs/hwui/private/hwui/WebViewFunctor.h
@@ -19,6 +19,7 @@
 
 #include <cutils/compiler.h>
 #include <private/hwui/DrawGlInfo.h>
+#include <private/hwui/DrawVkInfo.h>
 
 namespace android::uirenderer {
 
@@ -52,18 +53,12 @@
             // Called on RenderThread. initialize is guaranteed to happen before this call
             void (*draw)(int functor, void* data, 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 */);
+            void (*initialize)(int functor, void* data, const VkFunctorInitParams& params);
+            void (*draw)(int functor, void* data, const VkFunctorDrawParams& params);
+            void (*postDraw)(int functor, void*);
         } vk;
     };
 };
diff --git a/libs/hwui/renderthread/IRenderPipeline.h b/libs/hwui/renderthread/IRenderPipeline.h
index 42e17b273..d4dd629 100644
--- a/libs/hwui/renderthread/IRenderPipeline.h
+++ b/libs/hwui/renderthread/IRenderPipeline.h
@@ -84,6 +84,7 @@
     virtual void onPrepareTree() = 0;
     virtual SkColorType getSurfaceColorType() const = 0;
     virtual sk_sp<SkColorSpace> getSurfaceColorSpace() = 0;
+    virtual GrSurfaceOrigin getSurfaceOrigin() = 0;
 
     virtual ~IRenderPipeline() {}
 };
diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp
index c06fadd..8bef359 100644
--- a/libs/hwui/renderthread/RenderThread.cpp
+++ b/libs/hwui/renderthread/RenderThread.cpp
@@ -171,6 +171,9 @@
     mRenderState = new RenderState(*this);
     mVkManager = new VulkanManager(*this);
     mCacheManager = new CacheManager(mDisplayInfo);
+    if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaVulkan) {
+        mVkManager->initialize();
+    }
 }
 
 void RenderThread::requireGlContext() {
diff --git a/libs/hwui/renderthread/RenderThread.h b/libs/hwui/renderthread/RenderThread.h
index 5272227..1ef83fb 100644
--- a/libs/hwui/renderthread/RenderThread.h
+++ b/libs/hwui/renderthread/RenderThread.h
@@ -47,6 +47,10 @@
 class RenderState;
 class TestUtils;
 
+namespace skiapipeline {
+class VkFunctorDrawHandler;
+}
+
 namespace renderthread {
 
 class CanvasContext;
@@ -124,6 +128,7 @@
     friend class DummyVsyncSource;
     friend class android::uirenderer::TestUtils;
     friend class android::uirenderer::WebViewFunctor;
+    friend class android::uirenderer::skiapipeline::VkFunctorDrawHandler;
 
     RenderThread();
     virtual ~RenderThread();
diff --git a/libs/hwui/renderthread/VulkanManager.cpp b/libs/hwui/renderthread/VulkanManager.cpp
index aa7a141..6c540f6 100644
--- a/libs/hwui/renderthread/VulkanManager.cpp
+++ b/libs/hwui/renderthread/VulkanManager.cpp
@@ -34,6 +34,23 @@
 namespace uirenderer {
 namespace renderthread {
 
+static void free_features_extensions_structs(const VkPhysicalDeviceFeatures2& features) {
+    // All Vulkan structs that could be part of the features chain will start with the
+    // structure type followed by the pNext pointer. We cast to the CommonVulkanHeader
+    // so we can get access to the pNext for the next struct.
+    struct CommonVulkanHeader {
+        VkStructureType sType;
+        void*           pNext;
+    };
+
+    void* pNext = features.pNext;
+    while (pNext) {
+        void* current = pNext;
+        pNext = static_cast<CommonVulkanHeader*>(current)->pNext;
+        free(current);
+    }
+}
+
 #define GET_PROC(F) m##F = (PFN_vk##F)vkGetInstanceProcAddr(VK_NULL_HANDLE, "vk" #F)
 #define GET_INST_PROC(F) m##F = (PFN_vk##F)vkGetInstanceProcAddr(mInstance, "vk" #F)
 #define GET_DEV_PROC(F) m##F = (PFN_vk##F)vkGetDeviceProcAddr(mDevice, "vk" #F)
@@ -66,6 +83,11 @@
     mDevice = VK_NULL_HANDLE;
     mPhysicalDevice = VK_NULL_HANDLE;
     mInstance = VK_NULL_HANDLE;
+    mInstanceVersion = 0u;
+    mInstanceExtensions.clear();
+    mDeviceExtensions.clear();
+    free_features_extensions_structs(mPhysicalDeviceFeatures2);
+    mPhysicalDeviceFeatures2 = {};
 }
 
 bool VulkanManager::setupDevice(GrVkExtensions& grExtensions, VkPhysicalDeviceFeatures2& features) {
@@ -81,7 +103,6 @@
         VK_MAKE_VERSION(1, 1, 0),           // apiVersion
     };
 
-    std::vector<const char*> instanceExtensions;
     {
         GET_PROC(EnumerateInstanceExtensionProperties);
 
@@ -99,7 +120,7 @@
         bool hasKHRSurfaceExtension = false;
         bool hasKHRAndroidSurfaceExtension = false;
         for (uint32_t i = 0; i < extensionCount; ++i) {
-            instanceExtensions.push_back(extensions[i].extensionName);
+            mInstanceExtensions.push_back(extensions[i].extensionName);
             if (!strcmp(extensions[i].extensionName, VK_KHR_SURFACE_EXTENSION_NAME)) {
                 hasKHRSurfaceExtension = true;
             }
@@ -120,8 +141,8 @@
         &app_info,                                 // pApplicationInfo
         0,                                         // enabledLayerNameCount
         nullptr,                                   // ppEnabledLayerNames
-        (uint32_t) instanceExtensions.size(),      // enabledExtensionNameCount
-        instanceExtensions.data(),                 // ppEnabledExtensionNames
+        (uint32_t) mInstanceExtensions.size(),     // enabledExtensionNameCount
+        mInstanceExtensions.data(),                // ppEnabledExtensionNames
     };
 
     GET_PROC(CreateInstance);
@@ -201,7 +222,6 @@
     // presentation with any native window. So just use the first one.
     mPresentQueueIndex = 0;
 
-    std::vector<const char*> deviceExtensions;
     {
         uint32_t extensionCount = 0;
         err = mEnumerateDeviceExtensionProperties(mPhysicalDevice, nullptr, &extensionCount,
@@ -220,7 +240,7 @@
         }
         bool hasKHRSwapchainExtension = false;
         for (uint32_t i = 0; i < extensionCount; ++i) {
-            deviceExtensions.push_back(extensions[i].extensionName);
+            mDeviceExtensions.push_back(extensions[i].extensionName);
             if (!strcmp(extensions[i].extensionName, VK_KHR_SWAPCHAIN_EXTENSION_NAME)) {
                 hasKHRSwapchainExtension = true;
             }
@@ -237,8 +257,8 @@
         }
         return vkGetInstanceProcAddr(instance, proc_name);
     };
-    grExtensions.init(getProc, mInstance, mPhysicalDevice, instanceExtensions.size(),
-            instanceExtensions.data(), deviceExtensions.size(), deviceExtensions.data());
+    grExtensions.init(getProc, mInstance, mPhysicalDevice, mInstanceExtensions.size(),
+            mInstanceExtensions.data(), mDeviceExtensions.size(), mDeviceExtensions.data());
 
     if (!grExtensions.hasExtension(VK_KHR_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME, 1)) {
         this->destroy();
@@ -308,8 +328,8 @@
         queueInfo,                               // pQueueCreateInfos
         0,                                       // layerCount
         nullptr,                                 // ppEnabledLayerNames
-        (uint32_t) deviceExtensions.size(),      // extensionCount
-        deviceExtensions.data(),                 // ppEnabledExtensionNames
+        (uint32_t) mDeviceExtensions.size(),     // extensionCount
+        mDeviceExtensions.data(),                // ppEnabledExtensionNames
         nullptr,                                 // ppEnabledFeatures
     };
 
@@ -351,36 +371,17 @@
     return true;
 }
 
-static void free_features_extensions_structs(const VkPhysicalDeviceFeatures2& features) {
-    // All Vulkan structs that could be part of the features chain will start with the
-    // structure type followed by the pNext pointer. We cast to the CommonVulkanHeader
-    // so we can get access to the pNext for the next struct.
-    struct CommonVulkanHeader {
-        VkStructureType sType;
-        void*           pNext;
-    };
-
-    void* pNext = features.pNext;
-    while (pNext) {
-        void* current = pNext;
-        pNext = static_cast<CommonVulkanHeader*>(current)->pNext;
-        free(current);
-    }
-}
-
 void VulkanManager::initialize() {
     if (mDevice != VK_NULL_HANDLE) {
         return;
     }
 
     GET_PROC(EnumerateInstanceVersion);
-    uint32_t instanceVersion = 0;
-    LOG_ALWAYS_FATAL_IF(mEnumerateInstanceVersion(&instanceVersion));
-    LOG_ALWAYS_FATAL_IF(instanceVersion < VK_MAKE_VERSION(1, 1, 0));
+    LOG_ALWAYS_FATAL_IF(mEnumerateInstanceVersion(&mInstanceVersion));
+    LOG_ALWAYS_FATAL_IF(mInstanceVersion < VK_MAKE_VERSION(1, 1, 0));
 
     GrVkExtensions extensions;
-    VkPhysicalDeviceFeatures2 features;
-    LOG_ALWAYS_FATAL_IF(!this->setupDevice(extensions, features));
+    LOG_ALWAYS_FATAL_IF(!this->setupDevice(extensions, mPhysicalDeviceFeatures2));
 
     mGetDeviceQueue(mDevice, mGraphicsQueueIndex, 0, &mGraphicsQueue);
 
@@ -397,9 +398,9 @@
     backendContext.fDevice = mDevice;
     backendContext.fQueue = mGraphicsQueue;
     backendContext.fGraphicsQueueIndex = mGraphicsQueueIndex;
-    backendContext.fInstanceVersion = instanceVersion;
+    backendContext.fInstanceVersion = mInstanceVersion;
     backendContext.fVkExtensions = &extensions;
-    backendContext.fDeviceFeatures2 = &features;
+    backendContext.fDeviceFeatures2 = &mPhysicalDeviceFeatures2;
     backendContext.fGetProc = std::move(getProc);
 
     // create the command pool for the command buffers
@@ -433,13 +434,29 @@
     LOG_ALWAYS_FATAL_IF(!grContext.get());
     mRenderThread.setGrContext(grContext);
 
-    free_features_extensions_structs(features);
-
     if (Properties::enablePartialUpdates && Properties::useBufferAge) {
         mSwapBehavior = SwapBehavior::BufferAge;
     }
 }
 
+VkFunctorInitParams VulkanManager::getVkFunctorInitParams() const {
+    return VkFunctorInitParams{
+            .instance = mInstance,
+            .physical_device = mPhysicalDevice,
+            .device = mDevice,
+            .queue = mGraphicsQueue,
+            .graphics_queue_index = mGraphicsQueueIndex,
+            .instance_version = mInstanceVersion,
+            .enabled_instance_extension_names = mInstanceExtensions.data(),
+            .enabled_instance_extension_names_length =
+                    static_cast<uint32_t>(mInstanceExtensions.size()),
+            .enabled_device_extension_names = mDeviceExtensions.data(),
+            .enabled_device_extension_names_length =
+                    static_cast<uint32_t>(mDeviceExtensions.size()),
+            .device_features_2 = &mPhysicalDeviceFeatures2,
+    };
+}
+
 // Returns the next BackbufferInfo to use for the next draw. The function will make sure all
 // previous uses have finished before returning.
 VulkanSurface::BackbufferInfo* VulkanManager::getAvailableBackbuffer(VulkanSurface* surface) {
diff --git a/libs/hwui/renderthread/VulkanManager.h b/libs/hwui/renderthread/VulkanManager.h
index 9eb942c..105ee09 100644
--- a/libs/hwui/renderthread/VulkanManager.h
+++ b/libs/hwui/renderthread/VulkanManager.h
@@ -132,6 +132,9 @@
     // Creates a fence that is signaled, when all the pending Vulkan commands are flushed.
     status_t createReleaseFence(sp<Fence>& nativeFence);
 
+    // Returned pointers are owned by VulkanManager.
+    VkFunctorInitParams getVkFunctorInitParams() const;
+
 private:
     friend class RenderThread;
 
@@ -234,6 +237,12 @@
 
     VkCommandBuffer mDummyCB = VK_NULL_HANDLE;
 
+    // Variables saved to populate VkFunctorInitParams.
+    uint32_t mInstanceVersion = 0u;
+    std::vector<const char*> mInstanceExtensions;
+    std::vector<const char*> mDeviceExtensions;
+    VkPhysicalDeviceFeatures2 mPhysicalDeviceFeatures2{};
+
     enum class SwapBehavior {
         Discard,
         BufferAge,
diff --git a/libs/hwui/tests/unit/SkiaCanvasTests.cpp b/libs/hwui/tests/unit/SkiaCanvasTests.cpp
index f3a7648..f6178af 100644
--- a/libs/hwui/tests/unit/SkiaCanvasTests.cpp
+++ b/libs/hwui/tests/unit/SkiaCanvasTests.cpp
@@ -44,8 +44,8 @@
 }
 
 TEST(SkiaCanvas, colorSpaceXform) {
-    sk_sp<SkColorSpace> adobe = SkColorSpace::MakeRGB(SkColorSpace::kSRGB_RenderTargetGamma,
-                                                      SkColorSpace::kAdobeRGB_Gamut);
+    sk_sp<SkColorSpace> adobe = SkColorSpace::MakeRGB(SkNamedTransferFn::kSRGB,
+                                                      SkNamedGamut::kAdobeRGB);
 
     SkImageInfo adobeInfo = SkImageInfo::Make(1, 1, kN32_SkColorType, kOpaque_SkAlphaType, adobe);
     sk_sp<Bitmap> adobeBitmap = Bitmap::allocateHeapBitmap(adobeInfo);
diff --git a/libs/hwui/utils/Color.cpp b/libs/hwui/utils/Color.cpp
index dc347f6..4415a59 100644
--- a/libs/hwui/utils/Color.cpp
+++ b/libs/hwui/utils/Color.cpp
@@ -25,38 +25,6 @@
 namespace android {
 namespace uirenderer {
 
-static inline bool almostEqual(float a, float b) {
-    return std::abs(a - b) < 1e-2f;
-}
-
-bool transferFunctionCloseToSRGB(const SkColorSpace* colorSpace) {
-    if (colorSpace == nullptr) return true;
-    if (colorSpace->isSRGB()) return true;
-
-    SkColorSpaceTransferFn transferFunction;
-    if (colorSpace->isNumericalTransferFn(&transferFunction)) {
-        // sRGB transfer function params:
-        const float sRGBParamA = 1 / 1.055f;
-        const float sRGBParamB = 0.055f / 1.055f;
-        const float sRGBParamC = 1 / 12.92f;
-        const float sRGBParamD = 0.04045f;
-        const float sRGBParamE = 0.0f;
-        const float sRGBParamF = 0.0f;
-        const float sRGBParamG = 2.4f;
-
-        // This comparison will catch Display P3
-        return almostEqual(sRGBParamA, transferFunction.fA) &&
-               almostEqual(sRGBParamB, transferFunction.fB) &&
-               almostEqual(sRGBParamC, transferFunction.fC) &&
-               almostEqual(sRGBParamD, transferFunction.fD) &&
-               almostEqual(sRGBParamE, transferFunction.fE) &&
-               almostEqual(sRGBParamF, transferFunction.fF) &&
-               almostEqual(sRGBParamG, transferFunction.fG);
-    }
-
-    return false;
-}
-
 android::PixelFormat ColorTypeToPixelFormat(SkColorType colorType) {
     switch (colorType) {
         case kRGBA_8888_SkColorType:
@@ -79,19 +47,19 @@
 
 sk_sp<SkColorSpace> DataSpaceToColorSpace(android_dataspace dataspace) {
 
-    SkColorSpace::Gamut gamut;
+    skcms_Matrix3x3 gamut;
     switch (dataspace & HAL_DATASPACE_STANDARD_MASK) {
         case HAL_DATASPACE_STANDARD_BT709:
-            gamut = SkColorSpace::kSRGB_Gamut;
+            gamut = SkNamedGamut::kSRGB;
             break;
         case HAL_DATASPACE_STANDARD_BT2020:
-            gamut = SkColorSpace::kRec2020_Gamut;
+            gamut = SkNamedGamut::kRec2020;
             break;
         case HAL_DATASPACE_STANDARD_DCI_P3:
-            gamut = SkColorSpace::kDCIP3_D65_Gamut;
+            gamut = SkNamedGamut::kDCIP3;
             break;
         case HAL_DATASPACE_STANDARD_ADOBE_RGB:
-            gamut = SkColorSpace::kAdobeRGB_Gamut;
+            gamut = SkNamedGamut::kAdobeRGB;
             break;
         case HAL_DATASPACE_STANDARD_UNSPECIFIED:
             return nullptr;
@@ -109,9 +77,9 @@
 
     switch (dataspace & HAL_DATASPACE_TRANSFER_MASK) {
         case HAL_DATASPACE_TRANSFER_LINEAR:
-            return SkColorSpace::MakeRGB(SkColorSpace::kLinear_RenderTargetGamma, gamut);
+            return SkColorSpace::MakeRGB(SkNamedTransferFn::kLinear, gamut);
         case HAL_DATASPACE_TRANSFER_SRGB:
-            return SkColorSpace::MakeRGB(SkColorSpace::kSRGB_RenderTargetGamma, gamut);
+            return SkColorSpace::MakeRGB(SkNamedTransferFn::kSRGB, gamut);
         case HAL_DATASPACE_TRANSFER_GAMMA2_2:
             return SkColorSpace::MakeRGB({2.2f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, gamut);
         case HAL_DATASPACE_TRANSFER_GAMMA2_6:
diff --git a/libs/hwui/utils/Color.h b/libs/hwui/utils/Color.h
index 4473ce6..3880252 100644
--- a/libs/hwui/utils/Color.h
+++ b/libs/hwui/utils/Color.h
@@ -111,11 +111,6 @@
 #endif
 }
 
-// Returns whether the specified color space's transfer function can be
-// approximated with the native sRGB transfer function. This method
-// returns true for sRGB, gamma 2.2 and Display P3 for instance
-bool transferFunctionCloseToSRGB(const SkColorSpace* colorSpace);
-
 android::PixelFormat ColorTypeToPixelFormat(SkColorType colorType);
 
 ANDROID_API sk_sp<SkColorSpace> DataSpaceToColorSpace(android_dataspace dataspace);
diff --git a/libs/services/include/android/os/DropBoxManager.h b/libs/services/include/android/os/DropBoxManager.h
index 75b26c6..0747243 100644
--- a/libs/services/include/android/os/DropBoxManager.h
+++ b/libs/services/include/android/os/DropBoxManager.h
@@ -62,7 +62,7 @@
     // file descriptor.
     Status addFile(const String16& tag, int fd, int flags);
 
-    class Entry : public virtual RefBase, public Parcelable {
+    class Entry : public Parcelable {
     public:
         Entry();
         virtual ~Entry();
@@ -89,9 +89,6 @@
         friend class DropBoxManager;
     };
 
-    // Get the next entry from the drop box after the specified time.
-    Status getNextEntry(const String16& tag, long msec, Entry* entry);
-
 private:
     enum {
         HAS_BYTE_ARRAY = 8
diff --git a/libs/services/src/os/DropBoxManager.cpp b/libs/services/src/os/DropBoxManager.cpp
index 8282518..681d5f7 100644
--- a/libs/services/src/os/DropBoxManager.cpp
+++ b/libs/services/src/os/DropBoxManager.cpp
@@ -228,15 +228,4 @@
     return service->add(entry);
 }
 
-Status
-DropBoxManager::getNextEntry(const String16& tag, long msec, Entry* entry)
-{
-    sp<IDropBoxManagerService> service = interface_cast<IDropBoxManagerService>(
-        defaultServiceManager()->getService(android::String16("dropbox")));
-    if (service == NULL) {
-        return Status::fromExceptionCode(Status::EX_NULL_POINTER, "can't find dropbox service");
-    }
-    return service->getNextEntry(tag, msec, android::String16("android"), entry);
-}
-
 }} // namespace android::os
diff --git a/location/java/android/location/GnssMeasurement.java b/location/java/android/location/GnssMeasurement.java
index f179bc3..602cc3e 100644
--- a/location/java/android/location/GnssMeasurement.java
+++ b/location/java/android/location/GnssMeasurement.java
@@ -48,6 +48,7 @@
     private int mMultipathIndicator;
     private double mSnrInDb;
     private double mAutomaticGainControlLevelInDb;
+    private int mCodeType;
 
     // The following enumerations must be in sync with the values declared in gps.h
 
@@ -58,6 +59,7 @@
     private static final int HAS_CARRIER_PHASE = (1<<11);
     private static final int HAS_CARRIER_PHASE_UNCERTAINTY = (1<<12);
     private static final int HAS_AUTOMATIC_GAIN_CONTROL = (1<<13);
+    private static final int HAS_CODE_TYPE = (1 << 14);
 
     /**
      * The status of the multipath indicator.
@@ -202,6 +204,104 @@
     public static final int ADR_STATE_HALF_CYCLE_REPORTED = (1<<4);
 
     /**
+     * GNSS measurement code type.
+     * @hide
+     */
+    @IntDef(prefix = { "CODE_TYPE_" }, value = {
+            CODE_TYPE_UNKNOWN, CODE_TYPE_A, CODE_TYPE_B, CODE_TYPE_C, CODE_TYPE_I, CODE_TYPE_L,
+            CODE_TYPE_M, CODE_TYPE_P, CODE_TYPE_Q, CODE_TYPE_S, CODE_TYPE_W, CODE_TYPE_X,
+            CODE_TYPE_Y, CODE_TYPE_Z, CODE_TYPE_CODELESS
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface CodeType {}
+
+    /** The GNSS Measurement's code type is unknown. */
+    public static final int CODE_TYPE_UNKNOWN = -1;
+
+    /**
+     * The GNSS Measurement's code type is one of the following: GALILEO E1A, GALILEO E6A, IRNSS
+     * L5A, IRNSS SA.
+     */
+    public static final int CODE_TYPE_A = 0;
+
+    /**
+     * The GNSS Measurement's code type is one of the following: GALILEO E1B, GALILEO E6B, IRNSS
+     * L5B, IRNSS SB.
+     */
+    public static final int CODE_TYPE_B = 1;
+
+    /**
+     * The GNSS Measurement's code type is one of the following: GPS L1 C/A,  GPS L2 C/A, GLONASS G1
+     * C/A, GLONASS G2 C/A, GALILEO E1C, GALILEO E6C, SBAS L1 C/A, QZSS L1 C/A, IRNSS L5C.
+     */
+    public static final int CODE_TYPE_C = 2;
+
+    /**
+     * The GNSS Measurement's code type is one of the following: GPS L5 I, GLONASS G3 I, GALILEO E5a
+     * I, GALILEO E5b I, GALILEO E5a+b I, SBAS L5 I, QZSS L5 I, BDS B1 I, BDS B2 I, BDS B3 I.
+     */
+    public static final int CODE_TYPE_I = 3;
+
+    /**
+     * The GNSS Measurement's code type is one of the following: GPS L1C (P), GPS L2C (L), QZSS L1C
+     * (P), QZSS L2C (L), LEX(6) L.
+     */
+    public static final int CODE_TYPE_L = 4;
+
+    /**
+     * The GNSS Measurement's code type is one of the following: GPS L1M, GPS L2M.
+     */
+    public static final int CODE_TYPE_M = 5;
+
+    /**
+     * The GNSS Measurement's code type is one of the following: GPS L1P, GPS L2P, GLONASS G1P,
+     * GLONASS G2P.
+     */
+    public static final int CODE_TYPE_P = 6;
+
+    /**
+     * The GNSS Measurement's code type is one of the following: GPS L5 Q, GLONASS G3 Q, GALILEO E5a
+     * Q, GALILEO E5b Q, GALILEO E5a+b Q, SBAS L5 Q, QZSS L5 Q, BDS B1 Q, BDS B2 Q, BDS B3 Q.
+     */
+    public static final int CODE_TYPE_Q = 7;
+
+    /**
+     * The GNSS Measurement's code type is one of the following: GPS L1C (D), GPS L2C (M), QZSS L1C
+     * (D), QZSS L2C (M), LEX(6) S.
+     */
+    public static final int CODE_TYPE_S = 8;
+
+    /**
+     * The GNSS Measurement's code type is one of the following: GPS L1 Z-tracking, GPS L2
+     * Z-tracking.
+     */
+    public static final int CODE_TYPE_W = 9;
+
+    /**
+     * The GNSS Measurement's code type is one of the following: GPS L1C (D+P), GPS L2C (M+L), GPS
+     * L5 (I+Q), GLONASS G3 (I+Q), GALILEO E1 (B+C), GALILEO E5a (I+Q), GALILEO E5b (I+Q), GALILEO
+     * E5a+b(I+Q), GALILEO E6 (B+C), SBAS L5 (I+Q), QZSS L1C (D+P), QZSS L2C (M+L), QZSS L5 (I+Q),
+     * LEX(6) (S+L), BDS B1 (I+Q), BDS B2 (I+Q), BDS B3 (I+Q), IRNSS L5 (B+C).
+     */
+    public static final int CODE_TYPE_X = 10;
+
+    /**
+     * The GNSS Measurement's code type is one of the following: GPS L1Y, GPS L2Y.
+     */
+    public static final int CODE_TYPE_Y = 11;
+
+    /**
+     * The GNSS Measurement's code type is one of the following: GALILEO E1 (A+B+C), GALILEO E6
+     * (A+B+C), QZSS L1-SAIF.
+     */
+    public static final int CODE_TYPE_Z = 12;
+
+    /**
+     * The GNSS Measurement's code type is one of the following: GPS L1 codeless, GPS L2 codeless.
+     */
+    public static final int CODE_TYPE_CODELESS = 13;
+
+    /**
      * All the 'Accumulated Delta Range' flags.
      * @hide
      */
@@ -248,6 +348,7 @@
         mMultipathIndicator = measurement.mMultipathIndicator;
         mSnrInDb = measurement.mSnrInDb;
         mAutomaticGainControlLevelInDb = measurement.mAutomaticGainControlLevelInDb;
+        mCodeType = measurement.mCodeType;
     }
 
     /**
@@ -967,7 +1068,7 @@
      * <p>For internal and logging use only.
      */
     private String getMultipathIndicatorString() {
-        switch(mMultipathIndicator) {
+        switch (mMultipathIndicator) {
             case MULTIPATH_INDICATOR_UNKNOWN:
                 return "Unknown";
             case MULTIPATH_INDICATOR_DETECTED:
@@ -1063,6 +1164,89 @@
         mAutomaticGainControlLevelInDb = Double.NaN;
     }
 
+    /**
+     * Returns {@code true} if {@link #getCodeType()} is available,
+     * {@code false} otherwise.
+     */
+    public boolean hasCodeType() {
+        return isFlagSet(HAS_CODE_TYPE);
+    }
+
+    /**
+     * Gets the GNSS measurement's code type.
+     *
+     * <p>Similar to the Attribute field described in Rinex 3.03, e.g., in Tables 4-10, and Table
+     * A2 at the Rinex 3.03 Update 1 Document.
+     */
+    @CodeType
+    public int getCodeType() {
+        return mCodeType;
+    }
+
+    /**
+     * Sets the GNSS measurement's code type.
+     *
+     * @hide
+     */
+    @TestApi
+    public void setCodeType(@CodeType int codeType) {
+        setFlag(HAS_CODE_TYPE);
+        mCodeType = codeType;
+    }
+
+    /**
+     * Resets the GNSS measurement's code type.
+     *
+     * @hide
+     */
+    @TestApi
+    public void resetCodeType() {
+        resetFlag(HAS_CODE_TYPE);
+        mCodeType = CODE_TYPE_UNKNOWN;
+    }
+
+    /**
+     * Gets a string representation of the 'code type'.
+     *
+     * <p>For internal and logging use only.
+     */
+    private String getCodeTypeString() {
+        switch (mCodeType) {
+            case CODE_TYPE_UNKNOWN:
+                return "CODE_TYPE_UNKNOWN";
+            case CODE_TYPE_A:
+                return "CODE_TYPE_A";
+            case CODE_TYPE_B:
+                return "CODE_TYPE_B";
+            case CODE_TYPE_C:
+                return "CODE_TYPE_C";
+            case CODE_TYPE_I:
+                return "CODE_TYPE_I";
+            case CODE_TYPE_L:
+                return "CODE_TYPE_L";
+            case CODE_TYPE_M:
+                return "CODE_TYPE_M";
+            case CODE_TYPE_P:
+                return "CODE_TYPE_P";
+            case CODE_TYPE_Q:
+                return "CODE_TYPE_Q";
+            case CODE_TYPE_S:
+                return "CODE_TYPE_S";
+            case CODE_TYPE_W:
+                return "CODE_TYPE_W";
+            case CODE_TYPE_X:
+                return "CODE_TYPE_X";
+            case CODE_TYPE_Y:
+                return "CODE_TYPE_Y";
+            case CODE_TYPE_Z:
+                return "CODE_TYPE_Z";
+            case CODE_TYPE_CODELESS:
+                return "CODE_TYPE_CODELESS";
+            default:
+                return "<Invalid: " + mCodeType + ">";
+        }
+    }
+
     public static final Creator<GnssMeasurement> CREATOR = new Creator<GnssMeasurement>() {
         @Override
         public GnssMeasurement createFromParcel(Parcel parcel) {
@@ -1088,6 +1272,7 @@
             gnssMeasurement.mMultipathIndicator = parcel.readInt();
             gnssMeasurement.mSnrInDb = parcel.readDouble();
             gnssMeasurement.mAutomaticGainControlLevelInDb = parcel.readDouble();
+            gnssMeasurement.mCodeType = parcel.readInt();
 
             return gnssMeasurement;
         }
@@ -1120,6 +1305,7 @@
         parcel.writeInt(mMultipathIndicator);
         parcel.writeDouble(mSnrInDb);
         parcel.writeDouble(mAutomaticGainControlLevelInDb);
+        parcel.writeInt(mCodeType);
     }
 
     @Override
@@ -1191,9 +1377,13 @@
                 "SnrInDb",
                 hasSnrInDb() ? mSnrInDb : null));
         builder.append(String.format(
-            format,
-            "AgcLevelDb",
-            hasAutomaticGainControlLevelDb() ? mAutomaticGainControlLevelInDb : null));
+                format,
+                "AgcLevelDb",
+                hasAutomaticGainControlLevelDb() ? mAutomaticGainControlLevelInDb : null));
+        builder.append(String.format(
+                format,
+                "CodeType",
+                hasCodeType() ? getCodeTypeString() : null));
 
         return builder.toString();
     }
@@ -1218,6 +1408,7 @@
         setMultipathIndicator(MULTIPATH_INDICATOR_UNKNOWN);
         resetSnrInDb();
         resetAutomaticGainControlLevel();
+        resetCodeType();
     }
 
     private void setFlag(int flag) {
diff --git a/media/java/android/media/AudioAttributes.java b/media/java/android/media/AudioAttributes.java
index 4f23cca..f5a6f86 100644
--- a/media/java/android/media/AudioAttributes.java
+++ b/media/java/android/media/AudioAttributes.java
@@ -736,8 +736,9 @@
          * @param preset one of {@link MediaRecorder.AudioSource#DEFAULT},
          *     {@link MediaRecorder.AudioSource#MIC}, {@link MediaRecorder.AudioSource#CAMCORDER},
          *     {@link MediaRecorder.AudioSource#VOICE_RECOGNITION},
-         *     {@link MediaRecorder.AudioSource#VOICE_COMMUNICATION} or
-         *     {@link MediaRecorder.AudioSource#UNPROCESSED}
+         *     {@link MediaRecorder.AudioSource#VOICE_COMMUNICATION},
+         *     {@link MediaRecorder.AudioSource#UNPROCESSED} or
+         *     {@link MediaRecorder.AudioSource#VOICE_PERFORMANCE}
          * @return the same Builder instance.
          */
         @SystemApi
@@ -749,6 +750,7 @@
                 case MediaRecorder.AudioSource.VOICE_RECOGNITION:
                 case MediaRecorder.AudioSource.VOICE_COMMUNICATION:
                 case MediaRecorder.AudioSource.UNPROCESSED:
+                case MediaRecorder.AudioSource.VOICE_PERFORMANCE:
                     mSource = preset;
                     break;
                 default:
@@ -760,7 +762,7 @@
         /**
          * @hide
          * Same as {@link #setCapturePreset(int)} but authorizes the use of HOTWORD,
-         * REMOTE_SUBMIX, RADIO_TUNER, VOICE_DOWNLINK, VOICE_UPLINK and VOICE_CALL.
+         * REMOTE_SUBMIX, RADIO_TUNER, VOICE_DOWNLINK, VOICE_UPLINK, VOICE_CALL and ECHO_REFERENCE.
          * @param preset
          * @return the same Builder instance.
          */
@@ -771,7 +773,8 @@
                     || (preset == MediaRecorder.AudioSource.RADIO_TUNER)
                     || (preset == MediaRecorder.AudioSource.VOICE_DOWNLINK)
                     || (preset == MediaRecorder.AudioSource.VOICE_UPLINK)
-                    || (preset == MediaRecorder.AudioSource.VOICE_CALL)) {
+                    || (preset == MediaRecorder.AudioSource.VOICE_CALL)
+                    || (preset == MediaRecorder.AudioSource.ECHO_REFERENCE)) {
                 mSource = preset;
             } else {
                 setCapturePreset(preset);
diff --git a/media/java/android/media/AudioFormat.java b/media/java/android/media/AudioFormat.java
index 793aa27..5516086 100644
--- a/media/java/android/media/AudioFormat.java
+++ b/media/java/android/media/AudioFormat.java
@@ -17,6 +17,7 @@
 package android.media;
 
 import android.annotation.IntDef;
+import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.TestApi;
 import android.annotation.UnsupportedAppUsage;
@@ -816,7 +817,7 @@
      *
      * @return The audio frame size in bytes corresponding to the encoding and the channel mask.
      */
-    public int getFrameSizeInBytes() {
+    public @IntRange(from = 1) int getFrameSizeInBytes() {
         return mFrameSizeInBytes;
     }
 
diff --git a/media/java/android/media/AudioRecordingConfiguration.java b/media/java/android/media/AudioRecordingConfiguration.java
index de76aef..52771e4 100644
--- a/media/java/android/media/AudioRecordingConfiguration.java
+++ b/media/java/android/media/AudioRecordingConfiguration.java
@@ -172,7 +172,8 @@
         MediaRecorder.AudioSource.CAMCORDER,
         MediaRecorder.AudioSource.VOICE_RECOGNITION,
         MediaRecorder.AudioSource.VOICE_COMMUNICATION,
-        MediaRecorder.AudioSource.UNPROCESSED
+        MediaRecorder.AudioSource.UNPROCESSED,
+        MediaRecorder.AudioSource.VOICE_PERFORMANCE
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface AudioSource {}
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index 45cde0f..de73649 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -541,6 +541,8 @@
     public static final int DEVICE_IN_BUS = DEVICE_BIT_IN | 0x100000;
     public static final int DEVICE_IN_PROXY = DEVICE_BIT_IN | 0x1000000;
     public static final int DEVICE_IN_USB_HEADSET = DEVICE_BIT_IN | 0x2000000;
+    public static final int DEVICE_IN_BLUETOOTH_BLE = DEVICE_BIT_IN | 0x4000000;
+    public static final int DEVICE_IN_ECHO_REFERENCE = DEVICE_BIT_IN | 0x10000000;
     @UnsupportedAppUsage
     public static final int DEVICE_IN_DEFAULT = DEVICE_BIT_IN | DEVICE_BIT_DEFAULT;
 
@@ -567,6 +569,8 @@
                                              DEVICE_IN_BUS |
                                              DEVICE_IN_PROXY |
                                              DEVICE_IN_USB_HEADSET |
+                                             DEVICE_IN_BLUETOOTH_BLE |
+                                             DEVICE_IN_ECHO_REFERENCE |
                                              DEVICE_IN_DEFAULT);
     public static final int DEVICE_IN_ALL_SCO = DEVICE_IN_BLUETOOTH_SCO_HEADSET;
     public static final int DEVICE_IN_ALL_USB = (DEVICE_IN_USB_ACCESSORY |
@@ -641,6 +645,8 @@
     public static final String DEVICE_IN_BUS_NAME = "bus";
     public static final String DEVICE_IN_PROXY_NAME = "proxy";
     public static final String DEVICE_IN_USB_HEADSET_NAME = "usb_headset";
+    public static final String DEVICE_IN_BLUETOOTH_BLE_NAME = "bt_ble";
+    public static final String DEVICE_IN_ECHO_REFERENCE_NAME = "echo_reference";
 
     @UnsupportedAppUsage
     public static String getOutputDeviceName(int device)
@@ -757,6 +763,10 @@
             return DEVICE_IN_PROXY_NAME;
         case DEVICE_IN_USB_HEADSET:
             return DEVICE_IN_USB_HEADSET_NAME;
+        case DEVICE_IN_BLUETOOTH_BLE:
+            return DEVICE_IN_BLUETOOTH_BLE_NAME;
+        case DEVICE_IN_ECHO_REFERENCE:
+            return DEVICE_IN_ECHO_REFERENCE_NAME;
         case DEVICE_IN_DEFAULT:
         default:
             return Integer.toString(device);
diff --git a/media/java/android/media/IMediaSession2Service.aidl b/media/java/android/media/IMediaSession2Service.aidl
new file mode 100644
index 0000000..10ac1be
--- /dev/null
+++ b/media/java/android/media/IMediaSession2Service.aidl
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+import android.os.Bundle;
+import android.media.Controller2Link;
+
+/**
+ * Interface from MediaController2 to MediaSession2Service.
+ * <p>
+ * Keep this interface oneway. Otherwise a malicious app may implement fake version of this,
+ * and holds calls from controller to make controller owner(s) frozen.
+ * @hide
+ */
+oneway interface IMediaSession2Service {
+    void connect(in Controller2Link caller, int seq, in Bundle connectionRequest) = 0;
+    // Next Id : 1
+}
diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java
index 0375de3..bc9500d 100644
--- a/media/java/android/media/MediaCodec.java
+++ b/media/java/android/media/MediaCodec.java
@@ -2289,6 +2289,30 @@
          */
         public static final int ERROR_UNSUPPORTED_OPERATION = 6;
 
+        /**
+         * This indicates that the security level of the device is not
+         * sufficient to meet the requirements set by the content owner
+         * in the license policy.
+         */
+        public static final int ERROR_INSUFFICIENT_SECURITY = 7;
+
+        /**
+         * This indicates that the video frame being decrypted exceeds
+         * the size of the device's protected output buffers. When
+         * encountering this error the app should try playing content
+         * of a lower resolution.
+         */
+        public static final int ERROR_FRAME_TOO_LARGE = 8;
+
+        /**
+         * This error indicates that session state has been
+         * invalidated. It can occur on devices that are not capable
+         * of retaining crypto session state across device
+         * suspend/resume. The session must be closed and a new
+         * session opened to resume operation.
+         */
+        public static final int ERROR_LOST_STATE = 9;
+
         /** @hide */
         @IntDef({
             ERROR_NO_KEY,
@@ -2296,7 +2320,10 @@
             ERROR_RESOURCE_BUSY,
             ERROR_INSUFFICIENT_OUTPUT_PROTECTION,
             ERROR_SESSION_NOT_OPENED,
-            ERROR_UNSUPPORTED_OPERATION
+            ERROR_UNSUPPORTED_OPERATION,
+            ERROR_INSUFFICIENT_SECURITY,
+            ERROR_FRAME_TOO_LARGE,
+            ERROR_LOST_STATE
         })
         @Retention(RetentionPolicy.SOURCE)
         public @interface CryptoErrorCode {}
diff --git a/media/java/android/media/MediaConstants.java b/media/java/android/media/MediaConstants.java
index 275b0ac..5a5747a 100644
--- a/media/java/android/media/MediaConstants.java
+++ b/media/java/android/media/MediaConstants.java
@@ -24,7 +24,7 @@
     static final String KEY_PACKAGE_NAME = "android.media.key.PACKAGE_NAME";
 
     // Bundle key for Parcelable
-    static final String KEY_SESSION2_STUB = "android.media.key.SESSION2_STUB";
+    static final String KEY_SESSION2LINK = "android.media.key.SESSION2LINK";
     static final String KEY_ALLOWED_COMMANDS = "android.media.key.ALLOWED_COMMANDS";
 
     private MediaConstants() {
diff --git a/media/java/android/media/MediaController2.java b/media/java/android/media/MediaController2.java
index b8381a7..165ea41 100644
--- a/media/java/android/media/MediaController2.java
+++ b/media/java/android/media/MediaController2.java
@@ -19,19 +19,23 @@
 import static android.media.MediaConstants.KEY_ALLOWED_COMMANDS;
 import static android.media.MediaConstants.KEY_PACKAGE_NAME;
 import static android.media.MediaConstants.KEY_PID;
-import static android.media.MediaConstants.KEY_SESSION2_STUB;
+import static android.media.MediaConstants.KEY_SESSION2LINK;
 import static android.media.Session2Command.RESULT_ERROR_UNKNOWN_ERROR;
 import static android.media.Session2Command.RESULT_INFO_SKIPPED;
 import static android.media.Session2Token.TYPE_SESSION;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.content.ComponentName;
 import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Process;
+import android.os.RemoteException;
 import android.os.ResultReceiver;
 import android.util.ArrayMap;
 import android.util.ArraySet;
@@ -41,15 +45,15 @@
 
 /**
  * Allows an app to interact with an active {@link MediaSession2} or a
- * {@link MediaSession2Service} which would provide {@link MediaSession2}. Media buttons and other
+ * MediaSession2Service which would provide {@link MediaSession2}. Media buttons and other
  * commands can be sent to the session.
  * <p>
  * This API is not generally intended for third party application developers.
  * Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
  * <a href="{@docRoot}reference/androidx/media2/package-summary.html">Media2 Library</a>
  * for consistent behavior across all devices.
- * @hide
  */
+// TODO: use @link for MediaSession2Service
 public class MediaController2 implements AutoCloseable {
     static final String TAG = "MediaController2";
     static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
@@ -63,6 +67,7 @@
     private final Executor mCallbackExecutor;
     private final Controller2Link mControllerStub;
     private final Handler mResultHandler;
+    private final SessionServiceConnection mServiceConnection;
 
     private final Object mLock = new Object();
     //@GuardedBy("mLock")
@@ -118,20 +123,29 @@
         mPendingCommands = new ArrayMap<>();
         mRequestedCommandSeqNumbers = new ArraySet<>();
 
+        boolean connectRequested;
         if (token.getType() == TYPE_SESSION) {
-            connectToSession();
+            mServiceConnection = null;
+            connectRequested = requestConnectToSession();
         } else {
-            // TODO: Handle connect to session service.
+            mServiceConnection = new SessionServiceConnection();
+            connectRequested = requestConnectToService();
+        }
+        if (!connectRequested) {
+            close();
         }
     }
 
     @Override
     public void close() {
         synchronized (mLock) {
+            if (mServiceConnection != null) {
+                mContext.unbindService(mServiceConnection);
+            }
             if (mSessionBinder != null) {
                 try {
-                    mSessionBinder.unlinkToDeath(mDeathRecipient, 0);
                     mSessionBinder.disconnect(mControllerStub, getNextSeqNumber());
+                    mSessionBinder.unlinkToDeath(mDeathRecipient, 0);
                 } catch (RuntimeException e) {
                     // No-op
                 }
@@ -153,6 +167,7 @@
      * @return a token which will be sent together in {@link ControllerCallback#onCommandResult}
      *        when its result is received.
      */
+    @NonNull
     public Object sendSessionCommand(@NonNull Session2Command command, @Nullable Bundle args) {
         if (command == null) {
             throw new IllegalArgumentException("command shouldn't be null");
@@ -208,7 +223,7 @@
     void onConnected(int seq, Bundle connectionResult) {
         final long token = Binder.clearCallingIdentity();
         try {
-            Session2Link sessionBinder = connectionResult.getParcelable(KEY_SESSION2_STUB);
+            Session2Link sessionBinder = connectionResult.getParcelable(KEY_SESSION2LINK);
             Session2CommandGroup allowedCommands =
                     connectionResult.getParcelable(KEY_ALLOWED_COMMANDS);
             if (DEBUG) {
@@ -298,18 +313,55 @@
         }
     }
 
-    private void connectToSession() {
-        Session2Link sessionBinder = mSessionToken.getSessionLink();
+    private Bundle createConnectionRequest() {
         Bundle connectionRequest = new Bundle();
         connectionRequest.putString(KEY_PACKAGE_NAME, mContext.getPackageName());
         connectionRequest.putInt(KEY_PID, Process.myPid());
+        return connectionRequest;
+    }
 
+    private boolean requestConnectToSession() {
+        Session2Link sessionBinder = mSessionToken.getSessionLink();
+        Bundle connectionRequest = createConnectionRequest();
         try {
             sessionBinder.connect(mControllerStub, getNextSeqNumber(), connectionRequest);
         } catch (RuntimeException e) {
-            Log.w(TAG, "Failed to call connection request. Framework will retry"
-                    + " automatically");
+            Log.w(TAG, "Failed to call connection request", e);
+            return false;
         }
+        return true;
+    }
+
+    private boolean requestConnectToService() {
+        // Service. Needs to get fresh binder whenever connection is needed.
+        final Intent intent = new Intent(MediaSession2Service.SERVICE_INTERFACE);
+        intent.setClassName(mSessionToken.getPackageName(), mSessionToken.getServiceName());
+
+        // Use bindService() instead of startForegroundService() to start session service for three
+        // reasons.
+        // 1. Prevent session service owner's stopSelf() from destroying service.
+        //    With the startForegroundService(), service's call of stopSelf() will trigger immediate
+        //    onDestroy() calls on the main thread even when onConnect() is running in another
+        //    thread.
+        // 2. Minimize APIs for developers to take care about.
+        //    With bindService(), developers only need to take care about Service.onBind()
+        //    but Service.onStartCommand() should be also taken care about with the
+        //    startForegroundService().
+        // 3. Future support for UI-less playback
+        //    If a service wants to keep running, it should be either foreground service or
+        //    bound service. But there had been request for the feature for system apps
+        //    and using bindService() will be better fit with it.
+        synchronized (mLock) {
+            boolean result = mContext.bindService(
+                    intent, mServiceConnection, Context.BIND_AUTO_CREATE);
+            if (!result) {
+                Log.w(TAG, "bind to " + mSessionToken + " failed");
+                return false;
+            } else if (DEBUG) {
+                Log.d(TAG, "bind to " + mSessionToken + " succeeded");
+            }
+        }
+        return true;
     }
 
     /**
@@ -349,7 +401,7 @@
          * @return the result for the session command. A runtime exception will be thrown if null
          *         is returned.
          */
-        @NonNull
+        @Nullable
         public Session2Command.Result onSessionCommand(@NonNull MediaController2 controller,
                 @NonNull Session2Command command, @Nullable Bundle args) {
             return null;
@@ -366,4 +418,59 @@
         public void onCommandResult(@NonNull MediaController2 controller, @NonNull Object token,
                 @NonNull Session2Command command, @NonNull Session2Command.Result result) {}
     }
+
+    // This will be called on the main thread.
+    private class SessionServiceConnection implements ServiceConnection {
+        SessionServiceConnection() {
+        }
+
+        @Override
+        public void onServiceConnected(ComponentName name, IBinder service) {
+            // Note that it's always main-thread.
+            boolean connectRequested = false;
+            try {
+                if (DEBUG) {
+                    Log.d(TAG, "onServiceConnected " + name + " " + this);
+                }
+                // Sanity check
+                if (!mSessionToken.getPackageName().equals(name.getPackageName())) {
+                    Log.wtf(TAG, "Expected connection to " + mSessionToken.getPackageName()
+                            + " but is connected to " + name);
+                    return;
+                }
+                IMediaSession2Service iService = IMediaSession2Service.Stub.asInterface(service);
+                if (iService == null) {
+                    Log.wtf(TAG, "Service interface is missing.");
+                    return;
+                }
+                Bundle connectionRequest = createConnectionRequest();
+                iService.connect(mControllerStub, getNextSeqNumber(), connectionRequest);
+                connectRequested = true;
+            } catch (RemoteException e) {
+                Log.w(TAG, "Service " + name + " has died prematurely", e);
+            } finally {
+                if (!connectRequested) {
+                    close();
+                }
+            }
+        }
+
+        @Override
+        public void onServiceDisconnected(ComponentName name) {
+            // Temporal lose of the binding because of the service crash. System will automatically
+            // rebind, so just no-op.
+            if (DEBUG) {
+                Log.w(TAG, "Session service " + name + " is disconnected.");
+            }
+            close();
+        }
+
+        @Override
+        public void onBindingDied(ComponentName name) {
+            // Permanent lose of the binding because of the service package update or removed.
+            // This SessionServiceRecord will be removed accordingly, but forget session binder here
+            // for sure.
+            close();
+        }
+    }
 }
diff --git a/media/java/android/media/MediaDrm.java b/media/java/android/media/MediaDrm.java
index cdbc7b44..75b3915 100644
--- a/media/java/android/media/MediaDrm.java
+++ b/media/java/android/media/MediaDrm.java
@@ -132,11 +132,19 @@
     private static final String PERMISSION = android.Manifest.permission.ACCESS_DRM_CERTIFICATES;
 
     private EventHandler mEventHandler;
-    private EventHandler mOnKeyStatusChangeEventHandler;
-    private EventHandler mOnExpirationUpdateEventHandler;
+    private EventHandler mKeyStatusChangeHandler;
+    private EventHandler mExpirationUpdateHandler;
+    private EventHandler mSessionLostStateHandler;
+
     private OnEventListener mOnEventListener;
     private OnKeyStatusChangeListener mOnKeyStatusChangeListener;
     private OnExpirationUpdateListener mOnExpirationUpdateListener;
+    private OnSessionLostStateListener mOnSessionLostStateListener;
+
+    private final Object mEventLock = new Object();
+    private final Object mKeyStatusChangeLock = new Object();
+    private final Object mExpirationUpdateLock = new Object();
+    private final Object mSessionLostStateLock = new Object();
 
     private long mNativeContext;
 
@@ -200,6 +208,35 @@
     private static final native boolean isCryptoSchemeSupportedNative(
             @NonNull byte[] uuid, @Nullable String mimeType);
 
+    private EventHandler createHandler() {
+        Looper looper;
+        EventHandler handler;
+        if ((looper = Looper.myLooper()) != null) {
+            handler = new EventHandler(this, looper);
+        } else if ((looper = Looper.getMainLooper()) != null) {
+            handler = new EventHandler(this, looper);
+        } else {
+            handler = null;
+        }
+        return handler;
+    }
+
+    private EventHandler updateHandler(Handler handler) {
+        Looper looper;
+        EventHandler newHandler = null;
+        if (handler != null) {
+            looper = handler.getLooper();
+        } else {
+            looper = Looper.myLooper();
+        }
+        if (looper != null) {
+            if (handler == null || handler.getLooper() != looper) {
+                newHandler = new EventHandler(this, looper);
+            }
+        }
+        return newHandler;
+    }
+
     /**
      * Instantiate a MediaDrm object
      *
@@ -209,14 +246,10 @@
      * specified scheme UUID
      */
     public MediaDrm(@NonNull UUID uuid) throws UnsupportedSchemeException {
-        Looper looper;
-        if ((looper = Looper.myLooper()) != null) {
-            mEventHandler = new EventHandler(this, looper);
-        } else if ((looper = Looper.getMainLooper()) != null) {
-            mEventHandler = new EventHandler(this, looper);
-        } else {
-            mEventHandler = null;
-        }
+        mEventHandler = createHandler();
+        mKeyStatusChangeHandler = createHandler();
+        mExpirationUpdateHandler = createHandler();
+        mSessionLostStateHandler = createHandler();
 
         /* Native setup requires a weak reference to our object.
          * It's easier to create it here than in C++.
@@ -272,6 +305,40 @@
     }
 
     /**
+     * Thrown when an error occurs in any method that has a session context.
+     */
+    public static final class SessionException extends RuntimeException {
+        public SessionException(int errorCode, @Nullable String detailMessage) {
+            super(detailMessage);
+            mErrorCode = errorCode;
+        }
+
+        /**
+         * This indicates that apps using MediaDrm sessions are
+         * temporarily exceeding the capacity of available crypto
+         * resources. The app should retry the operation later.
+         */
+        public static final int ERROR_RESOURCE_CONTENTION = 1;
+
+        /** @hide */
+        @IntDef({
+            ERROR_RESOURCE_CONTENTION,
+        })
+        @Retention(RetentionPolicy.SOURCE)
+        public @interface SessionErrorCode {}
+
+        /**
+         * Retrieve the error code associated with the SessionException
+         */
+        @SessionErrorCode
+        public int getErrorCode() {
+            return mErrorCode;
+        }
+
+        private final int mErrorCode;
+    }
+
+    /**
      * Register a callback to be invoked when a session expiration update
      * occurs.  The app's OnExpirationUpdateListener will be notified
      * when the expiration time of the keys in the session have changed.
@@ -282,15 +349,12 @@
      */
     public void setOnExpirationUpdateListener(
             @Nullable OnExpirationUpdateListener listener, @Nullable Handler handler) {
-        if (listener != null) {
-            Looper looper = handler != null ? handler.getLooper() : Looper.myLooper();
-            if (looper != null) {
-                if (mEventHandler == null || mEventHandler.getLooper() != looper) {
-                    mEventHandler = new EventHandler(this, looper);
-                }
+        synchronized(mExpirationUpdateLock) {
+            if (listener != null) {
+                mExpirationUpdateHandler = updateHandler(handler);
             }
+            mOnExpirationUpdateListener = listener;
         }
-        mOnExpirationUpdateListener = listener;
     }
 
     /**
@@ -324,15 +388,12 @@
      */
     public void setOnKeyStatusChangeListener(
             @Nullable OnKeyStatusChangeListener listener, @Nullable Handler handler) {
-        if (listener != null) {
-            Looper looper = handler != null ? handler.getLooper() : Looper.myLooper();
-            if (looper != null) {
-                if (mEventHandler == null || mEventHandler.getLooper() != looper) {
-                    mEventHandler = new EventHandler(this, looper);
-                }
+        synchronized(mKeyStatusChangeLock) {
+            if (listener != null) {
+                mKeyStatusChangeHandler = updateHandler(handler);
             }
+            mOnKeyStatusChangeListener = listener;
         }
-        mOnKeyStatusChangeListener = listener;
     }
 
     /**
@@ -360,6 +421,46 @@
     }
 
     /**
+     * Register a callback to be invoked when session state has been
+     * lost. This event can occur on devices that are not capable of
+     * retaining crypto session state across device suspend/resume
+     * cycles.  When this event occurs, the session must be closed and
+     * a new session opened to resume operation.
+     *
+     * @param listener the callback that will be run, or {@code null} to unregister the
+     *     previously registered callback.
+     * @param handler the handler on which the listener should be invoked, or
+     *     {@code null} if the listener should be invoked on the calling thread's looper.
+     */
+    public void setOnSessionLostStateListener(
+            @Nullable OnSessionLostStateListener listener, @Nullable Handler handler) {
+        synchronized(mSessionLostStateLock) {
+            if (listener != null) {
+                mSessionLostStateHandler = updateHandler(handler);
+            }
+            mOnSessionLostStateListener = listener;
+        }
+    }
+
+    /**
+     * Interface definition for a callback to be invoked when the
+     * session state has been lost and is now invalid
+     */
+    public interface OnSessionLostStateListener
+    {
+        /**
+         * Called when session state has lost state, to inform the app
+         * about the condition so it can close the session and open a new
+         * one to resume operation.
+         *
+         * @param md the MediaDrm object on which the event occurred
+         * @param sessionId the DRM session ID on which the event occurred
+         */
+        void onSessionLostState(
+                @NonNull MediaDrm md, @NonNull byte[] sessionId);
+    }
+
+    /**
      * Defines the status of a key.
      * A KeyStatus for each key in a session is provided to the
      * {@link OnKeyStatusChangeListener#onKeyStatusChange}
@@ -437,7 +538,9 @@
      */
     public void setOnEventListener(@Nullable OnEventListener listener)
     {
-        mOnEventListener = listener;
+        synchronized(mEventLock) {
+            mOnEventListener = listener;
+        }
     }
 
     /**
@@ -513,6 +616,7 @@
     private static final int DRM_EVENT = 200;
     private static final int EXPIRATION_UPDATE = 201;
     private static final int KEY_STATUS_CHANGE = 202;
+    private static final int SESSION_LOST_STATE = 203;
 
     private class EventHandler extends Handler
     {
@@ -532,52 +636,72 @@
             switch(msg.what) {
 
             case DRM_EVENT:
-                if (mOnEventListener != null) {
-                    if (msg.obj != null && msg.obj instanceof Parcel) {
-                        Parcel parcel = (Parcel)msg.obj;
-                        byte[] sessionId = parcel.createByteArray();
-                        if (sessionId.length == 0) {
-                            sessionId = null;
-                        }
-                        byte[] data = parcel.createByteArray();
-                        if (data.length == 0) {
-                            data = null;
-                        }
+                synchronized(mEventLock) {
+                    if (mOnEventListener != null) {
+                        if (msg.obj != null && msg.obj instanceof Parcel) {
+                            Parcel parcel = (Parcel)msg.obj;
+                            byte[] sessionId = parcel.createByteArray();
+                            if (sessionId.length == 0) {
+                                sessionId = null;
+                            }
+                            byte[] data = parcel.createByteArray();
+                            if (data.length == 0) {
+                                data = null;
+                            }
 
-                        Log.i(TAG, "Drm event (" + msg.arg1 + "," + msg.arg2 + ")");
-                        mOnEventListener.onEvent(mMediaDrm, sessionId, msg.arg1, msg.arg2, data);
+                            Log.i(TAG, "Drm event (" + msg.arg1 + "," + msg.arg2 + ")");
+                            mOnEventListener.onEvent(mMediaDrm, sessionId, msg.arg1, msg.arg2, data);
+                        }
                     }
                 }
                 return;
 
             case KEY_STATUS_CHANGE:
-                if (mOnKeyStatusChangeListener != null) {
-                    if (msg.obj != null && msg.obj instanceof Parcel) {
-                        Parcel parcel = (Parcel)msg.obj;
-                        byte[] sessionId = parcel.createByteArray();
-                        if (sessionId.length > 0) {
-                            List<KeyStatus> keyStatusList = keyStatusListFromParcel(parcel);
-                            boolean hasNewUsableKey = (parcel.readInt() != 0);
+                synchronized(mKeyStatusChangeLock) {
+                    if (mOnKeyStatusChangeListener != null) {
+                        if (msg.obj != null && msg.obj instanceof Parcel) {
+                            Parcel parcel = (Parcel)msg.obj;
+                            byte[] sessionId = parcel.createByteArray();
+                            if (sessionId.length > 0) {
+                                List<KeyStatus> keyStatusList = keyStatusListFromParcel(parcel);
+                                boolean hasNewUsableKey = (parcel.readInt() != 0);
 
-                            Log.i(TAG, "Drm key status changed");
-                            mOnKeyStatusChangeListener.onKeyStatusChange(mMediaDrm, sessionId,
-                                    keyStatusList, hasNewUsableKey);
+                                Log.i(TAG, "Drm key status changed");
+                                mOnKeyStatusChangeListener.onKeyStatusChange(mMediaDrm, sessionId,
+                                        keyStatusList, hasNewUsableKey);
+                            }
                         }
                     }
                 }
                 return;
 
             case EXPIRATION_UPDATE:
-                if (mOnExpirationUpdateListener != null) {
-                    if (msg.obj != null && msg.obj instanceof Parcel) {
-                        Parcel parcel = (Parcel)msg.obj;
-                        byte[] sessionId = parcel.createByteArray();
-                        if (sessionId.length > 0) {
-                            long expirationTime = parcel.readLong();
+                synchronized(mExpirationUpdateLock) {
+                    if (mOnExpirationUpdateListener != null) {
+                        if (msg.obj != null && msg.obj instanceof Parcel) {
+                            Parcel parcel = (Parcel)msg.obj;
+                            byte[] sessionId = parcel.createByteArray();
+                            if (sessionId.length > 0) {
+                                long expirationTime = parcel.readLong();
 
-                            Log.i(TAG, "Drm key expiration update: " + expirationTime);
-                            mOnExpirationUpdateListener.onExpirationUpdate(mMediaDrm, sessionId,
-                                    expirationTime);
+                                Log.i(TAG, "Drm key expiration update: " + expirationTime);
+                                mOnExpirationUpdateListener.onExpirationUpdate(mMediaDrm, sessionId,
+                                        expirationTime);
+                            }
+                        }
+                    }
+                }
+                return;
+
+            case SESSION_LOST_STATE:
+                synchronized(mSessionLostStateLock) {
+                    if (mOnSessionLostStateListener != null) {
+                        if (msg.obj != null && msg.obj instanceof Parcel) {
+                            Parcel parcel = (Parcel)msg.obj;
+                            byte[] sessionId = parcel.createByteArray();
+                            Log.i(TAG, "Drm session lost state event: ");
+                            mOnSessionLostStateListener.onSessionLostState(mMediaDrm,
+                                    sessionId);
                         }
                     }
                 }
@@ -619,9 +743,42 @@
         if (md == null) {
             return;
         }
-        if (md.mEventHandler != null) {
-            Message m = md.mEventHandler.obtainMessage(what, eventType, extra, obj);
-            md.mEventHandler.sendMessage(m);
+        switch (what) {
+            case DRM_EVENT:
+                synchronized(md.mEventLock) {
+                    if (md.mEventHandler != null) {
+                        Message m = md.mEventHandler.obtainMessage(what, eventType, extra, obj);
+                        md.mEventHandler.sendMessage(m);
+                    }
+                }
+                break;
+            case EXPIRATION_UPDATE:
+                synchronized(md.mExpirationUpdateLock) {
+                    if (md.mExpirationUpdateHandler != null) {
+                        Message m = md.mExpirationUpdateHandler.obtainMessage(what, obj);
+                        md.mExpirationUpdateHandler.sendMessage(m);
+                    }
+                }
+                break;
+            case KEY_STATUS_CHANGE:
+                synchronized(md.mKeyStatusChangeLock) {
+                    if (md.mKeyStatusChangeHandler != null) {
+                        Message m = md.mKeyStatusChangeHandler.obtainMessage(what, obj);
+                        md.mKeyStatusChangeHandler.sendMessage(m);
+                    }
+                }
+                break;
+            case SESSION_LOST_STATE:
+                synchronized(md.mSessionLostStateLock) {
+                    if (md.mSessionLostStateHandler != null) {
+                        Message m = md.mSessionLostStateHandler.obtainMessage(what, obj);
+                        md.mSessionLostStateHandler.sendMessage(m);
+                    }
+                }
+                break;
+            default:
+                Log.e(TAG, "Unknown message type " + what);
+                break;
         }
     }
 
diff --git a/media/java/android/media/MediaPlayer2.java b/media/java/android/media/MediaPlayer2.java
index df96994..aa79c41 100644
--- a/media/java/android/media/MediaPlayer2.java
+++ b/media/java/android/media/MediaPlayer2.java
@@ -60,6 +60,7 @@
 import java.nio.ByteOrder;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.LinkedList;
@@ -67,6 +68,7 @@
 import java.util.Map;
 import java.util.Queue;
 import java.util.UUID;
+import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.ConcurrentLinkedQueue;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.Executor;
@@ -399,8 +401,7 @@
         clearSourceInfos();
 
         // Modular DRM clean up
-        mOnDrmConfigHelper = null;
-        synchronized (mDrmEventCbLock) {
+        synchronized (mDrmEventCallbackLock) {
             mDrmEventCallbackRecords.clear();
         }
 
@@ -775,7 +776,7 @@
                 }
                 boolean hasError = false;
                 for (DataSourceDesc dsd : dsds) {
-                    if (dsd != null) {
+                    if (dsd == null) {
                         hasError = true;
                         continue;
                     }
@@ -2889,7 +2890,7 @@
     }
 
     private void sendDrmEvent(final DrmEventNotifier notifier) {
-        synchronized (mDrmEventCbLock) {
+        synchronized (mDrmEventCallbackLock) {
             try {
                 for (Pair<Executor, DrmEventCallback> cb : mDrmEventCallbackRecords) {
                     cb.first.execute(() -> notifier.notify(cb.second));
@@ -2901,12 +2902,28 @@
         }
     }
 
+    private <T> T sendDrmEventWait(final DrmEventNotifier<T> notifier)
+            throws InterruptedException, ExecutionException {
+        synchronized (mDrmEventCallbackLock) {
+            mDrmEventCallbackRecords.get(0);
+            for (Pair<Executor, DrmEventCallback> cb : mDrmEventCallbackRecords) {
+                CompletableFuture<T> ret = new CompletableFuture<>();
+                cb.first.execute(() -> ret.complete(notifier.notifyWait(cb.second)));
+                return ret.get();
+            }
+        }
+        return null;
+    }
+
     private interface EventNotifier {
         void notify(EventCallback callback);
     }
 
-    private interface DrmEventNotifier {
-        void notify(DrmEventCallback callback);
+    private interface DrmEventNotifier<T> {
+        default void notify(DrmEventCallback callback) { }
+        default T notifyWait(DrmEventCallback callback) {
+            return null;
+        }
     }
 
     /* Do not change these values without updating their counterparts
@@ -3351,73 +3368,209 @@
     // Modular DRM begin
 
     /**
-     * Interface definition of a callback to be invoked when the app
-     * can do DRM configuration (get/set properties) before the session
-     * is opened. This facilitates configuration of the properties, like
-     * 'securityLevel', which has to be set after DRM scheme creation but
-     * before the DRM session is opened.
+     * An immutable structure per {@link DataSourceDesc} with settings required to initiate a DRM
+     * protected playback session.
      *
-     * The only allowed DRM calls in this listener are
-     * {@link MediaPlayer2#getDrmPropertyString(DataSourceDesc, String)}
-     * and {@link MediaPlayer2#setDrmPropertyString(DataSourceDesc, String, String)}.
-     * @hide
+     * @see DrmPreparationInfo.Builder
      */
-    public interface OnDrmConfigHelper {
+    public static final class DrmPreparationInfo {
+
         /**
-         * Called to give the app the opportunity to configure DRM before the session is created
-         *
-         * @param mp the {@code MediaPlayer2} associated with this callback
-         * @param dsd the DataSourceDesc of this data source
+         * Mutable builder to create a {@link MediaPlayer2.DrmPreparationInfo} object.
          */
-        public void onDrmConfig(MediaPlayer2 mp, DataSourceDesc dsd);
-    }
+        public static final class Builder {
 
-    /**
-     * Register a callback to be invoked for configuration of the DRM object before
-     * the session is created.
-     * The callback will be invoked synchronously during the execution
-     * of {@link #prepareDrm}.
-     *
-     * @param listener the callback that will be run
-     * @hide
-     */
-    // This is a synchronous call.
-    public void setOnDrmConfigHelper(OnDrmConfigHelper listener) {
-        mOnDrmConfigHelper = listener;
-    }
+            private UUID mUUID;
+            private byte[] mKeySetId;
+            private byte[] mInitData;
+            private String mMimeType;
+            private int mKeyType;
+            private Map<String, String> mOptionalParameters;
 
-    private OnDrmConfigHelper mOnDrmConfigHelper;
+            /**
+             * Set UUID of the crypto scheme selected to decrypt content. An UUID can be retrieved from
+             * the source listening to {@link MediaPlayer2.DrmEventCallback#onDrmInfo}.
+             *
+             * @param uuid of selected crypto scheme
+             * @return this
+             */
+            public Builder setUuid(@NonNull UUID uuid) {
+                this.mUUID = uuid;
+                return this;
+            }
+
+            /**
+             * Set identifier of a persisted offline key obtained from
+             * {@link MediaPlayer2.DrmEventCallback#onDrmPrepared(MediaPlayer2, DataSourceDesc, int, byte[])}.
+             *
+             * A {@code keySetId} can be used to restore persisted offline keys into a new playback
+             * session of a DRM protected data source. When {@code keySetId} is set, {@code initData},
+             * {@code mimeType}, {@code keyType}, {@code optionalParameters} are ignored.
+             *
+             * @param keySetId identifier of a persisted offline key
+             * @return this
+             */
+            public Builder setKeySetId(@Nullable byte[] keySetId) {
+                this.mKeySetId = keySetId;
+                return this;
+            }
+
+            /**
+             * Set container-specific DRM initialization data. Its meaning is interpreted based on
+             * {@code mimeType}. For example, it could contain the content ID, key ID or other data
+             * obtained from the content metadata that is required to generate a
+             * {@link MediaDrm.KeyRequest}.
+             *
+             * @param initData container-specific DRM initialization data
+             * @return this
+             */
+            public Builder setInitData(@Nullable byte[] initData) {
+                this.mInitData = initData;
+                return this;
+            }
+
+            /**
+             * Set mime type of the content
+             *
+             * @param mimeType mime type to the content
+             * @return this
+             */
+            public Builder setMimeType(@Nullable String mimeType) {
+                this.mMimeType = mimeType;
+                return this;
+            }
+
+            /**
+             * Set type of the key request. The request may be to acquire keys
+             * for streaming, {@link MediaDrm#KEY_TYPE_STREAMING}, or for offline content,
+             * {@link MediaDrm#KEY_TYPE_OFFLINE}. Releasing previously acquired keys
+             * ({@link MediaDrm#KEY_TYPE_RELEASE}) is not allowed.
+             *
+             * @param keyType type of the key request
+             * @return this
+             */
+            public Builder setKeyType(@MediaPlayer2.MediaDrmKeyType int keyType) {
+                this.mKeyType = keyType;
+                return this;
+            }
+
+            /**
+             * Set optional parameters to be included in a {@link MediaDrm.KeyRequest} message sent to
+             * the license server.
+             *
+             * @param optionalParameters optional parameters to be included in a key request
+             * @return this
+             */
+            public Builder setOptionalParameters(
+                    @Nullable Map<String, String> optionalParameters) {
+                this.mOptionalParameters = optionalParameters;
+                return this;
+            }
+
+            /**
+             * @return an immutable {@link MediaPlayer2.DrmPreparationInfo} representing the settings of this builder
+             */
+            public MediaPlayer2.DrmPreparationInfo build() {
+                return new MediaPlayer2.DrmPreparationInfo(mUUID, mKeySetId, mInitData, mMimeType, mKeyType,
+                        mOptionalParameters);
+            }
+
+        }
+
+        private final UUID mUUID;
+        private final byte[] mKeySetId;
+        private final byte[] mInitData;
+        private final String mMimeType;
+        private final int mKeyType;
+        private final Map<String, String> mOptionalParameters;
+
+        private DrmPreparationInfo(UUID mUUID, byte[] mKeySetId, byte[] mInitData, String mMimeType,
+                int mKeyType, Map<String, String> optionalParameters) {
+            this.mUUID = mUUID;
+            this.mKeySetId = mKeySetId;
+            this.mInitData = mInitData;
+            this.mMimeType = mMimeType;
+            this.mKeyType = mKeyType;
+            this.mOptionalParameters = optionalParameters;
+        }
+
+    }
 
     /**
      * Interface definition for callbacks to be invoked when the player has the corresponding
      * DRM events.
-     * @hide
      */
     public static class DrmEventCallback {
         /**
-         * Called to indicate DRM info is available
+         * Called to indicate DRM info is available. Return a {@link DrmPreparationInfo} object that
+         * bundles DRM initialization parameters.
          *
          * @param mp the {@code MediaPlayer2} associated with this callback
-         * @param dsd the DataSourceDesc of this data source
-         * @param drmInfo DRM info of the source including PSSH, and subset
-         *                of crypto schemes supported by this device
+         * @param dsd the {@link DataSourceDesc} of this data source
+         * @param drmInfo DRM info of the source including PSSH, and subset of crypto schemes
+         *        supported by this device
+         * @return a {@link DrmPreparationInfo} object to initialize DRM playback, or null to skip
+         *         DRM initialization
          */
-        public void onDrmInfo(MediaPlayer2 mp, DataSourceDesc dsd, DrmInfo drmInfo) { }
+        public DrmPreparationInfo onDrmInfo(MediaPlayer2 mp, DataSourceDesc dsd, DrmInfo drmInfo) {
+            return null;
+        }
 
         /**
-         * Called to notify the client that {@link MediaPlayer2#prepareDrm(DataSourceDesc, UUID)}
-         * is finished and ready for key request/response.
+         * Called to notify the client that {@code mp} is ready to decrypt DRM protected data source
+         * {@code dsd}
          *
          * @param mp the {@code MediaPlayer2} associated with this callback
-         * @param dsd the DataSourceDesc of this data source
+         * @param dsd the {@link DataSourceDesc} of this data source
          * @param status the result of DRM preparation.
+         * @param keySetId optional identifier that can be used to restore DRM playback initiated
+         *        with a {@link MediaDrm#KEY_TYPE_OFFLINE} key request.
+         *
+         * @see DrmPreparationInfo.Builder#setKeySetId(byte[])
          */
-        public void onDrmPrepared(
-                MediaPlayer2 mp, DataSourceDesc dsd, @PrepareDrmStatusCode int status) { }
+        public void onDrmPrepared(@NonNull MediaPlayer2 mp, @NonNull DataSourceDesc dsd,
+                @PrepareDrmStatusCode int status, @Nullable byte[] keySetId) { }
+
+        /**
+         * Called to give the app the opportunity to configure DRM before the session is created.
+         *
+         * This facilitates configuration of the properties, like 'securityLevel', which
+         * has to be set after DRM scheme creation but before the DRM session is opened.
+         *
+         * The only allowed DRM calls in this listener are
+         * {@link MediaDrm#getPropertyString(String)},
+         * {@link MediaDrm#getPropertyByteArray(String)},
+         * {@link MediaDrm#setPropertyString(String, String)},
+         * {@link MediaDrm#setPropertyByteArray(String, byte[])},
+         * {@link MediaDrm#setOnExpirationUpdateListener},
+         * and {@link MediaDrm#setOnKeyStatusChangeListener}.
+         *
+         * @param mp the {@code MediaPlayer2} associated with this callback
+         * @param dsd the {@link DataSourceDesc} of this data source
+         * @param drm handle to get/set DRM properties and listeners for this data source
+         */
+        public void onDrmConfig(@NonNull MediaPlayer2 mp, @NonNull DataSourceDesc dsd,
+                @NonNull MediaDrm drm) { }
+
+        /**
+         * Called to indicate the DRM session for {@code dsd} is ready for key request/response
+         *
+         * @param mp the {@code MediaPlayer2} associated with this callback
+         * @param dsd the {@link DataSourceDesc} of this data source
+         * @param request a {@link MediaDrm.KeyRequest} prepared using the
+         *        {@link DrmPreparationInfo} returned from
+         *        {@link #onDrmInfo(MediaPlayer2, DataSourceDesc, DrmInfo)}
+         * @return the response to {@code request} (from license server)
+         */
+        public byte[] onDrmKeyRequest(@NonNull MediaPlayer2 mp, @NonNull DataSourceDesc dsd,
+                @NonNull MediaDrm.KeyRequest request) {
+            return null;
+        }
+
     }
 
-    private final Object mDrmEventCbLock = new Object();
-    private ArrayList<Pair<Executor, DrmEventCallback>> mDrmEventCallbackRecords =
+    private final Object mDrmEventCallbackLock = new Object();
+    private List<Pair<Executor, DrmEventCallback>> mDrmEventCallbackRecords =
             new ArrayList<Pair<Executor, DrmEventCallback>>();
 
     /**
@@ -3425,10 +3578,9 @@
      *
      * @param eventCallback the callback that will be run
      * @param executor the executor through which the callback should be invoked
-     * @hide
      */
     // This is a synchronous call.
-    public void registerDrmEventCallback(@NonNull @CallbackExecutor Executor executor,
+    public void setDrmEventCallback(@NonNull @CallbackExecutor Executor executor,
             @NonNull DrmEventCallback eventCallback) {
         if (eventCallback == null) {
             throw new IllegalArgumentException("Illegal null EventCallback");
@@ -3437,8 +3589,9 @@
             throw new IllegalArgumentException(
                     "Illegal null Executor for the EventCallback");
         }
-        synchronized (mDrmEventCbLock) {
-            mDrmEventCallbackRecords.add(new Pair(executor, eventCallback));
+        synchronized (mDrmEventCallbackLock) {
+            mDrmEventCallbackRecords = Collections.singletonList(
+                    new Pair<Executor, DrmEventCallback>(executor, eventCallback));
         }
     }
 
@@ -3450,7 +3603,7 @@
      */
     // This is a synchronous call.
     public void unregisterDrmEventCallback(DrmEventCallback eventCallback) {
-        synchronized (mDrmEventCbLock) {
+        synchronized (mDrmEventCallbackLock) {
             for (Pair<Executor, DrmEventCallback> cb : mDrmEventCallbackRecords) {
                 if (cb.second == eventCallback) {
                     mDrmEventCallbackRecords.remove(cb);
@@ -3564,7 +3717,7 @@
     /**
      * Prepares the DRM for the given data source
      * <p>
-     * If {@link OnDrmConfigHelper} is registered, it will be called during
+     * If {@link DrmEventCallback} is registered, it will be called during
      * preparation to allow configuration of the DRM properties before opening the
      * DRM session. It should be used only for a series of
      * {@link #getDrmPropertyString(DataSourceDesc, String)} and
@@ -3587,8 +3740,7 @@
      * @param dsd The DRM protected data source
      *
      * @param uuid The UUID of the crypto scheme. If not known beforehand, it can be retrieved
-     * from the source through {@link #getDrmInfo(DataSourceDesc)} or registering a
-     * {@link DrmEventCallback#onDrmInfo}.
+     * from the source listening to {@link DrmEventCallback#onDrmInfo}.
      *
      * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
      * @hide
@@ -3661,8 +3813,8 @@
                     sendDrmEvent(new DrmEventNotifier() {
                         @Override
                         public void notify(DrmEventCallback callback) {
-                            callback.onDrmPrepared(
-                                    MediaPlayer2.this, dsd, prepareDrmStatus);
+                            callback.onDrmPrepared(MediaPlayer2.this, dsd, prepareDrmStatus,
+                                    /* TODO: keySetId */ null);
                         }
                     });
 
@@ -3876,7 +4028,6 @@
 
     /**
      * Encapsulates the DRM properties of the source.
-     * @hide
      */
     public static final class DrmInfo {
         private Map<UUID, byte[]> mMapPssh;
@@ -4013,10 +4164,8 @@
     };  // DrmInfo
 
     /**
-     * Thrown when a DRM method is called before preparing a DRM scheme through
-     * {@link MediaPlayer2#prepareDrm(DataSourceDesc, UUID)}.
+     * Thrown when a DRM method is called when there is no active DRM session.
      * Extends MediaDrm.MediaDrmException
-     * @hide
      */
     public static final class NoDrmSchemeException extends MediaDrmException {
         public NoDrmSchemeException(String detailMessage) {
@@ -4291,9 +4440,9 @@
         }
 
         void prepare(UUID uuid) throws UnsupportedSchemeException,
-                ResourceBusyException, NotProvisionedException {
-            final OnDrmConfigHelper onDrmConfigHelper = mOnDrmConfigHelper;
-            Log.v(TAG, "prepareDrm: uuid: " + uuid + " mOnDrmConfigHelper: " + onDrmConfigHelper);
+                ResourceBusyException, NotProvisionedException, InterruptedException,
+                ExecutionException {
+            Log.v(TAG, "prepareDrm: uuid: " + uuid);
 
             synchronized (this) {
                 if (mActiveDrmUUID != null) {
@@ -4334,9 +4483,13 @@
             }  // synchronized
 
             // call the callback outside the lock
-            if (onDrmConfigHelper != null)  {
-                onDrmConfigHelper.onDrmConfig(MediaPlayer2.this, mDSD);
-            }
+            sendDrmEventWait(new DrmEventNotifier<Void>() {
+                @Override
+                public Void notifyWait(DrmEventCallback callback) {
+                    callback.onDrmConfig(MediaPlayer2.this, mDSD, mDrmObj);
+                    return null;
+                }
+            });
 
             synchronized (this) {
                 mDrmConfigAllowed = false;
@@ -4506,7 +4659,7 @@
                 @Override
                 public void notify(DrmEventCallback callback) {
                     callback.onDrmPrepared(
-                            MediaPlayer2.this, mDSD, finalStatus);
+                            MediaPlayer2.this, mDSD, finalStatus, /* TODO: keySetId */ null);
                 }
             });
 
diff --git a/media/java/android/media/MediaRecorder.java b/media/java/android/media/MediaRecorder.java
index 25b1df2..1304afe 100644
--- a/media/java/android/media/MediaRecorder.java
+++ b/media/java/android/media/MediaRecorder.java
@@ -302,6 +302,34 @@
          *  {@link #DEFAULT} otherwise. */
         public static final int UNPROCESSED = 9;
 
+
+        /**
+         * Source for capturing audio meant to be processed in real time and played back for live
+         * performance (e.g karaoke).
+         * <p>
+         * The capture path will minimize latency and coupling with
+         * playback path.
+         * </p>
+         */
+        public static final int VOICE_PERFORMANCE = 10;
+
+        /**
+         * Source for an echo canceller to capture the reference signal to be cancelled.
+         * <p>
+         * The echo reference signal will be captured as close as possible to the DAC in order
+         * to include all post processing applied to the playback path.
+         * </p><p>
+         * Capturing the echo reference requires the
+         * {@link android.Manifest.permission#CAPTURE_AUDIO_OUTPUT} permission.
+         * This permission is reserved for use by system components and is not available to
+         * third-party applications.
+         * </p>
+         * @hide
+         */
+        @SystemApi
+        @RequiresPermission(android.Manifest.permission.CAPTURE_AUDIO_OUTPUT)
+        public static final int ECHO_REFERENCE = 1997;
+
         /**
          * Audio source for capturing broadcast radio tuner output.
          * @hide
@@ -343,6 +371,7 @@
         case AudioSource.VOICE_COMMUNICATION:
         //case REMOTE_SUBMIX:  considered "system" as it requires system permissions
         case AudioSource.UNPROCESSED:
+        case AudioSource.VOICE_PERFORMANCE:
             return false;
         default:
             return true;
@@ -372,6 +401,10 @@
             return "REMOTE_SUBMIX";
         case AudioSource.UNPROCESSED:
             return "UNPROCESSED";
+        case AudioSource.ECHO_REFERENCE:
+            return "ECHO_REFERENCE";
+        case AudioSource.VOICE_PERFORMANCE:
+            return "VOICE_PERFORMANCE";
         case AudioSource.RADIO_TUNER:
             return "RADIO_TUNER";
         case AudioSource.HOTWORD:
@@ -524,7 +557,7 @@
      * @see android.media.MediaRecorder.AudioSource
      */
     public static final int getAudioSourceMax() {
-        return AudioSource.UNPROCESSED;
+        return AudioSource.VOICE_PERFORMANCE;
     }
 
     /**
diff --git a/media/java/android/media/MediaSession2.java b/media/java/android/media/MediaSession2.java
index 1ee851f..dceef34 100644
--- a/media/java/android/media/MediaSession2.java
+++ b/media/java/android/media/MediaSession2.java
@@ -19,7 +19,7 @@
 import static android.media.MediaConstants.KEY_ALLOWED_COMMANDS;
 import static android.media.MediaConstants.KEY_PACKAGE_NAME;
 import static android.media.MediaConstants.KEY_PID;
-import static android.media.MediaConstants.KEY_SESSION2_STUB;
+import static android.media.MediaConstants.KEY_SESSION2LINK;
 import static android.media.Session2Command.RESULT_ERROR_UNKNOWN_ERROR;
 import static android.media.Session2Command.RESULT_INFO_SKIPPED;
 import static android.media.Session2Token.TYPE_SESSION;
@@ -31,7 +31,6 @@
 import android.content.Intent;
 import android.media.session.MediaSessionManager;
 import android.media.session.MediaSessionManager.RemoteUserInfo;
-import android.os.Binder;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Process;
@@ -41,7 +40,6 @@
 import android.util.Log;
 
 import java.util.ArrayList;
-import java.util.Collection;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -56,10 +54,9 @@
  * Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
  * <a href="{@docRoot}reference/androidx/media2/package-summary.html">Media2 Library</a>
  * for consistent behavior across all devices.
- * @hide
  */
 public class MediaSession2 implements AutoCloseable {
-    static final String TAG = "MediaSession";
+    static final String TAG = "MediaSession2";
     static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
     // Note: This checks the uniqueness of a session ID only in a single process.
@@ -89,7 +86,6 @@
     private final Handler mResultHandler;
 
     //@GuardedBy("mLock")
-    @SuppressWarnings("WeakerAccess") /* synthetic access */
     private boolean mClosed;
 
     MediaSession2(@NonNull Context context, @NonNull String id, PendingIntent sessionActivity,
@@ -113,20 +109,25 @@
                 Context.MEDIA_SESSION_SERVICE);
         // NOTE: mResultHandler uses main looper, so this MUST NOT be blocked.
         mResultHandler = new Handler(context.getMainLooper());
+        mClosed = false;
     }
 
     @Override
     public void close() {
         try {
+            List<ControllerInfo> controllerInfos;
+            synchronized (mLock) {
+                if (mClosed) {
+                    return;
+                }
+                mClosed = true;
+                controllerInfos = getConnectedControllers();
+                mConnectedControllers.clear();
+                mCallback.onSessionClosed(this);
+            }
             synchronized (MediaSession2.class) {
                 SESSION_ID_LIST.remove(mSessionId);
             }
-            Collection<ControllerInfo> controllerInfos;
-            synchronized (mLock) {
-                controllerInfos = mConnectedControllers.values();
-                mConnectedControllers.clear();
-                mClosed = true;
-            }
             for (ControllerInfo info : controllerInfos) {
                 info.notifyDisconnected();
             }
@@ -161,10 +162,7 @@
         if (command == null) {
             throw new IllegalArgumentException("command shouldn't be null");
         }
-        Collection<ControllerInfo> controllerInfos;
-        synchronized (mLock) {
-            controllerInfos = mConnectedControllers.values();
-        }
+        List<ControllerInfo> controllerInfos = getConnectedControllers();
         for (ControllerInfo controller : controllerInfos) {
             controller.sendSessionCommand(command, args, null);
         }
@@ -179,6 +177,7 @@
      * @return a token which will be sent together in {@link SessionCallback#onCommandResult}
      *     when its result is received.
      */
+    @NonNull
     public Object sendSessionCommand(@NonNull ControllerInfo controller,
             @NonNull Session2Command command, @Nullable Bundle args) {
         if (controller == null) {
@@ -206,7 +205,10 @@
      * @param controller the controller to get the session command
      * @param token the token which is returned from {@link #sendSessionCommand}.
      */
-    public void cancelSessionCommand(ControllerInfo controller, Object token) {
+    public void cancelSessionCommand(@NonNull ControllerInfo controller, @NonNull Object token) {
+        if (controller == null) {
+            throw new IllegalArgumentException("controller shouldn't be null");
+        }
         if (token == null) {
             throw new IllegalArgumentException("token shouldn't be null");
         }
@@ -219,23 +221,26 @@
         }
     }
 
-    // Called by Session2Link.onConnect
-    void onConnect(final Controller2Link controller, int seq, Bundle connectionRequest) {
-        if (controller == null || connectionRequest == null) {
-            return;
+    SessionCallback getCallback() {
+        return mCallback;
+    }
+
+    // Called by Session2Link.onConnect and MediaSession2Service.MediaSession2ServiceStub.connect
+    void onConnect(final Controller2Link controller, int callingPid, int callingUid, int seq,
+            Bundle connectionRequest) {
+        if (callingPid == 0) {
+            // The pid here is from Binder.getCallingPid(), which can be 0 for an oneway call from
+            // the remote process. If it's the case, use PID from the connectionRequest.
+            callingPid = connectionRequest.getInt(KEY_PID);
         }
-        final int uid = Binder.getCallingUid();
-        final int callingPid = Binder.getCallingPid();
-        final long token = Binder.clearCallingIdentity();
-        // Binder.getCallingPid() can be 0 for an oneway call from the remote process.
-        // If it's the case, use PID from the ConnectionRequest.
-        final int pid = (callingPid != 0) ? callingPid : connectionRequest.getInt(KEY_PID);
-        final String pkg = connectionRequest.getString(KEY_PACKAGE_NAME);
-        try {
-            RemoteUserInfo remoteUserInfo = new RemoteUserInfo(pkg, pid, uid);
-            final ControllerInfo controllerInfo = new ControllerInfo(remoteUserInfo,
-                    mSessionManager.isTrustedForMediaControl(remoteUserInfo), controller);
-            mCallbackExecutor.execute(() -> {
+        String callingPkg = connectionRequest.getString(KEY_PACKAGE_NAME);
+
+        RemoteUserInfo remoteUserInfo = new RemoteUserInfo(callingPkg, callingPid, callingUid);
+        final ControllerInfo controllerInfo = new ControllerInfo(remoteUserInfo,
+                mSessionManager.isTrustedForMediaControl(remoteUserInfo), controller);
+        mCallbackExecutor.execute(() -> {
+            boolean accept = false;
+            try {
                 if (isClosed()) {
                     return;
                 }
@@ -244,76 +249,67 @@
                 // Don't reject connection for the request from trusted app.
                 // Otherwise server will fail to retrieve session's information to dispatch
                 // media keys to.
-                boolean accept =
-                        controllerInfo.mAllowedCommands != null || controllerInfo.isTrusted();
-                if (accept) {
-                    if (controllerInfo.mAllowedCommands == null) {
-                        // For trusted apps, send non-null allowed commands to keep
-                        // connection.
-                        controllerInfo.mAllowedCommands = new Session2CommandGroup();
+                accept = controllerInfo.mAllowedCommands != null || controllerInfo.isTrusted();
+                if (!accept) {
+                    return;
+                }
+                if (controllerInfo.mAllowedCommands == null) {
+                    // For trusted apps, send non-null allowed commands to keep
+                    // connection.
+                    controllerInfo.mAllowedCommands =
+                            new Session2CommandGroup.Builder().build();
+                }
+                if (DEBUG) {
+                    Log.d(TAG, "Accepting connection: " + controllerInfo);
+                }
+                synchronized (mLock) {
+                    if (mConnectedControllers.containsKey(controller)) {
+                        Log.w(TAG, "Controller " + controllerInfo + " has sent connection"
+                                + " request multiple times");
                     }
-                    if (DEBUG) {
-                        Log.d(TAG, "Accepting connection: " + controllerInfo);
-                    }
-                    synchronized (mLock) {
-                        if (mConnectedControllers.containsKey(controller)) {
-                            Log.w(TAG, "Controller " + controllerInfo + " has sent connection"
-                                    + " request multiple times");
-                        }
-                        mConnectedControllers.put(controller, controllerInfo);
-                    }
-                    // If connection is accepted, notify the current state to the controller.
-                    // It's needed because we cannot call synchronous calls between
-                    // session/controller.
-                    Bundle connectionResult = new Bundle();
-                    connectionResult.putParcelable(KEY_SESSION2_STUB, mSessionStub);
-                    connectionResult.putParcelable(KEY_ALLOWED_COMMANDS,
-                            controllerInfo.mAllowedCommands);
+                    mConnectedControllers.put(controller, controllerInfo);
+                }
+                // If connection is accepted, notify the current state to the controller.
+                // It's needed because we cannot call synchronous calls between
+                // session/controller.
+                Bundle connectionResult = new Bundle();
+                connectionResult.putParcelable(KEY_SESSION2LINK, mSessionStub);
+                connectionResult.putParcelable(KEY_ALLOWED_COMMANDS,
+                        controllerInfo.mAllowedCommands);
 
-                    // Double check if session is still there, because close() can be called in
-                    // another thread.
-                    if (isClosed()) {
-                        return;
-                    }
-                    controllerInfo.notifyConnected(connectionResult);
-                } else {
+                // Double check if session is still there, because close() can be called in
+                // another thread.
+                if (isClosed()) {
+                    return;
+                }
+                controllerInfo.notifyConnected(connectionResult);
+            } finally {
+                if (!accept) {
                     if (DEBUG) {
                         Log.d(TAG, "Rejecting connection, controllerInfo=" + controllerInfo);
                     }
-                    controllerInfo.notifyDisconnected();
                 }
-            });
-        } finally {
-            Binder.restoreCallingIdentity(token);
-        }
+                controllerInfo.notifyDisconnected();
+            }
+        });
     }
 
     // Called by Session2Link.onDisconnect
-    void onDisconnect(final Controller2Link controller, int seq) {
-        if (controller == null) {
-            return;
-        }
+    void onDisconnect(@NonNull final Controller2Link controller, int seq) {
         final ControllerInfo controllerInfo;
         synchronized (mLock) {
-            controllerInfo = mConnectedControllers.get(controller);
+            controllerInfo = mConnectedControllers.remove(controller);
         }
         if (controllerInfo == null) {
             return;
         }
-
-        final long token = Binder.clearCallingIdentity();
-        try {
-            mCallbackExecutor.execute(() -> {
-                mCallback.onDisconnected(MediaSession2.this, controllerInfo);
-            });
-            mConnectedControllers.remove(controller);
-        } finally {
-            Binder.restoreCallingIdentity(token);
-        }
+        mCallbackExecutor.execute(() -> {
+            mCallback.onDisconnected(MediaSession2.this, controllerInfo);
+        });
     }
 
     // Called by Session2Link.onSessionCommand
-    void onSessionCommand(final Controller2Link controller, final int seq,
+    void onSessionCommand(@NonNull final Controller2Link controller, final int seq,
             final Session2Command command, final Bundle args,
             @Nullable ResultReceiver resultReceiver) {
         if (controller == null) {
@@ -328,34 +324,28 @@
         }
 
         // TODO: check allowed commands.
-        final long token = Binder.clearCallingIdentity();
-        try {
-            synchronized (mLock) {
-                controllerInfo.addRequestedCommandSeqNumber(seq);
-            }
-
-            mCallbackExecutor.execute(() -> {
-                if (!controllerInfo.removeRequestedCommandSeqNumber(seq)) {
-                    resultReceiver.send(RESULT_INFO_SKIPPED, null);
-                    return;
-                }
-                Session2Command.Result result = mCallback.onSessionCommand(
-                        MediaSession2.this, controllerInfo, command, args);
-                if (resultReceiver != null) {
-                    if (result == null) {
-                        throw new RuntimeException("onSessionCommand shouldn't return null");
-                    } else {
-                        resultReceiver.send(result.getResultCode(), result.getResultData());
-                    }
-                }
-            });
-        } finally {
-            Binder.restoreCallingIdentity(token);
+        synchronized (mLock) {
+            controllerInfo.addRequestedCommandSeqNumber(seq);
         }
+        mCallbackExecutor.execute(() -> {
+            if (!controllerInfo.removeRequestedCommandSeqNumber(seq)) {
+                resultReceiver.send(RESULT_INFO_SKIPPED, null);
+                return;
+            }
+            Session2Command.Result result = mCallback.onSessionCommand(
+                    MediaSession2.this, controllerInfo, command, args);
+            if (resultReceiver != null) {
+                if (result == null) {
+                    throw new RuntimeException("onSessionCommand shouldn't return null");
+                } else {
+                    resultReceiver.send(result.getResultCode(), result.getResultData());
+                }
+            }
+        });
     }
 
     // Called by Session2Link.onCancelCommand
-    void onCancelCommand(final Controller2Link controller, final int seq) {
+    void onCancelCommand(@NonNull final Controller2Link controller, final int seq) {
         final ControllerInfo controllerInfo;
         synchronized (mLock) {
             controllerInfo = mConnectedControllers.get(controller);
@@ -363,13 +353,15 @@
         if (controllerInfo == null) {
             return;
         }
+        controllerInfo.removeRequestedCommandSeqNumber(seq);
+    }
 
-        final long token = Binder.clearCallingIdentity();
-        try {
-            controllerInfo.removeRequestedCommandSeqNumber(seq);
-        } finally {
-            Binder.restoreCallingIdentity(token);
+    private List<ControllerInfo> getConnectedControllers() {
+        List<ControllerInfo> controllers = new ArrayList<>();
+        synchronized (mLock) {
+            controllers.addAll(mConnectedControllers.values());
         }
+        return controllers;
     }
 
     /**
@@ -557,7 +549,7 @@
         }
 
         @Override
-        public boolean equals(Object obj) {
+        public boolean equals(@Nullable Object obj) {
             if (!(obj instanceof ControllerInfo)) return false;
             if (this == obj) return true;
 
@@ -569,6 +561,7 @@
         }
 
         @Override
+        @NonNull
         public String toString() {
             return "ControllerInfo {pkg=" + mRemoteUserInfo.getPackageName() + ", uid="
                     + mRemoteUserInfo.getUid() + ", allowedCommands=" + mAllowedCommands + "})";
@@ -655,6 +648,8 @@
      * This API is not generally intended for third party application developers.
      */
     public abstract static class SessionCallback {
+        ForegroundServiceEventCallback mForegroundServiceEventCallback;
+
         /**
          * Called when a controller is created for this session. Return allowed commands for
          * controller. By default it returns {@code null}.
@@ -692,7 +687,7 @@
          * @return the result for the session command. A runtime exception will be thrown if null
          *         is returned.
          */
-        @NonNull
+        @Nullable
         public Session2Command.Result onSessionCommand(@NonNull MediaSession2 session,
                 @NonNull ControllerInfo controller, @NonNull Session2Command command,
                 @Nullable Bundle args) {
@@ -711,5 +706,19 @@
         public void onCommandResult(@NonNull MediaSession2 session,
                 @NonNull ControllerInfo controller, @NonNull Object token,
                 @NonNull Session2Command command, @NonNull Session2Command.Result result) {}
+
+        final void onSessionClosed(MediaSession2 session) {
+            if (mForegroundServiceEventCallback != null) {
+                mForegroundServiceEventCallback.onSessionClosed(session);
+            }
+        }
+
+        void setForegroundServiceEventCallback(ForegroundServiceEventCallback callback) {
+            mForegroundServiceEventCallback = callback;
+        }
+
+        abstract static class ForegroundServiceEventCallback {
+            public void onSessionClosed(MediaSession2 session) {}
+        }
     }
 }
diff --git a/media/java/android/media/MediaSession2Service.java b/media/java/android/media/MediaSession2Service.java
new file mode 100644
index 0000000..8fb00fe
--- /dev/null
+++ b/media/java/android/media/MediaSession2Service.java
@@ -0,0 +1,288 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+import android.annotation.CallSuper;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.Service;
+import android.content.Intent;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.util.ArrayMap;
+import android.util.Log;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Service containing {@link MediaSession2}.
+ * <p>
+ * This API is not generally intended for third party application developers.
+ * Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
+ * <a href="{@docRoot}reference/androidx/media2/package-summary.html">Media2 Library</a>
+ * for consistent behavior across all devices.
+ * @hide
+ */
+// TODO: Unhide
+// TODO: Add onUpdateNotification(), and calls it to get Notification for startForegroundService()
+//       when a session's player state becomes playing.
+public abstract class MediaSession2Service extends Service {
+    /**
+     * The {@link Intent} that must be declared as handled by the service.
+     */
+    public static final String SERVICE_INTERFACE = "android.media.MediaSession2Service";
+
+    private static final String TAG = "MediaSession2Service";
+    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+    private final Object mLock = new Object();
+    @GuardedBy("mLock")
+    private Map<String, MediaSession2> mSessions = new ArrayMap<>();
+
+    private MediaSession2ServiceStub mStub;
+
+    /**
+     * Called by the system when the service is first created. Do not call this method directly.
+     * <p>
+     * Override this method if you need your own initialization. Derived classes MUST call through
+     * to the super class's implementation of this method.
+     */
+    @CallSuper
+    @Override
+    public void onCreate() {
+        super.onCreate();
+        mStub = new MediaSession2ServiceStub(this);
+    }
+
+    @CallSuper
+    @Override
+    @Nullable
+    public IBinder onBind(@NonNull Intent intent) {
+        if (SERVICE_INTERFACE.equals(intent.getAction())) {
+            return mStub;
+        }
+        return null;
+    }
+
+    @CallSuper
+    @Override
+    public int onStartCommand(Intent intent, int flags, int startId) {
+        // TODO: Dispatch media key events to the primary session.
+        return START_STICKY;
+    }
+
+    /**
+     * Called by the system to notify that it is no longer used and is being removed. Do not call
+     * this method directly.
+     * <p>
+     * Override this method if you need your own clean up. Derived classes MUST call through
+     * to the super class's implementation of this method.
+     */
+    @CallSuper
+    @Override
+    public void onDestroy() {
+        super.onDestroy();
+        synchronized (mLock) {
+            for (MediaSession2 session : mSessions.values()) {
+                session.getCallback().setForegroundServiceEventCallback(null);
+            }
+            mSessions.clear();
+        }
+        mStub.close();
+    }
+
+    /**
+     * Called when a {@link MediaController2} is created with the this service's
+     * {@link Session2Token}. Return the primary session for telling the controller which session to
+     * connect.
+     * <p>
+     * Primary session is the highest priority session that this service manages. Here are some
+     * recommendations of the primary session.
+     * <ol>
+     * <li>When there's no {@link MediaSession2}, create and return a new session. Resume the
+     * playback that the app has the lastly played with the new session. The behavior is what
+     * framework expects when the framework sends key events to the service.</li>
+     * <li>When there's multiple {@link MediaSession2}s, pick the session that has the lastly
+     * started the playback. This is the same way as the framework prioritize sessions to receive
+     * media key events.</li>
+     * </ol>
+     * <p>
+     * Session returned here will be added to this service automatically. You don't need to call
+     * {@link #addSession(MediaSession2)} for that.
+     * <p>
+     * Session service will accept or reject the connection with the
+     * {@link MediaSession2.SessionCallback} in the session returned here.
+     * <p>
+     * This method is always called on the main thread.
+     *
+     * @return a new session
+     * @see MediaSession2.Builder
+     * @see #getSessions()
+     */
+    @NonNull
+    public abstract MediaSession2 onGetPrimarySession();
+
+    /**
+     * Adds a session to this service.
+     * <p>
+     * Added session will be removed automatically when it's closed, or removed when
+     * {@link #removeSession} is called.
+     *
+     * @param session a session to be added.
+     * @see #removeSession(MediaSession2)
+     */
+    public final void addSession(@NonNull MediaSession2 session) {
+        if (session == null) {
+            throw new IllegalArgumentException("session shouldn't be null");
+        }
+        if (session.isClosed()) {
+            throw new IllegalArgumentException("session is already closed");
+        }
+        synchronized (mLock) {
+            MediaSession2 previousSession = mSessions.get(session.getSessionId());
+            if (previousSession != session) {
+                if (previousSession != null) {
+                    Log.w(TAG, "Session ID should be unique, ID=" + session.getSessionId()
+                            + ", previous=" + previousSession + ", session=" + session);
+                }
+                return;
+            }
+            mSessions.put(session.getSessionId(), session);
+            session.getCallback().setForegroundServiceEventCallback(
+                    new MediaSession2.SessionCallback.ForegroundServiceEventCallback() {
+                        @Override
+                        public void onSessionClosed(MediaSession2 session) {
+                            removeSession(session);
+                        }
+                    });
+        }
+    }
+
+    /**
+     * Removes a session from this service.
+     *
+     * @param session a session to be removed.
+     * @see #addSession(MediaSession2)
+     */
+    public final void removeSession(@NonNull MediaSession2 session) {
+        if (session == null) {
+            throw new IllegalArgumentException("session shouldn't be null");
+        }
+        synchronized (mLock) {
+            mSessions.remove(session.getSessionId());
+        }
+    }
+
+    /**
+     * Gets the list of {@link MediaSession2}s that you've added to this service.
+     *
+     * @return sessions
+     */
+    public final @NonNull List<MediaSession2> getSessions() {
+        List<MediaSession2> list = new ArrayList<>();
+        synchronized (mLock) {
+            list.addAll(mSessions.values());
+        }
+        return list;
+    }
+
+    private static final class MediaSession2ServiceStub extends IMediaSession2Service.Stub
+            implements AutoCloseable {
+        final WeakReference<MediaSession2Service> mService;
+        final Handler mHandler;
+
+        MediaSession2ServiceStub(MediaSession2Service service) {
+            mService = new WeakReference<>(service);
+            mHandler = new Handler(service.getMainLooper());
+        }
+
+        @Override
+        public void connect(Controller2Link caller, int seq, Bundle connectionRequest) {
+            if (mService.get() == null) {
+                if (DEBUG) {
+                    Log.d(TAG, "Service is already destroyed");
+                }
+                return;
+            }
+            if (caller == null || connectionRequest == null) {
+                if (DEBUG) {
+                    Log.d(TAG, "Ignoring calls with illegal arguments, caller=" + caller
+                            + ", connectionRequest=" + connectionRequest);
+                }
+                return;
+            }
+            final int pid = Binder.getCallingPid();
+            final int uid = Binder.getCallingUid();
+            final long token = Binder.clearCallingIdentity();
+            try {
+                mHandler.post(() -> {
+                    boolean shouldNotifyDisconnected = true;
+                    try {
+                        final MediaSession2Service service = mService.get();
+                        if (service == null) {
+                            if (DEBUG) {
+                                Log.d(TAG, "Service isn't available");
+                            }
+                            return;
+                        }
+                        if (DEBUG) {
+                            Log.d(TAG, "Handling incoming connection request from the"
+                                    + " controller, controller=" + caller + ", uid=" + uid);
+                        }
+                        final MediaSession2 session;
+                        session = service.onGetPrimarySession();
+                        service.addSession(session);
+                        shouldNotifyDisconnected = false;
+                        session.onConnect(caller, pid, uid, seq, connectionRequest);
+                    } catch (Exception e) {
+                        // Don't propagate exception in service to the controller.
+                        Log.w(TAG, "Failed to add a session to session service", e);
+                    } finally {
+                        // Trick to call onDisconnected() in one place.
+                        if (shouldNotifyDisconnected) {
+                            if (DEBUG) {
+                                Log.d(TAG, "Service has destroyed prematurely."
+                                        + " Rejecting connection");
+                            }
+                            try {
+                                caller.notifyDisconnected(0);
+                            } catch (RuntimeException e) {
+                                // Controller may be died prematurely.
+                                // Not an issue because we'll ignore it anyway.
+                            }
+                        }
+                    }
+                });
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        @Override
+        public void close() {
+            mHandler.removeCallbacksAndMessages(null);
+            mService.clear();
+        }
+    }
+}
diff --git a/media/java/android/media/Session2CommandGroup.java b/media/java/android/media/Session2CommandGroup.java
index 4668ec4..a189c26 100644
--- a/media/java/android/media/Session2CommandGroup.java
+++ b/media/java/android/media/Session2CommandGroup.java
@@ -35,7 +35,6 @@
  * <a href="{@docRoot}reference/androidx/media2/package-summary.html">Media2 Library</a>
  * for consistent behavior across all devices.
  * </p>
- * @hide
  */
 public final class Session2CommandGroup implements Parcelable {
     private static final String TAG = "Session2CommandGroup";
@@ -56,16 +55,12 @@
     Set<Session2Command> mCommands = new HashSet<>();
 
     /**
-     * Default Constructor.
-     */
-    public Session2CommandGroup() {}
-
-    /**
      * Creates a new Session2CommandGroup with commands copied from another object.
      *
      * @param commands The collection of commands to copy.
      */
-    public Session2CommandGroup(@Nullable Collection<Session2Command> commands) {
+    @SuppressWarnings("WeakerAccess") /* synthetic access */
+    Session2CommandGroup(@Nullable Collection<Session2Command> commands) {
         if (commands != null) {
             mCommands.addAll(commands);
         }
@@ -129,7 +124,10 @@
     }
 
     @Override
-    public void writeToParcel(Parcel dest, int flags) {
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        if (dest == null) {
+            throw new IllegalArgumentException("parcel shouldn't be null");
+        }
         dest.writeParcelableArray(mCommands.toArray(new Session2Command[0]), 0);
     }
 
@@ -149,6 +147,9 @@
          * @param commandGroup
          */
         public Builder(@NonNull Session2CommandGroup commandGroup) {
+            if (commandGroup == null) {
+                throw new IllegalArgumentException("command group shouldn't be null");
+            }
             mCommands = commandGroup.getCommands();
         }
 
diff --git a/media/java/android/media/Session2Link.java b/media/java/android/media/Session2Link.java
index 5fe558d..08664aa 100644
--- a/media/java/android/media/Session2Link.java
+++ b/media/java/android/media/Session2Link.java
@@ -17,6 +17,7 @@
 package android.media;
 
 import android.annotation.NonNull;
+import android.os.Binder;
 import android.os.Bundle;
 import android.os.IBinder;
 import android.os.Parcel;
@@ -145,8 +146,9 @@
     }
 
     /** Stub implementation for IMediaSession2.connect */
-    public void onConnect(final Controller2Link caller, int seq, Bundle connectionRequest) {
-        mSession.onConnect(caller, seq, connectionRequest);
+    public void onConnect(final Controller2Link caller, int pid, int uid, int seq,
+            Bundle connectionRequest) {
+        mSession.onConnect(caller, pid, uid, seq, connectionRequest);
     }
 
     /** Stub implementation for IMediaSession2.disconnect */
@@ -168,23 +170,57 @@
     private class Session2Stub extends IMediaSession2.Stub {
         @Override
         public void connect(final Controller2Link caller, int seq, Bundle connectionRequest) {
-            Session2Link.this.onConnect(caller, seq, connectionRequest);
+            if (caller == null || connectionRequest == null) {
+                return;
+            }
+            final int pid = Binder.getCallingPid();
+            final int uid = Binder.getCallingUid();
+            final long token = Binder.clearCallingIdentity();
+            try {
+                Session2Link.this.onConnect(caller, pid, uid, seq, connectionRequest);
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
         }
 
         @Override
         public void disconnect(final Controller2Link caller, int seq) {
-            Session2Link.this.onDisconnect(caller, seq);
+            if (caller == null) {
+                return;
+            }
+            final long token = Binder.clearCallingIdentity();
+            try {
+                Session2Link.this.onDisconnect(caller, seq);
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
         }
 
         @Override
         public void sendSessionCommand(final Controller2Link caller, final int seq,
                 final Session2Command command, final Bundle args, ResultReceiver resultReceiver) {
-            Session2Link.this.onSessionCommand(caller, seq, command, args, resultReceiver);
+            if (caller == null) {
+                return;
+            }
+            final long token = Binder.clearCallingIdentity();
+            try {
+                Session2Link.this.onSessionCommand(caller, seq, command, args, resultReceiver);
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
         }
 
         @Override
         public void cancelSessionCommand(final Controller2Link caller, final int seq) {
-            Session2Link.this.onCancelCommand(caller, seq);
+            if (caller == null) {
+                return;
+            }
+            final long token = Binder.clearCallingIdentity();
+            try {
+                Session2Link.this.onCancelCommand(caller, seq);
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
         }
     }
 }
diff --git a/media/java/android/media/Session2Token.java b/media/java/android/media/Session2Token.java
index e1fff38..c7b8911 100644
--- a/media/java/android/media/Session2Token.java
+++ b/media/java/android/media/Session2Token.java
@@ -34,7 +34,7 @@
 import java.util.Objects;
 
 /**
- * Represents an ongoing {@link MediaSession2} or a {@link MediaSession2Service}.
+ * Represents an ongoing {@link MediaSession2} or a MediaSession2Service.
  * If it's representing a session service, it may not be ongoing.
  * <p>
  * This API is not generally intended for third party application developers.
@@ -45,9 +45,8 @@
  * This may be passed to apps by the session owner to allow them to create a
  * {@link MediaController2} to communicate with the session.
  * <p>
- * It can be also obtained by {@link MediaSessionManager}.
+ * It can be also obtained by {@link android.media.session.MediaSessionManager}.
  *
- * @hide
  */
 // New version of MediaSession2.Token for following reasons
 //   - Stop implementing Parcelable for updatable support
@@ -56,6 +55,7 @@
 //     This helps controller apps to keep target of dispatching media key events in uniform way.
 //     For details about the reason, see following. (Android O+)
 //         android.media.session.MediaSessionManager.Callback#onAddressedPlayerChanged
+// TODO: use @link for MediaSession2Service
 public final class Session2Token implements Parcelable {
     private static final String TAG = "Session2Token";
 
@@ -75,7 +75,7 @@
      * @hide
      */
     @Retention(RetentionPolicy.SOURCE)
-    @IntDef(prefix = "TYPE_", value = {TYPE_SESSION, TYPE_SESSION_SERVICE, TYPE_LIBRARY_SERVICE})
+    @IntDef(prefix = "TYPE_", value = {TYPE_SESSION, TYPE_SESSION_SERVICE})
     public @interface TokenType {
     }
 
@@ -85,15 +85,10 @@
     public static final int TYPE_SESSION = 0;
 
     /**
-     * Type for {@link MediaSession2Service}.
+     * Type for MediaSession2Service.
      */
     public static final int TYPE_SESSION_SERVICE = 1;
 
-    /**
-     * Type for {@link MediaLibrary2Service}.
-     */
-    public static final int TYPE_LIBRARY_SERVICE = 2;
-
     private final int mUid;
     private final @TokenType int mType;
     private final String mPackageName;
@@ -102,8 +97,7 @@
     private final ComponentName mComponentName;
 
     /**
-     * Constructor for the token with type {@link #TYPE_SESSION_SERVICE} or
-     * {@link #TYPE_LIBRARY_SERVICE}.
+     * Constructor for the token with type {@link #TYPE_SESSION_SERVICE}.
      *
      * @param context The context.
      * @param serviceComponent The component name of the service.
@@ -158,9 +152,7 @@
         mType = in.readInt();
         mPackageName = in.readString();
         mServiceName = in.readString();
-        // TODO: Uncomment below and stop hardcode mSessionLink
-        mSessionLink = null;
-        //mSessionLink = ISession.Stub.asInterface(in.readStrongBinder());
+        mSessionLink = Session2Link.CREATOR.createFromParcel(in);
         mComponentName = ComponentName.unflattenFromString(in.readString());
     }
 
@@ -170,8 +162,7 @@
         dest.writeInt(mType);
         dest.writeString(mPackageName);
         dest.writeString(mServiceName);
-        // TODO: Uncomment below
-        //dest.writeStrongBinder(mSessionLink.getBinder());
+        mSessionLink.writeToParcel(dest, flags);
         dest.writeString(mComponentName == null ? "" : mComponentName.flattenToString());
     }
 
@@ -239,7 +230,6 @@
      * @return type of the token
      * @see #TYPE_SESSION
      * @see #TYPE_SESSION_SERVICE
-     * @see #TYPE_LIBRARY_SERVICE
      */
     public @TokenType int getType() {
         return mType;
diff --git a/media/java/android/media/session/ISession2TokensListener.aidl b/media/java/android/media/session/ISession2TokensListener.aidl
new file mode 100644
index 0000000..7d1a4aa
--- /dev/null
+++ b/media/java/android/media/session/ISession2TokensListener.aidl
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.session;
+
+import android.media.Session2Token;
+
+/**
+ * Listens for changes to the list of session2 tokens.
+ * @hide
+ */
+oneway interface ISession2TokensListener {
+    void onSession2TokensChanged(in List<Session2Token> tokens);
+}
diff --git a/media/java/android/media/session/ISessionManager.aidl b/media/java/android/media/session/ISessionManager.aidl
index 7ac3ef2..46516e0 100644
--- a/media/java/android/media/session/ISessionManager.aidl
+++ b/media/java/android/media/session/ISessionManager.aidl
@@ -23,6 +23,7 @@
 import android.media.session.IOnMediaKeyListener;
 import android.media.session.IOnVolumeKeyLongPressListener;
 import android.media.session.ISession;
+import android.media.session.ISession2TokensListener;
 import android.media.session.SessionCallbackLink;
 import android.os.Bundle;
 import android.view.KeyEvent;
@@ -45,6 +46,8 @@
     void addSessionsListener(in IActiveSessionsListener listener, in ComponentName compName,
             int userId);
     void removeSessionsListener(in IActiveSessionsListener listener);
+    void addSession2TokensListener(in ISession2TokensListener listener, int userId);
+    void removeSession2TokensListener(in ISession2TokensListener listener);
 
     // This is for the system volume UI only
     void setRemoteVolumeController(in IRemoteVolumeController rvc);
@@ -56,6 +59,5 @@
     void setOnVolumeKeyLongPressListener(in IOnVolumeKeyLongPressListener listener);
     void setOnMediaKeyListener(in IOnMediaKeyListener listener);
 
-    // MediaSession2
     boolean isTrusted(String controllerPackageName, int controllerPid, int controllerUid);
 }
diff --git a/media/java/android/media/session/MediaSessionManager.java b/media/java/android/media/session/MediaSessionManager.java
index 56ea484..4596c22 100644
--- a/media/java/android/media/session/MediaSessionManager.java
+++ b/media/java/android/media/session/MediaSessionManager.java
@@ -41,6 +41,8 @@
 import android.util.Log;
 import android.view.KeyEvent;
 
+import com.android.internal.annotations.GuardedBy;
+
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Objects;
@@ -69,9 +71,13 @@
      */
     public static final int RESULT_MEDIA_KEY_HANDLED = 1;
 
+    private final Object mLock = new Object();
+    @GuardedBy("mLock")
     private final ArrayMap<OnActiveSessionsChangedListener, SessionsChangedWrapper> mListeners
             = new ArrayMap<OnActiveSessionsChangedListener, SessionsChangedWrapper>();
-    private final Object mLock = new Object();
+    @GuardedBy("mLock")
+    private final ArrayMap<OnSession2TokensChangedListener, Session2TokensChangedWrapper>
+            mSession2TokensListeners = new ArrayMap<>();
     private final ISessionManager mService;
 
     private Context mContext;
@@ -324,6 +330,87 @@
     }
 
     /**
+     * Adds a listener to be notified when the {@link #getSession2Tokens()} changes.
+     * <p>
+     * This API is not generally intended for third party application developers.
+     * Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
+     * <a href="{@docRoot}reference/androidx/media2/package-summary.html">Media2 Library</a>
+     * for consistent behavior across all devices.
+     *
+     * @param listener The listener to add
+     * @param handler The handler to call listener on. If {@code null}, calling thread's looper will
+     *                be used.
+     * @hide
+     */
+    // TODO(jaewan): Unhide
+    public void addOnSession2TokensChangedListener(
+            @NonNull OnSession2TokensChangedListener listener, @Nullable Handler handler) {
+        addOnSession2TokensChangedListener(UserHandle.myUserId(), listener, handler);
+    }
+
+    /**
+     * Adds a listener to be notified when the {@link #getSession2Tokens()} changes.
+     * <p>
+     * This API is not generally intended for third party application developers.
+     * Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
+     * <a href="{@docRoot}reference/androidx/media2/package-summary.html">Media2 Library</a>
+     * for consistent behavior across all devices.
+     *
+     * @param userId The userId to listen for changes on
+     * @param listener The listener to add
+     * @param handler The handler to call listener on. If {@code null}, calling thread's looper will
+     *                be used.
+     * @hide
+     */
+    public void addOnSession2TokensChangedListener(int userId,
+            @NonNull OnSession2TokensChangedListener listener, @Nullable Handler handler) {
+        if (listener == null) {
+            throw new IllegalArgumentException("listener shouldn't be null");
+        }
+        synchronized (mLock) {
+            if (mSession2TokensListeners.get(listener) != null) {
+                Log.w(TAG, "Attempted to add session listener twice, ignoring.");
+                return;
+            }
+            Session2TokensChangedWrapper wrapper =
+                    new Session2TokensChangedWrapper(listener, handler);
+            try {
+                mService.addSession2TokensListener(wrapper.getStub(), userId);
+                mSession2TokensListeners.put(listener, wrapper);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Error in addSessionTokensListener.", e);
+                e.rethrowFromSystemServer();
+            }
+        }
+    }
+
+    /**
+     * Removes the {@link OnSession2TokensChangedListener} to stop receiving session token updates.
+     *
+     * @param listener The listener to remove.
+     * @hide
+     */
+    // TODO(jaewan): Unhide
+    public void removeOnSession2TokensChangedListener(
+            @NonNull OnSession2TokensChangedListener listener) {
+        if (listener == null) {
+            throw new IllegalArgumentException("listener may not be null");
+        }
+        final Session2TokensChangedWrapper wrapper;
+        synchronized (mLock) {
+            wrapper = mSession2TokensListeners.remove(listener);
+        }
+        if (wrapper != null) {
+            try {
+                mService.removeSession2TokensListener(wrapper.getStub());
+            } catch (RemoteException e) {
+                Log.e(TAG, "Error in removeSessionTokensListener.", e);
+                e.rethrowFromSystemServer();
+            }
+        }
+    }
+
+    /**
      * Set the remote volume controller to receive volume updates on. Only for
      * use by system UI.
      *
@@ -590,6 +677,26 @@
     }
 
     /**
+     * Listens for changes to the {@link #getSession2Tokens()}. This can be added
+     * using {@link #addOnSession2TokensChangedListener(OnSession2TokensChangedListener, Handler)}.
+     * <p>
+     * This API is not generally intended for third party application developers.
+     * Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
+     * <a href="{@docRoot}reference/androidx/media2/package-summary.html">Media2 Library</a>
+     * for consistent behavior across all devices.
+     *
+     * @hide
+     */
+    public interface OnSession2TokensChangedListener {
+        /**
+         * Called when the {@link #getSession2Tokens()} is changed.
+         *
+         * @param tokens list of {@link Session2Token}
+         */
+        void onSession2TokensChanged(@NonNull List<Session2Token> tokens);
+    }
+
+    /**
      * Listens the volume key long-presses.
      * @hide
      */
@@ -807,6 +914,27 @@
         }
     }
 
+    private static final class Session2TokensChangedWrapper {
+        private final OnSession2TokensChangedListener mListener;
+        private final Handler mHandler;
+        private final ISession2TokensListener.Stub mStub =
+                new ISession2TokensListener.Stub() {
+                    @Override
+                    public void onSession2TokensChanged(final List<Session2Token> tokens) {
+                        mHandler.post(() -> mListener.onSession2TokensChanged(tokens));
+                    }
+                };
+
+        Session2TokensChangedWrapper(OnSession2TokensChangedListener listener, Handler handler) {
+            mListener = listener;
+            mHandler = (handler == null) ? new Handler() : new Handler(handler.getLooper());
+        }
+
+        public ISession2TokensListener.Stub getStub() {
+            return mStub;
+        }
+    }
+
     private static final class OnVolumeKeyLongPressListenerImpl
             extends IOnVolumeKeyLongPressListener.Stub {
         private OnVolumeKeyLongPressListener mListener;
diff --git a/media/jni/Android.bp b/media/jni/Android.bp
index d6b6339..0198470 100644
--- a/media/jni/Android.bp
+++ b/media/jni/Android.bp
@@ -34,7 +34,6 @@
         "libutils",
         "libbinder",
         "libmedia",
-        "libmediaextractor",
         "libmedia_omx",
         "libmediametrics",
         "libmediadrm",
@@ -85,7 +84,7 @@
 }
 
 cc_library_shared {
-    name: "libmedia2_jni",
+    name: "libmediaplayer2_jni",
 
     srcs: [
         "android_media_DataSourceCallback.cpp",
@@ -117,7 +116,10 @@
         "libz",
     ],
 
-    header_libs: ["libhardware_headers"],
+    header_libs: [
+        "libhardware_headers",
+        "libnativewindow_headers",
+    ],
 
     static_libs: [
         "libbase",
@@ -125,12 +127,12 @@
         "libcutils",
         "libmedia_helper",
         "libmedia_player2_util",
-        "libmediaextractor",
         "libmediaplayer2",
         "libmediaplayer2-protos",
         "libmediandk_utils",
         "libmediautils",
         "libprotobuf-cpp-lite",
+        "libstagefright",
         "libstagefright_esds",
         "libstagefright_foundation",
         "libstagefright_httplive",
diff --git a/media/jni/Android.mk b/media/jni/Android.mk
new file mode 100644
index 0000000..1d7d6de
--- /dev/null
+++ b/media/jni/Android.mk
@@ -0,0 +1,30 @@
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_CFLAGS := -Wall -Werror
+LOCAL_SRC_FILES := \
+    Media2Jni.cpp \
+
+# TODO: Move libmedia2_jni from system to media apex. Currently, libraries defined in
+#       Android.mk is not visible in apex build.
+LOCAL_MODULE:= libmedia2_jni
+LOCAL_SHARED_LIBRARIES := libdl liblog
+
+sanitizer_runtime_libraries := $(call normalize-path-list,$(addsuffix .so,\
+  $(ADDRESS_SANITIZER_RUNTIME_LIBRARY) \
+  $(UBSAN_RUNTIME_LIBRARY) \
+  $(TSAN_RUNTIME_LIBRARY)))
+
+# $(info Sanitizer:  $(sanitizer_runtime_libraries))
+
+ndk_libraries := $(call normalize-path-list,$(addprefix lib,$(addsuffix .so,\
+  $(NDK_PREBUILT_SHARED_LIBRARIES))))
+
+# $(info NDK:  $(ndk_libraries))
+
+LOCAL_CFLAGS += -DLINKED_LIBRARIES='"$(sanitizer_runtime_libraries):$(ndk_libraries)"'
+
+sanitizer_runtime_libraries :=
+ndk_libraries :=
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/media/jni/Media2Jni.cpp b/media/jni/Media2Jni.cpp
new file mode 100644
index 0000000..6c0a65c
--- /dev/null
+++ b/media/jni/Media2Jni.cpp
@@ -0,0 +1,89 @@
+/*
+**
+** Copyright 2019, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "MediaPlayer2-JNI"
+#include "utils/Log.h"
+
+#include "jni.h"
+#include <android/dlext.h>
+#include <dirent.h>
+#include <dlfcn.h>
+#include <errno.h>
+#include <string.h>
+
+extern "C" {
+  // Copied from GraphicsEnv.cpp
+  // TODO(b/37049319) Get this from a header once one exists
+  android_namespace_t* android_create_namespace(const char* name,
+                                                const char* ld_library_path,
+                                                const char* default_library_path,
+                                                uint64_t type,
+                                                const char* permitted_when_isolated_path,
+                                                android_namespace_t* parent);
+  bool android_link_namespaces(android_namespace_t* from,
+                               android_namespace_t* to,
+                               const char* shared_libs_sonames);
+  enum {
+     ANDROID_NAMESPACE_TYPE_ISOLATED = 1,
+  };
+
+}  // extern "C"
+
+static const char kApexLibPath[] =  "/apex/com.android.media/lib"
+#ifdef __LP64__
+    "64"
+#endif
+    "/";
+static const char kMediaPlayer2LibPath[] =  "/apex/com.android.media/lib"
+#ifdef __LP64__
+    "64"
+#endif
+    "/libmediaplayer2_jni.so";
+
+typedef jint (*Media2JniOnLoad)(JavaVM*, void*);
+
+JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved)
+{
+    android_namespace_t *media2Ns = android_create_namespace("media2",
+            nullptr,  // ld_library_path
+            kApexLibPath,
+            ANDROID_NAMESPACE_TYPE_ISOLATED,
+            nullptr,  // permitted_when_isolated_path
+            nullptr); // parent
+    if (!android_link_namespaces(media2Ns, nullptr, LINKED_LIBRARIES)) {
+        ALOGE("Failed to link namespace. Failed to load extractor plug-ins in apex.");
+        return -1;
+    }
+    const android_dlextinfo dlextinfo = {
+        .flags = ANDROID_DLEXT_USE_NAMESPACE,
+        .library_namespace = media2Ns,
+    };
+    // load libmediaplayer2_jni and call JNI_OnLoad.
+    void *libHandle = android_dlopen_ext(kMediaPlayer2LibPath, RTLD_NOW | RTLD_LOCAL, &dlextinfo);
+    if (libHandle == NULL) {
+        ALOGW("couldn't dlopen(%s) %s", kMediaPlayer2LibPath, strerror(errno));
+        return -1;
+    }
+    Media2JniOnLoad media2JniOnLoad = (Media2JniOnLoad) dlsym(libHandle, "JNI_OnLoad");
+    if (!media2JniOnLoad) {
+        ALOGW("%s does not contain JNI_OnLoad()", kMediaPlayer2LibPath);
+        dlclose(libHandle);
+        return -1;
+    }
+    return media2JniOnLoad(vm, reserved);
+}
diff --git a/media/jni/android_media_MediaCodec.cpp b/media/jni/android_media_MediaCodec.cpp
index 2578608..7b07bea3 100644
--- a/media/jni/android_media_MediaCodec.cpp
+++ b/media/jni/android_media_MediaCodec.cpp
@@ -72,7 +72,10 @@
     jint cryptoErrorResourceBusy;
     jint cryptoErrorInsufficientOutputProtection;
     jint cryptoErrorSessionNotOpened;
+    jint cryptoErrorInsufficientSecurity;
     jint cryptoErrorUnsupportedOperation;
+    jint cryptoErrorFrameTooLarge;
+    jint cryptoErrorLostState;
 } gCryptoErrorCodes;
 
 static struct CodecActionCodes {
@@ -1005,10 +1008,22 @@
             err = gCryptoErrorCodes.cryptoErrorSessionNotOpened;
             defaultMsg = "Attempted to use a closed session";
             break;
+        case ERROR_DRM_INSUFFICIENT_SECURITY:
+            err = gCryptoErrorCodes.cryptoErrorInsufficientSecurity;
+            defaultMsg = "Required security level is not met";
+            break;
         case ERROR_DRM_CANNOT_HANDLE:
             err = gCryptoErrorCodes.cryptoErrorUnsupportedOperation;
             defaultMsg = "Operation not supported in this configuration";
             break;
+        case ERROR_DRM_FRAME_TOO_LARGE:
+            err = gCryptoErrorCodes.cryptoErrorFrameTooLarge;
+            defaultMsg = "Decrytped frame exceeds size of output buffer";
+            break;
+        case ERROR_DRM_SESSION_LOST_STATE:
+            err = gCryptoErrorCodes.cryptoErrorLostState;
+            defaultMsg = "Session state was lost, open a new session and retry";
+            break;
         default:  /* Other negative DRM error codes go out as is. */
             break;
     }
@@ -1994,11 +2009,26 @@
     gCryptoErrorCodes.cryptoErrorSessionNotOpened =
         env->GetStaticIntField(clazz.get(), field);
 
+    field = env->GetStaticFieldID(clazz.get(), "ERROR_INSUFFICIENT_SECURITY", "I");
+    CHECK(field != NULL);
+    gCryptoErrorCodes.cryptoErrorInsufficientSecurity =
+        env->GetStaticIntField(clazz.get(), field);
+
     field = env->GetStaticFieldID(clazz.get(), "ERROR_UNSUPPORTED_OPERATION", "I");
     CHECK(field != NULL);
     gCryptoErrorCodes.cryptoErrorUnsupportedOperation =
         env->GetStaticIntField(clazz.get(), field);
 
+    field = env->GetStaticFieldID(clazz.get(), "ERROR_FRAME_TOO_LARGE", "I");
+    CHECK(field != NULL);
+    gCryptoErrorCodes.cryptoErrorFrameTooLarge =
+        env->GetStaticIntField(clazz.get(), field);
+
+    field = env->GetStaticFieldID(clazz.get(), "ERROR_LOST_STATE", "I");
+    CHECK(field != NULL);
+    gCryptoErrorCodes.cryptoErrorLostState =
+        env->GetStaticIntField(clazz.get(), field);
+
     clazz.reset(env->FindClass("android/media/MediaCodec$CodecException"));
     CHECK(clazz.get() != NULL);
     field = env->GetStaticFieldID(clazz.get(), "ACTION_TRANSIENT", "I");
diff --git a/media/jni/android_media_MediaDrm.cpp b/media/jni/android_media_MediaDrm.cpp
index be71dad5..8336459 100644
--- a/media/jni/android_media_MediaDrm.cpp
+++ b/media/jni/android_media_MediaDrm.cpp
@@ -110,6 +110,7 @@
     jint kWhatDrmEvent;
     jint kWhatExpirationUpdate;
     jint kWhatKeyStatusChange;
+    jint kWhatSessionLostState;
 } gEventWhat;
 
 struct KeyTypes {
@@ -141,6 +142,16 @@
     jclass classId;
 };
 
+struct SessionExceptionFields {
+    jmethodID init;
+    jclass classId;
+    jfieldID errorCode;
+};
+
+struct SessionExceptionErrorCodes {
+    jint kResourceContention;
+} gSessionExceptionErrorCodes;
+
 struct HDCPLevels {
     jint kHdcpLevelUnknown;
     jint kHdcpNone;
@@ -180,6 +191,7 @@
     EntryFields entry;
     CertificateFields certificate;
     StateExceptionFields stateException;
+    SessionExceptionFields sessionException;
     jclass certificateClassId;
     jclass hashmapClassId;
     jclass arraylistClassId;
@@ -310,6 +322,9 @@
          case DrmPlugin::kDrmPluginEventKeysChange:
             jwhat = gEventWhat.kWhatKeyStatusChange;
             break;
+         case DrmPlugin::kDrmPluginEventSessionLostState:
+            jwhat = gEventWhat.kWhatSessionLostState;
+            break;
         default:
             ALOGE("Invalid event DrmPlugin::EventType %d, ignored", (int)eventType);
             return;
@@ -343,6 +358,30 @@
     env->Throw(static_cast<jthrowable>(exception));
 }
 
+static void throwSessionException(JNIEnv *env, const char *msg, status_t err) {
+    ALOGE("Session exception: %s (%d)", msg, err);
+
+    jint jErrorCode = 0;
+    switch(err) {
+        case ERROR_DRM_RESOURCE_CONTENTION:
+            jErrorCode = gSessionExceptionErrorCodes.kResourceContention;
+            break;
+        default:
+            break;
+    }
+
+    jobject exception = env->NewObject(gFields.sessionException.classId,
+            gFields.sessionException.init, static_cast<int>(err),
+            env->NewStringUTF(msg));
+
+    env->SetIntField(exception, gFields.sessionException.errorCode, jErrorCode);
+    env->Throw(static_cast<jthrowable>(exception));
+}
+
+static bool isSessionException(status_t err) {
+    return err == ERROR_DRM_RESOURCE_CONTENTION;
+}
+
 static bool throwExceptionAsNecessary(
         JNIEnv *env, status_t err, const char *msg = NULL) {
 
@@ -370,7 +409,7 @@
     case ERROR_DRM_CANNOT_HANDLE:
         drmMessage = "Invalid parameter or data format";
         break;
-    case ERROR_DRM_TAMPER_DETECTED:
+    case ERROR_DRM_INVALID_STATE:
         drmMessage = "Invalid state";
         break;
     default:
@@ -399,6 +438,9 @@
         jniThrowException(env, "android/media/MediaDrmResetException",
                 "mediaserver died");
         return true;
+    } else if (isSessionException(err)) {
+        throwSessionException(env, msg, err);
+        return true;
     } else if (err != OK) {
         String8 errbuf;
         if (drmMessage != NULL) {
@@ -705,6 +747,8 @@
     gEventWhat.kWhatExpirationUpdate = env->GetStaticIntField(clazz, field);
     GET_STATIC_FIELD_ID(field, clazz, "KEY_STATUS_CHANGE", "I");
     gEventWhat.kWhatKeyStatusChange = env->GetStaticIntField(clazz, field);
+    GET_STATIC_FIELD_ID(field, clazz, "SESSION_LOST_STATE", "I");
+    gEventWhat.kWhatSessionLostState = env->GetStaticIntField(clazz, field);
 
     GET_STATIC_FIELD_ID(field, clazz, "KEY_TYPE_STREAMING", "I");
     gKeyTypes.kKeyTypeStreaming = env->GetStaticIntField(clazz, field);
@@ -831,6 +875,14 @@
     FIND_CLASS(clazz, "android/media/MediaDrm$MediaDrmStateException");
     GET_METHOD_ID(gFields.stateException.init, clazz, "<init>", "(ILjava/lang/String;)V");
     gFields.stateException.classId = static_cast<jclass>(env->NewGlobalRef(clazz));
+
+    FIND_CLASS(clazz, "android/media/MediaDrm$SessionException");
+    GET_METHOD_ID(gFields.sessionException.init, clazz, "<init>", "(ILjava/lang/String;)V");
+    gFields.sessionException.classId = static_cast<jclass>(env->NewGlobalRef(clazz));
+    GET_FIELD_ID(gFields.sessionException.errorCode, clazz, "mErrorCode", "I");
+
+    GET_STATIC_FIELD_ID(field, clazz, "ERROR_RESOURCE_CONTENTION", "I");
+    gSessionExceptionErrorCodes.kResourceContention = env->GetStaticIntField(clazz, field);
 }
 
 static void android_media_MediaDrm_native_setup(
diff --git a/native/android/Android.bp b/native/android/Android.bp
index fdcfc44..73d4c45 100644
--- a/native/android/Android.bp
+++ b/native/android/Android.bp
@@ -49,6 +49,7 @@
         "sharedmem.cpp",
         "storage_manager.cpp",
         "surface_texture.cpp",
+        "surface_control.cpp",
         "system_fonts.cpp",
         "trace.cpp",
     ],
diff --git a/native/android/libandroid.map.txt b/native/android/libandroid.map.txt
index 537aed4..8be8eda 100644
--- a/native/android/libandroid.map.txt
+++ b/native/android/libandroid.map.txt
@@ -205,6 +205,9 @@
     AStorageManager_mountObb;
     AStorageManager_new;
     AStorageManager_unmountObb;
+    ASurfaceControl_create; # introduced=29
+    ASurfaceControl_createFromWindow; # introduced=29
+    ASurfaceControl_destroy; # introduced=29
     ASurfaceTexture_acquireANativeWindow; # introduced=28
     ASurfaceTexture_attachToGLContext; # introduced=28
     ASurfaceTexture_detachFromGLContext; # introduced=28
@@ -213,6 +216,16 @@
     ASurfaceTexture_getTransformMatrix; # introduced=28
     ASurfaceTexture_release; # introduced=28
     ASurfaceTexture_updateTexImage; # introduced=28
+    ASurfaceTransaction_apply; # introduced=29
+    ASurfaceTransaction_create; # introduced=29
+    ASurfaceTransaction_delete; # introduced=29
+    ASurfaceTransaction_setBuffer; # introduced=29
+    ASurfaceTransaction_setBufferTransparency; # introduced=29
+    ASurfaceTransaction_setDamageRegion; # introduced=29
+    ASurfaceTransaction_setGeometry; # introduced=29
+    ASurfaceTransaction_setOnComplete; # introduced=29
+    ASurfaceTransaction_setVisibility; # introduced=29
+    ASurfaceTransaction_setZOrder; # introduced=29
     ASystemFontIterator_open; # introduced=29
     ASystemFontIterator_close; # introduced=29
     ASystemFontIterator_next; # introduced=29
diff --git a/native/android/sharedmem.cpp b/native/android/sharedmem.cpp
index 757aaec..4410bd6 100644
--- a/native/android/sharedmem.cpp
+++ b/native/android/sharedmem.cpp
@@ -71,7 +71,7 @@
     }
     int fd = env->CallIntMethod(javaSharedMemory, sSharedMemory.getFd);
     if (fd != -1) {
-        fd = dup(fd);
+        fd = fcntl(fd, F_DUPFD_CLOEXEC, 0);
     }
     return fd;
 }
diff --git a/native/android/surface_control.cpp b/native/android/surface_control.cpp
new file mode 100644
index 0000000..ead5b0b
--- /dev/null
+++ b/native/android/surface_control.cpp
@@ -0,0 +1,232 @@
+/*
+ * 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.
+ */
+
+#include <android/native_window.h>
+#include <android/surface_control.h>
+
+#include <gui/Surface.h>
+#include <gui/SurfaceComposerClient.h>
+#include <gui/SurfaceControl.h>
+
+using namespace android;
+
+using Transaction = SurfaceComposerClient::Transaction;
+
+#define CHECK_NOT_NULL(name) \
+    LOG_ALWAYS_FATAL_IF(name == nullptr, "nullptr passed as " #name " argument");
+
+#define CHECK_VALID_RECT(name)                                     \
+    LOG_ALWAYS_FATAL_IF(!static_cast<const Rect&>(name).isValid(), \
+                        "invalid arg passed as " #name " argument");
+
+Transaction* ASurfaceTransaction_to_Transaction(ASurfaceTransaction* aSurfaceTransaction) {
+    return reinterpret_cast<Transaction*>(aSurfaceTransaction);
+}
+
+SurfaceControl* ASurfaceControl_to_SurfaceControl(ASurfaceControl* aSurfaceControl) {
+    return reinterpret_cast<SurfaceControl*>(aSurfaceControl);
+}
+
+void SurfaceControl_acquire(SurfaceControl* surfaceControl) {
+    // incStrong/decStrong token must be the same, doesn't matter what it is
+    surfaceControl->incStrong((void*)SurfaceControl_acquire);
+}
+
+void SurfaceControl_release(SurfaceControl* surfaceControl) {
+    // incStrong/decStrong token must be the same, doesn't matter what it is
+    surfaceControl->decStrong((void*)SurfaceControl_acquire);
+}
+
+ASurfaceControl* ASurfaceControl_createFromWindow(ANativeWindow* window, const char* debug_name) {
+    CHECK_NOT_NULL(window);
+    CHECK_NOT_NULL(debug_name);
+
+    sp<SurfaceComposerClient> client = new SurfaceComposerClient();
+    if (client->initCheck() != NO_ERROR) {
+        return nullptr;
+    }
+
+    uint32_t flags = ISurfaceComposerClient::eFXSurfaceBufferState;
+    sp<SurfaceControl> surfaceControl =
+            client->createWithSurfaceParent(String8(debug_name), 0 /* width */, 0 /* height */,
+                                            // Format is only relevant for buffer queue layers.
+                                            PIXEL_FORMAT_UNKNOWN /* format */, flags,
+                                            static_cast<Surface*>(window));
+    if (!surfaceControl) {
+        return nullptr;
+    }
+
+    SurfaceControl_acquire(surfaceControl.get());
+    return reinterpret_cast<ASurfaceControl*>(surfaceControl.get());
+}
+
+ASurfaceControl* ASurfaceControl_create(ASurfaceControl* parent, const char* debug_name) {
+    CHECK_NOT_NULL(parent);
+    CHECK_NOT_NULL(debug_name);
+
+    SurfaceComposerClient* client = ASurfaceControl_to_SurfaceControl(parent)->getClient().get();
+
+    SurfaceControl* surfaceControlParent = ASurfaceControl_to_SurfaceControl(parent);
+
+    uint32_t flags = ISurfaceComposerClient::eFXSurfaceBufferState;
+    sp<SurfaceControl> surfaceControl =
+            client->createSurface(String8(debug_name), 0 /* width */, 0 /* height */,
+                                  // Format is only relevant for buffer queue layers.
+                                  PIXEL_FORMAT_UNKNOWN /* format */, flags,
+                                  surfaceControlParent);
+    if (!surfaceControl) {
+        return nullptr;
+    }
+
+    SurfaceControl_acquire(surfaceControl.get());
+    return reinterpret_cast<ASurfaceControl*>(surfaceControl.get());
+}
+
+void ASurfaceControl_destroy(ASurfaceControl* aSurfaceControl) {
+    sp<SurfaceControl> surfaceControl = ASurfaceControl_to_SurfaceControl(aSurfaceControl);
+
+    Transaction().reparent(surfaceControl, nullptr).apply();
+    SurfaceControl_release(surfaceControl.get());
+}
+
+ASurfaceTransaction* ASurfaceTransaction_create() {
+    Transaction* transaction = new Transaction;
+    return reinterpret_cast<ASurfaceTransaction*>(transaction);
+}
+
+void ASurfaceTransaction_delete(ASurfaceTransaction* aSurfaceTransaction) {
+    Transaction* transaction = ASurfaceTransaction_to_Transaction(aSurfaceTransaction);
+    delete transaction;
+}
+
+void ASurfaceTransaction_apply(ASurfaceTransaction* aSurfaceTransaction) {
+    CHECK_NOT_NULL(aSurfaceTransaction);
+
+    Transaction* transaction = ASurfaceTransaction_to_Transaction(aSurfaceTransaction);
+
+    transaction->apply();
+}
+
+void ASurfaceTransaction_setOnComplete(ASurfaceTransaction* aSurfaceTransaction, void* context,
+                                       ASurfaceTransaction_OnComplete func) {
+    CHECK_NOT_NULL(aSurfaceTransaction);
+    CHECK_NOT_NULL(context);
+    CHECK_NOT_NULL(func);
+
+    TransactionCompletedCallbackTakesContext callback = [func](void* callback_context,
+                                                               const TransactionStats& stats) {
+        int fence = (stats.presentFence) ? stats.presentFence->dup() : -1;
+        (*func)(callback_context, fence);
+    };
+
+    Transaction* transaction = ASurfaceTransaction_to_Transaction(aSurfaceTransaction);
+
+    transaction->addTransactionCompletedCallback(callback, context);
+}
+
+void ASurfaceTransaction_setVisibility(ASurfaceTransaction* aSurfaceTransaction, ASurfaceControl* aSurfaceControl,
+                                       int8_t visibility) {
+    CHECK_NOT_NULL(aSurfaceTransaction);
+    CHECK_NOT_NULL(aSurfaceControl);
+
+    sp<SurfaceControl> surfaceControl = ASurfaceControl_to_SurfaceControl(aSurfaceControl);
+    Transaction* transaction = ASurfaceTransaction_to_Transaction(aSurfaceTransaction);
+
+    switch (visibility) {
+    case ASURFACE_TRANSACTION_VISIBILITY_SHOW:
+        transaction->show(surfaceControl);
+        break;
+    case ASURFACE_TRANSACTION_VISIBILITY_HIDE:
+        transaction->hide(surfaceControl);
+        break;
+    default:
+        LOG_ALWAYS_FATAL("invalid visibility %d", visibility);
+    }
+}
+
+void ASurfaceTransaction_setZOrder(ASurfaceTransaction* aSurfaceTransaction, ASurfaceControl* aSurfaceControl,
+                                   int32_t z_order) {
+    CHECK_NOT_NULL(aSurfaceTransaction);
+    CHECK_NOT_NULL(aSurfaceControl);
+
+    sp<SurfaceControl> surfaceControl = ASurfaceControl_to_SurfaceControl(aSurfaceControl);
+    Transaction* transaction = ASurfaceTransaction_to_Transaction(aSurfaceTransaction);
+
+    transaction->setLayer(surfaceControl, z_order);
+}
+
+void ASurfaceTransaction_setBuffer(ASurfaceTransaction* aSurfaceTransaction, ASurfaceControl* aSurfaceControl,
+                                   AHardwareBuffer* buffer, int fence_fd) {
+    CHECK_NOT_NULL(aSurfaceTransaction);
+    CHECK_NOT_NULL(aSurfaceControl);
+
+    sp<SurfaceControl> surfaceControl = ASurfaceControl_to_SurfaceControl(aSurfaceControl);
+    Transaction* transaction = ASurfaceTransaction_to_Transaction(aSurfaceTransaction);
+
+    sp<GraphicBuffer> graphic_buffer(reinterpret_cast<GraphicBuffer*>(buffer));
+
+    transaction->setBuffer(surfaceControl, graphic_buffer);
+    if (fence_fd != -1) {
+        sp<Fence> fence = new Fence(fence_fd);
+        transaction->setAcquireFence(surfaceControl, fence);
+    }
+}
+
+void ASurfaceTransaction_setGeometry(ASurfaceTransaction* aSurfaceTransaction,
+                                     ASurfaceControl* aSurfaceControl, const ARect& source,
+                                     const ARect& destination, int32_t transform) {
+    CHECK_NOT_NULL(aSurfaceTransaction);
+    CHECK_NOT_NULL(aSurfaceControl);
+    CHECK_VALID_RECT(source);
+    CHECK_VALID_RECT(destination);
+
+    sp<SurfaceControl> surfaceControl = ASurfaceControl_to_SurfaceControl(aSurfaceControl);
+    Transaction* transaction = ASurfaceTransaction_to_Transaction(aSurfaceTransaction);
+
+    transaction->setCrop(surfaceControl, static_cast<const Rect&>(source));
+    transaction->setFrame(surfaceControl, static_cast<const Rect&>(destination));
+    transaction->setTransform(surfaceControl, transform);
+}
+
+void ASurfaceTransaction_setBufferTransparency(ASurfaceTransaction* aSurfaceTransaction,
+                                               ASurfaceControl* aSurfaceControl,
+                                               int8_t transparency) {
+    CHECK_NOT_NULL(aSurfaceTransaction);
+    CHECK_NOT_NULL(aSurfaceControl);
+
+    sp<SurfaceControl> surfaceControl = ASurfaceControl_to_SurfaceControl(aSurfaceControl);
+    Transaction* transaction = ASurfaceTransaction_to_Transaction(aSurfaceTransaction);
+
+    uint32_t flags = (transparency == ASURFACE_TRANSACTION_TRANSPARENCY_OPAQUE) ?
+                      layer_state_t::eLayerOpaque : 0;
+    transaction->setFlags(surfaceControl, flags, layer_state_t::eLayerOpaque);
+}
+
+void ASurfaceTransaction_setDamageRegion(ASurfaceTransaction* aSurfaceTransaction, ASurfaceControl* aSurfaceControl,
+                                         const ARect rects[], uint32_t count) {
+    CHECK_NOT_NULL(aSurfaceTransaction);
+    CHECK_NOT_NULL(aSurfaceControl);
+
+    sp<SurfaceControl> surfaceControl = ASurfaceControl_to_SurfaceControl(aSurfaceControl);
+    Transaction* transaction = ASurfaceTransaction_to_Transaction(aSurfaceTransaction);
+
+    Region region;
+    for (uint32_t i = 0; i < count; ++i) {
+        region.merge(static_cast<const Rect&>(rects[i]));
+    }
+
+    transaction->setSurfaceDamageRegion(surfaceControl, region);
+}
diff --git a/native/webview/plat_support/draw_fn.h b/native/webview/plat_support/draw_fn.h
index 6afd883..0490e65 100644
--- a/native/webview/plat_support/draw_fn.h
+++ b/native/webview/plat_support/draw_fn.h
@@ -74,7 +74,10 @@
   VkQueue queue;
   uint32_t graphics_queue_index;
   uint32_t instance_version;
-  const char* const* enabled_extension_names;
+  const char* const* enabled_instance_extension_names;
+  uint32_t enabled_instance_extension_names_length;
+  const char* const* enabled_device_extension_names;
+  uint32_t enabled_device_extension_names_length;
   // Only one of device_features and device_features_2 should be non-null.
   // If both are null then no features are enabled.
   VkPhysicalDeviceFeatures* device_features;
@@ -109,8 +112,15 @@
   // Input: Format of the destination surface.
   VkFormat format;
 
-  // Input: Color space transformation from linear RGB to D50-adapted XYZ
-  float matrix[9];
+  // Input: Color space parameters.
+  float transfer_function_g;
+  float transfer_function_a;
+  float transfer_function_b;
+  float transfer_function_c;
+  float transfer_function_d;
+  float transfer_function_e;
+  float transfer_function_f;
+  float color_space_toXYZD50[9];
 
   // Input: current clip rect
   int clip_left;
@@ -121,15 +131,13 @@
 
 struct AwDrawFn_PostDrawVkParams {
   int version;
-
-  // Input: Fence for the composite command buffer to signal it has finished its
-  // work on the GPU.
-  int fd;
 };
 
 // Called on render thread while UI thread is blocked. Called for both GL and
 // VK.
-typedef void AwDrawFn_OnSync(int functor, void* data, AwDrawFn_OnSyncParams* params);
+typedef void AwDrawFn_OnSync(int functor,
+                             void* data,
+                             AwDrawFn_OnSyncParams* params);
 
 // Called on render thread when either the context is destroyed _or_ when the
 // functor's last reference goes away. Will always be called with an active
@@ -143,17 +151,24 @@
 typedef void AwDrawFn_OnDestroyed(int functor, void* data);
 
 // Only called for GL.
-typedef void AwDrawFn_DrawGL(int functor, void* data, AwDrawFn_DrawGLParams* params);
+typedef void AwDrawFn_DrawGL(int functor,
+                             void* data,
+                             AwDrawFn_DrawGLParams* params);
 
 // Initialize vulkan state. Needs to be called again after any
 // OnContextDestroyed. Only called for Vulkan.
-typedef void AwDrawFn_InitVk(int functor, void* data, AwDrawFn_InitVkParams* params);
+typedef void AwDrawFn_InitVk(int functor,
+                             void* data,
+                             AwDrawFn_InitVkParams* params);
 
 // Only called for Vulkan.
-typedef void AwDrawFn_DrawVk(int functor, void* data, AwDrawFn_DrawVkParams* params);
+typedef void AwDrawFn_DrawVk(int functor,
+                             void* data,
+                             AwDrawFn_DrawVkParams* params);
 
 // Only called for Vulkan.
-typedef void AwDrawFn_PostDrawVk(int functor, void* data,
+typedef void AwDrawFn_PostDrawVk(int functor,
+                                 void* data,
                                  AwDrawFn_PostDrawVkParams* params);
 
 struct AwDrawFnFunctorCallbacks {
@@ -176,7 +191,8 @@
 typedef AwDrawFnRenderMode AwDrawFn_QueryRenderMode(void);
 
 // Create a functor. |functor_callbacks| should be valid until OnDestroyed.
-typedef int AwDrawFn_CreateFunctor(void* data, AwDrawFnFunctorCallbacks* functor_callbacks);
+typedef int AwDrawFn_CreateFunctor(void* data,
+                                   AwDrawFnFunctorCallbacks* functor_callbacks);
 
 // 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
diff --git a/native/webview/plat_support/draw_functor.cpp b/native/webview/plat_support/draw_functor.cpp
index 6c1ceab..b97bbc3 100644
--- a/native/webview/plat_support/draw_functor.cpp
+++ b/native/webview/plat_support/draw_functor.cpp
@@ -74,6 +74,79 @@
   support->callbacks.draw_gl(functor, support->data, &params);
 }
 
+void initializeVk(int functor, void* data,
+                  const uirenderer::VkFunctorInitParams& init_vk_params) {
+  SupportData* support = static_cast<SupportData*>(data);
+  VkPhysicalDeviceFeatures2 device_features_2;
+  if (init_vk_params.device_features_2)
+    device_features_2 = *init_vk_params.device_features_2;
+
+  AwDrawFn_InitVkParams params{
+      .version = kAwDrawFnVersion,
+      .instance = init_vk_params.instance,
+      .physical_device = init_vk_params.physical_device,
+      .device = init_vk_params.device,
+      .queue = init_vk_params.queue,
+      .graphics_queue_index = init_vk_params.graphics_queue_index,
+      .instance_version = init_vk_params.instance_version,
+      .enabled_instance_extension_names =
+          init_vk_params.enabled_instance_extension_names,
+      .enabled_instance_extension_names_length =
+          init_vk_params.enabled_instance_extension_names_length,
+      .enabled_device_extension_names =
+          init_vk_params.enabled_device_extension_names,
+      .enabled_device_extension_names_length =
+          init_vk_params.enabled_device_extension_names_length,
+      .device_features = nullptr,
+      .device_features_2 =
+          init_vk_params.device_features_2 ? &device_features_2 : nullptr,
+  };
+  support->callbacks.init_vk(functor, support->data, &params);
+}
+
+void drawVk(int functor, void* data, const uirenderer::VkFunctorDrawParams& draw_vk_params) {
+  SupportData* support = static_cast<SupportData*>(data);
+  float gabcdef[7];
+  draw_vk_params.color_space_ptr->transferFn(gabcdef);
+  AwDrawFn_DrawVkParams params{
+      .version = kAwDrawFnVersion,
+      .width = draw_vk_params.width,
+      .height = draw_vk_params.height,
+      .is_layer = draw_vk_params.is_layer,
+      .secondary_command_buffer = draw_vk_params.secondary_command_buffer,
+      .color_attachment_index = draw_vk_params.color_attachment_index,
+      .compatible_render_pass = draw_vk_params.compatible_render_pass,
+      .format = draw_vk_params.format,
+      .transfer_function_g = gabcdef[0],
+      .transfer_function_a = gabcdef[1],
+      .transfer_function_b = gabcdef[2],
+      .transfer_function_c = gabcdef[3],
+      .transfer_function_d = gabcdef[4],
+      .transfer_function_e = gabcdef[5],
+      .transfer_function_f = gabcdef[6],
+      .clip_left = draw_vk_params.clip_left,
+      .clip_top = draw_vk_params.clip_top,
+      .clip_right = draw_vk_params.clip_right,
+      .clip_bottom = draw_vk_params.clip_bottom,
+  };
+  COMPILE_ASSERT(sizeof(params.color_space_toXYZD50) == sizeof(skcms_Matrix3x3),
+                 gamut_transform_size_mismatch);
+  draw_vk_params.color_space_ptr->toXYZD50(
+      reinterpret_cast<skcms_Matrix3x3*>(&params.color_space_toXYZD50));
+  COMPILE_ASSERT(NELEM(params.transform) == NELEM(draw_vk_params.transform),
+                 mismatched_transform_matrix_sizes);
+  for (int i = 0; i < NELEM(params.transform); ++i) {
+    params.transform[i] = draw_vk_params.transform[i];
+  }
+  support->callbacks.draw_vk(functor, support->data, &params);
+}
+
+void postDrawVk(int functor, void* data) {
+  SupportData* support = static_cast<SupportData*>(data);
+  AwDrawFn_PostDrawVkParams params{.version = kAwDrawFnVersion};
+  support->callbacks.post_draw_vk(functor, support->data, &params);
+}
+
 int CreateFunctor(void* data, AwDrawFnFunctorCallbacks* functor_callbacks) {
   static bool callbacks_initialized = false;
   static uirenderer::WebViewFunctorCallbacks webview_functor_callbacks = {
@@ -82,9 +155,19 @@
       .onDestroyed = &onDestroyed,
   };
   if (!callbacks_initialized) {
-    // Under uirenderer::RenderMode::Vulkan, whether gles or vk union should
-    // be populated should match whether the vk-gl interop is used.
-    webview_functor_callbacks.gles.draw = &draw_gl;
+    switch (uirenderer::WebViewFunctor_queryPlatformRenderMode()) {
+      case uirenderer::RenderMode::OpenGL_ES:
+        webview_functor_callbacks.gles.draw = &draw_gl;
+        break;
+      case uirenderer::RenderMode::Vulkan:
+        webview_functor_callbacks.vk.initialize = &initializeVk;
+        webview_functor_callbacks.vk.draw = &drawVk;
+        webview_functor_callbacks.vk.postDraw = &postDrawVk;
+        // TODO(boliu): Remove this once SkiaRecordingCanvas::drawWebViewFunctor
+        // no longer uses GL interop.
+        webview_functor_callbacks.gles.draw = &draw_gl;
+        break;
+    }
     callbacks_initialized = true;
   }
   SupportData* support = new SupportData{
diff --git a/packages/ExtServices/src/android/ext/services/notification/Assistant.java b/packages/ExtServices/src/android/ext/services/notification/Assistant.java
index fab1bcc..499d493 100644
--- a/packages/ExtServices/src/android/ext/services/notification/Assistant.java
+++ b/packages/ExtServices/src/android/ext/services/notification/Assistant.java
@@ -286,6 +286,9 @@
             if (!isForCurrentUser(sbn)) {
                 return;
             }
+
+            mAgingHelper.onNotificationRemoved(sbn.getKey());
+
             boolean updatedImpressions = false;
             String channelId = mLiveNotifications.remove(sbn.getKey()).getChannel().getId();
             String key = getKey(sbn.getPackageName(), sbn.getUserId(), channelId);
@@ -382,11 +385,11 @@
     }
 
     @Override
-    public void onActionClicked(@NonNull String key, @NonNull Notification.Action action,
+    public void onActionInvoked(@NonNull String key, @NonNull Notification.Action action,
             @Source int source) {
         if (DEBUG) {
             Log.d(TAG,
-                    "onActionClicked() called with: key = [" + key + "], action = [" + action.title
+                    "onActionInvoked() called with: key = [" + key + "], action = [" + action.title
                             + "], source = [" + source + "]");
         }
         mSmartActionsHelper.onActionClicked(key, action, source);
diff --git a/packages/ExtServices/src/android/ext/services/notification/NotificationEntry.java b/packages/ExtServices/src/android/ext/services/notification/NotificationEntry.java
index 71fd9ce..acf1180 100644
--- a/packages/ExtServices/src/android/ext/services/notification/NotificationEntry.java
+++ b/packages/ExtServices/src/android/ext/services/notification/NotificationEntry.java
@@ -27,7 +27,9 @@
 import android.app.NotificationChannel;
 import android.app.Person;
 import android.app.RemoteInput;
+import android.content.pm.ApplicationInfo;
 import android.content.pm.IPackageManager;
+import android.content.pm.PackageManager;
 import android.media.AudioAttributes;
 import android.media.AudioSystem;
 import android.os.Build;
@@ -67,8 +69,12 @@
 
     private boolean isPreChannelsNotification() {
         try {
-            mTargetSdkVersion = mPackageManager.getApplicationInfo(
-                    mSbn.getPackageName(), 0, mSbn.getUserId()).targetSdkVersion;
+            ApplicationInfo info = mPackageManager.getApplicationInfo(
+                    mSbn.getPackageName(), PackageManager.MATCH_ALL,
+                    mSbn.getUserId());
+            if (info != null) {
+                mTargetSdkVersion = info.targetSdkVersion;
+            }
         } catch (RemoteException e) {
             Log.w(TAG, "Couldn't look up " + mSbn.getPackageName());
         }
diff --git a/packages/ExtServices/src/android/ext/services/notification/SmartActionsHelper.java b/packages/ExtServices/src/android/ext/services/notification/SmartActionsHelper.java
index a9d8f62..95df5f2 100644
--- a/packages/ExtServices/src/android/ext/services/notification/SmartActionsHelper.java
+++ b/packages/ExtServices/src/android/ext/services/notification/SmartActionsHelper.java
@@ -27,6 +27,7 @@
 import android.service.notification.NotificationAssistantService;
 import android.text.TextUtils;
 import android.util.LruCache;
+import android.view.textclassifier.ConversationAction;
 import android.view.textclassifier.ConversationActions;
 import android.view.textclassifier.TextClassification;
 import android.view.textclassifier.TextClassificationContext;
@@ -65,13 +66,13 @@
     private static final int MAX_MESSAGES_TO_EXTRACT = 5;
     private static final int MAX_RESULT_ID_TO_CACHE = 20;
 
-    private static final ConversationActions.TypeConfig TYPE_CONFIG =
-            new ConversationActions.TypeConfig.Builder().setIncludedTypes(
-                    Collections.singletonList(ConversationActions.TYPE_TEXT_REPLY))
+    private static final TextClassifier.EntityConfig TYPE_CONFIG =
+            new TextClassifier.EntityConfig.Builder().setIncludedTypes(
+                    Collections.singletonList(ConversationAction.TYPE_TEXT_REPLY))
                     .includeTypesFromTextClassifier(false)
                     .build();
     private static final List<String> HINTS =
-            Collections.singletonList(ConversationActions.HINT_FOR_NOTIFICATION);
+            Collections.singletonList(ConversationActions.Request.HINT_FOR_NOTIFICATION);
 
     private Context mContext;
     @Nullable
@@ -137,7 +138,7 @@
 
         ConversationActions conversationActionsResult =
                 mTextClassifier.suggestConversationActions(request);
-        List<ConversationActions.ConversationAction> conversationActions =
+        List<ConversationAction> conversationActions =
                 conversationActionsResult.getConversationActions();
         ArrayList<CharSequence> replies = conversationActions.stream()
                 .map(conversationAction -> conversationAction.getTextReply())
@@ -193,7 +194,7 @@
         }
         TextClassifierEvent textClassifierEvent =
                 createTextClassifierEventBuilder(TextClassifierEvent.TYPE_SMART_ACTION, resultId)
-                        .setEntityType(ConversationActions.TYPE_TEXT_REPLY)
+                        .setEntityType(ConversationAction.TYPE_TEXT_REPLY)
                         .build();
         mTextClassifier.onTextClassifierEvent(textClassifierEvent);
     }
diff --git a/packages/ExtServices/tests/src/android/ext/services/notification/SmartActionHelperTest.java b/packages/ExtServices/tests/src/android/ext/services/notification/SmartActionHelperTest.java
index 7d74788..7b7ce3d 100644
--- a/packages/ExtServices/tests/src/android/ext/services/notification/SmartActionHelperTest.java
+++ b/packages/ExtServices/tests/src/android/ext/services/notification/SmartActionHelperTest.java
@@ -32,6 +32,7 @@
 import android.service.notification.StatusBarNotification;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.runner.AndroidJUnit4;
+import android.view.textclassifier.ConversationAction;
 import android.view.textclassifier.ConversationActions;
 import android.view.textclassifier.TextClassificationManager;
 import android.view.textclassifier.TextClassifier;
@@ -65,9 +66,10 @@
     private static final String NOTIFICATION_KEY = "key";
     private static final String RESULT_ID = "id";
 
-    private static final ConversationActions.ConversationAction REPLY_ACTION =
-            new ConversationActions.ConversationAction.Builder(
-                    ConversationActions.TYPE_TEXT_REPLY).setTextReply("Home").build();
+    private static final ConversationAction REPLY_ACTION =
+            new ConversationAction.Builder(ConversationAction.TYPE_TEXT_REPLY)
+            .setTextReply("Home")
+            .build();
 
     private SmartActionsHelper mSmartActionsHelper;
     private Context mContext;
diff --git a/packages/NetworkStack/AndroidManifest.xml b/packages/NetworkStack/AndroidManifest.xml
index 0b0f1ec..7f8bb93 100644
--- a/packages/NetworkStack/AndroidManifest.xml
+++ b/packages/NetworkStack/AndroidManifest.xml
@@ -20,6 +20,7 @@
           package="com.android.mainline.networkstack"
           android:sharedUserId="android.uid.networkstack">
     <uses-permission android:name="android.permission.INTERNET" />
+    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
     <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
     <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
     <uses-permission android:name="android.permission.CONNECTIVITY_INTERNAL" />
diff --git a/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java b/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java
index 94ea1b9..4077d93 100644
--- a/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java
+++ b/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java
@@ -545,7 +545,9 @@
                     return HANDLED;
                 case CMD_FORCE_REEVALUATION:
                 case CMD_CAPTIVE_PORTAL_RECHECK:
-                    log("Forcing reevaluation for UID " + message.arg1);
+                    final int dnsCount = mDnsStallDetector.getConsecutiveTimeoutCount();
+                    validationLog("Forcing reevaluation for UID " + message.arg1
+                            + ". Dns signal count: " + dnsCount);
                     mUidResponsibleForReeval = message.arg1;
                     transitionTo(mEvaluatingState);
                     return HANDLED;
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 03c6205..16bfcc9 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -99,8 +99,12 @@
     <string name="connected_via_network_scorer_default">Automatically connected via network rating provider</string>
     <!-- Status message of Wi-Fi when it is connected by Passpoint configuration. [CHAR LIMIT=NONE] -->
     <string name="connected_via_passpoint">Connected via %1$s</string>
+    <!-- Status message of Wi-Fi when it is connected by Passpoint configuration. [CHAR LIMIT=NONE] -->
+    <string name="ssid_by_passpoint_provider"><xliff:g id="ssid" example="Cafe Wifi">%1$s</xliff:g> by <xliff:g id="passpointProvider" example="Passpoint Provider">%2$s</xliff:g></string>
     <!-- Status message of Wi-Fi when network has matching passpoint credentials. [CHAR LIMIT=NONE] -->
     <string name="available_via_passpoint">Available via %1$s</string>
+    <!-- Status message of OSU Provider network when not connected. [CHAR LIMIT=NONE] -->
+    <string name="tap_to_set_up">Tap to set up</string>
     <!-- Package name for Settings app-->
     <string name="settings_package" translatable="false">com.android.settings</string>
     <!-- Package name for Certinstaller app-->
@@ -123,6 +127,79 @@
     <!-- Status message of Wi-Fi when an available network is a carrier network. [CHAR LIMIT=NONE] -->
     <string name="available_via_carrier">Available via %1$s</string>
 
+    <!-- Status message of OSU Provider on receiving OSU_FAILURE_AP_CONNECTION. [CHAR LIMIT=NONE] -->
+    <string name="osu_failure_ap_connection">Connection failed</string>
+    <!-- Status message of OSU Provider on receiving OSU_FAILURE_SERVER_URL_INVALID. [CHAR LIMIT=NONE] -->
+    <string name="osu_failure_server_url_invalid">Invalid OSU server URL</string>
+    <!-- Status message of OSU Provider on receiving OSU_FAILURE_SERVER_CONNECTION. [CHAR LIMIT=NONE] -->
+    <string name="osu_failure_server_connection">OSU server connection failed</string>
+    <!-- Status message of OSU Provider on receiving OSU_FAILURE_SERVER_VALIDATION. [CHAR LIMIT=NONE] -->
+    <string name="osu_failure_server_validation">OSU server validation failed</string>
+    <!-- Status message of OSU Provider on receiving OSU_FAILURE_SERVICE_PROVIDER_VERIFICATION. [CHAR LIMIT=NONE] -->
+    <string name="osu_failure_service_provider_verification">Invalid OSU server certificate</string>
+    <!-- Status message of OSU Provider on receiving OSU_FAILURE_PROVISIONING_ABORTED. [CHAR LIMIT=NONE] -->
+    <string name="osu_failure_provisioning_aborted">Provisioning aborted</string>
+    <!-- Status message of OSU Provider on receiving OSU_FAILURE_PROVISIONING_NOT_AVAILABLE. [CHAR LIMIT=NONE] -->
+    <string name="osu_failure_provisioning_not_available">Provisioning not available</string>
+    <!-- Status message of OSU Provider on receiving OSU_FAILURE_INVALID_SERVER_URL. [CHAR LIMIT=NONE] -->
+    <string name="osu_failure_invalid_server_url">Invalid OSU server URL</string>
+    <!-- Status message of OSU Provider on receiving OSU_FAILURE_UNEXPECTED_COMMAND_TYPE. [CHAR LIMIT=NONE] -->
+    <string name="osu_failure_unexpected_command_type">Unexpected command type</string>
+    <!-- Status message of OSU Provider on receiving OSU_FAILURE_UNEXPECTED_SOAP_MESSAGE_TYPE. [CHAR LIMIT=NONE] -->
+    <string name="osu_failure_unexpected_soap_message_type">Unexpected SOAP message type</string>
+    <!-- Status message of OSU Provider on receiving OSU_FAILURE_SOAP_MESSAGE_EXCHANGE. [CHAR LIMIT=NONE] -->
+    <string name="osu_failure_soap_message_exchange">SOAP message exchange failed</string>
+    <!-- Status message of OSU Provider on receiving OSU_FAILURE_START_REDIRECT_LISTENER. [CHAR LIMIT=NONE] -->
+    <string name="osu_failure_start_redirect_listener">Redirect listener failed to start</string>
+    <!-- Status message of OSU Provider on receiving OSU_FAILURE_TIMED_OUT_REDIRECT_LISTENER. [CHAR LIMIT=NONE] -->
+    <string name="osu_failure_timed_out_redirect_listener">Timed out waiting for redirect</string>
+    <!-- Status message of OSU Provider on receiving OSU_FAILURE_NO_OSU_ACTIVITY_FOUND. [CHAR LIMIT=NONE] -->
+    <string name="osu_failure_no_osu_activity_found">No OSU activity found</string>
+    <!-- Status message of OSU Provider on receiving OSU_FAILURE_UNEXPECTED_SOAP_MESSAGE_STATUS. [CHAR LIMIT=NONE] -->
+    <string name="osu_failure_unexpected_soap_message_status">Unexpected SOAP message status</string>
+    <!-- Status message of OSU Provider on receiving OSU_FAILURE_NO_PPS_MO. [CHAR LIMIT=NONE] -->
+    <string name="osu_failure_no_pps_mo">Failed to find PPS-MO</string>
+    <!-- Status message of OSU Provider on receiving OSU_FAILURE_NO_AAA_SERVER_TRUST_ROOT_NODE. [CHAR LIMIT=NONE] -->
+    <string name="osu_failure_no_aaa_server_trust_root_node">Failed to find trust root node for AAA server</string>
+    <!-- Status message of OSU Provider on receiving OSU_FAILURE_NO_REMEDIATION_SERVER_TRUST_ROOT_NODE. [CHAR LIMIT=NONE] -->
+    <string name="osu_failure_no_remediation_server_trust_root_node">Failed to find trust root node for remediation server</string>
+    <!-- Status message of OSU Provider on receiving OSU_FAILURE_NO_POLICY_SERVER_TRUST_ROOT_NODE. [CHAR LIMIT=NONE] -->
+    <string name="osu_failure_no_policy_server_trust_root_node">Failed to find trust root node for policy server</string>
+    <!-- Status message of OSU Provider on receiving OSU_FAILURE_RETRIEVE_TRUST_ROOT_CERTIFICATES. [CHAR LIMIT=NONE] -->
+    <string name="osu_failure_retrieve_trust_root_certificates">Failed to retrieve trust root certificates</string>
+    <!-- Status message of OSU Provider on receiving OSU_FAILURE_NO_AAA_TRUST_ROOT_CERTIFICATE. [CHAR LIMIT=NONE] -->
+    <string name="osu_failure_no_aaa_trust_root_certificate">Failed to find trust root certificate for AAA server</string>
+    <!-- Status message of OSU Provider on receiving OSU_FAILURE_ADD_PASSPOINT_CONFIGURATION. [CHAR LIMIT=NONE] -->
+    <string name="osu_failure_add_passpoint_configuration">Failed to add PassPoint configuration</string>
+    <!-- Status message of OSU Provider on receiving OSU_FAILURE_OSU_PROVIDER_NOT_FOUND. [CHAR LIMIT=NONE] -->
+    <string name="osu_failure_osu_provider_not_found">Failed to find an OSU provider</string>
+
+    <!-- Status message of OSU Provider on receiving OSU_STATUS_AP_CONNECTING. [CHAR LIMIT=NONE] -->
+    <string name="osu_status_ap_connecting">Connecting</string>
+    <!-- Status message of OSU Provider on receiving OSU_STATUS_AP_CONNECTED. [CHAR LIMIT=NONE] -->
+    <string name="osu_status_ap_connected">Connected</string>
+    <!-- Status message of OSU Provider on receiving OSU_STATUS_SERVER_CONNECTING. [CHAR LIMIT=NONE] -->
+    <string name="osu_status_server_connecting">Connecting to OSU server</string>
+    <!-- Status message of OSU Provider on receiving OSU_STATUS_SERVER_VALIDATED. [CHAR LIMIT=NONE] -->
+    <string name="osu_status_server_validated">OSU server validated</string>
+    <!-- Status message of OSU Provider on receiving OSU_STATUS_SERVER_CONNECTED. [CHAR LIMIT=NONE] -->
+    <string name="osu_status_server_connected">Connected to OSU server</string>
+    <!-- Status message of OSU Provider on receiving OSU_STATUS_INIT_SOAP_EXCHANGE. [CHAR LIMIT=NONE] -->
+    <string name="osu_status_init_soap_exchange">Initial SOAP exchange</string>
+    <!-- Status message of OSU Provider on receiving OSU_STATUS_WAITING_FOR_REDIRECT_RESPONSE. [CHAR LIMIT=NONE] -->
+    <string name="osu_status_waiting_for_redirect_response">Waiting for redirect response</string>
+    <!-- Status message of OSU Provider on receiving OSU_STATUS_REDIRECT_RESPONSE_RECEIVED. [CHAR LIMIT=NONE] -->
+    <string name="osu_status_redirect_response_received">Received redirect response</string>
+    <!-- Status message of OSU Provider on receiving OSU_STATUS_SECOND_SOAP_EXCHANGE. [CHAR LIMIT=NONE] -->
+    <string name="osu_status_second_soap_exchange">Second SOAP exchange</string>
+    <!-- Status message of OSU Provider on receiving OSU_STATUS_THIRD_SOAP_EXCHANGE. [CHAR LIMIT=NONE] -->
+    <string name="osu_status_third_soap_exchange">Third SOAP exchange</string>
+    <!-- Status message of OSU Provider on receiving OSU_STATUS_RETRIEVING_TRUST_ROOT_CERTS. [CHAR LIMIT=NONE] -->
+    <string name="osu_status_retrieving_trust_root_certs">Retrieving trust root certificates</string>
+
+    <!-- Status message of OSU Provider on completing provisioning. [CHAR LIMIT=NONE] -->
+    <string name="osu_provisioning_complete">Provisioning complete</string>
+
     <!-- Speed label for very slow network speed -->
     <string name="speed_label_very_slow">Very Slow</string>
     <!-- Speed label for slow network speed -->
diff --git a/packages/SettingsLib/src/com/android/settingslib/Utils.java b/packages/SettingsLib/src/com/android/settingslib/Utils.java
index 595aeb3..c751c39 100644
--- a/packages/SettingsLib/src/com/android/settingslib/Utils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/Utils.java
@@ -38,8 +38,8 @@
     private static final String CURRENT_MODE_KEY = "CURRENT_MODE";
     private static final String NEW_MODE_KEY = "NEW_MODE";
     @VisibleForTesting
-    static final String STORAGE_MANAGER_SHOW_OPT_IN_PROPERTY =
-            "ro.storage_manager.show_opt_in";
+    static final String STORAGE_MANAGER_ENABLED_PROPERTY =
+            "ro.storage_manager.enabled";
 
     private static Signature[] sSystemSignature;
     private static String sPermissionControllerPackageName;
@@ -373,8 +373,7 @@
     public static boolean isStorageManagerEnabled(Context context) {
         boolean isDefaultOn;
         try {
-            // Turn off by default if the opt-in was shown.
-            isDefaultOn = !SystemProperties.getBoolean(STORAGE_MANAGER_SHOW_OPT_IN_PROPERTY, true);
+            isDefaultOn = SystemProperties.getBoolean(STORAGE_MANAGER_ENABLED_PROPERTY, false);
         } catch (Resources.NotFoundException e) {
             isDefaultOn = false;
         }
diff --git a/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/VisibilityLoggerMixin.java b/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/VisibilityLoggerMixin.java
index aed02a2..8090169 100644
--- a/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/VisibilityLoggerMixin.java
+++ b/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/VisibilityLoggerMixin.java
@@ -41,10 +41,6 @@
     private int mSourceMetricsCategory = MetricsProto.MetricsEvent.VIEW_UNKNOWN;
     private long mVisibleTimestamp;
 
-    private VisibilityLoggerMixin() {
-        mMetricsCategory = METRICS_CATEGORY_UNKNOWN;
-    }
-
     public VisibilityLoggerMixin(int metricsCategory, MetricsFeatureProvider metricsFeature) {
         mMetricsCategory = metricsCategory;
         mMetricsFeature = metricsFeature;
@@ -81,12 +77,4 @@
                 MetricsFeatureProvider.EXTRA_SOURCE_METRICS_CATEGORY,
                 MetricsProto.MetricsEvent.VIEW_UNKNOWN);
     }
-
-    /** Returns elapsed time since onResume() */
-    public long elapsedTimeSinceVisible() {
-        if (mVisibleTimestamp == 0) {
-            return 0;
-        }
-        return SystemClock.elapsedRealtime() - mVisibleTimestamp;
-    }
 }
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/CategoryKey.java b/packages/SettingsLib/src/com/android/settingslib/drawer/CategoryKey.java
index 12b8efb..4ab9a9a 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawer/CategoryKey.java
+++ b/packages/SettingsLib/src/com/android/settingslib/drawer/CategoryKey.java
@@ -55,6 +55,12 @@
             "com.android.settings.category.ia.privacy";
     public static final String CATEGORY_ENTERPRISE_PRIVACY =
             "com.android.settings.category.ia.enterprise_privacy";
+    public static final String CATEGORY_ABOUT_LEGAL =
+            "com.android.settings.category.ia.about_legal";
+    public static final String CATEGORY_MY_DEVICE_INFO =
+            "com.android.settings.category.ia.my_device_info";
+    public static final String CATEGORY_BATTERY_SAVER_SETTINGS =
+            "com.android.settings.category.ia.battery_saver_settings";
 
     public static final Map<String, String> KEY_COMPAT_MAP;
 
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
index 4f81daf..27dc628 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
@@ -41,7 +41,9 @@
 import android.net.wifi.WifiInfo;
 import android.net.wifi.WifiManager;
 import android.net.wifi.WifiNetworkScoreCache;
+import android.net.wifi.hotspot2.OsuProvider;
 import android.net.wifi.hotspot2.PasspointConfiguration;
+import android.net.wifi.hotspot2.ProvisioningCallback;
 import android.os.Bundle;
 import android.os.Parcelable;
 import android.os.RemoteException;
@@ -182,6 +184,10 @@
 
     public static final int UNREACHABLE_RSSI = Integer.MIN_VALUE;
 
+    public static final String KEY_PREFIX_AP = "AP:";
+    public static final String KEY_PREFIX_FQDN = "FQDN:";
+    public static final String KEY_PREFIX_OSU = "OSU:";
+
     private final Context mContext;
 
     private String ssid;
@@ -204,9 +210,6 @@
     @Speed private int mSpeed = Speed.NONE;
     private boolean mIsScoredNetworkMetered = false;
 
-    // used to co-relate internal vs returned accesspoint.
-    int mId;
-
     /**
      * Information associated with the {@link PasspointConfiguration}.  Only maintaining
      * the relevant info to preserve spaces.
@@ -215,6 +218,13 @@
     private String mProviderFriendlyName;
 
     private boolean mIsCarrierAp = false;
+
+    private OsuProvider mOsuProvider;
+
+    private String mOsuStatus;
+    private String mOsuFailure;
+    private boolean mOsuProvisioningComplete = false;
+
     /**
      * The EAP type {@link WifiEnterpriseConfig.Eap} associated with this AP if it is a carrier AP.
      */
@@ -280,14 +290,18 @@
         // Calculate required fields
         updateKey();
         updateRssi();
-
-        mId = sLastId.incrementAndGet();
     }
 
+    /**
+     * Creates an AccessPoint with only a WifiConfiguration. This is used for the saved networks
+     * page.
+     *
+     * Passpoint Credential AccessPoints should be created with this.
+     * Make sure to call setScanResults after constructing with this.
+     */
     public AccessPoint(Context context, WifiConfiguration config) {
         mContext = context;
         loadConfig(config);
-        mId = sLastId.incrementAndGet();
     }
 
     /**
@@ -298,7 +312,17 @@
         mContext = context;
         mFqdn = config.getHomeSp().getFqdn();
         mProviderFriendlyName = config.getHomeSp().getFriendlyName();
-        mId = sLastId.incrementAndGet();
+    }
+
+    /**
+     * Initialize an AccessPoint object for a Passpoint OSU Provider.
+     * Make sure to call setScanResults after constructing with this.
+     */
+    public AccessPoint(Context context, OsuProvider provider) {
+        mContext = context;
+        mOsuProvider = provider;
+        ssid = provider.getFriendlyName();
+        updateKey();
     }
 
     AccessPoint(Context context, Collection<ScanResult> results) {
@@ -324,33 +348,27 @@
         mIsCarrierAp = firstResult.isCarrierAp;
         mCarrierApEapType = firstResult.carrierApEapType;
         mCarrierName = firstResult.carrierName;
-
-        mId = sLastId.incrementAndGet();
     }
 
     @VisibleForTesting void loadConfig(WifiConfiguration config) {
         ssid = (config.SSID == null ? "" : removeDoubleQuotes(config.SSID));
         bssid = config.BSSID;
         security = getSecurity(config);
-        updateKey();
         networkId = config.networkId;
         mConfig = config;
+        updateKey();
     }
 
     /** Updates {@link #mKey} and should only called upon object creation/initialization. */
     private void updateKey() {
         // TODO(sghuman): Consolidate Key logic on ScanResultMatchInfo
-
-        StringBuilder builder = new StringBuilder();
-
-        if (TextUtils.isEmpty(getSsidStr())) {
-            builder.append(getBssid());
-        } else {
-            builder.append(getSsidStr());
+        if (isPasspoint()) {
+            mKey = getKey(mConfig);
+        } else if (isOsuProvider()) {
+            mKey = getKey(mOsuProvider);
+        } else { // Non-Passpoint AP
+            mKey = getKey(getSsidStr(), getBssid(), getSecurity());
         }
-
-        builder.append(',').append(getSecurity());
-        mKey = builder.toString();
     }
 
     /**
@@ -394,8 +412,8 @@
             return difference;
         }
 
-        // Sort by ssid.
-        difference = getSsidStr().compareToIgnoreCase(other.getSsidStr());
+        // Sort by title.
+        difference = getTitle().compareToIgnoreCase(other.getTitle());
         if (difference != 0) {
             return difference;
         }
@@ -591,28 +609,46 @@
     }
 
     public static String getKey(ScanResult result) {
-        StringBuilder builder = new StringBuilder();
-
-        if (TextUtils.isEmpty(result.SSID)) {
-            builder.append(result.BSSID);
-        } else {
-            builder.append(result.SSID);
-        }
-
-        builder.append(',').append(getSecurity(result));
-        return builder.toString();
+        return getKey(result.SSID, result.BSSID, getSecurity(result));
     }
 
+    /**
+     * Returns the AccessPoint key for a WifiConfiguration.
+     * This will return a special Passpoint key if the config is for Passpoint.
+     */
     public static String getKey(WifiConfiguration config) {
-        StringBuilder builder = new StringBuilder();
-
-        if (TextUtils.isEmpty(config.SSID)) {
-            builder.append(config.BSSID);
+        if (config.isPasspoint()) {
+            return new StringBuilder()
+                    .append(KEY_PREFIX_FQDN)
+                    .append(config.FQDN).toString();
         } else {
-            builder.append(removeDoubleQuotes(config.SSID));
+            return getKey(config.SSID, config.BSSID, getSecurity(config));
         }
+    }
 
-        builder.append(',').append(getSecurity(config));
+    /**
+     * Returns the AccessPoint key corresponding to the OsuProvider.
+     */
+    public static String getKey(OsuProvider provider) {
+        return new StringBuilder()
+                .append(KEY_PREFIX_OSU)
+                .append(provider.getFriendlyName())
+                .append(',')
+                .append(provider.getServerUri()).toString();
+    }
+
+    /**
+     * Returns the AccessPoint key for a normal non-Passpoint network by ssid/bssid and security.
+     */
+    private static String getKey(String ssid, String bssid, int security) {
+        StringBuilder builder = new StringBuilder();
+        builder.append(KEY_PREFIX_AP);
+        if (TextUtils.isEmpty(ssid)) {
+            builder.append(bssid);
+        } else {
+            builder.append(ssid);
+        }
+        builder.append(',').append(security);
         return builder.toString();
     }
 
@@ -621,9 +657,10 @@
     }
 
     public boolean matches(WifiConfiguration config) {
-        if (config.isPasspoint() && mConfig != null && mConfig.isPasspoint()) {
-            return ssid.equals(removeDoubleQuotes(config.SSID)) && config.FQDN.equals(mConfig.FQDN);
+        if (config.isPasspoint()) {
+            return (isPasspoint() && config.FQDN.equals(mConfig.FQDN));
         } else {
+            // Normal non-Passpoint network
             return ssid.equals(removeDoubleQuotes(config.SSID))
                     && security == getSecurity(config)
                     && (mConfig == null || mConfig.shared == config.shared);
@@ -828,86 +865,111 @@
         return "";
     }
 
+    /**
+     * Returns the display title for the AccessPoint, such as for an AccessPointPreference's title.
+     */
+    public String getTitle() {
+        if (isPasspoint()) {
+            return mConfig.providerFriendlyName;
+        } else if (isOsuProvider()) {
+            return mOsuProvider.getFriendlyName();
+        } else {
+            return getSsidStr();
+        }
+    }
+
     public String getSummary() {
-        return getSettingsSummary(mConfig);
+        return getSettingsSummary();
     }
 
     public String getSettingsSummary() {
-        return getSettingsSummary(mConfig);
-    }
-
-    private String getSettingsSummary(WifiConfiguration config) {
         // Update to new summary
         StringBuilder summary = new StringBuilder();
 
-        if (isActive() && config != null && config.isPasspoint()) {
-            // This is the active connection on passpoint
-            summary.append(getSummary(mContext, getDetailedState(),
-                    false, config.providerFriendlyName));
-        } else if (isActive() && config != null && getDetailedState() == DetailedState.CONNECTED
-                && mIsCarrierAp) {
-            summary.append(String.format(mContext.getString(R.string.connected_via_carrier), mCarrierName));
-        } else if (isActive()) {
-            // This is the active connection on non-passpoint network
-            summary.append(getSummary(mContext, getDetailedState(),
-                    mInfo != null && mInfo.isEphemeral()));
-        } else if (config != null && config.isPasspoint()
-                && config.getNetworkSelectionStatus().isNetworkEnabled()) {
-            String format = mContext.getString(R.string.available_via_passpoint);
-            summary.append(String.format(format, config.providerFriendlyName));
-        } else if (config != null && config.hasNoInternetAccess()) {
-            int messageID = config.getNetworkSelectionStatus().isNetworkPermanentlyDisabled()
-                    ? R.string.wifi_no_internet_no_reconnect
-                    : R.string.wifi_no_internet;
-            summary.append(mContext.getString(messageID));
-        } else if (config != null && !config.getNetworkSelectionStatus().isNetworkEnabled()) {
-            WifiConfiguration.NetworkSelectionStatus networkStatus =
-                    config.getNetworkSelectionStatus();
-            switch (networkStatus.getNetworkSelectionDisableReason()) {
-                case WifiConfiguration.NetworkSelectionStatus.DISABLED_AUTHENTICATION_FAILURE:
-                    summary.append(mContext.getString(R.string.wifi_disabled_password_failure));
-                    break;
-                case WifiConfiguration.NetworkSelectionStatus.DISABLED_BY_WRONG_PASSWORD:
-                    summary.append(mContext.getString(R.string.wifi_check_password_try_again));
-                    break;
-                case WifiConfiguration.NetworkSelectionStatus.DISABLED_DHCP_FAILURE:
-                case WifiConfiguration.NetworkSelectionStatus.DISABLED_DNS_FAILURE:
-                    summary.append(mContext.getString(R.string.wifi_disabled_network_failure));
-                    break;
-                case WifiConfiguration.NetworkSelectionStatus.DISABLED_ASSOCIATION_REJECTION:
-                    summary.append(mContext.getString(R.string.wifi_disabled_generic));
-                    break;
+        if (isOsuProvider()) {
+            if (mOsuProvisioningComplete) {
+                summary.append(mContext.getString(R.string.osu_provisioning_complete));
+            } else if (mOsuFailure != null) {
+                summary.append(mOsuFailure);
+            } else if (mOsuStatus != null) {
+                summary.append(mOsuStatus);
+            } else {
+                summary.append(mContext.getString(R.string.tap_to_set_up));
             }
-        } else if (config != null && config.getNetworkSelectionStatus().isNotRecommended()) {
-            summary.append(mContext.getString(R.string.wifi_disabled_by_recommendation_provider));
-        } else if (mIsCarrierAp) {
-            summary.append(String.format(mContext.getString(R.string.available_via_carrier), mCarrierName));
-        } else if (!isReachable()) { // Wifi out of range
-            summary.append(mContext.getString(R.string.wifi_not_in_range));
-        } else { // In range, not disabled.
-            if (config != null) { // Is saved network
-                // Last attempt to connect to this failed. Show reason why
-                switch (config.recentFailure.getAssociationStatus()) {
-                    case WifiConfiguration.RecentFailure.STATUS_AP_UNABLE_TO_HANDLE_NEW_STA:
-                        summary.append(mContext.getString(
-                                R.string.wifi_ap_unable_to_handle_new_sta));
+        } else if (isActive()) {
+            if (isPasspoint()) {
+                // This is the active connection on passpoint
+                summary.append(getSummary(mContext, ssid, getDetailedState(),
+                        false, mConfig.providerFriendlyName));
+            } else if (mConfig != null && getDetailedState() == DetailedState.CONNECTED
+                    && mIsCarrierAp) {
+                // This is the active connection on a carrier AP
+                summary.append(String.format(mContext.getString(R.string.connected_via_carrier),
+                        mCarrierName));
+            } else {
+                // This is the active connection on non-passpoint network
+                summary.append(getSummary(mContext, getDetailedState(),
+                        mInfo != null && mInfo.isEphemeral()));
+            }
+        } else { // not active
+            if (mConfig != null && mConfig.hasNoInternetAccess()) {
+                int messageID = mConfig.getNetworkSelectionStatus().isNetworkPermanentlyDisabled()
+                        ? R.string.wifi_no_internet_no_reconnect
+                        : R.string.wifi_no_internet;
+                summary.append(mContext.getString(messageID));
+            } else if (mConfig != null && !mConfig.getNetworkSelectionStatus().isNetworkEnabled()) {
+                WifiConfiguration.NetworkSelectionStatus networkStatus =
+                        mConfig.getNetworkSelectionStatus();
+                switch (networkStatus.getNetworkSelectionDisableReason()) {
+                    case WifiConfiguration.NetworkSelectionStatus.DISABLED_AUTHENTICATION_FAILURE:
+                        summary.append(mContext.getString(R.string.wifi_disabled_password_failure));
                         break;
-                    default:
-                        // "Saved"
-                        summary.append(mContext.getString(R.string.wifi_remembered));
+                    case WifiConfiguration.NetworkSelectionStatus.DISABLED_BY_WRONG_PASSWORD:
+                        summary.append(mContext.getString(R.string.wifi_check_password_try_again));
                         break;
+                    case WifiConfiguration.NetworkSelectionStatus.DISABLED_DHCP_FAILURE:
+                    case WifiConfiguration.NetworkSelectionStatus.DISABLED_DNS_FAILURE:
+                        summary.append(mContext.getString(R.string.wifi_disabled_network_failure));
+                        break;
+                    case WifiConfiguration.NetworkSelectionStatus.DISABLED_ASSOCIATION_REJECTION:
+                        summary.append(mContext.getString(R.string.wifi_disabled_generic));
+                        break;
+                }
+            } else if (mConfig != null && mConfig.getNetworkSelectionStatus().isNotRecommended()) {
+                summary.append(mContext.getString(
+                        R.string.wifi_disabled_by_recommendation_provider));
+            } else if (mIsCarrierAp) {
+                summary.append(String.format(mContext.getString(
+                        R.string.available_via_carrier), mCarrierName));
+            } else if (!isReachable()) { // Wifi out of range
+                summary.append(mContext.getString(R.string.wifi_not_in_range));
+            } else { // In range, not disabled.
+                if (mConfig != null) { // Is saved network
+                    // Last attempt to connect to this failed. Show reason why
+                    switch (mConfig.recentFailure.getAssociationStatus()) {
+                        case WifiConfiguration.RecentFailure.STATUS_AP_UNABLE_TO_HANDLE_NEW_STA:
+                            summary.append(mContext.getString(
+                                    R.string.wifi_ap_unable_to_handle_new_sta));
+                            break;
+                        default:
+                            // "Saved"
+                            summary.append(mContext.getString(R.string.wifi_remembered));
+                            break;
+                    }
                 }
             }
         }
 
+
+
         if (isVerboseLoggingEnabled()) {
-            summary.append(WifiUtils.buildLoggingSummary(this, config));
+            summary.append(WifiUtils.buildLoggingSummary(this, mConfig));
         }
 
-        if (config != null && (WifiUtils.isMeteredOverridden(config) || config.meteredHint)) {
+        if (mConfig != null && (WifiUtils.isMeteredOverridden(mConfig) || mConfig.meteredHint)) {
             return mContext.getResources().getString(
                     R.string.preference_summary_default_combination,
-                    WifiUtils.getMeteredLabel(mContext, config),
+                    WifiUtils.getMeteredLabel(mContext, mConfig),
                     summary.toString());
         }
 
@@ -960,11 +1022,33 @@
     }
 
     /**
+     * Return true if this AccessPoint represents an OSU Provider.
+     */
+    public boolean isOsuProvider() {
+        return mOsuProvider != null;
+    }
+
+    /**
+     * Starts the OSU Provisioning flow.
+     */
+    public void startOsuProvisioning() {
+        mContext.getSystemService(WifiManager.class).startSubscriptionProvisioning(
+                mOsuProvider,
+                new AccessPointProvisioningCallback(),
+                ThreadUtils.getUiThreadHandler()
+        );
+    }
+
+    /**
      * Return whether the given {@link WifiInfo} is for this access point.
      * If the current AP does not have a network Id then the config is used to
      * match based on SSID and security.
      */
     private boolean isInfoForThisAccessPoint(WifiConfiguration config, WifiInfo info) {
+        if (info.isOsuAp()) {
+            return (mOsuStatus != null);
+        }
+
         if (isPasspoint() == false && networkId != WifiConfiguration.INVALID_NETWORK_ID) {
             return networkId == info.getNetworkId();
         } else if (config != null) {
@@ -1048,18 +1132,21 @@
      */
     void setScanResults(Collection<ScanResult> scanResults) {
 
-        // Validate scan results are for current AP only
-        String key = getKey();
-        for (ScanResult result : scanResults) {
-            String scanResultKey = AccessPoint.getKey(result);
-            if (!mKey.equals(scanResultKey)) {
-                throw new IllegalArgumentException(
-                        String.format("ScanResult %s\nkey of %s did not match current AP key %s",
-                                      result, scanResultKey, key));
+        // Validate scan results are for current AP only by matching SSID/BSSID
+        // Passpoint networks are not bound to a specific SSID/BSSID, so skip this for passpoint.
+        if (!isPasspoint() && !isOsuProvider()) {
+            String key = getKey();
+            for (ScanResult result : scanResults) {
+                String scanResultKey = AccessPoint.getKey(result);
+                if (!mKey.equals(scanResultKey)) {
+                    throw new IllegalArgumentException(
+                            String.format(
+                                    "ScanResult %s\nkey of %s did not match current AP key %s",
+                                    result, scanResultKey, key));
+                }
             }
         }
 
-
         int oldLevel = getLevel();
         mScanResults.clear();
         mScanResults.addAll(scanResults);
@@ -1100,7 +1187,17 @@
         }
     }
 
-    /** Attempt to update the AccessPoint and return true if an update occurred. */
+    /**
+     * Attempt to update the AccessPoint with the current connection info.
+     * This is used to set an AccessPoint to the active one if the connection info matches, or
+     * conversely to set an AccessPoint to inactive if the connection info does not match. The RSSI
+     * is also updated upon a match. Listeners will be notified if an update occurred.
+     *
+     * This is called in {@link WifiTracker#updateAccessPoints} as well as in callbacks for handling
+     * NETWORK_STATE_CHANGED_ACTION, RSSI_CHANGED_ACTION, and onCapabilitiesChanged in WifiTracker.
+     *
+     * Returns true if an update occurred.
+     */
     public boolean update(
             @Nullable WifiConfiguration config, WifiInfo info, NetworkInfo networkInfo) {
 
@@ -1149,6 +1246,9 @@
 
     void update(@Nullable WifiConfiguration config) {
         mConfig = config;
+        if (mConfig != null) {
+            ssid = removeDoubleQuotes(mConfig.SSID);
+        }
         networkId = config != null ? config.networkId : WifiConfiguration.INVALID_NETWORK_ID;
         ThreadUtils.postOnMainThread(() -> {
             if (mAccessPointListener != null) {
@@ -1224,11 +1324,11 @@
 
     public static String getSummary(Context context, String ssid, DetailedState state,
             boolean isEphemeral, String passpointProvider) {
-        if (state == DetailedState.CONNECTED && ssid == null) {
-            if (TextUtils.isEmpty(passpointProvider) == false) {
+        if (state == DetailedState.CONNECTED) {
+            if (!TextUtils.isEmpty(passpointProvider)) {
                 // Special case for connected + passpoint networks.
-                String format = context.getString(R.string.connected_via_passpoint);
-                return String.format(format, passpointProvider);
+                String format = context.getString(R.string.ssid_by_passpoint_provider);
+                return String.format(format, ssid, passpointProvider);
             } else if (isEphemeral) {
                 // Special case for connected + ephemeral networks.
                 final NetworkScoreManager networkScoreManager = context.getSystemService(
@@ -1421,4 +1521,166 @@
     private static boolean isVerboseLoggingEnabled() {
         return WifiTracker.sVerboseLogging || Log.isLoggable(TAG, Log.VERBOSE);
     }
+
+    /**
+     * Callbacks relaying changes to the OSU provisioning status started in startOsuProvisioning().
+     *
+     * All methods are invoked on the Main Thread
+     */
+    private class AccessPointProvisioningCallback extends ProvisioningCallback {
+        // TODO: Remove logs and implement summary changing logic for these provisioning callbacks.
+        @Override
+        @MainThread public void onProvisioningFailure(int status) {
+            switch (status) {
+                case OSU_FAILURE_AP_CONNECTION:
+                    mOsuFailure = mContext.getString(R.string.osu_failure_ap_connection);
+                    break;
+                case OSU_FAILURE_SERVER_URL_INVALID:
+                    mOsuFailure = mContext.getString(R.string.osu_failure_server_url_invalid);
+                    break;
+                case OSU_FAILURE_SERVER_CONNECTION:
+                    mOsuFailure = mContext.getString(R.string.osu_failure_server_connection);
+                    break;
+                case OSU_FAILURE_SERVER_VALIDATION:
+                    mOsuFailure = mContext.getString(R.string.osu_failure_server_validation);
+                    break;
+                case OSU_FAILURE_SERVICE_PROVIDER_VERIFICATION:
+                    mOsuFailure = mContext.getString(
+                            R.string.osu_failure_service_provider_verification);
+                    break;
+                case OSU_FAILURE_PROVISIONING_ABORTED:
+                    mOsuFailure = mContext.getString(R.string.osu_failure_provisioning_aborted);
+                    break;
+                case OSU_FAILURE_PROVISIONING_NOT_AVAILABLE:
+                    mOsuFailure = mContext.getString(
+                            R.string.osu_failure_provisioning_not_available);
+                    break;
+                case OSU_FAILURE_INVALID_SERVER_URL:
+                    mOsuFailure = mContext.getString(R.string.osu_failure_invalid_server_url);
+                    break;
+                case OSU_FAILURE_UNEXPECTED_COMMAND_TYPE:
+                    mOsuFailure = mContext.getString(R.string.osu_failure_unexpected_command_type);
+                    break;
+                case OSU_FAILURE_UNEXPECTED_SOAP_MESSAGE_TYPE:
+                    mOsuFailure = mContext.getString(
+                            R.string.osu_failure_unexpected_soap_message_type);
+                    break;
+                case OSU_FAILURE_SOAP_MESSAGE_EXCHANGE:
+                    mOsuFailure = mContext.getString(R.string.osu_failure_soap_message_exchange);
+                    break;
+                case OSU_FAILURE_START_REDIRECT_LISTENER:
+                    mOsuFailure = mContext.getString(R.string.osu_failure_start_redirect_listener);
+                    break;
+                case OSU_FAILURE_TIMED_OUT_REDIRECT_LISTENER:
+                    mOsuFailure = mContext.getString(
+                            R.string.osu_failure_timed_out_redirect_listener);
+                    break;
+                case OSU_FAILURE_NO_OSU_ACTIVITY_FOUND:
+                    mOsuFailure = mContext.getString(R.string.osu_failure_no_osu_activity_found);
+                    break;
+                case OSU_FAILURE_UNEXPECTED_SOAP_MESSAGE_STATUS:
+                    mOsuFailure = mContext.getString(
+                            R.string.osu_failure_unexpected_soap_message_status);
+                    break;
+                case OSU_FAILURE_NO_PPS_MO:
+                    mOsuFailure = mContext.getString(
+                            R.string.osu_failure_no_pps_mo);
+                    break;
+                case OSU_FAILURE_NO_AAA_SERVER_TRUST_ROOT_NODE:
+                    mOsuFailure = mContext.getString(
+                            R.string.osu_failure_no_aaa_server_trust_root_node);
+                    break;
+                case OSU_FAILURE_NO_REMEDIATION_SERVER_TRUST_ROOT_NODE:
+                    mOsuFailure = mContext.getString(
+                            R.string.osu_failure_no_remediation_server_trust_root_node);
+                    break;
+                case OSU_FAILURE_NO_POLICY_SERVER_TRUST_ROOT_NODE:
+                    mOsuFailure = mContext.getString(
+                            R.string.osu_failure_no_policy_server_trust_root_node);
+                    break;
+                case OSU_FAILURE_RETRIEVE_TRUST_ROOT_CERTIFICATES:
+                    mOsuFailure = mContext.getString(
+                            R.string.osu_failure_retrieve_trust_root_certificates);
+                    break;
+                case OSU_FAILURE_NO_AAA_TRUST_ROOT_CERTIFICATE:
+                    mOsuFailure = mContext.getString(
+                            R.string.osu_failure_no_aaa_trust_root_certificate);
+                    break;
+                case OSU_FAILURE_ADD_PASSPOINT_CONFIGURATION:
+                    mOsuFailure = mContext.getString(
+                            R.string.osu_failure_add_passpoint_configuration);
+                    break;
+                case OSU_FAILURE_OSU_PROVIDER_NOT_FOUND:
+                    mOsuFailure = mContext.getString(R.string.osu_failure_osu_provider_not_found);
+                    break;
+            }
+            mOsuStatus = null;
+            mOsuProvisioningComplete = false;
+            ThreadUtils.postOnMainThread(() -> {
+                if (mAccessPointListener != null) {
+                    mAccessPointListener.onAccessPointChanged(AccessPoint.this);
+                }
+            });
+        }
+
+        @Override
+        @MainThread public void onProvisioningStatus(int status) {
+            switch (status) {
+                case OSU_STATUS_AP_CONNECTING:
+                    mOsuStatus = mContext.getString(R.string.osu_status_ap_connecting);
+                    break;
+                case OSU_STATUS_AP_CONNECTED:
+                    mOsuStatus = mContext.getString(R.string.osu_status_ap_connected);
+                    break;
+                case OSU_STATUS_SERVER_CONNECTING:
+                    mOsuStatus = mContext.getString(R.string.osu_status_server_connecting);
+                    break;
+                case OSU_STATUS_SERVER_VALIDATED:
+                    mOsuStatus = mContext.getString(R.string.osu_status_server_validated);
+                    break;
+                case OSU_STATUS_SERVER_CONNECTED:
+                    mOsuStatus = mContext.getString(R.string.osu_status_server_connected);
+                    break;
+                case OSU_STATUS_INIT_SOAP_EXCHANGE:
+                    mOsuStatus = mContext.getString(R.string.osu_status_init_soap_exchange);
+                    break;
+                case OSU_STATUS_WAITING_FOR_REDIRECT_RESPONSE:
+                    mOsuStatus = mContext.getString(
+                            R.string.osu_status_waiting_for_redirect_response);
+                    break;
+                case OSU_STATUS_REDIRECT_RESPONSE_RECEIVED:
+                    mOsuStatus = mContext.getString(R.string.osu_status_redirect_response_received);
+                    break;
+                case OSU_STATUS_SECOND_SOAP_EXCHANGE:
+                    mOsuStatus = mContext.getString(R.string.osu_status_second_soap_exchange);
+                    break;
+                case OSU_STATUS_THIRD_SOAP_EXCHANGE:
+                    mOsuStatus = mContext.getString(R.string.osu_status_third_soap_exchange);
+                    break;
+                case OSU_STATUS_RETRIEVING_TRUST_ROOT_CERTS:
+                    mOsuStatus = mContext.getString(
+                            R.string.osu_status_retrieving_trust_root_certs);
+                    break;
+            }
+            mOsuFailure = null;
+            mOsuProvisioningComplete = false;
+            ThreadUtils.postOnMainThread(() -> {
+                if (mAccessPointListener != null) {
+                    mAccessPointListener.onAccessPointChanged(AccessPoint.this);
+                }
+            });
+        }
+
+        @Override
+        @MainThread public void onProvisioningComplete() {
+            mOsuProvisioningComplete = true;
+            mOsuFailure = null;
+            mOsuStatus = null;
+            ThreadUtils.postOnMainThread(() -> {
+                if (mAccessPointListener != null) {
+                    mAccessPointListener.onAccessPointChanged(AccessPoint.this);
+                }
+            });
+        }
+    }
 }
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java
index db364a3..1fa7083 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java
@@ -266,7 +266,7 @@
         if (savedNetworks) {
             preference.setTitle(ap.getConfigName());
         } else {
-            preference.setTitle(ap.getSsidStr());
+            preference.setTitle(ap.getTitle());
         }
     }
 
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
index e47ca32..b9a5f23 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
@@ -35,6 +35,7 @@
 import android.net.wifi.WifiManager;
 import android.net.wifi.WifiNetworkScoreCache;
 import android.net.wifi.WifiNetworkScoreCache.CacheListener;
+import android.net.wifi.hotspot2.OsuProvider;
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.Message;
@@ -45,6 +46,7 @@
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.Log;
+import android.util.Pair;
 import android.widget.Toast;
 
 import androidx.annotation.GuardedBy;
@@ -66,6 +68,7 @@
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.List;
+import java.util.ListIterator;
 import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.atomic.AtomicBoolean;
@@ -573,9 +576,72 @@
                 accessPoints.add(accessPoint);
             }
 
+            List<ScanResult> cachedScanResults = new ArrayList<>(mScanResultCache.values());
+
+            // Add a unique Passpoint R1 AccessPoint for each Passpoint profile's FQDN.
+            List<Pair<WifiConfiguration, Map<Integer, List<ScanResult>>>> passpointConfigsAndScans =
+                    mWifiManager.getAllMatchingWifiConfigs(cachedScanResults);
+            Set<String> seenFQDNs = new ArraySet<>();
+            for (Pair<WifiConfiguration,
+                    Map<Integer, List<ScanResult>>> pairing : passpointConfigsAndScans) {
+                WifiConfiguration config = pairing.first;
+
+                List<ScanResult> scanResults = new ArrayList<>();
+
+                List<ScanResult> homeScans =
+                        pairing.second.get(WifiManager.PASSPOINT_HOME_NETWORK);
+                List<ScanResult> roamingScans =
+                        pairing.second.get(WifiManager.PASSPOINT_ROAMING_NETWORK);
+
+                if (homeScans == null) {
+                    homeScans = new ArrayList<>();
+                }
+                if (roamingScans == null) {
+                    roamingScans = new ArrayList<>();
+                }
+
+                // TODO(b/118705403): Differentiate home network vs roaming network for summary info
+                if (!homeScans.isEmpty()) {
+                    scanResults.addAll(homeScans);
+                } else {
+                    scanResults.addAll(roamingScans);
+                }
+
+                if (seenFQDNs.add(config.FQDN)) {
+                    int bestRssi = Integer.MIN_VALUE;
+                    for (ScanResult result : scanResults) {
+                        if (result.level >= bestRssi) {
+                            bestRssi = result.level;
+                            config.SSID = AccessPoint.convertToQuotedString(result.SSID);
+                        }
+                    }
+
+                    AccessPoint accessPoint =
+                            getCachedOrCreatePasspoint(scanResults, cachedAccessPoints, config);
+                    accessPoint.update(connectionConfig, mLastInfo, mLastNetworkInfo);
+                    accessPoints.add(accessPoint);
+                }
+            }
+
+            // Add Passpoint OSU Provider AccessPoints
+            Map<OsuProvider, List<ScanResult>> providersAndScans =
+                    mWifiManager.getMatchingOsuProviders(cachedScanResults);
+            Set<OsuProvider> alreadyProvisioned = mWifiManager
+                    .getMatchingPasspointConfigsForOsuProviders(
+                            providersAndScans.keySet()).keySet();
+            for (OsuProvider provider : providersAndScans.keySet()) {
+                if (!alreadyProvisioned.contains(provider)) {
+                    AccessPoint accessPointOsu =
+                            getCachedOrCreateOsu(providersAndScans.get(provider),
+                                    cachedAccessPoints, provider);
+                    accessPointOsu.update(connectionConfig, mLastInfo, mLastNetworkInfo);
+                    accessPoints.add(accessPointOsu);
+                }
+            }
+
+
             // If there were no scan results, create an AP for the currently connected network (if
             // it exists).
-            // TODO(b/b/73076869): Add support for passpoint (ephemeral) networks
             if (accessPoints.isEmpty() && connectionConfig != null) {
                 AccessPoint activeAp = new AccessPoint(mContext, connectionConfig);
                 activeAp.update(connectionConfig, mLastInfo, mLastNetworkInfo);
@@ -623,16 +689,49 @@
     AccessPoint getCachedOrCreate(
             List<ScanResult> scanResults,
             List<AccessPoint> cache) {
-        final int N = cache.size();
-        for (int i = 0; i < N; i++) {
-            if (cache.get(i).getKey().equals(AccessPoint.getKey(scanResults.get(0)))) {
-                AccessPoint ret = cache.remove(i);
-                ret.setScanResults(scanResults);
-                return ret;
+        AccessPoint accessPoint = getCachedByKey(cache, AccessPoint.getKey(scanResults.get(0)));
+        if (accessPoint == null) {
+            accessPoint = new AccessPoint(mContext, scanResults);
+        } else {
+            accessPoint.setScanResults(scanResults);
+        }
+        return accessPoint;
+    }
+
+    private AccessPoint getCachedOrCreatePasspoint(
+            List<ScanResult> scanResults,
+            List<AccessPoint> cache,
+            WifiConfiguration config) {
+        AccessPoint accessPoint = getCachedByKey(cache, AccessPoint.getKey(config));
+        if (accessPoint == null) {
+            accessPoint = new AccessPoint(mContext, config);
+        }
+        accessPoint.setScanResults(scanResults);
+        return accessPoint;
+    }
+
+    private AccessPoint getCachedOrCreateOsu(
+            List<ScanResult> scanResults,
+            List<AccessPoint> cache,
+            OsuProvider provider) {
+        AccessPoint accessPoint = getCachedByKey(cache, AccessPoint.getKey(provider));
+        if (accessPoint == null) {
+            accessPoint = new AccessPoint(mContext, provider);
+        }
+        accessPoint.setScanResults(scanResults);
+        return accessPoint;
+    }
+
+    private AccessPoint getCachedByKey(List<AccessPoint> cache, String key) {
+        ListIterator<AccessPoint> lit = cache.listIterator();
+        while (lit.hasNext()) {
+            AccessPoint currentAccessPoint = lit.next();
+            if (currentAccessPoint.getKey().equals(key)) {
+                lit.remove();
+                return currentAccessPoint;
             }
         }
-        final AccessPoint accessPoint = new AccessPoint(mContext, scanResults);
-        return accessPoint;
+        return null;
     }
 
     private void updateNetworkInfo(NetworkInfo networkInfo) {
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java
index 86f0438..92ebe44 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java
@@ -17,7 +17,7 @@
 
 import static android.Manifest.permission.WRITE_SECURE_SETTINGS;
 
-import static com.android.settingslib.Utils.STORAGE_MANAGER_SHOW_OPT_IN_PROPERTY;
+import static com.android.settingslib.Utils.STORAGE_MANAGER_ENABLED_PROPERTY;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -159,7 +159,7 @@
 
     @Test
     public void testIsStorageManagerEnabled_UsesSystemProperties() {
-        SystemProperties.set(STORAGE_MANAGER_SHOW_OPT_IN_PROPERTY, "false");
+        SystemProperties.set(STORAGE_MANAGER_ENABLED_PROPERTY, "true");
         assertThat(Utils.isStorageManagerEnabled(mContext)).isTrue();
     }
 
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
index e853399..ef90dc9 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
@@ -28,8 +28,8 @@
 import android.database.sqlite.SQLiteDatabase;
 import android.database.sqlite.SQLiteOpenHelper;
 import android.database.sqlite.SQLiteStatement;
-import android.media.AudioSystem;
 import android.media.AudioManager;
+import android.media.AudioSystem;
 import android.net.ConnectivityManager;
 import android.os.Build;
 import android.os.Environment;
@@ -1104,9 +1104,7 @@
         }
 
         if (upgradeVersion == 77) {
-            // Introduce "vibrate when ringing" setting
-            loadVibrateWhenRingingSetting(db);
-
+            // "vibrate when ringing" setting moved to SettingsProvider version 168
             upgradeVersion = 78;
         }
 
@@ -2223,8 +2221,6 @@
         } finally {
             if (stmt != null) stmt.close();
         }
-
-        loadVibrateWhenRingingSetting(db);
     }
 
     private void loadVibrateSetting(SQLiteDatabase db, boolean deleteOld) {
@@ -2250,24 +2246,6 @@
         }
     }
 
-    private void loadVibrateWhenRingingSetting(SQLiteDatabase db) {
-        // The default should be off. VIBRATE_SETTING_ONLY_SILENT should also be ignored here.
-        // Phone app should separately check whether AudioManager#getRingerMode() returns
-        // RINGER_MODE_VIBRATE, with which the device should vibrate anyway.
-        int vibrateSetting = getIntValueFromSystem(db, Settings.System.VIBRATE_ON,
-                AudioManager.VIBRATE_SETTING_OFF);
-        boolean vibrateWhenRinging = ((vibrateSetting & 3) == AudioManager.VIBRATE_SETTING_ON);
-
-        SQLiteStatement stmt = null;
-        try {
-            stmt = db.compileStatement("INSERT OR IGNORE INTO system(name,value)"
-                    + " VALUES(?,?);");
-            loadSetting(stmt, Settings.System.VIBRATE_WHEN_RINGING, vibrateWhenRinging ? 1 : 0);
-        } finally {
-            if (stmt != null) stmt.close();
-        }
-    }
-
     private void loadSettings(SQLiteDatabase db) {
         loadSystemSettings(db);
         loadSecureSettings(db);
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index bcf37ff..e843eb4 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -705,14 +705,17 @@
                 Settings.Global.GPU_DEBUG_LAYERS_GLES,
                 GlobalSettingsProto.Gpu.DEBUG_LAYERS_GLES);
         dumpSetting(s, p,
+                Settings.Global.GUP_DEV_ALL_APPS,
+                GlobalSettingsProto.Gpu.GUP_DEV_ALL_APPS);
+        dumpSetting(s, p,
                 Settings.Global.GUP_DEV_OPT_IN_APPS,
                 GlobalSettingsProto.Gpu.GUP_DEV_OPT_IN_APPS);
         dumpSetting(s, p,
                 Settings.Global.GUP_DEV_OPT_OUT_APPS,
                 GlobalSettingsProto.Gpu.GUP_DEV_OPT_OUT_APPS);
         dumpSetting(s, p,
-                Settings.Global.GUP_BLACK_LIST,
-                GlobalSettingsProto.Gpu.GUP_BLACK_LIST);
+                Settings.Global.GUP_BLACKLIST,
+                GlobalSettingsProto.Gpu.GUP_BLACKLIST);
         p.end(gpuToken);
 
         final long hdmiToken = p.start(GlobalSettingsProto.HDMI);
@@ -1879,6 +1882,9 @@
         dumpSetting(s, p,
                 Settings.Secure.DOZE_DOUBLE_TAP_GESTURE,
                 SecureSettingsProto.Doze.PULSE_ON_DOUBLE_TAP);
+        dumpSetting(s, p,
+                Settings.Secure.DOZE_TAP_SCREEN_GESTURE,
+                SecureSettingsProto.Doze.PULSE_ON_TAP);
         p.end(dozeToken);
 
         dumpSetting(s, p,
@@ -2363,6 +2369,14 @@
                 SecureSettingsProto.Zen.SETTINGS_SUGGESTION_VIEWED);
         p.end(zenToken);
 
+        dumpSetting(s, p,
+                Settings.Secure.SKIP_GESTURE,
+                SecureSettingsProto.SKIP_GESTURE_ENABLED);
+
+        dumpSetting(s, p,
+                Settings.Secure.SILENCE_GESTURE,
+                SecureSettingsProto.SILENCE_GESTURE_ENABLED);
+
         // Please insert new settings using the same order as in SecureSettingsProto.
         p.end(token);
 
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index fa95bf2..9b775e0 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -129,6 +129,7 @@
     <uses-permission android:name="android.permission.VIBRATE" />
     <uses-permission android:name="android.permission.MANAGE_ACTIVITY_STACKS" />
     <uses-permission android:name="android.permission.START_TASKS_FROM_RECENTS" />
+    <uses-permission android:name="android.permission.START_ACTIVITIES_FROM_BACKGROUND" />
     <uses-permission android:name="android.permission.ACTIVITY_EMBEDDING" />
     <uses-permission android:name="android.permission.CONNECTIVITY_INTERNAL" />
     <uses-permission android:name="android.permission.CHANGE_COMPONENT_ENABLED_STATE" />
diff --git a/packages/Shell/src/com/android/shell/BugreportProgressService.java b/packages/Shell/src/com/android/shell/BugreportProgressService.java
index 2530abc..2d7471d 100644
--- a/packages/Shell/src/com/android/shell/BugreportProgressService.java
+++ b/packages/Shell/src/com/android/shell/BugreportProgressService.java
@@ -56,6 +56,7 @@
 import android.accounts.Account;
 import android.accounts.AccountManager;
 import android.annotation.MainThread;
+import android.annotation.Nullable;
 import android.annotation.SuppressLint;
 import android.app.AlertDialog;
 import android.app.Notification;
@@ -799,6 +800,18 @@
             Log.wtf(TAG, "Missing " + EXTRA_BUGREPORT + " on intent " + intent);
             return;
         }
+        final int max = intent.getIntExtra(EXTRA_MAX, -1);
+        final File screenshotFile = getFileExtra(intent, EXTRA_SCREENSHOT);
+        final String shareTitle = intent.getStringExtra(EXTRA_TITLE);
+        final String shareDescription = intent.getStringExtra(EXTRA_DESCRIPTION);
+        onBugreportFinished(id, bugreportFile, screenshotFile, shareTitle, shareDescription, max);
+    }
+
+    /**
+     * Wraps up bugreport generation and triggers a notification to share the bugreport.
+     */
+    private void onBugreportFinished(int id, File bugreportFile, @Nullable File screenshotFile,
+        String shareTitle, String shareDescription, int max) {
         mInfoDialog.onBugreportFinished();
         BugreportInfo info = getInfo(id);
         if (info == null) {
@@ -809,22 +822,17 @@
         }
         info.renameScreenshots(mScreenshotsDir);
         info.bugreportFile = bugreportFile;
+        if (screenshotFile != null) {
+            info.addScreenshot(screenshotFile);
+        }
 
-        final int max = intent.getIntExtra(EXTRA_MAX, -1);
         if (max != -1) {
             MetricsLogger.histogram(this, "dumpstate_duration", max);
             info.max = max;
         }
 
-        final File screenshot = getFileExtra(intent, EXTRA_SCREENSHOT);
-        if (screenshot != null) {
-            info.addScreenshot(screenshot);
-        }
-
-        final String shareTitle = intent.getStringExtra(EXTRA_TITLE);
         if (!TextUtils.isEmpty(shareTitle)) {
             info.title = shareTitle;
-            final String shareDescription = intent.getStringExtra(EXTRA_DESCRIPTION);
             if (!TextUtils.isEmpty(shareDescription)) {
                 info.shareDescription= shareDescription;
             }
@@ -1944,6 +1952,22 @@
         }
 
         @Override
+        public void onProgress(int progress) throws RemoteException {
+            updateProgressInfo(progress, 100 /* progress is already a percentage; so max = 100 */);
+        }
+
+        @Override
+        public void onError(int errorCode) throws RemoteException {
+            // TODO(b/111441001): implement
+        }
+
+        @Override
+        public void onFinished(long durationMs, String title, String description)
+                throws RemoteException {
+            // TODO(b/111441001): implement
+        }
+
+        @Override
         public void onProgressUpdated(int progress) throws RemoteException {
             /*
              * Checks whether the progress changed in a way that should be displayed to the user:
@@ -1964,21 +1988,7 @@
             }
 
             if (newPercentage > oldPercentage) {
-                if (DEBUG) {
-                    if (progress != info.progress) {
-                        Log.v(TAG, "Updating progress for PID " + info.pid + "(id: " + info.id
-                                + ") from " + info.progress + " to " + progress);
-                    }
-                    if (max != info.max) {
-                        Log.v(TAG, "Updating max progress for PID " + info.pid + "(id: " + info.id
-                                + ") from " + info.max + " to " + max);
-                    }
-                }
-                info.progress = progress;
-                info.max = max;
-                info.lastUpdate = System.currentTimeMillis();
-
-                updateProgress(info);
+                updateProgressInfo(progress, max);
             }
         }
 
@@ -2000,5 +2010,23 @@
         public void dump(String prefix, PrintWriter pw) {
             pw.print(prefix); pw.print("token: "); pw.println(token);
         }
+
+        private void updateProgressInfo(int progress, int max) {
+            if (DEBUG) {
+                if (progress != info.progress) {
+                    Log.v(TAG, "Updating progress for PID " + info.pid + "(id: " + info.id
+                            + ") from " + info.progress + " to " + progress);
+                }
+                if (max != info.max) {
+                    Log.v(TAG, "Updating max progress for PID " + info.pid + "(id: " + info.id
+                            + ") from " + info.max + " to " + max);
+                }
+            }
+            info.progress = progress;
+            info.max = max;
+            info.lastUpdate = System.currentTimeMillis();
+
+            updateProgress(info);
+        }
     }
 }
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 1c1a140..b4f2711 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -87,6 +87,7 @@
     <uses-permission android:name="android.permission.STOP_APP_SWITCHES" />
     <uses-permission android:name="android.permission.SET_SCREEN_COMPATIBILITY" />
     <uses-permission android:name="android.permission.START_ANY_ACTIVITY" />
+    <uses-permission android:name="android.permission.START_ACTIVITIES_FROM_BACKGROUND" />
     <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS" />
     <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" />
     <uses-permission android:name="android.permission.GET_TOP_ACTIVITY_INFO" />
diff --git a/packages/SystemUI/res-keyguard/drawable/bubble_hour_hand.xml b/packages/SystemUI/res-keyguard/drawable/bubble_hour_hand.xml
new file mode 100644
index 0000000..d3c3a51
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/drawable/bubble_hour_hand.xml
@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:height="200dp"
+    android:width="200dp"
+    android:viewportHeight="100"
+    android:viewportWidth="100">
+    <path
+        android:fillColor="#000000"
+        android:pathData="M50.082,14.199m-13.985,0a13.985,13.985 0,1 1,27.97 0a13.985,13.985 0,1 1,-27.97 0"/>
+</vector>
diff --git a/packages/SystemUI/res-keyguard/drawable/bubble_minute_hand.xml b/packages/SystemUI/res-keyguard/drawable/bubble_minute_hand.xml
new file mode 100644
index 0000000..a4417fb
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/drawable/bubble_minute_hand.xml
@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:height="200dp"
+    android:width="200dp"
+    android:viewportHeight="100"
+    android:viewportWidth="100" >
+    <path
+        android:fillColor="#000000"
+        android:pathData="M50.082,0.025L50.082,0.025A13.985,15.63 0,0 1,64.067 15.656L64.067,49.029A13.985,15.63 0,0 1,50.082 64.659L50.082,64.659A13.985,15.63 0,0 1,36.097 49.029L36.097,15.656A13.985,15.63 0,0 1,50.082 0.025z"/>
+</vector>
diff --git a/packages/SystemUI/res-keyguard/layout/bubble_clock.xml b/packages/SystemUI/res-keyguard/layout/bubble_clock.xml
new file mode 100644
index 0000000..0d72657
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/layout/bubble_clock.xml
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2019 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+<com.android.keyguard.clock.ClockLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+  >
+  <TextClock
+      android:id="@+id/digital_clock"
+      android:layout_width="wrap_content"
+      android:layout_height="wrap_content"
+      android:letterSpacing="0.03"
+      android:singleLine="true"
+      style="@style/widget_big"
+      android:format12Hour="@string/keyguard_widget_12_hours_format"
+      android:format24Hour="@string/keyguard_widget_24_hours_format"
+  />
+  <com.android.keyguard.clock.ImageClock
+      android:id="@+id/analog_clock"
+      android:layout_width="wrap_content"
+      android:layout_height="wrap_content"
+  >
+    <ImageView
+        android:id="@+id/minute_hand"
+        android:layout_width="300dp"
+        android:layout_height="300dp"
+        android:src="@drawable/bubble_minute_hand"
+        android:tint="@color/bubbleMinuteHandColor"
+    />
+    <ImageView
+        android:id="@+id/hour_hand"
+        android:layout_width="300dp"
+        android:layout_height="300dp"
+        android:src="@drawable/bubble_hour_hand"
+        android:tint="@color/bubbleHourHandColor"
+    />
+  </com.android.keyguard.clock.ImageClock>
+</com.android.keyguard.clock.ClockLayout>
diff --git a/packages/SystemUI/res-keyguard/layout/digital_clock.xml b/packages/SystemUI/res-keyguard/layout/digital_clock.xml
new file mode 100644
index 0000000..cf4a573
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/layout/digital_clock.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2019 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+<FrameLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:layout_gravity="center_horizontal"
+    android:layout_alignParentTop="true">
+  <TextClock
+      android:id="@+id/lock_screen_clock"
+      android:layout_width="wrap_content"
+      android:layout_height="wrap_content"
+      android:layout_gravity="center_horizontal"
+      android:letterSpacing="0.03"
+      android:singleLine="true"
+      style="@style/widget_big"
+      android:format12Hour="@string/keyguard_widget_12_hours_format"
+      android:format24Hour="@string/keyguard_widget_24_hours_format" />
+  />
+</FrameLayout>
+
diff --git a/packages/SystemUI/res-keyguard/layout/stretchanalog_clock.xml b/packages/SystemUI/res-keyguard/layout/stretchanalog_clock.xml
new file mode 100644
index 0000000..9033fce
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/layout/stretchanalog_clock.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2019 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+<com.android.keyguard.clock.ClockLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+  >
+  <TextClock
+      android:id="@+id/digital_clock"
+      android:layout_width="wrap_content"
+      android:layout_height="wrap_content"
+      android:letterSpacing="0.03"
+      android:singleLine="true"
+      style="@style/widget_big"
+      android:format12Hour="@string/keyguard_widget_12_hours_format"
+      android:format24Hour="@string/keyguard_widget_24_hours_format"
+  />
+  <com.android.keyguard.clock.StretchAnalogClock
+      android:id="@+id/analog_clock"
+      android:layout_width="match_parent"
+      android:layout_height="match_parent"
+  />
+</com.android.keyguard.clock.ClockLayout>
diff --git a/packages/SystemUI/res-keyguard/values/colors.xml b/packages/SystemUI/res-keyguard/values/colors.xml
new file mode 100644
index 0000000..7a849eb
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/values/colors.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2019 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+<resources>
+  <!-- Default color for hour hand of Bubble clock. -->
+  <color name="bubbleHourHandColor">#C97343</color>
+  <!-- Default color for minute hand of Bubble clock. -->
+  <color name="bubbleMinuteHandColor">#F5C983</color>
+</resources>
diff --git a/packages/SystemUI/res/layout/bubble_view.xml b/packages/SystemUI/res/layout/bubble_view.xml
new file mode 100644
index 0000000..204408cd
--- /dev/null
+++ b/packages/SystemUI/res/layout/bubble_view.xml
@@ -0,0 +1,38 @@
+<?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
+  -->
+<com.android.systemui.bubbles.BubbleView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_height="wrap_content"
+    android:layout_width="wrap_content"
+    android:id="@+id/bubble_view">
+
+    <com.android.systemui.bubbles.BadgedImageView
+        android:id="@+id/bubble_image"
+        android:layout_width="@dimen/bubble_size"
+        android:layout_height="@dimen/bubble_size"
+        android:padding="@dimen/bubble_view_padding"
+        android:clipToPadding="false"/>
+
+    <TextView
+        android:id="@+id/message_view"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:minWidth="@dimen/bubble_message_min_width"
+        android:maxWidth="@dimen/bubble_message_max_width"
+        android:padding="@dimen/bubble_message_padding"/>
+
+</com.android.systemui.bubbles.BubbleView>
diff --git a/packages/SystemUI/res/layout/ongoing_privacy_dialog_item.xml b/packages/SystemUI/res/layout/ongoing_privacy_dialog_item.xml
index ecfbfb4..c8e0845 100644
--- a/packages/SystemUI/res/layout/ongoing_privacy_dialog_item.xml
+++ b/packages/SystemUI/res/layout/ongoing_privacy_dialog_item.xml
@@ -25,12 +25,18 @@
     android:focusable="true"
     android:layout_gravity="center_vertical">
 
-    <ImageView
-        android:id="@+id/app_icon"
+    <FrameLayout
         android:layout_height="@dimen/ongoing_appops_dialog_app_icon_size"
         android:layout_width="@dimen/ongoing_appops_dialog_app_icon_size"
-        android:layout_gravity="start|center_vertical"
-    />
+        android:layout_gravity="start|center_vertical">
+
+        <ImageView
+            android:id="@+id/app_icon"
+            android:layout_height="@dimen/ongoing_appops_dialog_app_icon_size"
+            android:layout_width="@dimen/ongoing_appops_dialog_app_icon_size"
+            android:layout_gravity="center"
+            />
+    </FrameLayout>
 
     <TextView
         android:id="@+id/app_name"
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 7cc5524..476089a 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -454,6 +454,10 @@
          RemoteInput.Builder.setEditChoicesBeforeSending. -->
     <bool name="config_smart_replies_in_notifications_edit_choices_before_sending">false</bool>
 
+    <!-- Smart replies in notifications: Whether smart suggestions in notifications are enabled in
+         heads-up notifications.  -->
+    <bool name="config_smart_replies_in_notifications_show_in_heads_up">true</bool>
+
     <!-- Screenshot editing default activity.  Must handle ACTION_EDIT image/png intents.
          Blank sends the user to the Chooser first.
          This name is in the ComponentName flattened format (package/class)  -->
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 10e5f74..5a00b45 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -952,6 +952,8 @@
     <dimen name="ongoing_appops_dialog_icon_margin">8dp</dimen>
     <!-- Height and width of Application icons in Ongoing App Ops dialog -->
     <dimen name="ongoing_appops_dialog_app_icon_size">32dp</dimen>
+    <!-- Height and width of Plus sign in Ongoing App Ops dialog -->
+    <dimen name="ongoing_appops_dialog_app_plus_size">24dp</dimen>
     <!-- Height of line in Ongoing App Ops dialog-->
     <dimen name="ongoing_appops_dialog_line_height">48dp</dimen>
     <!-- Side margin of title in Ongoing App Ops dialog -->
@@ -981,14 +983,16 @@
 
     <!-- How much a bubble is elevated -->
     <dimen name="bubble_elevation">8dp</dimen>
+    <!-- Padding around a collapsed bubble -->
+    <dimen name="bubble_view_padding">0dp</dimen>
     <!-- Padding between bubbles when displayed in expanded state -->
     <dimen name="bubble_padding">8dp</dimen>
-    <!-- Padding around the view displayed when the bubble is expanded -->
-    <dimen name="bubble_expanded_view_padding">8dp</dimen>
     <!-- Size of the collapsed bubble -->
     <dimen name="bubble_size">56dp</dimen>
-    <!-- Size of an icon displayed within the bubble -->
-    <dimen name="bubble_icon_size">24dp</dimen>
+    <!-- How much to inset the icon in the circle -->
+    <dimen name="bubble_icon_inset">16dp</dimen>
+    <!-- Padding around the view displayed when the bubble is expanded -->
+    <dimen name="bubble_expanded_view_padding">8dp</dimen>
     <!-- Default height of the expanded view shown when the bubble is expanded -->
     <dimen name="bubble_expanded_default_height">400dp</dimen>
     <!-- Height of the triangle that points to the expanded bubble -->
@@ -1001,4 +1005,10 @@
     <dimen name="bubble_expanded_header_height">48dp</dimen>
     <!-- Left and right padding applied to the header. -->
     <dimen name="bubble_expanded_header_horizontal_padding">24dp</dimen>
+    <!-- Max width of the message bubble-->
+    <dimen name="bubble_message_max_width">144dp</dimen>
+    <!-- Min width of the message bubble -->
+    <dimen name="bubble_message_min_width">32dp</dimen>
+    <!-- Interior padding of the message bubble -->
+    <dimen name="bubble_message_padding">4dp</dimen>
 </resources>
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 523720d5..1a684a0 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
@@ -323,7 +323,7 @@
                     return null;
                 }
                 // Create our own ClassLoader so we can use our own code as the parent.
-                ClassLoader classLoader = mManager.getClassLoader(info.sourceDir, info.packageName);
+                ClassLoader classLoader = mManager.getClassLoader(info);
                 Context pluginContext = new PluginContextWrapper(
                         mContext.createApplicationContext(info, 0), classLoader);
                 Class<?> pluginClass = Class.forName(cls, true, classLoader);
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 da143f9..7139708 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
@@ -14,6 +14,7 @@
 
 package com.android.systemui.shared.plugins;
 
+import android.app.LoadedApk;
 import android.app.Notification;
 import android.app.Notification.Action;
 import android.app.NotificationManager;
@@ -44,15 +45,17 @@
 import com.android.systemui.plugins.Plugin;
 import com.android.systemui.plugins.PluginListener;
 import com.android.systemui.plugins.annotations.ProvidesInterface;
-import com.android.systemui.shared.plugins.PluginInstanceManager.PluginContextWrapper;
 import com.android.systemui.shared.plugins.PluginInstanceManager.PluginInfo;
 
 import dalvik.system.PathClassLoader;
 
+import java.io.File;
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.lang.Thread.UncaughtExceptionHandler;
+import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.List;
 import java.util.Map;
 /**
  * @see Plugin
@@ -117,6 +120,7 @@
         return mPluginEnabler;
     }
 
+    // TODO(mankoff): This appears to be only called from tests. Remove?
     public <T extends Plugin> T getOneShotPlugin(Class<T> cls) {
         ProvidesInterface info = cls.getDeclaredAnnotation(ProvidesInterface.class);
         if (info == null) {
@@ -282,17 +286,25 @@
         }
     }
 
-    public ClassLoader getClassLoader(String sourceDir, String pkg) {
-        if (!isDebuggable && !mWhitelistedPlugins.contains(pkg)) {
-            Log.w(TAG, "Cannot get class loader for non-whitelisted plugin. Src:" + sourceDir +
-                    ", pkg: " + pkg);
+    /** Returns class loader specific for the given plugin. */
+    public ClassLoader getClassLoader(ApplicationInfo appInfo) {
+        if (!isDebuggable && !mWhitelistedPlugins.contains(appInfo.packageName)) {
+            Log.w(TAG, "Cannot get class loader for non-whitelisted plugin. Src:"
+                    + appInfo.sourceDir + ", pkg: " + appInfo.packageName);
             return null;
         }
-        if (mClassLoaders.containsKey(pkg)) {
-            return mClassLoaders.get(pkg);
+        if (mClassLoaders.containsKey(appInfo.packageName)) {
+            return mClassLoaders.get(appInfo.packageName);
         }
-        ClassLoader classLoader = new PathClassLoader(sourceDir, getParentClassLoader());
-        mClassLoaders.put(pkg, classLoader);
+
+        List<String> zipPaths = new ArrayList<>();
+        List<String> libPaths = new ArrayList<>();
+        LoadedApk.makePaths(null, true, appInfo, zipPaths, libPaths);
+        ClassLoader classLoader = new PathClassLoader(
+                TextUtils.join(File.pathSeparator, zipPaths),
+                TextUtils.join(File.pathSeparator, libPaths),
+                getParentClassLoader());
+        mClassLoaders.put(appInfo.packageName, classLoader);
         return classLoader;
     }
 
@@ -309,11 +321,6 @@
         return mParentClassLoader;
     }
 
-    public Context getContext(ApplicationInfo info, String pkg) throws NameNotFoundException {
-        ClassLoader classLoader = getClassLoader(info.sourceDir, pkg);
-        return new PluginContextWrapper(mContext.createApplicationContext(info, 0), classLoader);
-    }
-
     public <T> boolean dependsOn(Plugin p, Class<T> cls) {
         for (int i = 0; i < mPluginMap.size(); i++) {
             if (mPluginMap.valueAt(i).dependsOn(p, cls)) {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java
index 41e9eba..a055950 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java
@@ -46,6 +46,7 @@
     protected View mEcaView;
     protected boolean mEnableHaptics;
     private boolean mDismissing;
+    protected boolean mResumed;
     private CountDownTimer mCountdownTimer = null;
 
     // To avoid accidental lockout due to events while the device in in the pocket, ignore
@@ -263,6 +264,8 @@
 
     @Override
     public void onPause() {
+        mResumed = false;
+
         if (mCountdownTimer != null) {
             mCountdownTimer.cancel();
             mCountdownTimer = null;
@@ -276,6 +279,7 @@
 
     @Override
     public void onResume(int reason) {
+        mResumed = true;
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
index 3cfd6a9..a8094d20 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
@@ -1,9 +1,15 @@
 package com.android.keyguard;
 
+import android.content.ContentResolver;
 import android.content.Context;
+import android.database.ContentObserver;
 import android.graphics.Paint;
 import android.graphics.Paint.Style;
+import android.os.Handler;
+import android.os.Looper;
+import android.provider.Settings;
 import android.util.AttributeSet;
+import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.FrameLayout;
@@ -12,6 +18,8 @@
 
 import androidx.annotation.VisibleForTesting;
 
+import com.android.keyguard.clock.BubbleClockController;
+import com.android.keyguard.clock.StretchAnalogClockController;
 import com.android.systemui.Dependency;
 import com.android.systemui.plugins.ClockPlugin;
 import com.android.systemui.statusbar.StatusBarState;
@@ -19,13 +27,19 @@
 import com.android.systemui.statusbar.policy.ExtensionController;
 import com.android.systemui.statusbar.policy.ExtensionController.Extension;
 
+import java.util.Objects;
 import java.util.TimeZone;
 import java.util.function.Consumer;
+import java.util.function.Supplier;
 
 /**
  * Switch to show plugin clock when plugin is connected, otherwise it will show default clock.
  */
 public class KeyguardClockSwitch extends RelativeLayout {
+
+    private LayoutInflater mLayoutInflater;
+
+    private final ContentResolver mContentResolver;
     /**
      * Optional/alternative clock injected via plugin.
      */
@@ -79,12 +93,25 @@
                 }
     };
 
+    private final ContentObserver mContentObserver =
+            new ContentObserver(new Handler(Looper.getMainLooper())) {
+                @Override
+                public void onChange(boolean selfChange) {
+                    super.onChange(selfChange);
+                    if (mClockExtension != null) {
+                        mClockExtension.reload();
+                    }
+                }
+    };
+
     public KeyguardClockSwitch(Context context) {
         this(context, null);
     }
 
     public KeyguardClockSwitch(Context context, AttributeSet attrs) {
         super(context, attrs);
+        mLayoutInflater = LayoutInflater.from(context);
+        mContentResolver = context.getContentResolver();
     }
 
     /**
@@ -108,7 +135,28 @@
         mClockExtension = Dependency.get(ExtensionController.class).newExtension(ClockPlugin.class)
                 .withPlugin(ClockPlugin.class)
                 .withCallback(mClockPluginConsumer)
+                // Using withDefault even though this isn't the default as a workaround.
+                // ExtensionBulider doesn't provide the ability to supply a ClockPlugin
+                // instance based off of the value of a setting. Since multiple "default"
+                // can be provided, using a supplier that changes the settings value.
+                // A null return will cause Extension#reload to look at the next "default"
+                // supplier.
+                .withDefault(
+                        new SettingsGattedSupplier(
+                                mContentResolver,
+                                Settings.Secure.LOCK_SCREEN_CUSTOM_CLOCK_FACE,
+                                BubbleClockController.class.getName(),
+                                () -> BubbleClockController.build(mLayoutInflater)))
+                .withDefault(
+                        new SettingsGattedSupplier(
+                                mContentResolver,
+                                Settings.Secure.LOCK_SCREEN_CUSTOM_CLOCK_FACE,
+                                StretchAnalogClockController.class.getName(),
+                                () -> StretchAnalogClockController.build(mLayoutInflater)))
                 .build();
+        mContentResolver.registerContentObserver(
+                Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_CUSTOM_CLOCK_FACE),
+                false, mContentObserver);
         Dependency.get(StatusBarStateController.class).addCallback(mStateListener);
     }
 
@@ -116,6 +164,7 @@
     protected void onDetachedFromWindow() {
         super.onDetachedFromWindow();
         mClockExtension.destroy();
+        mContentResolver.unregisterContentObserver(mContentObserver);
         Dependency.get(StatusBarStateController.class).removeCallback(mStateListener);
     }
 
@@ -269,4 +318,44 @@
     StatusBarStateController.StateListener getStateListener() {
         return mStateListener;
     }
+
+    /**
+     * Supplier that only gets an instance when a settings value matches expected value.
+     */
+    private static class SettingsGattedSupplier implements Supplier<ClockPlugin> {
+
+        private final ContentResolver mContentResolver;
+        private final String mKey;
+        private final String mValue;
+        private final Supplier<ClockPlugin> mSupplier;
+
+        /**
+         * Constructs a supplier that changes secure setting key against value.
+         *
+         * @param contentResolver Used to look up settings value.
+         * @param key Settings key.
+         * @param value If the setting matches this values that get supplies a ClockPlugin
+         *        instance.
+         * @param supplier Supplier of ClockPlugin instance, only used if the setting
+         *        matches value.
+         */
+        SettingsGattedSupplier(ContentResolver contentResolver, String key, String value,
+                Supplier<ClockPlugin> supplier) {
+            mContentResolver = contentResolver;
+            mKey = key;
+            mValue = value;
+            mSupplier = supplier;
+        }
+
+        /**
+         * Returns null if the settings value doesn't match the expected value.
+         *
+         * A null return causes Extension#reload to skip this supplier and move to the next.
+         */
+        @Override
+        public ClockPlugin get() {
+            final String currentValue = Settings.Secure.getString(mContentResolver, mKey);
+            return Objects.equals(currentValue, mValue) ? mSupplier.get() : null;
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java
index 41afa9a..3296c10 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java
@@ -81,6 +81,11 @@
     protected void resetState() {
         mSecurityMessageDisplay.setMessage("");
         final boolean wasDisabled = mPasswordEntry.isEnabled();
+        // Don't set enabled password entry & showSoftInput when PasswordEntry is invisible or in
+        // pausing stage.
+        if (!mResumed || !mPasswordEntry.isVisibleToUser()) {
+            return;
+        }
         setPasswordEntryEnabled(true);
         setPasswordEntryInputEnabled(true);
         if (wasDisabled) {
diff --git a/packages/SystemUI/src/com/android/keyguard/clock/BubbleClockController.java b/packages/SystemUI/src/com/android/keyguard/clock/BubbleClockController.java
new file mode 100644
index 0000000..db6127f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/clock/BubbleClockController.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.keyguard.clock;
+
+import android.graphics.Paint.Style;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.TextClock;
+
+import com.android.keyguard.R;
+import com.android.systemui.plugins.ClockPlugin;
+
+import java.util.TimeZone;
+
+/**
+ * Controller for Bubble clock that can appear on lock screen and AOD.
+ */
+public class BubbleClockController implements ClockPlugin {
+
+    /**
+     * Custom clock shown on AOD screen and behind stack scroller on lock.
+     */
+    private View mView;
+    private TextClock mDigitalClock;
+    private ImageClock mAnalogClock;
+
+    /**
+     * Small clock shown on lock screen above stack scroller.
+     */
+    private View mLockClockContainer;
+    private TextClock mLockClock;
+
+    /**
+     * Controller for transition to dark state.
+     */
+    private CrossFadeDarkController mDarkController;
+
+    private BubbleClockController() { }
+
+    /**
+     * Create a BubbleClockController instance.
+     *
+     * @param layoutInflater Inflater used to inflate custom clock views.
+     */
+    public static BubbleClockController build(LayoutInflater layoutInflater) {
+        BubbleClockController controller = new BubbleClockController();
+        controller.createViews(layoutInflater);
+        return controller;
+    }
+
+    private void createViews(LayoutInflater layoutInflater) {
+        mView = layoutInflater.inflate(R.layout.bubble_clock, null);
+        mDigitalClock = (TextClock) mView.findViewById(R.id.digital_clock);
+        mAnalogClock = (ImageClock) mView.findViewById(R.id.analog_clock);
+
+        mLockClockContainer = layoutInflater.inflate(R.layout.digital_clock, null);
+        mLockClock = (TextClock) mLockClockContainer.findViewById(R.id.lock_screen_clock);
+        mLockClock.setVisibility(View.GONE);
+
+        mDarkController = new CrossFadeDarkController(mDigitalClock, mLockClock);
+    }
+
+    @Override
+    public View getView() {
+        return mLockClockContainer;
+    }
+
+    @Override
+    public View getBigClockView() {
+        return mView;
+    }
+
+    @Override
+    public void setStyle(Style style) {}
+
+    @Override
+    public void setTextColor(int color) {
+        mLockClock.setTextColor(color);
+        mDigitalClock.setTextColor(color);
+    }
+
+    @Override
+    public void dozeTimeTick() {
+        mAnalogClock.onTimeChanged();
+    }
+
+    @Override
+    public void setDarkAmount(float darkAmount) {
+        mDarkController.setDarkAmount(darkAmount);
+    }
+
+    @Override
+    public void onTimeZoneChanged(TimeZone timeZone) {
+        mAnalogClock.onTimeZoneChanged(timeZone);
+    }
+
+    @Override
+    public boolean shouldShowStatusArea() {
+        return false;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/keyguard/clock/ClockLayout.java b/packages/SystemUI/src/com/android/keyguard/clock/ClockLayout.java
new file mode 100644
index 0000000..5aa5668
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/clock/ClockLayout.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.keyguard.clock;
+
+import static com.android.systemui.doze.util.BurnInHelperKt.getBurnInOffset;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.FrameLayout;
+
+import com.android.keyguard.R;
+
+/**
+ * Positions clock faces (analog, digital, typographic) and handles pixel shifting
+ * to prevent screen burn-in.
+ */
+public class ClockLayout extends FrameLayout {
+
+    /**
+     * Clock face views.
+     */
+    private View mDigitalClock;
+    private View mAnalogClock;
+
+    /**
+     * Pixel shifting amplitidues used to prevent screen burn-in.
+     */
+    private int mBurnInPreventionOffsetX;
+    private int mBurnInPreventionOffsetY;
+
+    public ClockLayout(Context context) {
+        this(context, null);
+    }
+
+    public ClockLayout(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public ClockLayout(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+        mDigitalClock = findViewById(R.id.digital_clock);
+        mAnalogClock = findViewById(R.id.analog_clock);
+
+        // Get pixel shifting X, Y amplitudes from resources.
+        Resources resources = getResources();
+        mBurnInPreventionOffsetX = resources.getDimensionPixelSize(
+            R.dimen.burn_in_prevention_offset_x);
+        mBurnInPreventionOffsetY = resources.getDimensionPixelSize(
+            R.dimen.burn_in_prevention_offset_y);
+    }
+
+    @Override
+    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+        super.onLayout(changed, left, top, right, bottom);
+
+        final float offsetX = getBurnInOffset(mBurnInPreventionOffsetX, true);
+        final float offsetY = getBurnInOffset(mBurnInPreventionOffsetY, false);
+
+        // Put digital clock in two left corner of the screen.
+        if (mDigitalClock != null) {
+            mDigitalClock.setX(0.1f * getWidth() + offsetX);
+            mDigitalClock.setY(0.1f * getHeight() + offsetY);
+        }
+
+        // Put the analog clock in the middle of the screen.
+        if (mAnalogClock != null) {
+            mAnalogClock.setX(Math.max(0f, 0.5f * (getWidth() - mAnalogClock.getWidth()))
+                    + offsetX);
+            mAnalogClock.setY(Math.max(0f, 0.5f * (getHeight() - mAnalogClock.getHeight()))
+                    + offsetY);
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/keyguard/clock/CrossFadeDarkController.java b/packages/SystemUI/src/com/android/keyguard/clock/CrossFadeDarkController.java
new file mode 100644
index 0000000..3c3f475
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/clock/CrossFadeDarkController.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.keyguard.clock;
+
+import android.view.View;
+
+/**
+ * Controls transition to dark state by cross fading between views.
+ */
+final class CrossFadeDarkController {
+
+    private final View mFadeInView;
+    private final View mFadeOutView;
+
+    /**
+     * Creates a new controller that fades between views.
+     *
+     * @param fadeInView View to fade in when transitioning to AOD.
+     * @param fadeOutView View to fade out when transitioning to AOD.
+     */
+    CrossFadeDarkController(View fadeInView, View fadeOutView) {
+        mFadeInView = fadeInView;
+        mFadeOutView = fadeOutView;
+    }
+
+    /**
+     * Sets the amount the system has transitioned to the dark state.
+     *
+     * @param darkAmount Amount of transition to dark state: 1f for AOD and 0f for lock screen.
+     */
+    void setDarkAmount(float darkAmount) {
+        mFadeInView.setAlpha(Math.max(0f, 2f * darkAmount - 1f));
+        if (darkAmount == 0f) {
+            mFadeInView.setVisibility(View.GONE);
+        } else {
+            if (mFadeInView.getVisibility() == View.GONE) {
+                mFadeInView.setVisibility(View.VISIBLE);
+            }
+        }
+        mFadeOutView.setAlpha(Math.max(0f, 1f - 2f * darkAmount));
+        if (darkAmount == 1f) {
+            mFadeOutView.setVisibility(View.GONE);
+        } else {
+            if (mFadeOutView.getVisibility() == View.GONE) {
+                mFadeOutView.setVisibility(View.VISIBLE);
+            }
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/keyguard/clock/ImageClock.java b/packages/SystemUI/src/com/android/keyguard/clock/ImageClock.java
new file mode 100644
index 0000000..2c709e0
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/clock/ImageClock.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.keyguard.clock;
+
+import android.content.Context;
+import android.text.format.DateFormat;
+import android.util.AttributeSet;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+
+import com.android.keyguard.R;
+
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.TimeZone;
+
+/**
+ * Clock composed of two images that rotate with the time.
+ *
+ * The images are the clock hands. ImageClock expects two child ImageViews
+ * with ids hour_hand and minute_hand.
+ */
+public class ImageClock extends FrameLayout {
+
+    private ImageView mHourHand;
+    private ImageView mMinuteHand;
+    private Calendar mTime;
+    private String mDescFormat;
+    private TimeZone mTimeZone;
+
+    public ImageClock(Context context) {
+        this(context, null);
+    }
+
+    public ImageClock(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public ImageClock(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+        mTime = Calendar.getInstance();
+        mDescFormat = ((SimpleDateFormat) DateFormat.getTimeFormat(context)).toLocalizedPattern();
+    }
+
+    /**
+     * Call when the time changes to update the rotation of the clock hands.
+     */
+    public void onTimeChanged() {
+        mTime.setTimeInMillis(System.currentTimeMillis());
+        final float hourAngle = mTime.get(Calendar.HOUR) * 30f;
+        mHourHand.setRotation(hourAngle);
+        final float minuteAngle = mTime.get(Calendar.MINUTE) * 6f;
+        mMinuteHand.setRotation(minuteAngle);
+        setContentDescription(DateFormat.format(mDescFormat, mTime));
+        invalidate();
+    }
+
+    /**
+     * Call when the time zone has changed to update clock hands.
+     *
+     * @param timeZone The updated time zone that will be used.
+     */
+    public void onTimeZoneChanged(TimeZone timeZone) {
+        mTimeZone = timeZone;
+        mTime.setTimeZone(timeZone);
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+        mHourHand = findViewById(R.id.hour_hand);
+        mMinuteHand = findViewById(R.id.minute_hand);
+    }
+
+    @Override
+    protected void onAttachedToWindow() {
+        super.onAttachedToWindow();
+        mTime = Calendar.getInstance(mTimeZone != null ? mTimeZone : TimeZone.getDefault());
+        onTimeChanged();
+    }
+}
diff --git a/packages/SystemUI/src/com/android/keyguard/clock/StretchAnalogClock.java b/packages/SystemUI/src/com/android/keyguard/clock/StretchAnalogClock.java
new file mode 100644
index 0000000..91cec863
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/clock/StretchAnalogClock.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.keyguard.clock;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.util.AttributeSet;
+import android.view.View;
+
+import java.util.Calendar;
+import java.util.TimeZone;
+
+/**
+ * Analog clock where the minute hand extends off of the screen.
+ */
+public class StretchAnalogClock extends View {
+
+    private static final int DEFAULT_COLOR = Color.parseColor("#F5C983");
+    private static final float HOUR_STROKE_WIDTH = 60f;
+    private static final float MINUTE_STROKE_WIDTH = 20f;
+    private static final float CENTER_GAP_AND_CIRCLE_RADIUS = 80f;
+
+    private final Paint mHourPaint = new Paint();
+    private final Paint mMinutePaint = new Paint();
+    private Calendar mTime;
+    private TimeZone mTimeZone;
+
+    public StretchAnalogClock(Context context) {
+        this(context, null);
+    }
+
+    public StretchAnalogClock(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public StretchAnalogClock(Context context, AttributeSet attrs, int defStyleAttr) {
+        this(context, attrs, defStyleAttr, 0);
+    }
+
+    public StretchAnalogClock(Context context, AttributeSet attrs, int defStyleAttr,
+            int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+        init();
+    }
+
+    /**
+     * Call when the time changes to update the clock hands.
+     */
+    public void onTimeChanged() {
+        mTime.setTimeInMillis(System.currentTimeMillis());
+        invalidate();
+    }
+
+    /**
+     * Call when the time zone has changed to update clock hands.
+     *
+     * @param timeZone The updated time zone that will be used.
+     */
+    public void onTimeZoneChanged(TimeZone timeZone) {
+        mTime.setTimeZone(timeZone);
+    }
+
+    /**
+     * Set the color of the minute hand.
+     */
+    public void setMinuteHandColor(int color) {
+        mMinutePaint.setColor(color);
+    }
+
+    private void init() {
+        mHourPaint.setColor(DEFAULT_COLOR);
+        mHourPaint.setStrokeWidth(HOUR_STROKE_WIDTH);
+        mHourPaint.setAntiAlias(true);
+        mHourPaint.setStrokeCap(Paint.Cap.ROUND);
+
+        mMinutePaint.setColor(Color.WHITE);
+        mMinutePaint.setStrokeWidth(MINUTE_STROKE_WIDTH);
+        mMinutePaint.setAntiAlias(true);
+        mMinutePaint.setStrokeCap(Paint.Cap.ROUND);
+    }
+
+    @Override
+    protected void onDraw(Canvas canvas) {
+        final float centerX = getWidth() / 2f;
+        final float centerY = getHeight() / 2f;
+
+        final float minutesRotation = mTime.get(Calendar.MINUTE) * 6f;
+        final float hoursRotation = (mTime.get(Calendar.HOUR) * 30);
+
+        // Compute length of clock hands. Hour hand is 60% the length from center to edge
+        // and minute hand is twice the length to make sure it extends past screen edge.
+        double sMinuteHandLengthFactor = Math.sin(2d * Math.PI * minutesRotation / 360d);
+        float sMinuteHandLength = (float) (2d * (centerY + (centerX - centerY)
+                * sMinuteHandLengthFactor * sMinuteHandLengthFactor));
+        double sHourHandLengthFactor = Math.sin(2d * Math.PI * hoursRotation / 360d);
+        float sHourHandLength = (float) (0.6d * (centerY + (centerX - centerY)
+                * sHourHandLengthFactor * sHourHandLengthFactor));
+
+        canvas.save();
+
+        canvas.rotate(minutesRotation, centerX, centerY);
+        canvas.drawLine(
+                centerX,
+                centerY + CENTER_GAP_AND_CIRCLE_RADIUS,
+                centerX,
+                centerY - sMinuteHandLength,
+                mMinutePaint);
+
+        canvas.rotate(hoursRotation - minutesRotation, centerX, centerY);
+        canvas.drawLine(
+                centerX,
+                centerY + CENTER_GAP_AND_CIRCLE_RADIUS,
+                centerX,
+                centerY - sHourHandLength,
+                mHourPaint);
+
+        canvas.restore();
+    }
+
+    @Override
+    protected void onAttachedToWindow() {
+        super.onAttachedToWindow();
+        mTime = Calendar.getInstance(mTimeZone != null ? mTimeZone : TimeZone.getDefault());
+        onTimeChanged();
+    }
+}
diff --git a/packages/SystemUI/src/com/android/keyguard/clock/StretchAnalogClockController.java b/packages/SystemUI/src/com/android/keyguard/clock/StretchAnalogClockController.java
new file mode 100644
index 0000000..0a39158
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/clock/StretchAnalogClockController.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.keyguard.clock;
+
+import android.graphics.Paint.Style;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.TextClock;
+
+import com.android.keyguard.R;
+import com.android.systemui.plugins.ClockPlugin;
+
+import java.util.TimeZone;
+
+/**
+ * Controller for Stretch clock that can appear on lock screen and AOD.
+ */
+public class StretchAnalogClockController implements ClockPlugin {
+
+    /**
+     * Custom clock shown on AOD screen and behind stack scroller on lock.
+     */
+    private View mBigClockView;
+    private TextClock mDigitalClock;
+    private StretchAnalogClock mAnalogClock;
+
+    /**
+     * Small clock shown on lock screen above stack scroller.
+     */
+    private View mView;
+    private TextClock mLockClock;
+
+    /**
+     * Controller for transition to dark state.
+     */
+    private CrossFadeDarkController mDarkController;
+
+    private StretchAnalogClockController() { }
+
+    /**
+     * Create a BubbleClockController instance.
+     *
+     * @param layoutInflater Inflater used to inflate custom clock views.
+     */
+    public static StretchAnalogClockController build(LayoutInflater layoutInflater) {
+        StretchAnalogClockController controller = new StretchAnalogClockController();
+        controller.createViews(layoutInflater);
+        return controller;
+    }
+
+    private void createViews(LayoutInflater layoutInflater) {
+        mBigClockView = layoutInflater.inflate(R.layout.stretchanalog_clock, null);
+        mAnalogClock = mBigClockView.findViewById(R.id.analog_clock);
+        mDigitalClock = mBigClockView.findViewById(R.id.digital_clock);
+
+        mView = layoutInflater.inflate(R.layout.digital_clock, null);
+        mLockClock = mView.findViewById(R.id.lock_screen_clock);
+        mLockClock.setVisibility(View.GONE);
+
+        mDarkController = new CrossFadeDarkController(mDigitalClock, mLockClock);
+    }
+
+    @Override
+    public View getView() {
+        return mView;
+    }
+
+    @Override
+    public View getBigClockView() {
+        return mBigClockView;
+    }
+
+    @Override
+    public void setStyle(Style style) {}
+
+    @Override
+    public void setTextColor(int color) {
+        mLockClock.setTextColor(color);
+        mDigitalClock.setTextColor(color);
+        mAnalogClock.setMinuteHandColor(color);
+    }
+
+    @Override
+    public void dozeTimeTick() {
+        mAnalogClock.onTimeChanged();
+    }
+
+    @Override
+    public void setDarkAmount(float darkAmount) {
+        mDarkController.setDarkAmount(darkAmount);
+    }
+
+    @Override
+    public void onTimeZoneChanged(TimeZone timeZone) {
+        mAnalogClock.onTimeZoneChanged(timeZone);
+    }
+
+    @Override
+    public boolean shouldShowStatusArea() {
+        return false;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BadgeRenderer.java b/packages/SystemUI/src/com/android/systemui/bubbles/BadgeRenderer.java
new file mode 100644
index 0000000..845b084
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BadgeRenderer.java
@@ -0,0 +1,80 @@
+/*
+ * 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.bubbles;
+
+import static android.graphics.Paint.ANTI_ALIAS_FLAG;
+import static android.graphics.Paint.FILTER_BITMAP_FLAG;
+
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.util.Log;
+
+// XXX: Mostly opied from launcher code / can we share?
+/**
+ * Contains parameters necessary to draw a badge for an icon (e.g. the size of the badge).
+ */
+public class BadgeRenderer {
+
+    private static final String TAG = "BadgeRenderer";
+
+    // The badge sizes are defined as percentages of the app icon size.
+    private static final float SIZE_PERCENTAGE = 0.38f;
+
+    // Extra scale down of the dot
+    private static final float DOT_SCALE = 0.6f;
+
+    private final float mDotCenterOffset;
+    private final float mCircleRadius;
+    private final Paint mCirclePaint = new Paint(ANTI_ALIAS_FLAG | FILTER_BITMAP_FLAG);
+
+    public BadgeRenderer(int iconSizePx) {
+        mDotCenterOffset = SIZE_PERCENTAGE * iconSizePx;
+        int size = (int) (DOT_SCALE * mDotCenterOffset);
+        mCircleRadius = size / 2f;
+    }
+
+    /**
+     * Draw a circle in the top right corner of the given bounds.
+     *
+     * @param color The color (based on the icon) to use for the badge.
+     * @param iconBounds The bounds of the icon being badged.
+     * @param badgeScale The progress of the animation, from 0 to 1.
+     * @param spaceForOffset How much space to offset the badge up and to the left or right.
+     * @param onLeft Whether the badge should be draw on left or right side.
+     */
+    public void draw(Canvas canvas, int color, Rect iconBounds, float badgeScale,
+            Point spaceForOffset, boolean onLeft) {
+        if (iconBounds == null) {
+            Log.e(TAG, "Invalid null argument(s) passed in call to draw.");
+            return;
+        }
+        canvas.save();
+        // We draw the badge relative to its center.
+        int x = onLeft ? iconBounds.left : iconBounds.right;
+        float offset = onLeft ? (mDotCenterOffset / 2) : -(mDotCenterOffset / 2);
+        float badgeCenterX = x + offset;
+        float badgeCenterY = iconBounds.top + mDotCenterOffset / 2;
+
+        canvas.translate(badgeCenterX + spaceForOffset.x, badgeCenterY - spaceForOffset.y);
+
+        canvas.scale(badgeScale, badgeScale);
+        mCirclePaint.setColor(color);
+        canvas.drawCircle(0, 0, mCircleRadius, mCirclePaint);
+        canvas.restore();
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BadgedImageView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BadgedImageView.java
new file mode 100644
index 0000000..92d3cc1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BadgedImageView.java
@@ -0,0 +1,129 @@
+/*
+ * 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.bubbles;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Path;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.util.AttributeSet;
+import android.widget.ImageView;
+
+import com.android.systemui.R;
+
+/**
+ * View that circle crops its contents and supports displaying a coloured dot on a top corner.
+ */
+public class BadgedImageView extends ImageView {
+
+    private BadgeRenderer mDotRenderer;
+    private int mIconSize;
+    private Rect mTempBounds = new Rect();
+    private Point mTempPoint = new Point();
+    private Path mClipPath = new Path();
+
+    private float mDotScale = 0f;
+    private int mUpdateDotColor;
+    private boolean mShowUpdateDot;
+    private boolean mOnLeft;
+
+    public BadgedImageView(Context context) {
+        this(context, null);
+    }
+
+    public BadgedImageView(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public BadgedImageView(Context context, AttributeSet attrs, int defStyleAttr) {
+        this(context, attrs, defStyleAttr, 0);
+    }
+
+    public BadgedImageView(Context context, AttributeSet attrs, int defStyleAttr,
+            int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+        setScaleType(ScaleType.CENTER_CROP);
+        mIconSize = getResources().getDimensionPixelSize(R.dimen.bubble_size);
+        mDotRenderer = new BadgeRenderer(mIconSize);
+    }
+
+    // TODO: Clipping oval path isn't great: rerender image into a separate, rounded bitmap and
+    // then draw would be better
+    @Override
+    public void onDraw(Canvas canvas) {
+        canvas.save();
+        // Circle crop
+        mClipPath.addOval(getPaddingStart(), getPaddingTop(),
+                getWidth() - getPaddingEnd(), getHeight() - getPaddingBottom(), Path.Direction.CW);
+        canvas.clipPath(mClipPath);
+        super.onDraw(canvas);
+
+        // After we've circle cropped what we're showing, restore so we don't clip the badge
+        canvas.restore();
+
+        // Draw the badge
+        if (mShowUpdateDot) {
+            getDrawingRect(mTempBounds);
+            mTempPoint.set((getWidth() - mIconSize) / 2, getPaddingTop());
+            mDotRenderer.draw(canvas, mUpdateDotColor, mTempBounds, mDotScale, mTempPoint,
+                    mOnLeft);
+        }
+    }
+
+    /**
+     * Set whether the dot should appear on left or right side of the view.
+     */
+    public void setDotPosition(boolean onLeft) {
+        mOnLeft = onLeft;
+        invalidate();
+    }
+
+    /**
+     * Set whether the dot should show or not.
+     */
+    public void setShowDot(boolean showBadge) {
+        mShowUpdateDot = showBadge;
+        invalidate();
+    }
+
+    /**
+     * @return whether the dot is being displayed.
+     */
+    public boolean isShowingDot() {
+        return mShowUpdateDot;
+    }
+
+    /**
+     * The colour to use for the dot.
+     */
+    public void setDotColor(int color) {
+        mUpdateDotColor = color;
+        invalidate();
+    }
+
+    /**
+     * How big the dot should be, fraction from 0 to 1.
+     */
+    public void setDotScale(float fraction) {
+        mDotScale = fraction;
+        invalidate();
+    }
+
+    public float getDotScale() {
+        return mDotScale;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
index 9f3ff78..957d772 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
@@ -21,33 +21,42 @@
 import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
 
 import static com.android.systemui.bubbles.BubbleMovementHelper.EDGE_OVERLAP;
+import static com.android.systemui.statusbar.StatusBarState.SHADE;
+import static com.android.systemui.statusbar.notification.NotificationAlertingManager.alertAgain;
 
+import android.annotation.Nullable;
+import android.app.INotificationManager;
 import android.app.Notification;
 import android.app.PendingIntent;
 import android.content.Context;
 import android.content.pm.ActivityInfo;
 import android.graphics.Point;
 import android.graphics.Rect;
+import android.os.RemoteException;
+import android.os.ServiceManager;
 import android.provider.Settings;
 import android.service.notification.StatusBarNotification;
 import android.util.Log;
+import android.view.LayoutInflater;
 import android.view.ViewGroup;
 import android.view.WindowManager;
 import android.widget.FrameLayout;
 
-import androidx.annotation.Nullable;
-
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.statusbar.NotificationVisibility;
 import com.android.systemui.Dependency;
 import com.android.systemui.R;
+import com.android.systemui.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.notification.NotificationEntryListener;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.row.NotificationInflater;
 import com.android.systemui.statusbar.phone.StatusBarWindowController;
 
-import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.Map;
+import java.util.Set;
 
 import javax.inject.Inject;
 import javax.inject.Singleton;
@@ -66,8 +75,6 @@
 
     // Enables some subset of notifs to automatically become bubbles
     private static final boolean DEBUG_ENABLE_AUTO_BUBBLE = false;
-    // When a bubble is dismissed, recreate it as a notification
-    private static final boolean DEBUG_DEMOTE_TO_NOTIF = false;
 
     // Secure settings
     private static final String ENABLE_AUTO_BUBBLE_MESSAGES = "experiment_autobubble_messaging";
@@ -80,6 +87,7 @@
     private final NotificationEntryManager mNotificationEntryManager;
     private BubbleStateChangeListener mStateChangeListener;
     private BubbleExpandListener mExpandListener;
+    private LayoutInflater mInflater;
 
     private final Map<String, BubbleView> mBubbles = new HashMap<>();
     private BubbleStackView mStackView;
@@ -87,6 +95,12 @@
 
     // Bubbles get added to the status bar view
     private final StatusBarWindowController mStatusBarWindowController;
+    private StatusBarStateListener mStatusBarStateListener;
+
+    private final NotificationInterruptionStateProvider mNotificationInterruptionStateProvider =
+            Dependency.get(NotificationInterruptionStateProvider.class);
+
+    private INotificationManager mNotificationManagerService;
 
     // Used for determining view rect for touch interaction
     private Rect mTempRect = new Rect();
@@ -107,23 +121,53 @@
     public interface BubbleExpandListener {
         /**
          * Called when the expansion state of the bubble stack changes.
-         *
          * @param isExpanding whether it's expanding or collapsing
-         * @param amount fraction of how expanded or collapsed it is, 1 being fully, 0 at the start
+         * @param key the notification key associated with bubble being expanded
          */
-        void onBubbleExpandChanged(boolean isExpanding, float amount);
+        void onBubbleExpandChanged(boolean isExpanding, String key);
+    }
+
+    /**
+     * Listens for the current state of the status bar and updates the visibility state
+     * of bubbles as needed.
+     */
+    private class StatusBarStateListener implements StatusBarStateController.StateListener {
+        private int mState;
+        /**
+         * Returns the current status bar state.
+         */
+        public int getCurrentState() {
+            return mState;
+        }
+
+        @Override
+        public void onStateChanged(int newState) {
+            mState = newState;
+            updateVisibility();
+        }
     }
 
     @Inject
     public BubbleController(Context context, StatusBarWindowController statusBarWindowController) {
         mContext = context;
-        mNotificationEntryManager = Dependency.get(NotificationEntryManager.class);
         WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
         mDisplaySize = new Point();
         wm.getDefaultDisplay().getSize(mDisplaySize);
-        mStatusBarWindowController = statusBarWindowController;
+        mInflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
 
+        mNotificationEntryManager = Dependency.get(NotificationEntryManager.class);
         mNotificationEntryManager.addNotificationEntryListener(mEntryListener);
+
+        try {
+            mNotificationManagerService = INotificationManager.Stub.asInterface(
+                    ServiceManager.getServiceOrThrow(Context.NOTIFICATION_SERVICE));
+        } catch (ServiceManager.ServiceNotFoundException e) {
+            e.printStackTrace();
+        }
+
+        mStatusBarWindowController = statusBarWindowController;
+        mStatusBarStateListener = new StatusBarStateListener();
+        Dependency.get(StatusBarStateController.class).addCallback(mStatusBarStateListener);
     }
 
     /**
@@ -148,7 +192,12 @@
      * screen (e.g. if on AOD).
      */
     public boolean hasBubbles() {
-        return mBubbles.size() > 0;
+        for (BubbleView bv : mBubbles.values()) {
+            if (!bv.getEntry().isBubbleDismissed()) {
+                return true;
+            }
+        }
+        return false;
     }
 
     /**
@@ -163,7 +212,7 @@
      */
     public void collapseStack() {
         if (mStackView != null) {
-            mStackView.animateExpansion(false);
+            mStackView.collapseStack();
         }
     }
 
@@ -174,33 +223,32 @@
         if (mStackView == null) {
             return;
         }
-        Point startPoint = getStartPoint(mStackView.getStackWidth(), mDisplaySize);
-        // Reset the position of the stack (TODO - or should we save / respect last user position?)
-        mStackView.setPosition(startPoint.x, startPoint.y);
-        for (String key: mBubbles.keySet()) {
-            removeBubble(key);
+        Set<String> keys = mBubbles.keySet();
+        for (String key: keys) {
+            mBubbles.get(key).getEntry().setBubbleDismissed(true);
         }
+        mStackView.stackDismissed();
+
+        // Reset the position of the stack (TODO - or should we save / respect last user position?)
+        Point startPoint = getStartPoint(mStackView.getStackWidth(), mDisplaySize);
+        mStackView.setPosition(startPoint.x, startPoint.y);
+
+        updateVisibility();
         mNotificationEntryManager.updateNotifications();
-        updateBubblesShowing();
     }
 
     /**
-     * Adds a bubble associated with the provided notification entry or updates it if it exists.
+     * Adds or updates a bubble associated with the provided notification entry.
+     *
+     * @param notif the notification associated with this bubble.
+     * @param updatePosition whether this update should promote the bubble to the top of the stack.
      */
-    public void addBubble(NotificationEntry notif) {
+    public void updateBubble(NotificationEntry notif, boolean updatePosition) {
         if (mBubbles.containsKey(notif.key)) {
             // It's an update
             BubbleView bubble = mBubbles.get(notif.key);
-            mStackView.updateBubble(bubble, notif);
+            mStackView.updateBubble(bubble, notif, updatePosition);
         } else {
-            // It's new
-            BubbleView bubble = new BubbleView(mContext);
-            bubble.setNotif(notif);
-            if (shouldUseActivityView(mContext)) {
-                bubble.setAppOverlayIntent(getAppOverlayIntent(notif));
-            }
-            mBubbles.put(bubble.getKey(), bubble);
-
             boolean setPosition = mStackView != null && mStackView.getVisibility() != VISIBLE;
             if (mStackView == null) {
                 setPosition = true;
@@ -215,22 +263,30 @@
                     mStackView.setExpandListener(mExpandListener);
                 }
             }
+            // It's new
+            BubbleView bubble = (BubbleView) mInflater.inflate(
+                    R.layout.bubble_view, mStackView, false /* attachToRoot */);
+            bubble.setNotif(notif);
+            if (shouldUseActivityView(mContext)) {
+                bubble.setAppOverlayIntent(getAppOverlayIntent(notif));
+            }
+            mBubbles.put(bubble.getKey(), bubble);
             mStackView.addBubble(bubble);
             if (setPosition) {
                 // Need to add the bubble to the stack before we can know the width
                 Point startPoint = getStartPoint(mStackView.getStackWidth(), mDisplaySize);
                 mStackView.setPosition(startPoint.x, startPoint.y);
-                mStackView.setVisibility(VISIBLE);
             }
-            updateBubblesShowing();
         }
+        updateVisibility();
     }
 
     @Nullable
     private PendingIntent getAppOverlayIntent(NotificationEntry notif) {
         Notification notification = notif.notification.getNotification();
-        if (canLaunchInActivityView(notification.getAppOverlayIntent())) {
-            return notification.getAppOverlayIntent();
+        if (canLaunchInActivityView(notification.getBubbleMetadata() != null
+                ? notification.getBubbleMetadata().getIntent() : null)) {
+            return notification.getBubbleMetadata().getIntent();
         } else if (shouldUseContentIntent(mContext)
                 && canLaunchInActivityView(notification.contentIntent)) {
             Log.d(TAG, "[addBubble " + notif.key
@@ -245,79 +301,96 @@
      * Removes the bubble associated with the {@param uri}.
      */
     void removeBubble(String key) {
-        BubbleView bv = mBubbles.get(key);
+        BubbleView bv = mBubbles.remove(key);
         if (mStackView != null && bv != null) {
             mStackView.removeBubble(bv);
             bv.destroyActivityView(mStackView);
-            bv.getEntry().setBubbleDismissed(true);
         }
 
-        NotificationEntry entry = mNotificationEntryManager.getNotificationData().get(key);
+        NotificationEntry entry = bv != null ? bv.getEntry() : null;
         if (entry != null) {
             entry.setBubbleDismissed(true);
-            if (!DEBUG_DEMOTE_TO_NOTIF) {
-                mNotificationEntryManager.performRemoveNotification(entry.notification);
-            }
+            mNotificationEntryManager.updateNotifications();
         }
-        mNotificationEntryManager.updateNotifications();
-
-        updateBubblesShowing();
+        updateVisibility();
     }
 
     @SuppressWarnings("FieldCanBeLocal")
     private final NotificationEntryListener mEntryListener = new NotificationEntryListener() {
         @Override
         public void onPendingEntryAdded(NotificationEntry entry) {
-            if (shouldAutoBubble(mContext, entry)) {
+            if (shouldAutoBubble(mContext, entry) || shouldBubble(entry)) {
+                // TODO: handle group summaries
+                // It's a new notif, it shows in the shade and as a bubble
                 entry.setIsBubble(true);
+                entry.setShowInShadeWhenBubble(true);
+            }
+        }
+
+        @Override
+        public void onEntryInflated(NotificationEntry entry,
+                @NotificationInflater.InflationFlag int inflatedFlags) {
+            if (entry.isBubble() && mNotificationInterruptionStateProvider.shouldBubbleUp(entry)) {
+                updateBubble(entry, true /* updatePosition */);
+            }
+        }
+
+        @Override
+        public void onPreEntryUpdated(NotificationEntry entry) {
+            if (mNotificationInterruptionStateProvider.shouldBubbleUp(entry)
+                    && alertAgain(entry, entry.notification.getNotification())) {
+                entry.setShowInShadeWhenBubble(true);
+                entry.setBubbleDismissed(false); // updates come back as bubbles even if dismissed
+                if (mBubbles.containsKey(entry.key)) {
+                    mBubbles.get(entry.key).updateDotVisibility();
+                }
+                updateBubble(entry, true /* updatePosition */);
+            }
+        }
+
+        @Override
+        public void onEntryRemoved(NotificationEntry entry,
+                @Nullable NotificationVisibility visibility,
+                boolean removedByUser) {
+            entry.setShowInShadeWhenBubble(false);
+            if (mBubbles.containsKey(entry.key)) {
+                mBubbles.get(entry.key).updateDotVisibility();
+            }
+            if (!removedByUser) {
+                // This was a cancel so we should remove the bubble
+                removeBubble(entry.key);
             }
         }
     };
 
+    /**
+     * Lets any listeners know if bubble state has changed.
+     */
     private void updateBubblesShowing() {
-        boolean hasBubblesShowing = false;
-        for (BubbleView bv : mBubbles.values()) {
-            if (!bv.getEntry().isBubbleDismissed()) {
-                hasBubblesShowing = true;
-                break;
-            }
+        if (mStackView == null) {
+            return;
         }
+
         boolean hadBubbles = mStatusBarWindowController.getBubblesShowing();
+        boolean hasBubblesShowing = hasBubbles() && mStackView.getVisibility() == VISIBLE;
         mStatusBarWindowController.setBubblesShowing(hasBubblesShowing);
-        if (mStackView != null && !hasBubblesShowing) {
-            mStackView.setVisibility(INVISIBLE);
-        }
         if (mStateChangeListener != null && hadBubbles != hasBubblesShowing) {
             mStateChangeListener.onHasBubblesChanged(hasBubblesShowing);
         }
     }
 
     /**
-     * Sets the visibility of the bubbles, doesn't un-bubble them, just changes visibility.
+     * Updates the visibility of the bubbles based on current state.
+     * Does not un-bubble, just hides or un-hides. Will notify any
+     * {@link BubbleStateChangeListener}s if visibility changes.
      */
-    public void updateVisibility(boolean visible) {
-        if (mStackView == null) {
-            return;
-        }
-        ArrayList<BubbleView> viewsToRemove = new ArrayList<>();
-        for (BubbleView bv : mBubbles.values()) {
-            NotificationEntry entry = bv.getEntry();
-            if (entry != null) {
-                if (entry.isRowRemoved() || entry.isBubbleDismissed() || entry.isRowDismissed()) {
-                    viewsToRemove.add(bv);
-                }
-            }
-        }
-        for (BubbleView bubbleView : viewsToRemove) {
-            mBubbles.remove(bubbleView.getKey());
-            mStackView.removeBubble(bubbleView);
-            bubbleView.destroyActivityView(mStackView);
-        }
-        if (mStackView != null) {
-            mStackView.setVisibility(visible ? VISIBLE : INVISIBLE);
-            if (!visible) {
-                collapseStack();
-            }
+    public void updateVisibility() {
+        if (mStatusBarStateListener.getCurrentState() == SHADE && hasBubbles()) {
+            // Bubbles only appear in unlocked shade
+            mStackView.setVisibility(hasBubbles() ? VISIBLE : INVISIBLE);
+        } else if (mStackView != null) {
+            mStackView.setVisibility(INVISIBLE);
+            collapseStack();
         }
         updateBubblesShowing();
     }
@@ -368,18 +441,42 @@
     }
 
     /**
-     * Whether the notification should bubble or not.
+     * Whether the notification has been developer configured to bubble and is allowed by the user.
      */
-    private static boolean shouldAutoBubble(Context context, NotificationEntry entry) {
+    private boolean shouldBubble(NotificationEntry entry) {
+        StatusBarNotification n = entry.notification;
+        boolean canAppOverlay = false;
+        try {
+            canAppOverlay = mNotificationManagerService.areBubblesAllowedForPackage(
+                    n.getPackageName(), n.getUid());
+        } catch (RemoteException e) {
+            Log.w(TAG, "Error calling NoMan to determine if app can overlay", e);
+        }
+
+        boolean canChannelOverlay = mNotificationEntryManager.getNotificationData().getChannel(
+                entry.key).canBubble();
+        boolean hasOverlayIntent = n.getNotification().getBubbleMetadata() != null
+                && n.getNotification().getBubbleMetadata().getIntent() != null;
+        return hasOverlayIntent && canChannelOverlay && canAppOverlay;
+    }
+
+    /**
+     * Whether the notification should bubble or not. Gated by debug flag.
+     * <p>
+     * If a notification has been set to bubble via proper bubble APIs or if it is an important
+     * message-like notification.
+     * </p>
+     */
+    private boolean shouldAutoBubble(Context context, NotificationEntry entry) {
         if (entry.isBubbleDismissed()) {
             return false;
         }
+        StatusBarNotification n = entry.notification;
 
         boolean autoBubbleMessages = shouldAutoBubbleMessages(context) || DEBUG_ENABLE_AUTO_BUBBLE;
         boolean autoBubbleOngoing = shouldAutoBubbleOngoing(context) || DEBUG_ENABLE_AUTO_BUBBLE;
         boolean autoBubbleAll = shouldAutoBubbleAll(context) || DEBUG_ENABLE_AUTO_BUBBLE;
 
-        StatusBarNotification n = entry.notification;
         boolean hasRemoteInput = false;
         if (n.getNotification().actions != null) {
             for (Notification.Action action : n.getNotification().actions) {
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedViewContainer.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedViewContainer.java
index badefe1..71ae1f8 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedViewContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedViewContainer.java
@@ -21,6 +21,7 @@
 import android.content.res.Resources;
 import android.graphics.Color;
 import android.graphics.drawable.ShapeDrawable;
+import android.text.TextUtils;
 import android.util.AttributeSet;
 import android.view.View;
 import android.widget.LinearLayout;
@@ -88,6 +89,7 @@
      */
     public void setHeaderText(CharSequence text) {
         mHeaderView.setText(text);
+        mHeaderView.setVisibility(TextUtils.isEmpty(text) ? GONE : VISIBLE);
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
index 3280a33..9a11b96 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
@@ -64,9 +64,9 @@
 
     private boolean mIsExpanded;
     private int mExpandedBubbleHeight;
+    private BubbleTouchHandler mTouchHandler;
     private BubbleView mExpandedBubble;
     private Point mCollapsedPosition;
-    private BubbleTouchHandler mTouchHandler;
     private BubbleController.BubbleExpandListener mExpandListener;
 
     private boolean mViewUpdatedRequested = false;
@@ -211,13 +211,24 @@
      */
     public void setExpandedBubble(BubbleView bubbleToExpand) {
         mExpandedBubble = bubbleToExpand;
+        boolean prevExpanded = mIsExpanded;
         mIsExpanded = true;
-        updateExpandedBubble();
-        requestUpdate();
+        if (!prevExpanded) {
+            // If we weren't previously expanded we should animate open.
+            animateExpansion(true /* expand */);
+        } else {
+            // If we were expanded just update the views
+            updateExpandedBubble();
+            requestUpdate();
+        }
+        mExpandedBubble.getEntry().setShowInShadeWhenBubble(false);
+        notifyExpansionChanged(mExpandedBubble, true /* expanded */);
     }
 
     /**
-     * Adds a bubble to the stack.
+     * Adds a bubble to the top of the stack.
+     *
+     * @param bubbleView the view to add to the stack.
      */
     public void addBubble(BubbleView bubbleView) {
         mBubbleContainer.addView(bubbleView, 0,
@@ -234,17 +245,26 @@
         mBubbleContainer.removeView(bubbleView);
         boolean wasExpanded = mIsExpanded;
         int bubbleCount = mBubbleContainer.getChildCount();
-        if (bubbleView.equals(mExpandedBubble) && bubbleCount > 0) {
+        if (mIsExpanded && bubbleView.equals(mExpandedBubble) && bubbleCount > 0) {
             // If we have other bubbles and are expanded go to the next one or previous
             // if the bubble removed was last
             int nextIndex = bubbleCount > removedIndex ? removedIndex : bubbleCount - 1;
-            mExpandedBubble = (BubbleView) mBubbleContainer.getChildAt(nextIndex);
+            BubbleView expandedBubble = (BubbleView) mBubbleContainer.getChildAt(nextIndex);
+            setExpandedBubble(expandedBubble);
         }
         mIsExpanded = wasExpanded && mBubbleContainer.getChildCount() > 0;
-        requestUpdate();
-        if (wasExpanded && !mIsExpanded && mExpandListener != null) {
-            mExpandListener.onBubbleExpandChanged(mIsExpanded, 1 /* amount */);
+        if (wasExpanded != mIsExpanded) {
+            notifyExpansionChanged(mExpandedBubble, mIsExpanded);
         }
+        requestUpdate();
+    }
+
+    /**
+     * Dismiss the stack of bubbles.
+     */
+    public void stackDismissed() {
+        collapseStack();
+        mBubbleContainer.removeAllViews();
     }
 
     /**
@@ -252,11 +272,19 @@
      *
      * @param bubbleView the view to update in the stack.
      * @param entry the entry to update it with.
+     * @param updatePosition whether this bubble should be moved to top of the stack.
      */
-    public void updateBubble(BubbleView bubbleView, NotificationEntry entry) {
-        // TODO - move to top of bubble stack, make it show its update if it makes sense
+    public void updateBubble(BubbleView bubbleView, NotificationEntry entry,
+            boolean updatePosition) {
         bubbleView.update(entry);
-        if (bubbleView.equals(mExpandedBubble)) {
+        if (updatePosition && !mIsExpanded) {
+            // If alerting it gets promoted to top of the stack
+            mBubbleContainer.removeView(bubbleView);
+            mBubbleContainer.addView(bubbleView, 0);
+            requestUpdate();
+        }
+        if (mIsExpanded && bubbleView.equals(mExpandedBubble)) {
+            entry.setShowInShadeWhenBubble(false);
             requestUpdate();
         }
     }
@@ -287,17 +315,36 @@
     }
 
     /**
+     * Collapses the stack of bubbles.
+     */
+    public void collapseStack() {
+        if (mIsExpanded) {
+            // TODO: Save opened bubble & move it to top of stack
+            animateExpansion(false /* shouldExpand */);
+            notifyExpansionChanged(mExpandedBubble, mIsExpanded);
+        }
+    }
+
+    /**
+     * Expands the stack fo bubbles.
+     */
+    public void expandStack() {
+        if (!mIsExpanded) {
+            mExpandedBubble = getTopBubble();
+            mExpandedBubble.getEntry().setShowInShadeWhenBubble(false);
+            animateExpansion(true /* shouldExpand */);
+            notifyExpansionChanged(mExpandedBubble, true /* expanded */);
+        }
+    }
+
+    /**
      * Tell the stack to animate to collapsed or expanded state.
      */
-    public void animateExpansion(boolean shouldExpand) {
+    private void animateExpansion(boolean shouldExpand) {
         if (mIsExpanded != shouldExpand) {
             mIsExpanded = shouldExpand;
-            mExpandedBubble = shouldExpand ? getTopBubble() : null;
             updateExpandedBubble();
 
-            if (mExpandListener != null) {
-                mExpandListener.onBubbleExpandChanged(mIsExpanded, 1 /* amount */);
-            }
             if (shouldExpand) {
                 // Save current position so that we might return there
                 savePosition();
@@ -347,6 +394,13 @@
         mCollapsedPosition = getPosition();
     }
 
+    private void notifyExpansionChanged(BubbleView bubbleView, boolean expanded) {
+        if (mExpandListener != null) {
+            NotificationEntry entry = bubbleView != null ? bubbleView.getEntry() : null;
+            mExpandListener.onBubbleExpandChanged(expanded, entry != null ? entry.key : null);
+        }
+    }
+
     private BubbleView getTopBubble() {
         return getBubbleAt(0);
     }
@@ -400,6 +454,7 @@
         }
 
         if (mExpandedBubble.hasAppOverlayIntent()) {
+            // Bubble with activity view expanded state
             ActivityView expandedView = mExpandedBubble.getActivityView();
             expandedView.setLayoutParams(new ViewGroup.LayoutParams(
                     ViewGroup.LayoutParams.MATCH_PARENT, mExpandedBubbleHeight));
@@ -417,19 +472,27 @@
 
                 @Override
                 public void onActivityViewDestroyed(ActivityView view) {
-                    NotificationEntry entry = mExpandedBubble.getEntry();
+                    NotificationEntry entry = mExpandedBubble != null
+                            ? mExpandedBubble.getEntry() : null;
                     Log.d(TAG, "onActivityViewDestroyed(key="
                             + ((entry != null) ? entry.key : "(none)") + "): " + view);
                 }
             });
         } else {
+            // Bubble with notification view expanded state
             ExpandableNotificationRow row = mExpandedBubble.getRowView();
-            if (!row.equals(mExpandedViewContainer.getExpandedView())) {
-                // Different expanded view than what we have
+            if (row.getParent() != null) {
+                // Row might still be in the shade when we expand
+                ((ViewGroup) row.getParent()).removeView(row);
+            }
+            if (mIsExpanded) {
+                mExpandedViewContainer.setExpandedView(row);
+            } else {
                 mExpandedViewContainer.setExpandedView(null);
             }
-            mExpandedViewContainer.setExpandedView(row);
+            // Bubble with notification as expanded state doesn't need a header / title
             mExpandedViewContainer.setHeaderText(null);
+
         }
         int pointerPosition = mExpandedBubble.getPosition().x
                 + (mExpandedBubble.getWidth() / 2);
@@ -456,7 +519,8 @@
         int bubbsCount = mBubbleContainer.getChildCount();
         for (int i = 0; i < bubbsCount; i++) {
             BubbleView bv = (BubbleView) mBubbleContainer.getChildAt(i);
-            bv.setZ(bubbsCount - 1);
+            bv.updateDotVisibility();
+            bv.setZ(bubbsCount - i);
 
             int transX = mIsExpanded ? (bv.getWidth() + mBubblePadding) * i : mBubblePadding * i;
             ViewState viewState = new ViewState();
@@ -510,6 +574,7 @@
     private void applyRowState(ExpandableNotificationRow view) {
         view.reset();
         view.setHeadsUp(false);
+        view.resetTranslation();
         view.setOnKeyguard(false);
         view.setOnAmbient(false);
         view.setClipBottomAmount(0);
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleTouchHandler.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleTouchHandler.java
index 96b2dba..97784b0 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleTouchHandler.java
@@ -110,7 +110,7 @@
                 : stack.getTargetView(event);
         boolean isFloating = targetView instanceof FloatingView;
         if (!isFloating || targetView == null || action == MotionEvent.ACTION_OUTSIDE) {
-            stack.animateExpansion(false /* shouldExpand */);
+            stack.collapseStack();
             cleanUpDismissTarget();
             resetTouches();
             return false;
@@ -196,9 +196,13 @@
                         mMovementHelper.getTranslateAnim(floatingView, toGoTo, 100, 0).start();
                     }
                 } else if (floatingView.equals(stack.getExpandedBubble())) {
-                    stack.animateExpansion(false /* shouldExpand */);
+                    stack.collapseStack();
                 } else if (isBubbleStack) {
-                    stack.animateExpansion(!stack.isExpanded() /* shouldExpand */);
+                    if (stack.isExpanded()) {
+                        stack.collapseStack();
+                    } else {
+                        stack.expandStack();
+                    }
                 } else {
                     stack.setExpandedBubble((BubbleView) floatingView);
                 }
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java
index c1bbb93..91893ef 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2012 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,40 +16,47 @@
 
 package com.android.systemui.bubbles;
 
+import android.annotation.Nullable;
 import android.app.ActivityView;
 import android.app.Notification;
 import android.app.PendingIntent;
 import android.content.Context;
 import android.graphics.Color;
 import android.graphics.Point;
+import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.Icon;
-import android.graphics.drawable.ShapeDrawable;
-import android.graphics.drawable.shapes.OvalShape;
+import android.graphics.drawable.InsetDrawable;
+import android.graphics.drawable.LayerDrawable;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.view.View;
 import android.view.ViewGroup;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
+import android.widget.FrameLayout;
+import android.widget.TextView;
 
-import com.android.internal.util.ContrastColorUtil;
+import com.android.internal.graphics.ColorUtils;
+import com.android.systemui.Interpolators;
 import com.android.systemui.R;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 
 /**
- * A floating object on the screen that has a collapsed and expanded state.
+ * A floating object on the screen that can post message updates.
  */
-class BubbleView extends LinearLayout implements BubbleTouchHandler.FloatingView {
+public class BubbleView extends FrameLayout implements BubbleTouchHandler.FloatingView {
     private static final String TAG = "BubbleView";
 
+    // Same value as Launcher3 badge code
+    private static final float WHITE_SCRIM_ALPHA = 0.54f;
     private Context mContext;
-    private View mIconView;
+
+    private BadgedImageView mBadgedImageView;
+    private TextView mMessageView;
+    private int mPadding;
+    private int mIconInset;
 
     private NotificationEntry mEntry;
-    private int mBubbleSize;
-    private int mIconSize;
     private PendingIntent mAppOverlayIntent;
     private ActivityView mActivityView;
 
@@ -67,66 +74,156 @@
 
     public BubbleView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
         super(context, attrs, defStyleAttr, defStyleRes);
-        setOrientation(LinearLayout.VERTICAL);
         mContext = context;
-        mBubbleSize = getResources().getDimensionPixelSize(R.dimen.bubble_size);
-        mIconSize = getResources().getDimensionPixelSize(R.dimen.bubble_icon_size);
+        // XXX: can this padding just be on the view and we look it up?
+        mPadding = getResources().getDimensionPixelSize(R.dimen.bubble_view_padding);
+        mIconInset = getResources().getDimensionPixelSize(R.dimen.bubble_icon_inset);
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+        mBadgedImageView = (BadgedImageView) findViewById(R.id.bubble_image);
+        mMessageView = (TextView) findViewById(R.id.message_view);
+        mMessageView.setVisibility(GONE);
+        mMessageView.setPivotX(0);
+    }
+
+    @Override
+    protected void onAttachedToWindow() {
+        super.onAttachedToWindow();
+        updateViews();
+    }
+
+    @Override
+    protected void onMeasure(int widthSpec, int heightSpec) {
+        measureChild(mBadgedImageView, widthSpec, heightSpec);
+        measureChild(mMessageView, widthSpec, heightSpec);
+        boolean messageGone = mMessageView.getVisibility() == GONE;
+        int imageHeight = mBadgedImageView.getMeasuredHeight();
+        int imageWidth = mBadgedImageView.getMeasuredWidth();
+        int messageHeight = messageGone ? 0 : mMessageView.getMeasuredHeight();
+        int messageWidth = messageGone ? 0 : mMessageView.getMeasuredWidth();
+        setMeasuredDimension(
+                getPaddingStart() + imageWidth + mPadding + messageWidth + getPaddingEnd(),
+                getPaddingTop() + Math.max(imageHeight, messageHeight) + getPaddingBottom());
+    }
+
+    @Override
+    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+        left = getPaddingStart();
+        top = getPaddingTop();
+        int imageWidth = mBadgedImageView.getMeasuredWidth();
+        int imageHeight = mBadgedImageView.getMeasuredHeight();
+        int messageWidth = mMessageView.getMeasuredWidth();
+        int messageHeight = mMessageView.getMeasuredHeight();
+        mBadgedImageView.layout(left, top, left + imageWidth, top + imageHeight);
+        mMessageView.layout(left + imageWidth + mPadding, top,
+                left + imageWidth + mPadding + messageWidth, top + messageHeight);
     }
 
     /**
      * Populates this view with a notification.
+     * <p>
+     * This should only be called when a new notification is being set on the view, updates to the
+     * current notification should use {@link #update(NotificationEntry)}.
      *
      * @param entry the notification to display as a bubble.
      */
     public void setNotif(NotificationEntry entry) {
-        removeAllViews();
-        // TODO: migrate to inflater
-        mIconView = new ImageView(mContext);
-        addView(mIconView);
-
-        LinearLayout.LayoutParams iconLp = (LinearLayout.LayoutParams) mIconView.getLayoutParams();
-        iconLp.width = mBubbleSize;
-        iconLp.height = mBubbleSize;
-        mIconView.setLayoutParams(iconLp);
-
-        update(entry);
-    }
-
-    /**
-     * Updates the UI based on the entry.
-     */
-    public void update(NotificationEntry entry) {
         mEntry = entry;
-        Notification n = entry.notification.getNotification();
-        Icon ic = n.getLargeIcon() != null ? n.getLargeIcon() : n.getSmallIcon();
-
-        if (n.getLargeIcon() == null) {
-            createCircledIcon(n.color, ic, ((ImageView) mIconView));
-        } else {
-            ((ImageView) mIconView).setImageIcon(ic);
-        }
+        updateViews();
     }
 
     /**
-     * @return the key identifying this bubble / notification entry associated with this
-     * bubble, if it exists.
+     * The {@link NotificationEntry} associated with this view, if one exists.
      */
-    public String getKey() {
-        return mEntry == null ? null : mEntry.key;
-    }
-
-    /**
-     * @return the notification entry associated with this bubble.
-     */
+    @Nullable
     public NotificationEntry getEntry() {
         return mEntry;
     }
 
     /**
-     * @return the view to display notification content when the bubble is expanded.
+     * The key for the {@link NotificationEntry} associated with this view, if one exists.
      */
+    @Nullable
+    public String getKey() {
+        return (mEntry != null) ? mEntry.key : null;
+    }
+
+    /**
+     * Updates the UI based on the entry, updates badge and animates messages as needed.
+     */
+    public void update(NotificationEntry entry) {
+        mEntry = entry;
+        updateViews();
+    }
+
+
+    /**
+     * @return the {@link ExpandableNotificationRow} view to display notification content when the
+     * bubble is expanded.
+     */
+    @Nullable
     public ExpandableNotificationRow getRowView() {
-        return mEntry.getRow();
+        return (mEntry != null) ? mEntry.getRow() : null;
+    }
+
+    /**
+     * Marks this bubble as "read", i.e. no badge should show.
+     */
+    public void updateDotVisibility() {
+        boolean showDot = getEntry().showInShadeWhenBubble();
+        animateDot(showDot);
+    }
+
+    /**
+     * Animates the badge to show or hide.
+     */
+    private void animateDot(boolean showDot) {
+        if (mBadgedImageView.isShowingDot() != showDot) {
+            mBadgedImageView.setShowDot(showDot);
+            mBadgedImageView.clearAnimation();
+            mBadgedImageView.animate().setDuration(200)
+                    .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
+                    .setUpdateListener((valueAnimator) -> {
+                        float fraction = valueAnimator.getAnimatedFraction();
+                        fraction = showDot ? fraction : 1 - fraction;
+                        mBadgedImageView.setDotScale(fraction);
+                    }).withEndAction(() -> {
+                        if (!showDot) {
+                            mBadgedImageView.setShowDot(false);
+                        }
+                    }).start();
+        }
+    }
+
+    private void updateViews() {
+        if (mEntry == null) {
+            return;
+        }
+        Notification n = mEntry.notification.getNotification();
+        boolean isLarge = n.getLargeIcon() != null;
+        Icon ic = isLarge ? n.getLargeIcon() : n.getSmallIcon();
+        Drawable iconDrawable = ic.loadDrawable(mContext);
+        if (!isLarge) {
+            // Center icon on coloured background
+            iconDrawable.setTint(Color.WHITE); // TODO: dark mode
+            Drawable bg = new ColorDrawable(n.color);
+            InsetDrawable d = new InsetDrawable(iconDrawable, mIconInset);
+            Drawable[] layers = {bg, d};
+            mBadgedImageView.setImageDrawable(new LayerDrawable(layers));
+        } else {
+            mBadgedImageView.setImageDrawable(iconDrawable);
+        }
+        int badgeColor = determineDominateColor(iconDrawable, n.color);
+        mBadgedImageView.setDotColor(badgeColor);
+        animateDot(mEntry.showInShadeWhenBubble() /* showDot */);
+    }
+
+    private int determineDominateColor(Drawable d, int defaultTint) {
+        // XXX: should we pull from the drawable, app icon, notif tint?
+        return ColorUtils.blendARGB(defaultTint, Color.WHITE, WHITE_SCRIM_ALPHA);
     }
 
     /**
@@ -170,8 +267,8 @@
 
     @Override
     public void setPosition(int x, int y) {
-        setTranslationX(x);
-        setTranslationY(y);
+        setPositionX(x);
+        setPositionY(y);
     }
 
     @Override
@@ -189,25 +286,6 @@
         return new Point((int) getTranslationX(), (int) getTranslationY());
     }
 
-    // Seems sub optimal
-    private void createCircledIcon(int tint, Icon icon, ImageView v) {
-        // TODO: dark mode
-        icon.setTint(Color.WHITE);
-        icon.scaleDownIfNecessary(mIconSize, mIconSize);
-        v.setImageDrawable(icon.loadDrawable(mContext));
-        v.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
-        LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) v.getLayoutParams();
-        int color = ContrastColorUtil.ensureContrast(tint, Color.WHITE,
-                false /* isBgDarker */, 3);
-        Drawable d = new ShapeDrawable(new OvalShape());
-        d.setTint(color);
-        v.setBackgroundDrawable(d);
-
-        lp.width = mBubbleSize;
-        lp.height = mBubbleSize;
-        v.setLayoutParams(lp);
-    }
-
     /**
      * @return whether an ActivityView should be used to display the content of this Bubble
      */
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java b/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java
index 4cb1fee..bd7a421 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java
@@ -38,7 +38,13 @@
     void setAnimateWakeup(boolean animateWakeup);
     void setAnimateScreenOff(boolean animateScreenOff);
 
-    void onDoubleTap(float x, float y);
+    /**
+     * Reports that a tap event happend on the Sensors Low Power Island.
+     *
+     * @param x Touch X, or -1 if sensor doesn't support touch location.
+     * @param y Touch Y, or -1 if sensor doesn't support touch location.
+     */
+    void onSlpiTap(float x, float y);
 
     default void setAodDimmingScrim(float scrimOpacity) {}
     void setDozeScreenBrightness(int value);
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java b/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
index 50003e3..a784773 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 = 9;
+    private static final int REASONS = 10;
 
     public static final int PULSE_REASON_NONE = -1;
     public static final int PULSE_REASON_INTENT = 0;
@@ -47,6 +47,7 @@
     public static final int PULSE_REASON_DOCKING = 6;
     public static final int REASON_SENSOR_WAKE_UP = 7;
     public static final int PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN = 8;
+    public static final int PULSE_REASON_SENSOR_TAP = 9;
 
     private static boolean sRegisterKeyguardCallback = true;
 
@@ -207,6 +208,7 @@
             case PULSE_REASON_DOCKING: return "docking";
             case PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN: return "wakelockscreen";
             case REASON_SENSOR_WAKE_UP: return "wakeup";
+            case PULSE_REASON_SENSOR_TAP: return "tap";
             default: throw new IllegalArgumentException("bad reason: " + pulseReason);
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
index b6fc355..78374a0 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
@@ -107,6 +107,13 @@
                         dozeParameters.doubleTapReportsTouchCoordinates(),
                         true /* touchscreen */),
                 new TriggerSensor(
+                        findSensorWithType(config.tapSensorType()),
+                        Settings.Secure.DOZE_TAP_SCREEN_GESTURE,
+                        true /* configured */,
+                        DozeLog.PULSE_REASON_SENSOR_TAP,
+                        false /* reports touch coordinates */,
+                        true /* touchscreen */),
+                new TriggerSensor(
                         findSensorWithType(config.longPressSensorType()),
                         Settings.Secure.DOZE_PULSE_ON_LONG_PRESS,
                         false /* settingDef */,
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
index 6a9b689..e2e448b 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
@@ -132,6 +132,7 @@
     private void onSensor(int pulseReason, boolean sensorPerformedProxCheck,
             float screenX, float screenY, float[] rawValues) {
         boolean isDoubleTap = pulseReason == DozeLog.PULSE_REASON_SENSOR_DOUBLE_TAP;
+        boolean isTap = pulseReason == DozeLog.PULSE_REASON_SENSOR_TAP;
         boolean isPickup = pulseReason == DozeLog.PULSE_REASON_SENSOR_PICKUP;
         boolean isLongPress = pulseReason == DozeLog.PULSE_REASON_SENSOR_LONG_PRESS;
         boolean isWakeDisplay = pulseReason == DozeLog.REASON_SENSOR_WAKE_UP;
@@ -148,8 +149,10 @@
                     // In pocket, drop event.
                     return;
                 }
-                if (isDoubleTap) {
-                    mDozeHost.onDoubleTap(screenX, screenY);
+                if (isDoubleTap || isTap) {
+                    if (screenX != -1 && screenY != -1) {
+                        mDozeHost.onSlpiTap(screenX, screenY);
+                    }
                     mMachine.wakeUp();
                 } else if (isPickup) {
                     mMachine.wakeUp();
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyDialog.kt b/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyDialog.kt
index 6ed1eba..77e25e3 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyDialog.kt
@@ -20,6 +20,7 @@
 import android.content.DialogInterface
 import android.content.Intent
 import android.content.res.ColorStateList
+import android.util.IconDrawableFactory
 import android.view.Gravity
 import android.view.LayoutInflater
 import android.view.View
@@ -37,11 +38,22 @@
 
     private val iconSize = context.resources.getDimensionPixelSize(
             R.dimen.ongoing_appops_dialog_icon_size)
+    private val plusSize = context.resources.getDimensionPixelSize(
+            R.dimen.ongoing_appops_dialog_app_plus_size)
     private val iconColor = context.resources.getColor(
             com.android.internal.R.color.text_color_primary, context.theme)
+    private val plusColor: Int
     private val iconMargin = context.resources.getDimensionPixelSize(
             R.dimen.ongoing_appops_dialog_icon_margin)
     private val MAX_ITEMS = context.resources.getInteger(R.integer.ongoing_appops_dialog_max_apps)
+    private val iconFactory = IconDrawableFactory.newInstance(context, true)
+
+    init {
+        val a = context.theme.obtainStyledAttributes(
+                intArrayOf(com.android.internal.R.attr.colorAccent))
+        plusColor = a.getColor(0, 0)
+        a.recycle()
+    }
 
     fun createDialog(): Dialog {
         val builder = AlertDialog.Builder(context).apply {
@@ -87,9 +99,15 @@
                     numItems - MAX_ITEMS
             )
             val overflowPlus = overflow.findViewById(R.id.app_icon) as ImageView
+            val lp = overflowPlus.layoutParams.apply {
+                height = plusSize
+                width = plusSize
+            }
+            overflowPlus.layoutParams = lp
             overflowPlus.apply {
-                imageTintList = ColorStateList.valueOf(iconColor)
-                setImageDrawable(context.getDrawable(R.drawable.plus))
+                val plus = context.getDrawable(R.drawable.plus)
+                imageTintList = ColorStateList.valueOf(plusColor)
+                setImageDrawable(plus)
             }
         }
 
@@ -114,7 +132,7 @@
         }
 
         app.icon.let {
-            appIcon.setImageDrawable(it)
+            appIcon.setImageDrawable(iconFactory.getShadowedIcon(it))
         }
 
         appName.text = app.applicationName
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
index bf6caa0..f2ff85b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
@@ -16,8 +16,6 @@
 
 package com.android.systemui.statusbar;
 
-import static com.android.systemui.statusbar.StatusBarState.SHADE;
-
 import android.content.Context;
 import android.content.res.Resources;
 import android.os.Trace;
@@ -26,7 +24,6 @@
 import android.view.ViewGroup;
 
 import com.android.systemui.R;
-import com.android.systemui.bubbles.BubbleController;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.VisualStabilityManager;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
@@ -66,7 +63,6 @@
     protected final VisualStabilityManager mVisualStabilityManager;
     private final StatusBarStateController mStatusBarStateController;
     private final NotificationEntryManager mEntryManager;
-    private final BubbleController mBubbleController;
 
     // Lazy
     private final Lazy<ShadeController> mShadeController;
@@ -80,41 +76,6 @@
 
     private NotificationPresenter mPresenter;
     private NotificationListContainer mListContainer;
-    private StatusBarStateListener mStatusBarStateListener;
-
-    /**
-     * Listens for the current state of the status bar and updates the visibility state
-     * of bubbles as needed.
-     */
-    public class StatusBarStateListener implements StatusBarStateController.StateListener {
-        private int mState;
-        private BubbleController mController;
-
-        public StatusBarStateListener(BubbleController controller) {
-            mController = controller;
-        }
-
-        /**
-         * Returns the current status bar state.
-         */
-        public int getCurrentState() {
-            return mState;
-        }
-
-        @Override
-        public void onStateChanged(int newState) {
-            mState = newState;
-            // Order here matters because we need to remove the expandable notification row
-            // from it's current parent (NSSL or bubble) before it can be added to the new parent
-            if (mState == SHADE) {
-                updateNotificationViews();
-                mController.updateVisibility(true);
-            } else {
-                mController.updateVisibility(false);
-                updateNotificationViews();
-            }
-        }
-    }
 
     @Inject
     public NotificationViewHierarchyManager(Context context,
@@ -123,20 +84,16 @@
             VisualStabilityManager visualStabilityManager,
             StatusBarStateController statusBarStateController,
             NotificationEntryManager notificationEntryManager,
-            BubbleController bubbleController,
             Lazy<ShadeController> shadeController) {
         mLockscreenUserManager = notificationLockscreenUserManager;
         mGroupManager = groupManager;
         mVisualStabilityManager = visualStabilityManager;
         mStatusBarStateController = statusBarStateController;
         mEntryManager = notificationEntryManager;
-        mBubbleController = bubbleController;
         mShadeController = shadeController;
         Resources res = context.getResources();
         mAlwaysExpandNonGroupedNotification =
                 res.getBoolean(R.bool.config_alwaysExpandNonGroupedNotifications);
-        mStatusBarStateListener = new StatusBarStateListener(mBubbleController);
-        mStatusBarStateController.addCallback(mStatusBarStateListener);
     }
 
     public void setUpWithPresenter(NotificationPresenter presenter,
@@ -153,7 +110,6 @@
         ArrayList<NotificationEntry> activeNotifications = mEntryManager.getNotificationData()
                 .getActiveNotifications();
         ArrayList<ExpandableNotificationRow> toShow = new ArrayList<>(activeNotifications.size());
-        ArrayList<NotificationEntry> toBubble = new ArrayList<>();
         final int N = activeNotifications.size();
         for (int i = 0; i < N; i++) {
             NotificationEntry ent = activeNotifications.get(i);
@@ -162,13 +118,6 @@
                 // temporarily become children if they were isolated before.
                 continue;
             }
-            ent.getRow().setStatusBarState(mStatusBarStateListener.getCurrentState());
-            boolean showAsBubble = ent.isBubble() && !ent.isBubbleDismissed()
-                    && mStatusBarStateListener.getCurrentState() == SHADE;
-            if (showAsBubble) {
-                toBubble.add(ent);
-                continue;
-            }
 
             int userId = ent.notification.getUserId();
 
@@ -269,12 +218,6 @@
 
         }
 
-        for (int i = 0; i < toBubble.size(); i++) {
-            // TODO: might make sense to leave them in the shade and just reposition them
-            NotificationEntry ent = toBubble.get(i);
-            mBubbleController.addBubble(ent);
-        }
-
         mVisualStabilityManager.onReorderingFinished();
         // clear the map again for the next usage
         mTmpChildOrderMap.clear();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationAlertingManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationAlertingManager.java
index 60d8cf4..5605f3d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationAlertingManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationAlertingManager.java
@@ -150,7 +150,14 @@
         }
     }
 
-    private static boolean alertAgain(
+    /**
+     * Checks whether an update for a notification warrants an alert for the user.
+     *
+     * @param oldEntry the entry for this notification.
+     * @param newNotification the new notification for this entry.
+     * @return whether this notification should alert the user.
+     */
+    public static boolean alertAgain(
             NotificationEntry oldEntry, Notification newNotification) {
         return oldEntry == null || !oldEntry.hasInterrupted()
                 || (newNotification.flags & Notification.FLAG_ONLY_ALERT_ONCE) == 0;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationFilter.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationFilter.java
index e199ead..154d7b35 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationFilter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationFilter.java
@@ -134,6 +134,10 @@
             }
         }
 
+        if (entry.isBubble() && !entry.showInShadeWhenBubble()) {
+            return true;
+        }
+
         return false;
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInterruptionStateProvider.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInterruptionStateProvider.java
index fc7a2b3..c50f10b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInterruptionStateProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInterruptionStateProvider.java
@@ -135,6 +135,29 @@
     }
 
     /**
+     * Whether the notification should appear as a bubble with a fly-out on top of the screen.
+     *
+     * @param entry the entry to check
+     * @return true if the entry should bubble up, false otherwise
+     */
+    public boolean shouldBubbleUp(NotificationEntry entry) {
+        StatusBarNotification sbn = entry.notification;
+        if (!entry.isBubble()) {
+            if (DEBUG) {
+                Log.d(TAG, "No bubble up: notification " + sbn.getKey()
+                        + " is bubble? " + entry.isBubble());
+            }
+            return false;
+        }
+
+        if (!canHeadsUpCommon(entry)) {
+            return false;
+        }
+
+        return true;
+    }
+
+    /**
      * Whether the notification should peek in from the top and alert the user.
      *
      * @param entry the entry to check
@@ -150,10 +173,12 @@
             return false;
         }
 
-        // TODO: need to changes this, e.g. should still heads up in expanded shade, might want
-        // message bubble from the bubble to go through heads up path
         boolean inShade = mStatusBarStateController.getState() == SHADE;
-        if (entry.isBubble() && !entry.isBubbleDismissed() && inShade) {
+        if (entry.isBubble() && inShade) {
+            if (DEBUG) {
+                Log.d(TAG, "No heads up: in unlocked shade where notification is shown as a "
+                        + "bubble: " + sbn.getKey());
+            }
             return false;
         }
 
@@ -164,9 +189,13 @@
             return false;
         }
 
-        if (!mUseHeadsUp || mPresenter.isDeviceInVrMode()) {
+        if (!canHeadsUpCommon(entry)) {
+            return false;
+        }
+
+        if (entry.importance < NotificationManager.IMPORTANCE_HIGH) {
             if (DEBUG) {
-                Log.d(TAG, "No heads up: no huns or vr mode");
+                Log.d(TAG, "No heads up: unimportant notification: " + sbn.getKey());
             }
             return false;
         }
@@ -186,34 +215,6 @@
             return false;
         }
 
-        if (entry.shouldSuppressPeek()) {
-            if (DEBUG) {
-                Log.d(TAG, "No heads up: suppressed by DND: " + sbn.getKey());
-            }
-            return false;
-        }
-
-        if (isSnoozedPackage(sbn)) {
-            if (DEBUG) {
-                Log.d(TAG, "No heads up: snoozed package: " + sbn.getKey());
-            }
-            return false;
-        }
-
-        if (entry.hasJustLaunchedFullScreenIntent()) {
-            if (DEBUG) {
-                Log.d(TAG, "No heads up: recent fullscreen: " + sbn.getKey());
-            }
-            return false;
-        }
-
-        if (entry.importance < NotificationManager.IMPORTANCE_HIGH) {
-            if (DEBUG) {
-                Log.d(TAG, "No heads up: unimportant notification: " + sbn.getKey());
-            }
-            return false;
-        }
-
         if (!mHeadsUpSuppressor.canHeadsUp(entry, sbn)) {
             return false;
         }
@@ -302,6 +303,49 @@
         return true;
     }
 
+    /**
+     * Common checks between heads up alerting and bubble fly out alerting. See
+     * {@link #shouldHeadsUp(NotificationEntry)} and
+     * {@link #shouldBubbleUp(NotificationEntry)}. Notifications that fail any of these
+     * checks should not interrupt the user on screen.
+     *
+     * @param entry the entry to check
+     * @return true if these checks pass, false if the notification should not interrupt on screen
+     */
+    public boolean canHeadsUpCommon(NotificationEntry entry) {
+        StatusBarNotification sbn = entry.notification;
+
+        if (!mUseHeadsUp || mPresenter.isDeviceInVrMode()) {
+            if (DEBUG) {
+                Log.d(TAG, "No heads up: no huns or vr mode");
+            }
+            return false;
+        }
+
+        if (entry.shouldSuppressPeek()) {
+            if (DEBUG) {
+                Log.d(TAG, "No heads up: suppressed by DND: " + sbn.getKey());
+            }
+            return false;
+        }
+
+        if (isSnoozedPackage(sbn)) {
+            if (DEBUG) {
+                Log.d(TAG, "No heads up: snoozed package: " + sbn.getKey());
+            }
+            return false;
+        }
+
+        if (entry.hasJustLaunchedFullScreenIntent()) {
+            if (DEBUG) {
+                Log.d(TAG, "No heads up: recent fullscreen: " + sbn.getKey());
+            }
+            return false;
+        }
+
+        return true;
+    }
+
     private boolean isSnoozedPackage(StatusBarNotification sbn) {
         return mHeadsUpManager.isSnoozed(sbn.getPackageName());
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
index 58aa02c..ee551ee 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
@@ -141,6 +141,14 @@
     private boolean mIsBubble;
 
     /**
+     * Whether this notification should be shown in the shade when it is also displayed as a bubble.
+     *
+     * <p>When a notification is a bubble we don't show it in the shade once the bubble has been
+     * expanded</p>
+     */
+    private boolean mShowInShadeWhenBubble;
+
+    /**
      * Whether the user has dismissed this notification when it was in bubble form.
      */
     private boolean mUserDismissedBubble;
@@ -200,6 +208,23 @@
     }
 
     /**
+     * Sets whether this notification should be shown in the shade when it is also displayed as a
+     * bubble.
+     */
+    public void setShowInShadeWhenBubble(boolean showInShade) {
+        mShowInShadeWhenBubble = showInShade;
+    }
+
+    /**
+     * Whether this notification should be shown in the shade when it is also displayed as a
+     * bubble.
+     */
+    public boolean showInShadeWhenBubble() {
+        // We always show it in the shade if non-clearable
+        return !isClearable() || mShowInShadeWhenBubble;
+    }
+
+    /**
      * Resets the notification entry to be re-used.
      */
     public void reset() {
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 95bd1ce..df0189f 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
@@ -16,7 +16,6 @@
 
 package com.android.systemui.statusbar.notification.row;
 
-import static com.android.systemui.statusbar.StatusBarState.SHADE;
 import static com.android.systemui.statusbar.notification.ActivityLaunchAnimator.ExpandAnimationParameters;
 import static com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_AMBIENT;
 import static com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_CONTRACTED;
@@ -2322,7 +2321,7 @@
     }
 
     private boolean isShownAsBubble() {
-        return mEntry.isBubble() && (mStatusBarState == SHADE || mStatusBarState == -1);
+        return mEntry.isBubble() && !mEntry.showInShadeWhenBubble() && !mEntry.isBubbleDismissed();
     }
 
     /**
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 607d96d..6df72fe 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
@@ -20,11 +20,13 @@
         .USER_SENTIMENT_NEGATIVE;
 
 import android.content.Context;
+import android.metrics.LogMaker;
 import android.util.Log;
 
 import androidx.annotation.VisibleForTesting;
 
 import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.systemui.Dependency;
 import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
@@ -58,6 +60,8 @@
      */
     private boolean mIsShadeExpanded;
 
+    private MetricsLogger mMetricsLogger = new MetricsLogger();
+
     @Inject
     public NotificationBlockingHelperManager(Context context) {
         mContext = context;
@@ -100,6 +104,11 @@
             mBlockingHelperRow = row;
             mBlockingHelperRow.setBlockingHelperShowing(true);
 
+            // Log triggering of blocking helper by the system. This log line
+            // should be emitted before the "display" log line.
+            mMetricsLogger.write(
+                    getLogMaker().setSubtype(MetricsEvent.BLOCKING_HELPER_TRIGGERED_BY_SYSTEM));
+
             // We don't care about the touch origin (x, y) since we're opening guts without any
             // explicit user interaction.
             manager.openGuts(mBlockingHelperRow, 0, 0, menuRow.getLongpressMenuItem(mContext));
@@ -153,6 +162,13 @@
                 || mNonBlockablePkgs.contains(makeChannelKey(packageName, channelName));
     }
 
+    private LogMaker getLogMaker() {
+        return mBlockingHelperRow.getStatusBarNotification()
+            .getLogMaker()
+            .setCategory(MetricsEvent.NOTIFICATION_ITEM)
+            .setType(MetricsEvent.NOTIFICATION_BLOCKING_HELPER);
+    }
+
     // Format must stay in sync with frameworks/base/core/res/res/values/config.xml
     // config_nonBlockableNotificationPackages
     private String makeChannelKey(String pkg, String channel) {
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 bf30cf9..c161da3 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
@@ -1489,7 +1489,7 @@
                 }
             }
         }
-        if (mHeadsUpChild != null) {
+        if (mHeadsUpChild != null && mSmartReplyConstants.getShowInHeadsUp()) {
             mHeadsUpSmartReplyView =
                     applySmartReplyView(mHeadsUpChild, smartRepliesAndActions, entry);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java
index b1eab80..5253e38 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java
@@ -123,11 +123,15 @@
     private OnClickListener mOnKeepShowing = v -> {
         mExitReason = NotificationCounters.BLOCKING_HELPER_KEEP_SHOWING;
         closeControls(v);
+        mMetricsLogger.write(getLogMaker().setType(MetricsEvent.NOTIFICATION_BLOCKING_HELPER)
+                .setSubtype(MetricsEvent.BLOCKING_HELPER_CLICK_STAY_SILENT));
     };
 
     private OnClickListener mOnToggleSilent = v -> {
         Runnable saveImportance = () -> {
             swapContent(ACTION_TOGGLE_SILENT, true /* animate */);
+            mMetricsLogger.write(getLogMaker().setType(MetricsEvent.NOTIFICATION_BLOCKING_HELPER)
+                    .setSubtype(MetricsEvent.BLOCKING_HELPER_CLICK_ALERT_ME));
         };
         if (mCheckSaveListener != null) {
             mCheckSaveListener.checkSave(saveImportance, mSbn);
@@ -139,6 +143,8 @@
     private OnClickListener mOnStopOrMinimizeNotifications = v -> {
         Runnable saveImportance = () -> {
             swapContent(ACTION_BLOCK, true /* animate */);
+            mMetricsLogger.write(getLogMaker().setType(MetricsEvent.NOTIFICATION_BLOCKING_HELPER)
+                    .setSubtype(MetricsEvent.BLOCKING_HELPER_CLICK_BLOCKED));
         };
         if (mCheckSaveListener != null) {
             mCheckSaveListener.checkSave(saveImportance, mSbn);
@@ -153,6 +159,8 @@
         logBlockingHelperCounter(NotificationCounters.BLOCKING_HELPER_UNDO);
         mMetricsLogger.write(importanceChangeLogMaker().setType(MetricsEvent.TYPE_DISMISS));
         swapContent(ACTION_UNDO, true /* animate */);
+        mMetricsLogger.write(getLogMaker().setType(MetricsEvent.NOTIFICATION_BLOCKING_HELPER)
+                .setSubtype(MetricsEvent.BLOCKING_HELPER_CLICK_UNDO));
     };
 
     public NotificationInfo(Context context, AttributeSet attrs) {
@@ -251,6 +259,9 @@
         bindHeader();
         bindPrompt();
         bindButtons();
+
+        mMetricsLogger.write(getLogMaker().setType(MetricsEvent.NOTIFICATION_BLOCKING_HELPER)
+                .setSubtype(MetricsEvent.BLOCKING_HELPER_DISPLAY));
     }
 
     private void bindHeader() throws RemoteException {
@@ -588,6 +599,8 @@
         confirmation.setAlpha(1f);
         header.setVisibility(VISIBLE);
         header.setAlpha(1f);
+        mMetricsLogger.write(getLogMaker().setType(MetricsEvent.NOTIFICATION_BLOCKING_HELPER)
+                .setSubtype(MetricsEvent.BLOCKING_HELPER_DISMISS));
     }
 
     @Override
@@ -733,4 +746,8 @@
             }
         }
     }
+
+    private LogMaker getLogMaker() {
+        return mSbn.getLogMaker().setCategory(MetricsEvent.NOTIFICATION_ITEM);
+    }
 }
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 3f93192..7569a50 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -461,13 +461,6 @@
     private NotificationMediaManager mMediaManager;
     protected NotificationLockscreenUserManager mLockscreenUserManager;
     protected NotificationRemoteInputManager mRemoteInputManager;
-    protected BubbleController mBubbleController;
-    private final BubbleController.BubbleExpandListener mBubbleExpandListener =
-            (isExpanding, amount) -> {
-                if (amount == 1) {
-                    updateScrimController();
-                }
-            };
 
     private final BroadcastReceiver mWallpaperChangedReceiver = new BroadcastReceiver() {
         @Override
@@ -589,6 +582,12 @@
     private NotificationActivityStarter mNotificationActivityStarter;
     private boolean mPulsing;
     private ContentObserver mFeatureFlagObserver;
+    protected BubbleController mBubbleController;
+    private final BubbleController.BubbleExpandListener mBubbleExpandListener =
+            (isExpanding, key) -> {
+                mEntryManager.updateNotifications();
+                updateScrimController();
+            };
 
     @Override
     public void onActiveStateChanged(int code, int uid, String packageName, boolean active) {
@@ -4000,7 +3999,7 @@
         }
 
         @Override
-        public void onDoubleTap(float screenX, float screenY) {
+        public void onSlpiTap(float screenX, float screenY) {
             if (screenX > 0 && screenY > 0 && mAmbientIndicationContainer != null
                 && mAmbientIndicationContainer.getVisibility() == View.VISIBLE) {
                 mAmbientIndicationContainer.getLocationOnScreen(mTmpInt2);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
index 4f61009..04d24dc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
@@ -346,7 +346,7 @@
     }
 
     private void handleFullScreenIntent(NotificationEntry entry) {
-        boolean isHeadsUped = mNotificationInterruptionStateProvider.shouldHeadsUp(entry);
+        boolean isHeadsUped = mNotificationInterruptionStateProvider.canHeadsUpCommon(entry);
         if (!isHeadsUped && entry.notification.getNotification().fullScreenIntent != null) {
             if (shouldSuppressFullScreenIntent(entry)) {
                 if (DEBUG) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
index 9422101..f79ad71 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
@@ -541,7 +541,8 @@
 
     @VisibleForTesting
     void doUpdateMobileControllers() {
-        List<SubscriptionInfo> subscriptions = mSubscriptionManager.getActiveSubscriptionInfoList();
+        List<SubscriptionInfo> subscriptions = mSubscriptionManager
+                .getActiveSubscriptionInfoList(true);
         if (subscriptions == null) {
             subscriptions = Collections.emptyList();
         }
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 0c63e29..3bd0d45 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyConstants.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyConstants.java
@@ -45,16 +45,19 @@
             "max_squeeze_remeasure_attempts";
     private static final String KEY_EDIT_CHOICES_BEFORE_SENDING =
             "edit_choices_before_sending";
+    private static final String KEY_SHOW_IN_HEADS_UP = "show_in_heads_up";
 
     private final boolean mDefaultEnabled;
     private final boolean mDefaultRequiresP;
     private final int mDefaultMaxSqueezeRemeasureAttempts;
     private final boolean mDefaultEditChoicesBeforeSending;
+    private final boolean mDefaultShowInHeadsUp;
 
     private boolean mEnabled;
     private boolean mRequiresTargetingP;
     private int mMaxSqueezeRemeasureAttempts;
     private boolean mEditChoicesBeforeSending;
+    private boolean mShowInHeadsUp;
 
     private final Context mContext;
     private final KeyValueListParser mParser = new KeyValueListParser(',');
@@ -73,6 +76,8 @@
                 R.integer.config_smart_replies_in_notifications_max_squeeze_remeasure_attempts);
         mDefaultEditChoicesBeforeSending = resources.getBoolean(
                 R.bool.config_smart_replies_in_notifications_edit_choices_before_sending);
+        mDefaultShowInHeadsUp = resources.getBoolean(
+                R.bool.config_smart_replies_in_notifications_show_in_heads_up);
 
         mContext.getContentResolver().registerContentObserver(
                 Settings.Global.getUriFor(Settings.Global.SMART_REPLIES_IN_NOTIFICATIONS_FLAGS),
@@ -99,6 +104,7 @@
                     KEY_MAX_SQUEEZE_REMEASURE_ATTEMPTS, mDefaultMaxSqueezeRemeasureAttempts);
             mEditChoicesBeforeSending = mParser.getBoolean(
                     KEY_EDIT_CHOICES_BEFORE_SENDING, mDefaultEditChoicesBeforeSending);
+            mShowInHeadsUp = mParser.getBoolean(KEY_SHOW_IN_HEADS_UP, mDefaultShowInHeadsUp);
         }
     }
 
@@ -142,4 +148,11 @@
                 return mEditChoicesBeforeSending;
         }
     }
+
+    /**
+     * Returns whether smart suggestions should be enabled in heads-up notifications.
+     */
+    public boolean getShowInHeadsUp() {
+        return mShowInHeadsUp;
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/clock/BubbleClockControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/clock/BubbleClockControllerTest.java
new file mode 100644
index 0000000..9e946fa
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/keyguard/clock/BubbleClockControllerTest.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.keyguard.clock;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.test.suitebuilder.annotation.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper.RunWithLooper;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@RunWithLooper
+public final class BubbleClockControllerTest extends SysuiTestCase {
+
+    private BubbleClockController mClockController;
+
+    @Before
+    public void setUp() {
+        LayoutInflater layoutInflater = LayoutInflater.from(getContext());
+        mClockController = BubbleClockController.build(layoutInflater);
+    }
+
+    @Test
+    public void setDarkAmount_fadeIn() {
+        ViewGroup smallClockFrame = (ViewGroup) mClockController.getView();
+        View smallClock = smallClockFrame.getChildAt(0);
+        // WHEN dark amount is set to AOD
+        mClockController.setDarkAmount(1f);
+        // THEN small clock should not be shown.
+        assertThat(smallClock.getVisibility()).isEqualTo(View.GONE);
+    }
+
+    @Test
+    public void setTextColor_setDigitalClock() {
+        ViewGroup smallClock = (ViewGroup) mClockController.getView();
+        // WHEN text color is set
+        mClockController.setTextColor(42);
+        // THEN child of small clock should have text color set.
+        TextView digitalClock = (TextView) smallClock.getChildAt(0);
+        assertThat(digitalClock.getCurrentTextColor()).isEqualTo(42);
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/clock/CrossFadeDarkControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/clock/CrossFadeDarkControllerTest.java
new file mode 100644
index 0000000..fd7657f
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/keyguard/clock/CrossFadeDarkControllerTest.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.keyguard.clock;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.test.suitebuilder.annotation.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper.RunWithLooper;
+import android.view.View;
+import android.widget.TextView;
+
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@RunWithLooper
+public final class CrossFadeDarkControllerTest extends SysuiTestCase {
+
+    private View mViewFadeIn;
+    private View mViewFadeOut;
+    private CrossFadeDarkController mDarkController;
+
+    @Before
+    public void setUp() {
+        mViewFadeIn = new TextView(getContext());
+        mViewFadeIn.setVisibility(View.VISIBLE);
+        mViewFadeIn.setAlpha(1f);
+        mViewFadeOut = new TextView(getContext());
+        mViewFadeOut.setVisibility(View.VISIBLE);
+        mViewFadeOut.setAlpha(1f);
+
+        mDarkController = new CrossFadeDarkController(mViewFadeIn, mViewFadeOut);
+    }
+
+    @Test
+    public void setDarkAmount_fadeIn() {
+        // WHEN dark amount corresponds to AOD
+        mDarkController.setDarkAmount(1f);
+        // THEN fade in view should be faded in and fade out view faded out.
+        assertThat(mViewFadeIn.getAlpha()).isEqualTo(1f);
+        assertThat(mViewFadeIn.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(mViewFadeOut.getAlpha()).isEqualTo(0f);
+        assertThat(mViewFadeOut.getVisibility()).isEqualTo(View.GONE);
+    }
+
+    @Test
+    public void setDarkAmount_fadeOut() {
+        // WHEN dark amount corresponds to lock screen
+        mDarkController.setDarkAmount(0f);
+        // THEN fade out view should bed faded out and fade in view faded in.
+        assertThat(mViewFadeIn.getAlpha()).isEqualTo(0f);
+        assertThat(mViewFadeIn.getVisibility()).isEqualTo(View.GONE);
+        assertThat(mViewFadeOut.getAlpha()).isEqualTo(1f);
+        assertThat(mViewFadeOut.getVisibility()).isEqualTo(View.VISIBLE);
+    }
+
+    @Test
+    public void setDarkAmount_partialFadeIn() {
+        // WHEN dark amount corresponds to a partial transition
+        mDarkController.setDarkAmount(0.9f);
+        // THEN views should have intermediate alpha value.
+        assertThat(mViewFadeIn.getAlpha()).isGreaterThan(0f);
+        assertThat(mViewFadeIn.getAlpha()).isLessThan(1f);
+        assertThat(mViewFadeIn.getVisibility()).isEqualTo(View.VISIBLE);
+    }
+
+    @Test
+    public void setDarkAmount_partialFadeOut() {
+        // WHEN dark amount corresponds to a partial transition
+        mDarkController.setDarkAmount(0.1f);
+        // THEN views should have intermediate alpha value.
+        assertThat(mViewFadeOut.getAlpha()).isGreaterThan(0f);
+        assertThat(mViewFadeOut.getAlpha()).isLessThan(1f);
+        assertThat(mViewFadeOut.getVisibility()).isEqualTo(View.VISIBLE);
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/clock/StretchAnalogClockControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/clock/StretchAnalogClockControllerTest.java
new file mode 100644
index 0000000..8de8f3d
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/keyguard/clock/StretchAnalogClockControllerTest.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.keyguard.clock;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.test.suitebuilder.annotation.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper.RunWithLooper;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@RunWithLooper
+public final class StretchAnalogClockControllerTest extends SysuiTestCase {
+
+    private StretchAnalogClockController mClockController;
+
+    @Before
+    public void setUp() {
+        LayoutInflater layoutInflater = LayoutInflater.from(getContext());
+        mClockController = StretchAnalogClockController.build(layoutInflater);
+    }
+
+    @Test
+    public void setDarkAmount_fadeIn() {
+        ViewGroup smallClockFrame = (ViewGroup) mClockController.getView();
+        View smallClock = smallClockFrame.getChildAt(0);
+        // WHEN dark amount is set to AOD
+        mClockController.setDarkAmount(1f);
+        // THEN small clock should not be shown.
+        assertThat(smallClock.getVisibility()).isEqualTo(View.GONE);
+    }
+
+    @Test
+    public void setTextColor_setDigitalClock() {
+        ViewGroup smallClock = (ViewGroup) mClockController.getView();
+        // WHEN text color is set
+        mClockController.setTextColor(42);
+        // THEN child of small clock should have text color set.
+        TextView digitalClock = (TextView) smallClock.getChildAt(0);
+        assertThat(digitalClock.getCurrentTextColor()).isEqualTo(42);
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
index 21d3652..fa5cf04 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
@@ -19,7 +19,6 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.Mockito.atLeastOnce;
-import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
@@ -74,7 +73,8 @@
     private ExpandableNotificationRow mRow;
     private ExpandableNotificationRow mRow2;
 
-    private final NotificationData mNotificationData = new NotificationData();
+    @Mock
+    private NotificationData mNotificationData;
 
     @Before
     public void setUp() throws Exception {
@@ -93,6 +93,7 @@
 
         // Return non-null notification data from the NEM
         when(mNotificationEntryManager.getNotificationData()).thenReturn(mNotificationData);
+        when(mNotificationData.getChannel(mRow.getEntry().key)).thenReturn(mRow.getEntry().channel);
 
         mBubbleController = new TestableBubbleController(mContext, mStatusBarWindowController);
 
@@ -103,26 +104,21 @@
     }
 
     @Test
-    public void testIsBubble() {
-        assertTrue(mRow.getEntry().isBubble());
-    }
-
-    @Test
     public void testAddBubble() {
-        mBubbleController.addBubble(mRow.getEntry());
+        mBubbleController.updateBubble(mRow.getEntry(), true /* updatePosition */);
         assertTrue(mBubbleController.hasBubbles());
     }
 
     @Test
     public void testHasBubbles() {
         assertFalse(mBubbleController.hasBubbles());
-        mBubbleController.addBubble(mRow.getEntry());
+        mBubbleController.updateBubble(mRow.getEntry(), true /* updatePosition */);
         assertTrue(mBubbleController.hasBubbles());
     }
 
     @Test
     public void testRemoveBubble() {
-        mBubbleController.addBubble(mRow.getEntry());
+        mBubbleController.updateBubble(mRow.getEntry(), true /* updatePosition */);
         assertTrue(mBubbleController.hasBubbles());
 
         mBubbleController.removeBubble(mRow.getEntry().key);
@@ -133,35 +129,35 @@
 
     @Test
     public void testDismissStack() {
-        mBubbleController.addBubble(mRow.getEntry());
-        mBubbleController.addBubble(mRow2.getEntry());
+        mBubbleController.updateBubble(mRow.getEntry(), true /* updatePosition */);
+        mBubbleController.updateBubble(mRow2.getEntry(), true /* updatePosition */);
         assertTrue(mBubbleController.hasBubbles());
 
         mBubbleController.dismissStack();
         assertFalse(mStatusBarWindowController.getBubblesShowing());
-        verify(mNotificationEntryManager, times(3)).updateNotifications();
+        verify(mNotificationEntryManager).updateNotifications();
     }
 
     @Test
     public void testIsStackExpanded() {
         assertFalse(mBubbleController.isStackExpanded());
-        mBubbleController.addBubble(mRow.getEntry());
+        mBubbleController.updateBubble(mRow.getEntry(), true /* updatePosition */);
 
         BubbleStackView stackView = mBubbleController.getStackView();
-        stackView.animateExpansion(true /* expanded */);
+        stackView.expandStack();
         assertTrue(mBubbleController.isStackExpanded());
 
-        stackView.animateExpansion(false /* expanded */);
+        stackView.collapseStack();
         assertFalse(mBubbleController.isStackExpanded());
     }
 
     @Test
     public void testCollapseStack() {
-        mBubbleController.addBubble(mRow.getEntry());
-        mBubbleController.addBubble(mRow2.getEntry());
+        mBubbleController.updateBubble(mRow.getEntry(), true /* updatePosition */);
+        mBubbleController.updateBubble(mRow2.getEntry(), true /* updatePosition */);
 
         BubbleStackView stackView = mBubbleController.getStackView();
-        stackView.animateExpansion(true /* expanded */);
+        stackView.expandStack();
         assertTrue(mBubbleController.isStackExpanded());
 
         mBubbleController.collapseStack();
@@ -174,6 +170,12 @@
         assertTrue(mRow.getEntry().isBubble());
     }
 
+    @Test
+    public void testMarkNewNotificationAsShowInShade() {
+        mEntryListener.onPendingEntryAdded(mRow.getEntry());
+        assertTrue(mRow.getEntry().showInShadeWhenBubble());
+    }
+
     static class TestableBubbleController extends BubbleController {
 
         TestableBubbleController(Context context,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeHostFake.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeHostFake.java
index ce28b50..dc42872 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeHostFake.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeHostFake.java
@@ -104,7 +104,7 @@
     }
 
     @Override
-    public void onDoubleTap(float x, float y) {
+    public void onSlpiTap(float x, float y) {
         doubleTapX = y;
         doubleTapY = y;
     }
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 4583770..3415c72 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
@@ -91,7 +91,7 @@
         mMockPm = mock(PackageManager.class);
         mMockListener = mock(PluginListener.class);
         mMockManager = mock(PluginManagerImpl.class);
-        when(mMockManager.getClassLoader(any(), any())).thenReturn(getClass().getClassLoader());
+        when(mMockManager.getClassLoader(any())).thenReturn(getClass().getClassLoader());
         mMockEnabler = mock(PluginEnabler.class);
         when(mMockManager.getPluginEnabler()).thenReturn(mMockEnabler);
         mMockVersionInfo = mock(VersionInfo.class);
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 76e68f1..536c043 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
@@ -25,6 +25,7 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.net.Uri;
 import android.test.suitebuilder.annotation.SmallTest;
@@ -135,9 +136,13 @@
         });
         resetExceptionHandler();
 
+        String sourceDir = "myPlugin";
+        ApplicationInfo applicationInfo = new ApplicationInfo();
+        applicationInfo.sourceDir = sourceDir;
+        applicationInfo.packageName = WHITELISTED_PACKAGE;
         mPluginManager.addPluginListener("myAction", mMockListener, TestPlugin.class);
-        assertNull(mPluginManager.getOneShotPlugin("myPlugin", TestPlugin.class));
-        assertNull(mPluginManager.getClassLoader("myPlugin", WHITELISTED_PACKAGE));
+        assertNull(mPluginManager.getOneShotPlugin(sourceDir, TestPlugin.class));
+        assertNull(mPluginManager.getClassLoader(applicationInfo));
     }
 
     @Test
@@ -152,9 +157,16 @@
         });
         resetExceptionHandler();
 
+        String sourceDir = "myPlugin";
+        ApplicationInfo whiteListedApplicationInfo = new ApplicationInfo();
+        whiteListedApplicationInfo.sourceDir = sourceDir;
+        whiteListedApplicationInfo.packageName = WHITELISTED_PACKAGE;
+        ApplicationInfo invalidApplicationInfo = new ApplicationInfo();
+        invalidApplicationInfo.sourceDir = sourceDir;
+        invalidApplicationInfo.packageName = "com.android.invalidpackage";
         mPluginManager.addPluginListener("myAction", mMockListener, TestPlugin.class);
-        assertNotNull(mPluginManager.getClassLoader("myPlugin", WHITELISTED_PACKAGE));
-        assertNull(mPluginManager.getClassLoader("myPlugin", "com.android.invalidpackage"));
+        assertNotNull(mPluginManager.getClassLoader(whiteListedApplicationInfo));
+        assertNull(mPluginManager.getClassLoader(invalidApplicationInfo));
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
index 529da82..8952594 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
@@ -17,13 +17,17 @@
 package com.android.systemui.statusbar;
 
 import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
+import static android.app.NotificationManager.IMPORTANCE_HIGH;
 
 import android.annotation.Nullable;
 import android.app.ActivityManager;
 import android.app.Instrumentation;
 import android.app.Notification;
 import android.app.NotificationChannel;
+import android.app.PendingIntent;
 import android.content.Context;
+import android.content.Intent;
+import android.graphics.drawable.Icon;
 import android.os.UserHandle;
 import android.service.notification.StatusBarNotification;
 import android.support.test.InstrumentationRegistry;
@@ -86,8 +90,7 @@
      * @throws Exception
      */
     public ExpandableNotificationRow createRow(String pkg, int uid) throws Exception {
-        return createRow(pkg, uid, false /* isGroupSummary */, null /* groupKey */,
-                false /* isBubble */);
+        return createRow(pkg, uid, false /* isGroupSummary */, null /* groupKey */);
     }
 
     /**
@@ -98,8 +101,7 @@
      * @throws Exception
      */
     public ExpandableNotificationRow createRow(Notification notification) throws Exception {
-        return generateRow(notification, PKG, UID, 0 /* extraInflationFlags */,
-                false /* isBubble */);
+        return generateRow(notification, PKG, UID, 0 /* extraInflationFlags */);
     }
 
     /**
@@ -112,8 +114,7 @@
      */
     public ExpandableNotificationRow createRow(@InflationFlag int extraInflationFlags)
             throws Exception {
-        return generateRow(createNotification(), PKG, UID, extraInflationFlags,
-                false /* isBubble */);
+        return generateRow(createNotification(), PKG, UID, extraInflationFlags);
     }
 
     /**
@@ -134,20 +135,21 @@
         return createGroup(2);
     }
 
-    /**
-     * Retursn an {@link ExpandableNotificationRow} that should be a bubble.
-     */
-    public ExpandableNotificationRow createBubble() throws Exception {
-        return createRow(PKG, UID, false /* isGroupSummary */, null /* groupKey */,
-                true /* isBubble */);
-    }
-
     private ExpandableNotificationRow createGroupSummary(String groupkey) throws Exception {
-        return createRow(PKG, UID, true /* isGroupSummary */, groupkey, false);
+        return createRow(PKG, UID, true /* isGroupSummary */, groupkey);
     }
 
     private ExpandableNotificationRow createGroupChild(String groupkey) throws Exception {
-        return createRow(PKG, UID, false /* isGroupSummary */, groupkey, false);
+        return createRow(PKG, UID, false /* isGroupSummary */, groupkey);
+    }
+
+    /**
+     * Returns an {@link ExpandableNotificationRow} that should be shown as a bubble.
+     */
+    public ExpandableNotificationRow createBubble() throws Exception {
+        Notification n = createNotification(false /* isGroupSummary */,
+                null /* groupKey */, true /* isBubble */);
+        return generateRow(n, PKG, UID, 0 /* extraInflationFlags */, IMPORTANCE_HIGH);
     }
 
     /**
@@ -157,7 +159,6 @@
      * @param uid uid used for creating a {@link StatusBarNotification}
      * @param isGroupSummary whether the notification row is a group summary
      * @param groupKey the group key for the notification group used across notifications
-     * @param isBubble
      * @return a row with that's either a standalone notification or a group notification if the
      *         groupKey is non-null
      * @throws Exception
@@ -166,10 +167,10 @@
             String pkg,
             int uid,
             boolean isGroupSummary,
-            @Nullable String groupKey, boolean isBubble)
+            @Nullable String groupKey)
             throws Exception {
         Notification notif = createNotification(isGroupSummary, groupKey);
-        return generateRow(notif, pkg, uid, 0 /* inflationFlags */, isBubble);
+        return generateRow(notif, pkg, uid, 0 /* inflationFlags */);
     }
 
     /**
@@ -188,8 +189,20 @@
      * @param groupKey the group key for the notification group used across notifications
      * @return a notification that is in the group specified or standalone if unspecified
      */
+    private Notification createNotification(boolean isGroupSummary, @Nullable String groupKey) {
+        return createNotification(isGroupSummary, groupKey, false /* isBubble */);
+    }
+
+    /**
+     * Creates a notification with the given parameters.
+     *
+     * @param isGroupSummary whether the notification is a group summary
+     * @param groupKey the group key for the notification group used across notifications
+     * @param isBubble whether this notification should bubble
+     * @return a notification that is in the group specified or standalone if unspecified
+     */
     private Notification createNotification(boolean isGroupSummary,
-            @Nullable String groupKey) {
+            @Nullable String groupKey, boolean isBubble) {
         Notification publicVersion = new Notification.Builder(mContext).setSmallIcon(
                 R.drawable.ic_person)
                 .setCustomContentView(new RemoteViews(mContext.getPackageName(),
@@ -207,6 +220,9 @@
         if (!TextUtils.isEmpty(groupKey)) {
             notificationBuilder.setGroup(groupKey);
         }
+        if (isBubble) {
+            notificationBuilder.setBubbleMetadata(makeBubbleMetadata());
+        }
         return notificationBuilder.build();
     }
 
@@ -214,7 +230,17 @@
             Notification notification,
             String pkg,
             int uid,
-            @InflationFlag int extraInflationFlags, boolean isBubble)
+            @InflationFlag int extraInflationFlags)
+            throws Exception {
+        return generateRow(notification, pkg, uid, extraInflationFlags, IMPORTANCE_DEFAULT);
+    }
+
+    private ExpandableNotificationRow generateRow(
+            Notification notification,
+            String pkg,
+            int uid,
+            @InflationFlag int extraInflationFlags,
+            int importance)
             throws Exception {
         LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(
                 mContext.LAYOUT_INFLATER_SERVICE);
@@ -242,9 +268,8 @@
         entry.setRow(row);
         entry.createIcons(mContext, sbn);
         entry.channel = new NotificationChannel(
-                notification.getChannelId(), notification.getChannelId(), IMPORTANCE_DEFAULT);
+                notification.getChannelId(), notification.getChannelId(), importance);
         entry.channel.setBlockableSystem(true);
-        entry.setIsBubble(isBubble);
         row.setEntry(entry);
         row.getNotificationInflater().addInflationFlags(extraInflationFlags);
         NotificationInflaterTest.runThenWaitForInflation(
@@ -257,4 +282,14 @@
         mGroupManager.onEntryAdded(entry);
         return row;
     }
+
+    private Notification.BubbleMetadata makeBubbleMetadata() {
+        PendingIntent bubbleIntent = PendingIntent.getActivity(mContext, 0, new Intent(), 0);
+        return new Notification.BubbleMetadata.Builder()
+                .setIntent(bubbleIntent)
+                .setTitle("bubble title")
+                .setIcon(Icon.createWithResource(mContext, 1))
+                .setDesiredHeight(314)
+                .build();
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
index bf91305..56e1fc6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
@@ -36,7 +36,6 @@
 import com.android.systemui.Dependency;
 import com.android.systemui.InitController;
 import com.android.systemui.SysuiTestCase;
-import com.android.systemui.bubbles.BubbleController;
 import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.VisualStabilityManager;
@@ -96,7 +95,7 @@
 
         mViewHierarchyManager = new NotificationViewHierarchyManager(mContext,
                 mLockscreenUserManager, mGroupManager, mVisualStabilityManager,
-                mock(StatusBarStateController.class), mEntryManager, mock(BubbleController.class),
+                mock(StatusBarStateController.class), mEntryManager,
                 () -> mShadeController);
         Dependency.get(InitController.class).executePostInitTasks();
         mViewHierarchyManager.setUpWithPresenter(mPresenter, mListContainer);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java
index ecb0cf8..f6791dd5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java
@@ -45,7 +45,6 @@
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
 import static org.mockito.Mockito.when;
 
 import android.app.INotificationManager;
@@ -73,6 +72,7 @@
 
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.systemui.Dependency;
 import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
@@ -498,12 +498,15 @@
     }
 
     @Test
-    public void testLogBlockingHelperCounter_doesntLogForNormalGutsView() throws Exception {
+    public void testLogBlockingHelperCounter_logGutsViewDisplayed() throws Exception {
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
                 IMPORTANCE_DEFAULT);
         mNotificationInfo.logBlockingHelperCounter("HowCanNotifsBeRealIfAppsArent");
-        verifyZeroInteractions(mMetricsLogger);
+        verify(mMetricsLogger).write(argThat(logMaker ->
+                logMaker.getType() == MetricsEvent.NOTIFICATION_BLOCKING_HELPER
+                        && logMaker.getSubtype() == MetricsEvent.BLOCKING_HELPER_DISPLAY
+        ));
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
index fdbf090..c1f8885 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
@@ -182,6 +182,7 @@
             subs.add(subscription);
         }
         when(mMockSm.getActiveSubscriptionInfoList()).thenReturn(subs);
+        when(mMockSm.getActiveSubscriptionInfoList(anyBoolean())).thenReturn(subs);
         mNetworkController.doUpdateMobileControllers();
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyConstantsTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyConstantsTest.java
index 37a56a3..3cbf902 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyConstantsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyConstantsTest.java
@@ -54,6 +54,7 @@
                 R.integer.config_smart_replies_in_notifications_max_squeeze_remeasure_attempts, 7);
         resources.addOverride(
                 R.bool.config_smart_replies_in_notifications_edit_choices_before_sending, false);
+        resources.addOverride(R.bool.config_smart_replies_in_notifications_show_in_heads_up, true);
         mConstants = new SmartReplyConstants(Handler.createAsync(Looper.myLooper()), mContext);
     }
 
@@ -152,6 +153,26 @@
                         RemoteInput.EDIT_CHOICES_BEFORE_SENDING_DISABLED));
     }
 
+    @Test
+    public void testShowInHeadsUpWithNoConfig() {
+        assertTrue(mConstants.isEnabled());
+        assertTrue(mConstants.getShowInHeadsUp());
+    }
+
+    @Test
+    public void testShowInHeadsUpEnabled() {
+        overrideSetting("enabled=true,show_in_heads_up=true");
+        triggerConstantsOnChange();
+        assertTrue(mConstants.getShowInHeadsUp());
+    }
+
+    @Test
+    public void testShowInHeadsUpDisabled() {
+        overrideSetting("enabled=true,show_in_heads_up=false");
+        triggerConstantsOnChange();
+        assertFalse(mConstants.getShowInHeadsUp());
+    }
+
     private void overrideSetting(String flags) {
         Settings.Global.putString(mContext.getContentResolver(),
                 Settings.Global.SMART_REPLIES_IN_NOTIFICATIONS_FLAGS, flags);
diff --git a/packages/WallpaperCropper/Android.mk b/packages/WallpaperCropper/Android.mk
index 848f2bd..2fa1dde 100644
--- a/packages/WallpaperCropper/Android.mk
+++ b/packages/WallpaperCropper/Android.mk
@@ -8,6 +8,7 @@
 LOCAL_PACKAGE_NAME := WallpaperCropper
 LOCAL_PRIVATE_PLATFORM_APIS := true
 LOCAL_CERTIFICATE := platform
+LOCAL_PRODUCT_MODULE := true
 LOCAL_PRIVILEGED_MODULE := true
 
 LOCAL_PROGUARD_FLAG_FILES := proguard.flags
diff --git a/packages/WallpaperCropper/CleanSpec.mk b/packages/WallpaperCropper/CleanSpec.mk
new file mode 100644
index 0000000..e6d8d5a
--- /dev/null
+++ b/packages/WallpaperCropper/CleanSpec.mk
@@ -0,0 +1,50 @@
+# Copyright (C) 2019 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+# 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)/system/priv-app/WallpaperCropper)
+
+# ************************************************
+# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
+# ************************************************
diff --git a/packages/services/PacProcessor/Android.mk b/packages/services/PacProcessor/Android.mk
index 295b3d8b..be9ba43 100644
--- a/packages/services/PacProcessor/Android.mk
+++ b/packages/services/PacProcessor/Android.mk
@@ -26,6 +26,6 @@
 LOCAL_PRIVATE_PLATFORM_APIS := true
 LOCAL_CERTIFICATE := platform
 
-LOCAL_JNI_SHARED_LIBRARIES := libjni_pacprocessor libpac
+LOCAL_JNI_SHARED_LIBRARIES := libjni_pacprocessor
 
 include $(BUILD_PACKAGE)
diff --git a/proto/src/metrics_constants/metrics_constants.proto b/proto/src/metrics_constants/metrics_constants.proto
index aff5e13..a07411d 100644
--- a/proto/src/metrics_constants/metrics_constants.proto
+++ b/proto/src/metrics_constants/metrics_constants.proto
@@ -213,6 +213,25 @@
         IOOP_SYNC = 4;
     }
 
+  // Subtypes of notifications blocking helper view
+  // (NOTIFICATION_BLOCKING_HELPER).
+  enum NotificationBlockingHelper {
+    BLOCKING_HELPER_UNKNOWN = 0;
+    BLOCKING_HELPER_DISPLAY = 1;
+    BLOCKING_HELPER_DISMISS = 2;
+    // When the view of the notification blocking helper was triggered by
+    // system.
+    BLOCKING_HELPER_TRIGGERED_BY_SYSTEM = 3;
+    // "block" was clicked.
+    BLOCKING_HELPER_CLICK_BLOCKED = 4;
+    // "stay silent" was clicked.
+    BLOCKING_HELPER_CLICK_STAY_SILENT = 5;
+    // "alert me" was clicked.
+    BLOCKING_HELPER_CLICK_ALERT_ME = 6;
+    // "undo" was clicked (enables the user to undo "stop notification" action).
+    BLOCKING_HELPER_CLICK_UNDO = 7;
+  }
+
   // Known visual elements: views or controls.
   enum View {
     // Unknown view
@@ -6768,8 +6787,34 @@
     // OS: Q
     ACCESSIBILITY_VIBRATION_RING = 1620;
 
-    // ---- End Q Constants, all Q constants go above this line ----
+    // ACTION: Notification blocking helper view, which helps the user to block
+    // application or channel from showing notifications.
+    // SUBTYPE: NotificationBlockingHelper enum.
+    // CATEGORY: NOTIFICATION
+    // OS: Q
+    NOTIFICATION_BLOCKING_HELPER = 1621;
 
+    // ACTION: Tap & Pay -> Default Application Setting -> Use Forground
+    // OS: Q
+    ACTION_NFC_PAYMENT_FOREGROUND_SETTING = 1622;
+
+    // ACTION: Tap & Pay -> Default Application Setting -> Use Default
+    // OS: Q
+    ACTION_NFC_PAYMENT_ALWAYS_SETTING = 1623;
+
+    // OPEN: Settings > System > Input & Gesture > Skip song gesture
+    // OS: Q
+    SETTINGS_GESTURE_SKIP_SONG = 1624;
+
+    // OPEN: Settings > System > Input & Gesture > Silence gesture
+    // OS: Q
+    SETTINGS_GESTURE_SILENCE = 1625;
+
+    // OPEN: Settings > System > Input & Gesture > Tap screen gesture
+    // OS: Q
+    SETTINGS_GESTURE_TAP_SCREEN = 1626;
+
+    // ---- End Q Constants, all Q constants go above this line ----
     // Add new aosp constants above this line.
     // END OF AOSP CONSTANTS
   }
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
index 6cccd62..ec6d20d 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
@@ -19,6 +19,7 @@
 import static android.Manifest.permission.MANAGE_AUTO_FILL;
 import static android.content.Context.AUTOFILL_MANAGER_SERVICE;
 import static android.util.DebugUtils.flagsToString;
+import static android.view.autofill.AutofillManager.MAX_TEMP_AUGMENTED_SERVICE_DURATION_MS;
 
 import static com.android.server.autofill.Helper.sDebug;
 import static com.android.server.autofill.Helper.sFullScreenMode;
@@ -101,8 +102,6 @@
 
     private static final Object sLock = AutofillManagerService.class;
 
-    private static final int MAX_TEMP_AUGMENTED_SERVICE_DURATION_MS = 1_000 * 60 * 2; // 2 minutes
-
     /**
      * IME supports Smart Suggestions.
      */
@@ -702,6 +701,11 @@
         public void onBackKeyPressed() {
             if (sDebug) Slog.d(TAG, "onBackKeyPressed()");
             mUi.hideAll(null);
+            synchronized (mLock) {
+                final AutofillManagerServiceImpl service =
+                        getServiceForUserLocked(UserHandle.getCallingUserId());
+                service.onBackKeyPressed();
+            }
         }
 
         @Override
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index a6bb049..d037b08 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -187,6 +187,15 @@
     }
 
     @GuardedBy("mLock")
+    void onBackKeyPressed() {
+        final RemoteAugmentedAutofillService remoteService =
+                getRemoteAugmentedAutofillServiceLocked();
+        if (remoteService != null) {
+            remoteService.onDestroyAutofillWindowsRequest();
+        }
+    }
+
+    @GuardedBy("mLock")
     @Override // from PerUserSystemService
     protected boolean updateLocked(boolean disabled) {
         destroySessionsLocked();
diff --git a/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java b/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java
index 239a386..5d8d8fa 100644
--- a/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java
+++ b/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java
@@ -109,8 +109,8 @@
     /**
      * Called by {@link Session} when it's time to destroy all augmented autofill requests.
      */
-    public void onDestroyAutofillWindowsRequest(int sessionId) {
-        scheduleAsyncRequest((s) -> s.onDestroyFillWindowRequest(sessionId));
+    public void onDestroyAutofillWindowsRequest() {
+        scheduleAsyncRequest((s) -> s.onDestroyAllFillWindowsRequest());
     }
 
     // TODO(b/111330312): inline into PendingAutofillRequest if it doesn't have any other subclass
@@ -168,7 +168,7 @@
                 }
             };
 
-            // TODO(b/111330312): set cancellation signal, timeout (from both mClient and service),
+            // TODO(b/122728762): set cancellation signal, timeout (from both mClient and service),
             // cache IAugmentedAutofillManagerClient reference, etc...
             try {
                 mClient.getAugmentedAutofillClient(receiver);
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 0348f2b..a5ef21a 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -2581,7 +2581,10 @@
 
         final RemoteAugmentedAutofillService remoteService = mService
                 .getRemoteAugmentedAutofillServiceLocked();
-        if (remoteService == null) return null;
+        if (remoteService == null) {
+            if (sVerbose) Slog.v(TAG, "triggerAugmentedAutofillLocked(): no service for user");
+            return null;
+        }
 
         // Define which mode will be used
         final int mode;
@@ -2616,7 +2619,7 @@
                 currentValue);
 
         if (mAugmentedAutofillDestroyer == null) {
-            mAugmentedAutofillDestroyer = () -> remoteService.onDestroyAutofillWindowsRequest(id);
+            mAugmentedAutofillDestroyer = () -> remoteService.onDestroyAutofillWindowsRequest();
         }
         return mAugmentedAutofillDestroyer;
     }
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index 12db4f3..ff378b3 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -149,6 +149,9 @@
 
         if (userBackupManagerService != null) {
             userBackupManagerService.tearDownService();
+
+            KeyValueBackupJob.cancel(userId, mContext);
+            FullBackupJob.cancel(userId, mContext);
         }
     }
 
@@ -577,9 +580,9 @@
      * @return Whether ongoing work will continue. The return value here will be passed along as the
      *     return value to the callback {@link JobService#onStartJob(JobParameters)}.
      */
-    public boolean beginFullBackup(FullBackupJob scheduledJob) {
+    public boolean beginFullBackup(@UserIdInt int userId, FullBackupJob scheduledJob) {
         UserBackupManagerService userBackupManagerService =
-                getServiceForUserIfCallerHasPermission(UserHandle.USER_SYSTEM, "beginFullBackup()");
+                getServiceForUserIfCallerHasPermission(userId, "beginFullBackup()");
 
         return userBackupManagerService != null
                 && userBackupManagerService.beginFullBackup(scheduledJob);
@@ -589,9 +592,9 @@
      * Used by the {@link JobScheduler} to end the current full backup task when conditions are no
      * longer met for running the full backup job.
      */
-    public void endFullBackup() {
+    public void endFullBackup(@UserIdInt int userId) {
         UserBackupManagerService userBackupManagerService =
-                getServiceForUserIfCallerHasPermission(UserHandle.USER_SYSTEM, "endFullBackup()");
+                getServiceForUserIfCallerHasPermission(userId, "endFullBackup()");
 
         if (userBackupManagerService != null) {
             userBackupManagerService.endFullBackup();
diff --git a/services/backup/java/com/android/server/backup/FullBackupJob.java b/services/backup/java/com/android/server/backup/FullBackupJob.java
index 5708b1c..33d21cb0 100644
--- a/services/backup/java/com/android/server/backup/FullBackupJob.java
+++ b/services/backup/java/com/android/server/backup/FullBackupJob.java
@@ -22,18 +22,30 @@
 import android.app.job.JobService;
 import android.content.ComponentName;
 import android.content.Context;
+import android.os.Bundle;
+import android.util.SparseArray;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
 
 public class FullBackupJob extends JobService {
+    private static final String USER_ID_EXTRA_KEY = "userId";
+
+    @VisibleForTesting
+    static final int MIN_JOB_ID = 52418896;
+    @VisibleForTesting
+    static final int MAX_JOB_ID = 52419896;
+
     private static ComponentName sIdleService =
             new ComponentName("android", FullBackupJob.class.getName());
 
-    private static final int JOB_ID = 0x5038;
+    @GuardedBy("mParamsForUser")
+    private final SparseArray<JobParameters> mParamsForUser = new SparseArray<>();
 
-    private JobParameters mParams;
-
-    public static void schedule(Context ctx, long minDelay, BackupManagerConstants constants) {
+    public static void schedule(int userId, Context ctx, long minDelay,
+            BackupManagerConstants constants) {
         JobScheduler js = (JobScheduler) ctx.getSystemService(Context.JOB_SCHEDULER_SERVICE);
-        JobInfo.Builder builder = new JobInfo.Builder(JOB_ID, sIdleService);
+        JobInfo.Builder builder = new JobInfo.Builder(getJobIdForUserId(userId), sIdleService);
         synchronized (constants) {
             builder.setRequiresDeviceIdle(true)
                     .setRequiredNetworkType(constants.getFullBackupRequiredNetworkType())
@@ -42,14 +54,28 @@
         if (minDelay > 0) {
             builder.setMinimumLatency(minDelay);
         }
+
+        Bundle extraInfo = new Bundle();
+        extraInfo.putInt(USER_ID_EXTRA_KEY, userId);
+        builder.setTransientExtras(extraInfo);
+
         js.schedule(builder.build());
     }
 
+    public static void cancel(int userId, Context ctx) {
+        JobScheduler js = (JobScheduler) ctx.getSystemService(
+                Context.JOB_SCHEDULER_SERVICE);
+        js.cancel(getJobIdForUserId(userId));
+    }
+
     // callback from the Backup Manager Service: it's finished its work for this pass
-    public void finishBackupPass() {
-        if (mParams != null) {
-            jobFinished(mParams, false);
-            mParams = null;
+    public void finishBackupPass(int userId) {
+        synchronized (mParamsForUser) {
+            JobParameters jobParameters = mParamsForUser.get(userId);
+            if (jobParameters != null) {
+                jobFinished(jobParameters, false);
+                mParamsForUser.remove(userId);
+            }
         }
     }
 
@@ -57,19 +83,33 @@
 
     @Override
     public boolean onStartJob(JobParameters params) {
-        mParams = params;
+        int userId = params.getTransientExtras().getInt(USER_ID_EXTRA_KEY);
+
+        synchronized (mParamsForUser) {
+            mParamsForUser.put(userId, params);
+        }
+
         Trampoline service = BackupManagerService.getInstance();
-        return service.beginFullBackup(this);
+        return service.beginFullBackup(userId, this);
     }
 
     @Override
     public boolean onStopJob(JobParameters params) {
-        if (mParams != null) {
-            mParams = null;
-            Trampoline service = BackupManagerService.getInstance();
-            service.endFullBackup();
+        int userId = params.getTransientExtras().getInt(USER_ID_EXTRA_KEY);
+
+        synchronized (mParamsForUser) {
+            if (mParamsForUser.removeReturnOld(userId) == null) {
+                return false;
+            }
         }
+
+        Trampoline service = BackupManagerService.getInstance();
+        service.endFullBackup(userId);
+
         return false;
     }
 
+    private static int getJobIdForUserId(int userId) {
+        return JobIdManager.getJobIdForUserId(MIN_JOB_ID, MAX_JOB_ID, userId);
+    }
 }
diff --git a/services/backup/java/com/android/server/backup/JobIdManager.java b/services/backup/java/com/android/server/backup/JobIdManager.java
new file mode 100644
index 0000000..2e834dbf
--- /dev/null
+++ b/services/backup/java/com/android/server/backup/JobIdManager.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup;
+
+/**
+ * Allocates job IDs for {@link FullBackupJob} and {@link KeyValueBackupJob}
+ */
+public class JobIdManager {
+    public static int getJobIdForUserId(int minJobId, int maxJobId, int userId) {
+        if (minJobId + userId > maxJobId) {
+            throw new RuntimeException("No job IDs available in the given range");
+        }
+
+        return minJobId + userId;
+    }
+}
diff --git a/services/backup/java/com/android/server/backup/KeyValueBackupJob.java b/services/backup/java/com/android/server/backup/KeyValueBackupJob.java
index f2e7435..d43859e 100644
--- a/services/backup/java/com/android/server/backup/KeyValueBackupJob.java
+++ b/services/backup/java/com/android/server/backup/KeyValueBackupJob.java
@@ -25,8 +25,14 @@
 import android.app.job.JobService;
 import android.content.ComponentName;
 import android.content.Context;
+import android.os.Bundle;
 import android.os.RemoteException;
 import android.util.Slog;
+import android.util.SparseBooleanArray;
+import android.util.SparseLongArray;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
 
 import java.util.Random;
 
@@ -38,7 +44,8 @@
     private static final String TAG = "KeyValueBackupJob";
     private static ComponentName sKeyValueJobService =
             new ComponentName("android", KeyValueBackupJob.class.getName());
-    private static final int JOB_ID = 0x5039;
+
+    private static final String USER_ID_EXTRA_KEY = "userId";
 
     // Once someone asks for a backup, this is how long we hold off until we find
     // an on-charging opportunity.  If we hit this max latency we will run the operation
@@ -46,16 +53,22 @@
     // BackupManager.backupNow().
     private static final long MAX_DEFERRAL = AlarmManager.INTERVAL_DAY;
 
-    private static boolean sScheduled = false;
-    private static long sNextScheduled = 0;
+    @GuardedBy("KeyValueBackupJob.class")
+    private static final SparseBooleanArray sScheduledForUserId = new SparseBooleanArray();
+    @GuardedBy("KeyValueBackupJob.class")
+    private static final SparseLongArray sNextScheduledForUserId = new SparseLongArray();
 
-    public static void schedule(Context ctx, BackupManagerConstants constants) {
-        schedule(ctx, 0, constants);
+    private static final int MIN_JOB_ID = 52417896;
+    private static final int MAX_JOB_ID = 52418896;
+
+    public static void schedule(int userId, Context ctx, BackupManagerConstants constants) {
+        schedule(userId, ctx, 0, constants);
     }
 
-    public static void schedule(Context ctx, long delay, BackupManagerConstants constants) {
+    public static void schedule(int userId, Context ctx, long delay,
+            BackupManagerConstants constants) {
         synchronized (KeyValueBackupJob.class) {
-            if (sScheduled) {
+            if (sScheduledForUserId.get(userId)) {
                 return;
             }
 
@@ -76,51 +89,61 @@
             if (DEBUG_SCHEDULING) {
                 Slog.v(TAG, "Scheduling k/v pass in " + (delay / 1000 / 60) + " minutes");
             }
-            JobInfo.Builder builder = new JobInfo.Builder(JOB_ID, sKeyValueJobService)
+
+            JobInfo.Builder builder = new JobInfo.Builder(getJobIdForUserId(userId),
+                    sKeyValueJobService)
                     .setMinimumLatency(delay)
                     .setRequiredNetworkType(networkType)
                     .setRequiresCharging(needsCharging)
                     .setOverrideDeadline(MAX_DEFERRAL);
+
+            Bundle extraInfo = new Bundle();
+            extraInfo.putInt(USER_ID_EXTRA_KEY, userId);
+            builder.setTransientExtras(extraInfo);
+
             JobScheduler js = (JobScheduler) ctx.getSystemService(Context.JOB_SCHEDULER_SERVICE);
             js.schedule(builder.build());
 
-            sNextScheduled = System.currentTimeMillis() + delay;
-            sScheduled = true;
+            sScheduledForUserId.put(userId, true);
+            sNextScheduledForUserId.put(userId, System.currentTimeMillis() + delay);
         }
     }
 
-    public static void cancel(Context ctx) {
+    public static void cancel(int userId, Context ctx) {
         synchronized (KeyValueBackupJob.class) {
-            JobScheduler js = (JobScheduler) ctx.getSystemService(Context.JOB_SCHEDULER_SERVICE);
-            js.cancel(JOB_ID);
-            sNextScheduled = 0;
-            sScheduled = false;
+            JobScheduler js = (JobScheduler) ctx.getSystemService(
+                    Context.JOB_SCHEDULER_SERVICE);
+            js.cancel(getJobIdForUserId(userId));
+
+            clearScheduledForUserId(userId);
         }
     }
 
-    public static long nextScheduled() {
+    public static long nextScheduled(int userId) {
         synchronized (KeyValueBackupJob.class) {
-            return sNextScheduled;
+            return sNextScheduledForUserId.get(userId);
         }
     }
 
-    public static boolean isScheduled() {
+    @VisibleForTesting
+    public static boolean isScheduled(int userId) {
         synchronized (KeyValueBackupJob.class) {
-            return sScheduled;
+            return sScheduledForUserId.get(userId);
         }
     }
 
     @Override
     public boolean onStartJob(JobParameters params) {
+        int userId = params.getTransientExtras().getInt(USER_ID_EXTRA_KEY);
+
         synchronized (KeyValueBackupJob.class) {
-            sNextScheduled = 0;
-            sScheduled = false;
+            clearScheduledForUserId(userId);
         }
 
         // Time to run a key/value backup!
         Trampoline service = BackupManagerService.getInstance();
         try {
-            service.backupNow();
+            service.backupNowForUser(userId);
         } catch (RemoteException e) {}
 
         // This was just a trigger; ongoing wakelock management is done by the
@@ -134,4 +157,13 @@
         return false;
     }
 
+    @GuardedBy("KeyValueBackupJob.class")
+    private static void clearScheduledForUserId(int userId) {
+        sScheduledForUserId.delete(userId);
+        sNextScheduledForUserId.delete(userId);
+    }
+
+    private static int getJobIdForUserId(int userId) {
+        return JobIdManager.getJobIdForUserId(MIN_JOB_ID, MAX_JOB_ID, userId);
+    }
 }
diff --git a/services/backup/java/com/android/server/backup/PackageManagerBackupAgent.java b/services/backup/java/com/android/server/backup/PackageManagerBackupAgent.java
index e4ce62d..4a1e5b9 100644
--- a/services/backup/java/com/android/server/backup/PackageManagerBackupAgent.java
+++ b/services/backup/java/com/android/server/backup/PackageManagerBackupAgent.java
@@ -23,9 +23,9 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.PackageManagerInternal;
 import android.content.pm.ResolveInfo;
-import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.Signature;
 import android.content.pm.SigningInfo;
 import android.os.Build;
@@ -49,9 +49,8 @@
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
-import java.util.Set;
-
 import java.util.Objects;
+import java.util.Set;
 
 /**
  * We back up the signatures of each package so that during a system restore,
@@ -95,6 +94,7 @@
     // is coming from pre-Android P device.
     private static final int UNDEFINED_ANCESTRAL_RECORD_VERSION = -1;
 
+    private int mUserId;
     private List<PackageInfo> mAllPackages;
     private PackageManager mPackageManager;
     // version & signature info of each app in a restore set
@@ -129,17 +129,18 @@
 
     // We're constructed with the set of applications that are participating
     // in backup.  This set changes as apps are installed & removed.
-    public PackageManagerBackupAgent(PackageManager packageMgr, List<PackageInfo> packages) {
-        init(packageMgr, packages);
+    public PackageManagerBackupAgent(
+            PackageManager packageMgr, List<PackageInfo> packages, int userId) {
+        init(packageMgr, packages, userId);
     }
 
-    public PackageManagerBackupAgent(PackageManager packageMgr) {
-        init(packageMgr, null);
+    public PackageManagerBackupAgent(PackageManager packageMgr, int userId) {
+        init(packageMgr, null, userId);
 
         evaluateStorablePackages();
     }
 
-    private void init(PackageManager packageMgr, List<PackageInfo> packages) {
+    private void init(PackageManager packageMgr, List<PackageInfo> packages, int userId) {
         mPackageManager = packageMgr;
         mAllPackages = packages;
         mRestoredSignatures = null;
@@ -147,17 +148,19 @@
 
         mStoredSdkVersion = Build.VERSION.SDK_INT;
         mStoredIncrementalVersion = Build.VERSION.INCREMENTAL;
+        mUserId = userId;
     }
 
     // We will need to refresh our understanding of what is eligible for
     // backup periodically; this entry point serves that purpose.
     public void evaluateStorablePackages() {
-        mAllPackages = getStorableApplications(mPackageManager);
+        mAllPackages = getStorableApplications(mPackageManager, mUserId);
     }
 
-    public static List<PackageInfo> getStorableApplications(PackageManager pm) {
-        List<PackageInfo> pkgs;
-        pkgs = pm.getInstalledPackages(PackageManager.GET_SIGNING_CERTIFICATES);
+    /** Gets all packages installed on user {@code userId} eligible for backup. */
+    public static List<PackageInfo> getStorableApplications(PackageManager pm, int userId) {
+        List<PackageInfo> pkgs =
+                pm.getInstalledPackagesAsUser(PackageManager.GET_SIGNING_CERTIFICATES, userId);
         int N = pkgs.size();
         for (int a = N-1; a >= 0; a--) {
             PackageInfo pkg = pkgs.get(a);
@@ -237,8 +240,8 @@
         ComponentName home = getPreferredHomeComponent();
         if (home != null) {
             try {
-                homeInfo = mPackageManager.getPackageInfo(home.getPackageName(),
-                        PackageManager.GET_SIGNING_CERTIFICATES);
+                homeInfo = mPackageManager.getPackageInfoAsUser(home.getPackageName(),
+                        PackageManager.GET_SIGNING_CERTIFICATES, mUserId);
                 homeInstaller = mPackageManager.getInstallerPackageName(home.getPackageName());
                 homeVersion = homeInfo.getLongVersionCode();
                 SigningInfo signingInfo = homeInfo.signingInfo;
@@ -315,8 +318,8 @@
                 } else {
                     PackageInfo info = null;
                     try {
-                        info = mPackageManager.getPackageInfo(packName,
-                                PackageManager.GET_SIGNING_CERTIFICATES);
+                        info = mPackageManager.getPackageInfoAsUser(packName,
+                                PackageManager.GET_SIGNING_CERTIFICATES, mUserId);
                     } catch (NameNotFoundException e) {
                         // Weird; we just found it, and now are told it doesn't exist.
                         // Treat it as having been removed from the device.
diff --git a/services/backup/java/com/android/server/backup/Trampoline.java b/services/backup/java/com/android/server/backup/Trampoline.java
index 79d4a2c..4ca2545 100644
--- a/services/backup/java/com/android/server/backup/Trampoline.java
+++ b/services/backup/java/com/android/server/backup/Trampoline.java
@@ -698,15 +698,15 @@
 
     // Full backup/restore entry points - non-Binder; called directly
     // by the full-backup scheduled job
-    /* package */ boolean beginFullBackup(FullBackupJob scheduledJob) {
+    /* package */ boolean beginFullBackup(@UserIdInt int userId, FullBackupJob scheduledJob) {
         BackupManagerService svc = mService;
-        return (svc != null) ? svc.beginFullBackup(scheduledJob) : false;
+        return (svc != null) ? svc.beginFullBackup(userId, scheduledJob) : false;
     }
 
-    /* package */ void endFullBackup() {
+    /* package */ void endFullBackup(@UserIdInt int userId) {
         BackupManagerService svc = mService;
         if (svc != null) {
-            svc.endFullBackup();
+            svc.endFullBackup(userId);
         }
     }
 }
diff --git a/services/backup/java/com/android/server/backup/TransportManager.java b/services/backup/java/com/android/server/backup/TransportManager.java
index ddce6bb..a7bada0 100644
--- a/services/backup/java/com/android/server/backup/TransportManager.java
+++ b/services/backup/java/com/android/server/backup/TransportManager.java
@@ -17,6 +17,7 @@
 package com.android.server.backup;
 
 import android.annotation.Nullable;
+import android.annotation.UserIdInt;
 import android.annotation.WorkerThread;
 import android.app.backup.BackupManager;
 import android.app.backup.BackupTransport;
@@ -29,7 +30,6 @@
 import android.content.pm.ResolveInfo;
 import android.os.Bundle;
 import android.os.RemoteException;
-import android.os.UserHandle;
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.Slog;
@@ -61,7 +61,7 @@
     public static final String SERVICE_ACTION_TRANSPORT_HOST = "android.backup.TRANSPORT_HOST";
 
     private final Intent mTransportServiceIntent = new Intent(SERVICE_ACTION_TRANSPORT_HOST);
-    private final Context mContext;
+    private final @UserIdInt int mUserId;
     private final PackageManager mPackageManager;
     private final Set<ComponentName> mTransportWhitelist;
     private final TransportClientManager mTransportClientManager;
@@ -86,22 +86,24 @@
     @Nullable
     private volatile String mCurrentTransportName;
 
-    TransportManager(Context context, Set<ComponentName> whitelist, String selectedTransport) {
-        mContext = context;
+    TransportManager(@UserIdInt int userId, Context context, Set<ComponentName> whitelist,
+            String selectedTransport) {
+        mUserId = userId;
         mPackageManager = context.getPackageManager();
         mTransportWhitelist = Preconditions.checkNotNull(whitelist);
         mCurrentTransportName = selectedTransport;
         mTransportStats = new TransportStats();
-        mTransportClientManager = new TransportClientManager(context, mTransportStats);
+        mTransportClientManager = new TransportClientManager(mUserId, context, mTransportStats);
     }
 
     @VisibleForTesting
     TransportManager(
+            @UserIdInt int userId,
             Context context,
             Set<ComponentName> whitelist,
             String selectedTransport,
             TransportClientManager transportClientManager) {
-        mContext = context;
+        mUserId = userId;
         mPackageManager = context.getPackageManager();
         mTransportWhitelist = Preconditions.checkNotNull(whitelist);
         mCurrentTransportName = selectedTransport;
@@ -560,7 +562,7 @@
     private void registerTransportsFromPackage(
             String packageName, Predicate<ComponentName> transportComponentFilter) {
         try {
-            mPackageManager.getPackageInfo(packageName, 0);
+            mPackageManager.getPackageInfoAsUser(packageName, 0, mUserId);
         } catch (PackageManager.NameNotFoundException e) {
             Slog.e(TAG, "Trying to register transports from package not found " + packageName);
             return;
@@ -575,7 +577,7 @@
     private void registerTransportsForIntent(
             Intent intent, Predicate<ComponentName> transportComponentFilter) {
         List<ResolveInfo> hosts =
-                mPackageManager.queryIntentServicesAsUser(intent, 0, UserHandle.USER_SYSTEM);
+                mPackageManager.queryIntentServicesAsUser(intent, 0, mUserId);
         if (hosts == null) {
             return;
         }
@@ -597,7 +599,8 @@
             return false;
         }
         try {
-            PackageInfo packInfo = mPackageManager.getPackageInfo(transport.getPackageName(), 0);
+            PackageInfo packInfo =
+                    mPackageManager.getPackageInfoAsUser(transport.getPackageName(), 0, mUserId);
             if ((packInfo.applicationInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED)
                     == 0) {
                 Slog.w(TAG, "Transport package " + transport.getPackageName() + " not privileged");
diff --git a/services/backup/java/com/android/server/backup/UserBackupManagerService.java b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
index 6b0adfb..e0e81ff 100644
--- a/services/backup/java/com/android/server/backup/UserBackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
@@ -249,11 +249,11 @@
     private final TransportManager mTransportManager;
     private final HandlerThread mUserBackupThread;
 
-    private Context mContext;
-    private PackageManager mPackageManager;
-    private IPackageManager mPackageManagerBinder;
-    private IActivityManager mActivityManager;
-    private ActivityManagerInternal mActivityManagerInternal;
+    private final Context mContext;
+    private final PackageManager mPackageManager;
+    private final IPackageManager mPackageManagerBinder;
+    private final IActivityManager mActivityManager;
+    private final ActivityManagerInternal mActivityManagerInternal;
     private PowerManager mPowerManager;
     private final AlarmManager mAlarmManager;
     private final IStorageManager mStorageManager;
@@ -384,7 +384,7 @@
             Slog.v(TAG, "Starting with transport " + currentTransport);
         }
         TransportManager transportManager =
-                new TransportManager(context, transportWhitelist, currentTransport);
+                new TransportManager(userId, context, transportWhitelist, currentTransport);
 
         File baseStateDir = UserBackupManagerFiles.getBaseStateDir(userId);
         File dataDir = UserBackupManagerFiles.getDataDir(userId);
@@ -495,12 +495,19 @@
 
         mBaseStateDir = checkNotNull(baseStateDir, "baseStateDir cannot be null");
         mBaseStateDir.mkdirs();
-        if (!SELinux.restorecon(mBaseStateDir)) {
-            Slog.e(TAG, "SELinux restorecon failed on " + mBaseStateDir);
+        if (!SELinux.restoreconRecursive(mBaseStateDir)) {
+            Slog.w(TAG, "SELinux restorecon failed on " + mBaseStateDir);
         }
 
         mDataDir = checkNotNull(dataDir, "dataDir cannot be null");
-
+        // TODO(b/120424138): Remove when the system user moves out of the cache dir. The cache dir
+        // is managed by init.rc so we don't have to create it below.
+        if (userId != UserHandle.USER_SYSTEM) {
+            mDataDir.mkdirs();
+            if (!SELinux.restoreconRecursive(mDataDir)) {
+                Slog.w(TAG, "SELinux restorecon failed on " + mDataDir);
+            }
+        }
         mBackupPasswordManager = new BackupPasswordManager(mContext, mBaseStateDir, mRng);
 
         // Receivers for scheduled backups and transport initialization operations.
@@ -577,7 +584,7 @@
         mBackupHandler.postDelayed(this::parseLeftoverJournals, INITIALIZATION_DELAY_MILLIS);
 
         // Power management
-        mWakelock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*backup*");
+        mWakelock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*backup*-" + userId);
     }
 
     void initializeBackupEnableState() {
@@ -797,7 +804,7 @@
      * non-lifecycle agent instance, so we manually set up the context topology for it.
      */
     public BackupAgent makeMetadataAgent() {
-        PackageManagerBackupAgent pmAgent = new PackageManagerBackupAgent(mPackageManager);
+        PackageManagerBackupAgent pmAgent = new PackageManagerBackupAgent(mPackageManager, mUserId);
         pmAgent.attach(mContext);
         pmAgent.onCreate();
         return pmAgent;
@@ -808,7 +815,7 @@
      */
     public PackageManagerBackupAgent makeMetadataAgent(List<PackageInfo> packages) {
         PackageManagerBackupAgent pmAgent =
-                new PackageManagerBackupAgent(mPackageManager, packages);
+                new PackageManagerBackupAgent(mPackageManager, packages, mUserId);
         pmAgent.attach(mContext);
         pmAgent.onCreate();
         return pmAgent;
@@ -879,7 +886,7 @@
         boolean changed = false;
         ArrayList<FullBackupEntry> schedule = null;
         List<PackageInfo> apps =
-                PackageManagerBackupAgent.getStorableApplications(mPackageManager);
+                PackageManagerBackupAgent.getStorableApplications(mPackageManager, mUserId);
 
         if (mFullBackupScheduleFile.exists()) {
             try (FileInputStream fstream = new FileInputStream(mFullBackupScheduleFile);
@@ -1345,7 +1352,7 @@
     private List<PackageInfo> allAgentPackages() {
         // !!! TODO: cache this and regenerate only when necessary
         int flags = PackageManager.GET_SIGNING_CERTIFICATES;
-        List<PackageInfo> packages = mPackageManager.getInstalledPackages(flags);
+        List<PackageInfo> packages = mPackageManager.getInstalledPackagesAsUser(flags, mUserId);
         int numPackages = packages.size();
         for (int a = numPackages - 1; a >= 0; a--) {
             PackageInfo pkg = packages.get(a);
@@ -1359,8 +1366,8 @@
                     // we will need the shared library path, so look that up and store it here.
                     // This is used implicitly when we pass the PackageInfo object off to
                     // the Activity Manager to launch the app for backup/restore purposes.
-                    app = mPackageManager.getApplicationInfo(pkg.packageName,
-                            PackageManager.GET_SHARED_LIBRARY_FILES);
+                    app = mPackageManager.getApplicationInfoAsUser(pkg.packageName,
+                            PackageManager.GET_SHARED_LIBRARY_FILES, mUserId);
                     pkg.applicationInfo.sharedLibraryFiles = app.sharedLibraryFiles;
                     pkg.applicationInfo.sharedLibraryInfos = app.sharedLibraryInfos;
                 }
@@ -1385,7 +1392,7 @@
             notification.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES
                     | Intent.FLAG_RECEIVER_FOREGROUND);
             notification.putExtra(BACKUP_FINISHED_PACKAGE_EXTRA, packageName);
-            mContext.sendBroadcastAsUser(notification, UserHandle.OWNER);
+            mContext.sendBroadcastAsUser(notification, UserHandle.of(mUserId));
         }
 
         mProcessedPackagesJournal.addPackage(packageName);
@@ -1428,8 +1435,7 @@
             mConnecting = true;
             mConnectedAgent = null;
             try {
-                if (mActivityManager.bindBackupAgent(app.packageName, mode,
-                        UserHandle.USER_OWNER)) {
+                if (mActivityManager.bindBackupAgent(app.packageName, mode, mUserId)) {
                     Slog.d(TAG, "awaiting agent for " + app);
 
                     // success; wait for the agent to arrive
@@ -1460,11 +1466,7 @@
             }
         }
         if (agent == null) {
-            try {
-                mActivityManager.clearPendingBackup();
-            } catch (RemoteException e) {
-                // can't happen - ActivityManager is local
-            }
+            mActivityManagerInternal.clearPendingBackup(mUserId);
         }
         return agent;
     }
@@ -1488,7 +1490,7 @@
     public void clearApplicationDataSynchronous(String packageName, boolean keepSystemState) {
         // Don't wipe packages marked allowClearUserData=false
         try {
-            PackageInfo info = mPackageManager.getPackageInfo(packageName, 0);
+            PackageInfo info = mPackageManager.getPackageInfoAsUser(packageName, 0, mUserId);
             if ((info.applicationInfo.flags & ApplicationInfo.FLAG_ALLOW_CLEAR_USER_DATA) == 0) {
                 if (MORE_DEBUG) {
                     Slog.i(TAG, "allowClearUserData=false so not wiping "
@@ -1507,7 +1509,7 @@
             mClearingData = true;
             try {
                 mActivityManager.clearApplicationUserData(
-                        packageName, keepSystemState, observer, 0);
+                        packageName, keepSystemState, observer, mUserId);
             } catch (RemoteException e) {
                 // can't happen because the activity manager is in this process
             }
@@ -1616,8 +1618,8 @@
                 continue;
             }
             try {
-                PackageInfo packageInfo = mPackageManager.getPackageInfo(packageName,
-                        PackageManager.GET_SIGNING_CERTIFICATES);
+                PackageInfo packageInfo = mPackageManager.getPackageInfoAsUser(packageName,
+                        PackageManager.GET_SIGNING_CERTIFICATES, mUserId);
                 if (!AppBackupUtils.appIsEligibleForBackup(packageInfo.applicationInfo,
                         mPackageManager)) {
                     BackupObserverUtils.sendBackupOnPackageResult(observer, packageName,
@@ -1674,8 +1676,8 @@
             }
             // We don't want the backup jobs to kick in any time soon.
             // Reschedules them to run in the distant future.
-            KeyValueBackupJob.schedule(mContext, BUSY_BACKOFF_MIN_MILLIS, mConstants);
-            FullBackupJob.schedule(mContext, 2 * BUSY_BACKOFF_MIN_MILLIS, mConstants);
+            KeyValueBackupJob.schedule(mUserId, mContext, BUSY_BACKOFF_MIN_MILLIS, mConstants);
+            FullBackupJob.schedule(mUserId, mContext, 2 * BUSY_BACKOFF_MIN_MILLIS, mConstants);
         } finally {
             Binder.restoreCallingIdentity(oldToken);
         }
@@ -1910,7 +1912,7 @@
                 Runnable r = new Runnable() {
                     @Override
                     public void run() {
-                        FullBackupJob.schedule(mContext, latency, mConstants);
+                        FullBackupJob.schedule(mUserId, mContext, latency, mConstants);
                     }
                 };
                 mBackupHandler.postDelayed(r, 2500);
@@ -2033,7 +2035,7 @@
                 mPowerManager.getPowerSaveState(ServiceType.FULL_BACKUP);
         if (result.batterySaverEnabled) {
             if (DEBUG) Slog.i(TAG, "Deferring scheduled full backups in battery saver mode");
-            FullBackupJob.schedule(mContext, keyValueBackupInterval, mConstants);
+            FullBackupJob.schedule(mUserId, mContext, keyValueBackupInterval, mConstants);
             return false;
         }
 
@@ -2147,7 +2149,7 @@
                 mBackupHandler.post(new Runnable() {
                     @Override
                     public void run() {
-                        FullBackupJob.schedule(mContext, deferTime, mConstants);
+                        FullBackupJob.schedule(mUserId, mContext, deferTime, mConstants);
                     }
                 });
                 return false;
@@ -2206,11 +2208,10 @@
     /** Used by both incremental and full restore to restore widget data. */
     public void restoreWidgetData(String packageName, byte[] widgetData) {
         // Apply the restored widget state and generate the ID update for the app
-        // TODO: http://b/22388012
         if (MORE_DEBUG) {
             Slog.i(TAG, "Incorporating restored widget data");
         }
-        AppWidgetBackupBridge.restoreWidgetState(packageName, widgetData, UserHandle.USER_SYSTEM);
+        AppWidgetBackupBridge.restoreWidgetState(packageName, widgetData, mUserId);
     }
 
     // *****************************
@@ -2251,7 +2252,7 @@
         }
 
         // ...and schedule a backup pass if necessary
-        KeyValueBackupJob.schedule(mContext, mConstants);
+        KeyValueBackupJob.schedule(mUserId, mContext, mConstants);
     }
 
     // Note: packageName is currently unused, but may be in the future
@@ -2289,20 +2290,6 @@
 
     /** Sent from an app's backup agent to let the service know that there's new data to backup. */
     public void dataChanged(final String packageName) {
-        final int callingUserHandle = UserHandle.getCallingUserId();
-        if (callingUserHandle != UserHandle.USER_SYSTEM) {
-            // TODO: http://b/22388012
-            // App is running under a non-owner user profile.  For now, we do not back
-            // up data from secondary user profiles.
-            // TODO: backups for all user profiles although don't add backup for profiles
-            // without adding admin control in DevicePolicyManager.
-            if (MORE_DEBUG) {
-                Slog.v(TAG, "dataChanged(" + packageName + ") ignored because it's user "
-                        + callingUserHandle);
-            }
-            return;
-        }
-
         final HashSet<String> targets = dataChangedTargets(packageName);
         if (targets == null) {
             Slog.w(TAG, "dataChanged but no participant pkg='" + packageName + "'"
@@ -2339,8 +2326,8 @@
         if (DEBUG) Slog.v(TAG, "clearBackupData() of " + packageName + " on " + transportName);
         PackageInfo info;
         try {
-            info = mPackageManager.getPackageInfo(packageName,
-                    PackageManager.GET_SIGNING_CERTIFICATES);
+            info = mPackageManager.getPackageInfoAsUser(packageName,
+                    PackageManager.GET_SIGNING_CERTIFICATES, mUserId);
         } catch (NameNotFoundException e) {
             Slog.d(TAG, "No such package '" + packageName + "' - not clearing backup data");
             return;
@@ -2401,7 +2388,8 @@
                     mPowerManager.getPowerSaveState(ServiceType.KEYVALUE_BACKUP);
             if (result.batterySaverEnabled) {
                 if (DEBUG) Slog.v(TAG, "Not running backup while in battery save mode");
-                KeyValueBackupJob.schedule(mContext, mConstants);   // try again in several hours
+                // Try again in several hours.
+                KeyValueBackupJob.schedule(mUserId, mContext, mConstants);
             } else {
                 if (DEBUG) Slog.v(TAG, "Scheduling immediate backup pass");
                 synchronized (mQueueLock) {
@@ -2414,7 +2402,7 @@
                     }
 
                     // ...and cancel any pending scheduled job, because we've just superseded it
-                    KeyValueBackupJob.cancel(mContext);
+                    KeyValueBackupJob.cancel(mUserId, mContext);
                 }
             }
         } finally {
@@ -2737,13 +2725,13 @@
             synchronized (mQueueLock) {
                 if (enable && !wasEnabled && mSetupComplete) {
                     // if we've just been enabled, start scheduling backup passes
-                    KeyValueBackupJob.schedule(mContext, mConstants);
+                    KeyValueBackupJob.schedule(mUserId, mContext, mConstants);
                     scheduleNextFullBackupJob(0);
                 } else if (!enable) {
                     // No longer enabled, so stop running backups
                     if (MORE_DEBUG) Slog.i(TAG, "Opting out of backup");
 
-                    KeyValueBackupJob.cancel(mContext);
+                    KeyValueBackupJob.cancel(mUserId, mContext);
 
                     // This also constitutes an opt-out, so we wipe any data for
                     // this device from the backend.  We start that process with
@@ -2918,7 +2906,7 @@
         try {
             int transportUid =
                     mContext.getPackageManager()
-                            .getPackageUid(transportComponent.getPackageName(), 0);
+                            .getPackageUidAsUser(transportComponent.getPackageName(), 0, mUserId);
             if (callingUid != transportUid) {
                 throw new SecurityException("Only the transport can change its description");
             }
@@ -3257,7 +3245,7 @@
             if (packageName != null) {
                 PackageInfo app = null;
                 try {
-                    app = mPackageManager.getPackageInfo(packageName, 0);
+                    app = mPackageManager.getPackageInfoAsUser(packageName, 0, mUserId);
                 } catch (NameNotFoundException nnf) {
                     Slog.w(TAG, "Asked to restore nonexistent pkg " + packageName);
                     throw new IllegalArgumentException("Package " + packageName + " not found");
@@ -3361,7 +3349,7 @@
                     mTransportManager.getCurrentTransportClient(callerLogString);
             boolean eligible =
                     AppBackupUtils.appIsRunningAndEligibleForBackupWithTransport(
-                            transportClient, packageName, mPackageManager);
+                            transportClient, packageName, mPackageManager, mUserId);
             if (transportClient != null) {
                 mTransportManager.disposeOfTransportClient(transportClient, callerLogString);
             }
@@ -3385,7 +3373,7 @@
             for (String packageName : packages) {
                 if (AppBackupUtils
                         .appIsRunningAndEligibleForBackupWithTransport(
-                                transportClient, packageName, mPackageManager)) {
+                                transportClient, packageName, mPackageManager, mUserId)) {
                     eligibleApps.add(packageName);
                 }
             }
@@ -3451,7 +3439,7 @@
             pw.println(isBackupOperationInProgress() ? "Backup in progress" : "No backups running");
             pw.println("Last backup pass started: " + mLastBackupPass
                     + " (now = " + System.currentTimeMillis() + ')');
-            pw.println("  next scheduled: " + KeyValueBackupJob.nextScheduled());
+            pw.println("  next scheduled: " + KeyValueBackupJob.nextScheduled(mUserId));
 
             pw.println("Transport whitelist:");
             for (ComponentName transport : mTransportManager.getTransportWhitelist()) {
diff --git a/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java b/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java
index 2ee96d1..0fb4f93 100644
--- a/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java
+++ b/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java
@@ -633,7 +633,7 @@
             unregisterTask();
 
             if (mJob != null) {
-                mJob.finishBackupPass();
+                mJob.finishBackupPass(backupManagerService.getUserId());
             }
 
             synchronized (backupManagerService.getQueueLock()) {
diff --git a/services/backup/java/com/android/server/backup/internal/SetupObserver.java b/services/backup/java/com/android/server/backup/internal/SetupObserver.java
index 62ae3eb..c5e912e 100644
--- a/services/backup/java/com/android/server/backup/internal/SetupObserver.java
+++ b/services/backup/java/com/android/server/backup/internal/SetupObserver.java
@@ -77,7 +77,8 @@
                 if (MORE_DEBUG) {
                     Slog.d(TAG, "Setup complete so starting backups");
                 }
-                KeyValueBackupJob.schedule(mContext, mUserBackupManagerService.getConstants());
+                KeyValueBackupJob.schedule(mUserBackupManagerService.getUserId(), mContext,
+                        mUserBackupManagerService.getConstants());
                 mUserBackupManagerService.scheduleNextFullBackupJob(0);
             }
         }
diff --git a/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java b/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java
index f39d795..862ca71 100644
--- a/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java
+++ b/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java
@@ -45,7 +45,6 @@
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.SELinux;
-import android.os.UserHandle;
 import android.os.WorkSource;
 
 import com.android.internal.annotations.GuardedBy;
@@ -241,6 +240,7 @@
     private final boolean mUserInitiated;
     private final boolean mNonIncremental;
     private final int mCurrentOpToken;
+    private final int mUserId;
     private final File mStateDirectory;
     private final File mDataDirectory;
     private final File mBlankStateFile;
@@ -320,6 +320,7 @@
         mCurrentOpToken = backupManagerService.generateRandomIntegerToken();
         mQueueLock = mBackupManagerService.getQueueLock();
         mBlankStateFile = new File(mStateDirectory, BLANK_STATE_FILE_NAME);
+        mUserId = backupManagerService.getUserId();
     }
 
     private void registerTask() {
@@ -480,8 +481,8 @@
         final PackageInfo packageInfo;
         try {
             packageInfo =
-                    mPackageManager.getPackageInfo(
-                            packageName, PackageManager.GET_SIGNING_CERTIFICATES);
+                    mPackageManager.getPackageInfoAsUser(
+                            packageName, PackageManager.GET_SIGNING_CERTIFICATES, mUserId);
         } catch (PackageManager.NameNotFoundException e) {
             mReporter.onAgentUnknown(packageName);
             throw AgentException.permanent(e);
@@ -770,8 +771,7 @@
 
     private void writeWidgetPayloadIfAppropriate(FileDescriptor fd, String pkgName)
             throws IOException {
-        // TODO: http://b/22388012
-        byte[] widgetState = AppWidgetBackupBridge.getWidgetState(pkgName, UserHandle.USER_SYSTEM);
+        byte[] widgetState = AppWidgetBackupBridge.getWidgetState(pkgName, mUserId);
         File widgetFile = new File(mStateDirectory, pkgName + "_widget");
         boolean priorStateExists = widgetFile.exists();
         if (!priorStateExists && widgetState == null) {
@@ -1003,7 +1003,7 @@
             // Use the scheduler's default.
             delay = 0;
         }
-        KeyValueBackupJob.schedule(
+        KeyValueBackupJob.schedule(mBackupManagerService.getUserId(),
                 mBackupManagerService.getContext(), delay, mBackupManagerService.getConstants());
 
         for (String packageName : mOriginalQueue) {
diff --git a/services/backup/java/com/android/server/backup/restore/ActiveRestoreSession.java b/services/backup/java/com/android/server/backup/restore/ActiveRestoreSession.java
index e273b32..0fa0f89 100644
--- a/services/backup/java/com/android/server/backup/restore/ActiveRestoreSession.java
+++ b/services/backup/java/com/android/server/backup/restore/ActiveRestoreSession.java
@@ -54,6 +54,7 @@
     private final TransportManager mTransportManager;
     private final String mTransportName;
     private final UserBackupManagerService mBackupManagerService;
+    private final int mUserId;
     @Nullable private final String mPackageName;
     public RestoreSet[] mRestoreSets = null;
     boolean mEnded = false;
@@ -67,6 +68,7 @@
         mPackageName = packageName;
         mTransportManager = backupManagerService.getTransportManager();
         mTransportName = transportName;
+        mUserId = backupManagerService.getUserId();
     }
 
     public void markTimedOut() {
@@ -304,7 +306,8 @@
 
         final PackageInfo app;
         try {
-            app = mBackupManagerService.getPackageManager().getPackageInfo(packageName, 0);
+            app = mBackupManagerService.getPackageManager().getPackageInfoAsUser(
+                    packageName, 0, mUserId);
         } catch (NameNotFoundException nnf) {
             Slog.w(TAG, "Asked to restore nonexistent pkg " + packageName);
             return -1;
diff --git a/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java b/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java
index 45a398f..b3d9fbc 100644
--- a/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java
+++ b/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java
@@ -68,6 +68,8 @@
 public class FullRestoreEngine extends RestoreEngine {
 
     private final UserBackupManagerService mBackupManagerService;
+    private final int mUserId;
+
     // Task in charge of monitoring timeouts
     private final BackupRestoreTask mMonitorTask;
 
@@ -146,6 +148,7 @@
                 backupManagerService.getAgentTimeoutParameters(),
                 "Timeout parameters cannot be null");
         mIsAdbRestore = isAdbRestore;
+        mUserId = backupManagerService.getUserId();
     }
 
     public IBackupAgent getAgent() {
@@ -272,7 +275,7 @@
                                         instream, mBackupManagerService.getContext(),
                                         mDeleteObserver, mManifestSignatures,
                                         mPackagePolicies, info, installerPackageName,
-                                        bytesReadListener);
+                                        bytesReadListener, mUserId);
                                 // good to go; promote to ACCEPT
                                 mPackagePolicies.put(pkg, isSuccessfullyInstalled
                                         ? RestorePolicy.ACCEPT
@@ -329,9 +332,8 @@
                         }
 
                         try {
-                            mTargetApp =
-                                    mBackupManagerService.getPackageManager().getApplicationInfo(
-                                            pkg, 0);
+                            mTargetApp = mBackupManagerService.getPackageManager()
+                                    .getApplicationInfoAsUser(pkg, 0, mUserId);
 
                             // If we haven't sent any data to this app yet, we probably
                             // need to clear it first. Check that.
@@ -684,7 +686,7 @@
         String packageListString = Settings.Secure.getStringForUser(
                 mBackupManagerService.getContext().getContentResolver(),
                 Settings.Secure.PACKAGES_TO_CLEAR_DATA_BEFORE_FULL_RESTORE,
-                mBackupManagerService.getUserId());
+                mUserId);
         if (TextUtils.isEmpty(packageListString)) {
             return false;
         }
diff --git a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
index f7efad6..d01f77b 100644
--- a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
+++ b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
@@ -48,7 +48,6 @@
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.SystemClock;
-import android.os.UserHandle;
 import android.util.EventLog;
 import android.util.Slog;
 
@@ -81,6 +80,7 @@
 public class PerformUnifiedRestoreTask implements BackupRestoreTask {
 
     private UserBackupManagerService backupManagerService;
+    private final int mUserId;
     private final TransportManager mTransportManager;
     // Transport client we're working with to do the restore
     private final TransportClient mTransportClient;
@@ -175,6 +175,7 @@
             @Nullable String[] filterSet,
             OnTaskFinishedListener listener) {
         this.backupManagerService = backupManagerService;
+        mUserId = backupManagerService.getUserId();
         mTransportManager = backupManagerService.getTransportManager();
         mEphemeralOpToken = backupManagerService.generateRandomIntegerToken();
         mState = UnifiedRestoreState.INITIAL;
@@ -204,7 +205,7 @@
                 // We want everything and a pony
                 List<PackageInfo> apps =
                         PackageManagerBackupAgent.getStorableApplications(
-                                backupManagerService.getPackageManager());
+                                backupManagerService.getPackageManager(), mUserId);
                 filterSet = packagesToNames(apps);
                 if (DEBUG) {
                     Slog.i(TAG, "Full restore; asking about " + filterSet.length + " apps");
@@ -221,7 +222,7 @@
             for (int i = 0; i < filterSet.length; i++) {
                 try {
                     PackageManager pm = backupManagerService.getPackageManager();
-                    PackageInfo info = pm.getPackageInfo(filterSet[i], 0);
+                    PackageInfo info = pm.getPackageInfoAsUser(filterSet[i], 0, mUserId);
                     if ("android".equals(info.packageName)) {
                         hasSystem = true;
                         continue;
@@ -240,16 +241,16 @@
             }
             if (hasSystem) {
                 try {
-                    mAcceptSet.add(0,
-                            backupManagerService.getPackageManager().getPackageInfo("android", 0));
+                    mAcceptSet.add(0, backupManagerService.getPackageManager().getPackageInfoAsUser(
+                                    "android", 0, mUserId));
                 } catch (NameNotFoundException e) {
                     // won't happen; we know a priori that it's valid
                 }
             }
             if (hasSettings) {
                 try {
-                    mAcceptSet.add(backupManagerService.getPackageManager().getPackageInfo(
-                            SETTINGS_PACKAGE, 0));
+                    mAcceptSet.add(backupManagerService.getPackageManager().getPackageInfoAsUser(
+                            SETTINGS_PACKAGE, 0, mUserId));
                 } catch (NameNotFoundException e) {
                     // this one is always valid too
                 }
@@ -359,8 +360,7 @@
 
         // If we're starting a full-system restore, set up to begin widget ID remapping
         if (mIsSystemRestore) {
-            // TODO: http://b/22388012
-            AppWidgetBackupBridge.restoreStarting(UserHandle.USER_SYSTEM);
+            AppWidgetBackupBridge.restoreStarting(mUserId);
         }
 
         try {
@@ -508,8 +508,8 @@
             }
 
             try {
-                mCurrentPackage = backupManagerService.getPackageManager().getPackageInfo(
-                        pkgName, PackageManager.GET_SIGNING_CERTIFICATES);
+                mCurrentPackage = backupManagerService.getPackageManager().getPackageInfoAsUser(
+                        pkgName, PackageManager.GET_SIGNING_CERTIFICATES, mUserId);
             } catch (NameNotFoundException e) {
                 // Whoops, we thought we could restore this package but it
                 // turns out not to be present.  Skip it.
@@ -1078,8 +1078,7 @@
         }
 
         // Kick off any work that may be needed regarding app widget restores
-        // TODO: http://b/22388012
-        AppWidgetBackupBridge.restoreFinished(UserHandle.USER_SYSTEM);
+        AppWidgetBackupBridge.restoreFinished(mUserId);
 
         // If this was a full-system restore, record the ancestral
         // dataset information
diff --git a/services/backup/java/com/android/server/backup/transport/TransportClient.java b/services/backup/java/com/android/server/backup/transport/TransportClient.java
index e4dcb25..7c5a57c 100644
--- a/services/backup/java/com/android/server/backup/transport/TransportClient.java
+++ b/services/backup/java/com/android/server/backup/transport/TransportClient.java
@@ -20,6 +20,7 @@
 
 import android.annotation.IntDef;
 import android.annotation.Nullable;
+import android.annotation.UserIdInt;
 import android.annotation.WorkerThread;
 import android.content.ComponentName;
 import android.content.Context;
@@ -34,7 +35,6 @@
 import android.text.format.DateFormat;
 import android.util.ArrayMap;
 import android.util.EventLog;
-import android.util.Slog;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
@@ -79,6 +79,7 @@
     @VisibleForTesting static final String TAG = "TransportClient";
     private static final int LOG_BUFFER_SIZE = 5;
 
+    private final @UserIdInt int mUserId;
     private final Context mContext;
     private final TransportStats mTransportStats;
     private final Intent mBindIntent;
@@ -106,6 +107,7 @@
     private volatile IBackupTransport mTransport;
 
     TransportClient(
+            @UserIdInt int userId,
             Context context,
             TransportStats transportStats,
             Intent bindIntent,
@@ -113,6 +115,7 @@
             String identifier,
             String caller) {
         this(
+                userId,
                 context,
                 transportStats,
                 bindIntent,
@@ -124,6 +127,7 @@
 
     @VisibleForTesting
     TransportClient(
+            @UserIdInt int userId,
             Context context,
             TransportStats transportStats,
             Intent bindIntent,
@@ -131,6 +135,7 @@
             String identifier,
             String caller,
             Handler listenerHandler) {
+        mUserId = userId;
         mContext = context;
         mTransportStats = transportStats;
         mTransportComponent = transportComponent;
@@ -213,7 +218,7 @@
                                     mBindIntent,
                                     mConnection,
                                     Context.BIND_AUTO_CREATE,
-                                    UserHandle.SYSTEM);
+                                    UserHandle.of(mUserId));
                     if (hasBound) {
                         // We don't need to set a time-out because we are guaranteed to get a call
                         // back in ServiceConnection, either an onServiceConnected() or
diff --git a/services/backup/java/com/android/server/backup/transport/TransportClientManager.java b/services/backup/java/com/android/server/backup/transport/TransportClientManager.java
index f4e3928..a4e9b10 100644
--- a/services/backup/java/com/android/server/backup/transport/TransportClientManager.java
+++ b/services/backup/java/com/android/server/backup/transport/TransportClientManager.java
@@ -19,12 +19,15 @@
 import static com.android.server.backup.TransportManager.SERVICE_ACTION_TRANSPORT_HOST;
 import static com.android.server.backup.transport.TransportUtils.formatMessage;
 
+import android.annotation.UserIdInt;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.os.Bundle;
+
 import com.android.server.backup.TransportManager;
 import com.android.server.backup.transport.TransportUtils.Priority;
+
 import java.io.PrintWriter;
 import java.util.Map;
 import java.util.WeakHashMap;
@@ -36,13 +39,16 @@
 public class TransportClientManager {
     private static final String TAG = "TransportClientManager";
 
+    private final @UserIdInt int mUserId;
     private final Context mContext;
     private final TransportStats mTransportStats;
     private final Object mTransportClientsLock = new Object();
     private int mTransportClientsCreated = 0;
     private Map<TransportClient, String> mTransportClientsCallerMap = new WeakHashMap<>();
 
-    public TransportClientManager(Context context, TransportStats transportStats) {
+    public TransportClientManager(@UserIdInt int userId, Context context,
+            TransportStats transportStats) {
+        mUserId = userId;
         mContext = context;
         mTransportStats = transportStats;
     }
@@ -89,6 +95,7 @@
         synchronized (mTransportClientsLock) {
             TransportClient transportClient =
                     new TransportClient(
+                            mUserId,
                             mContext,
                             mTransportStats,
                             bindIntent,
diff --git a/services/backup/java/com/android/server/backup/utils/AppBackupUtils.java b/services/backup/java/com/android/server/backup/utils/AppBackupUtils.java
index e465c7e..054879b 100644
--- a/services/backup/java/com/android/server/backup/utils/AppBackupUtils.java
+++ b/services/backup/java/com/android/server/backup/utils/AppBackupUtils.java
@@ -91,10 +91,13 @@
      * </ol>
      */
     public static boolean appIsRunningAndEligibleForBackupWithTransport(
-            @Nullable TransportClient transportClient, String packageName, PackageManager pm) {
+            @Nullable TransportClient transportClient,
+            String packageName,
+            PackageManager pm,
+            int userId) {
         try {
-            PackageInfo packageInfo = pm.getPackageInfo(packageName,
-                    PackageManager.GET_SIGNING_CERTIFICATES);
+            PackageInfo packageInfo = pm.getPackageInfoAsUser(packageName,
+                    PackageManager.GET_SIGNING_CERTIFICATES, userId);
             ApplicationInfo applicationInfo = packageInfo.applicationInfo;
             if (!appIsEligibleForBackup(applicationInfo, pm)
                     || appIsStopped(applicationInfo)
diff --git a/services/backup/java/com/android/server/backup/utils/RestoreUtils.java b/services/backup/java/com/android/server/backup/utils/RestoreUtils.java
index df7e6d4..cce5b3b 100644
--- a/services/backup/java/com/android/server/backup/utils/RestoreUtils.java
+++ b/services/backup/java/com/android/server/backup/utils/RestoreUtils.java
@@ -72,7 +72,9 @@
             HashMap<String, Signature[]> manifestSignatures,
             HashMap<String, RestorePolicy> packagePolicies,
             FileMetadata info,
-            String installerPackageName, BytesReadListener bytesReadListener) {
+            String installerPackageName,
+            BytesReadListener bytesReadListener,
+            int userId) {
         boolean okay = true;
 
         if (DEBUG) {
@@ -144,8 +146,8 @@
                     uninstall = true;
                 } else {
                     try {
-                        PackageInfo pkg = packageManager.getPackageInfo(info.packageName,
-                                PackageManager.GET_SIGNING_CERTIFICATES);
+                        PackageInfo pkg = packageManager.getPackageInfoAsUser(info.packageName,
+                                PackageManager.GET_SIGNING_CERTIFICATES, userId);
                         if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_ALLOW_BACKUP)
                                 == 0) {
                             Slog.w(TAG, "Restore stream contains apk of package "
diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java
index 09aa421..1dae2ce 100644
--- a/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java
+++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java
@@ -17,6 +17,9 @@
 package com.android.server.contentcapture;
 
 import static android.service.contentcapture.ContentCaptureService.setClientState;
+import static android.view.contentcapture.ContentCaptureSession.STATE_DISABLED;
+import static android.view.contentcapture.ContentCaptureSession.STATE_DUPLICATED_ID;
+import static android.view.contentcapture.ContentCaptureSession.STATE_NO_SERVICE;
 
 import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_CONTENT;
 import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_DATA;
@@ -36,10 +39,11 @@
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.service.contentcapture.ContentCaptureService;
+import android.service.contentcapture.IContentCaptureServiceCallback;
 import android.service.contentcapture.SnapshotData;
 import android.util.ArrayMap;
+import android.util.Log;
 import android.util.Slog;
-import android.view.contentcapture.ContentCaptureSession;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.os.IResultReceiver;
@@ -48,6 +52,7 @@
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
+import java.util.List;
 
 /**
  * Per-user instance of {@link ContentCaptureManagerService}.
@@ -72,6 +77,9 @@
     @GuardedBy("mLock")
     private RemoteContentCaptureService mRemoteService;
 
+    private final ContentCaptureServiceRemoteCallback mRemoteServiceCallback =
+            new ContentCaptureServiceRemoteCallback();
+
     // TODO(b/111276913): add mechanism to prune stale sessions, similar to Autofill's
 
     ContentCapturePerUserService(@NonNull ContentCaptureManagerService master,
@@ -100,10 +108,10 @@
         }
 
         if (!disabled) {
-            mRemoteService = new RemoteContentCaptureService(
-                  mMaster.getContext(),
-                  ContentCaptureService.SERVICE_INTERFACE, serviceComponentName, mUserId, this,
-                  mMaster.isBindInstantServiceAllowed(), mMaster.verbose);
+            mRemoteService = new RemoteContentCaptureService(mMaster.getContext(),
+                    ContentCaptureService.SERVICE_INTERFACE, serviceComponentName,
+                    mRemoteServiceCallback, mUserId, this, mMaster.isBindInstantServiceAllowed(),
+                    mMaster.verbose);
         }
     }
 
@@ -165,7 +173,7 @@
         if (!isEnabledLocked()) {
             // TODO: it would be better to split in differet reasons, like
             // STATE_DISABLED_NO_SERVICE and STATE_DISABLED_BY_DEVICE_POLICY
-            setClientState(clientReceiver, ContentCaptureSession.STATE_DISABLED_NO_SERVICE,
+            setClientState(clientReceiver, STATE_DISABLED | STATE_NO_SERVICE,
                     /* binder= */ null);
             return;
         }
@@ -184,7 +192,7 @@
         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,
+            setClientState(clientReceiver, STATE_DISABLED | STATE_DUPLICATED_ID,
                     /* binder=*/ null);
             return;
         }
@@ -197,8 +205,7 @@
             // TODO(b/119613670): log metrics
             Slog.w(TAG, "startSession(id=" + existingSession + ", token=" + activityToken
                     + ": ignoring because service is not set");
-            // TODO(b/111276913): use a new disabled state?
-            setClientState(clientReceiver, ContentCaptureSession.STATE_DISABLED_NO_SERVICE,
+            setClientState(clientReceiver, STATE_DISABLED | STATE_NO_SERVICE,
                     /* binder= */ null);
             return;
         }
@@ -338,4 +345,50 @@
         }
         return null;
     }
+
+    private final class ContentCaptureServiceRemoteCallback extends
+            IContentCaptureServiceCallback.Stub {
+
+        @Override
+        public void setContentCaptureWhitelist(List<String> packages,
+                List<ComponentName> activities) {
+            if (mMaster.verbose) {
+                Log.v(TAG, "setContentCaptureWhitelist(packages=" + packages + ", activities="
+                        + activities + ")");
+            }
+            // TODO(b/122595322): implement
+            // TODO(b/119613670): log metrics
+        }
+
+        @Override
+        public void setActivityContentCaptureEnabled(ComponentName activity, boolean enabled) {
+            if (mMaster.verbose) {
+                Log.v(TAG, "setActivityContentCaptureEnabled(activity=" + activity + ", enabled="
+                        + enabled + ")");
+            }
+            // TODO(b/122595322): implement
+            // TODO(b/119613670): log metrics
+        }
+
+        @Override
+        public void setPackageContentCaptureEnabled(String packageName, boolean enabled) {
+            if (mMaster.verbose) {
+                Log.v(TAG,
+                        "setPackageContentCaptureEnabled(packageName=" + packageName + ", enabled="
+                                + enabled + ")");
+            }
+            // TODO(b/122595322): implement
+            // TODO(b/119613670): log metrics
+        }
+
+        @Override
+        public void getContentCaptureDisabledActivities(IResultReceiver receiver) {
+            // TODO(b/122595322): implement
+        }
+
+        @Override
+        public void getContentCaptureDisabledPackages(IResultReceiver receiver) {
+            // TODO(b/122595322): implement
+        }
+    }
 }
diff --git a/services/contentcapture/java/com/android/server/contentcapture/RemoteContentCaptureService.java b/services/contentcapture/java/com/android/server/contentcapture/RemoteContentCaptureService.java
index 8241628..12742ca 100644
--- a/services/contentcapture/java/com/android/server/contentcapture/RemoteContentCaptureService.java
+++ b/services/contentcapture/java/com/android/server/contentcapture/RemoteContentCaptureService.java
@@ -21,6 +21,7 @@
 import android.content.Context;
 import android.os.IBinder;
 import android.service.contentcapture.IContentCaptureService;
+import android.service.contentcapture.IContentCaptureServiceCallback;
 import android.service.contentcapture.SnapshotData;
 import android.text.format.DateUtils;
 import android.util.Slog;
@@ -35,12 +36,15 @@
 
     private static final long TIMEOUT_REMOTE_REQUEST_MILLIS = 2 * DateUtils.SECOND_IN_MILLIS;
 
+    private final IBinder mServerCallback;
+
     RemoteContentCaptureService(Context context, String serviceInterface,
-            ComponentName componentName, int userId,
+            ComponentName serviceComponentName, IContentCaptureServiceCallback callback, int userId,
             ContentCaptureServiceCallbacks callbacks, boolean bindInstantServiceAllowed,
             boolean verbose) {
-        super(context, serviceInterface, componentName, userId, callbacks,
+        super(context, serviceInterface, serviceComponentName, userId, callbacks,
                 bindInstantServiceAllowed, verbose, /* initialCapacity= */ 2);
+        mServerCallback = callback.asBinder();
 
         // Bind right away, which will trigger a onConnected() on service's
         scheduleBind();
@@ -69,7 +73,11 @@
             scheduleUnbind();
         }
         try {
-            mService.onConnectedStateChanged(state);
+            if (state) {
+                mService.onConnected(mServerCallback);
+            } else {
+                mService.onDisconnected();
+            }
         } catch (Exception e) {
             Slog.w(mTag, "Exception calling onConnectedStateChanged(" + state + "): " + e);
         }
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index d0666b9..d6f3e2b 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -4879,7 +4879,7 @@
         final NetworkCapabilities nc = new NetworkCapabilities(networkCapabilities);
         final NetworkAgentInfo nai = new NetworkAgentInfo(messenger, new AsyncChannel(),
                 new Network(reserveNetId()), new NetworkInfo(networkInfo), lp, nc, currentScore,
-                mContext, mTrackerHandler, new NetworkMisc(networkMisc), this);
+                mContext, mTrackerHandler, new NetworkMisc(networkMisc), this, mNetd, mNMS);
         // Make sure the network capabilities reflect what the agent info says.
         nai.networkCapabilities = mixInCapabilities(nai, nc);
         final String extraInfo = networkInfo.getExtraInfo();
diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java
index 869d564..0b4c01e 100644
--- a/services/core/java/com/android/server/LocationManagerService.java
+++ b/services/core/java/com/android/server/LocationManagerService.java
@@ -257,6 +257,9 @@
         packageManagerInternal.setLocationPackagesProvider(
                 userId -> mContext.getResources().getStringArray(
                         com.android.internal.R.array.config_locationProviderPackageNames));
+        packageManagerInternal.setLocationExtraPackagesProvider(
+                userId -> mContext.getResources().getStringArray(
+                      com.android.internal.R.array.config_locationExtraPackageNames));
 
         // most startup is deferred until systemRunning()
     }
@@ -1041,12 +1044,13 @@
 
         @Override
         public void onSetProperties(ProviderProperties properties) {
-            // move calls coming from below LMS onto a different thread to avoid deadlock
-            runInternal(() -> {
-                synchronized (mLock) {
-                    mProperties = properties;
-                }
-            });
+            // because this does not invoke any other methods which might result in calling back
+            // into the location provider, it is safe to run this on the calling thread. it is also
+            // currently necessary to run this on the calling thread to ensure that property changes
+            // are publicly visibly immediately, ie for mock providers which are created.
+            synchronized (mLock) {
+                mProperties = properties;
+            }
         }
 
         @GuardedBy("mLock")
diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index b0ca2df..aed0684 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -17,13 +17,11 @@
 package com.android.server;
 
 import static android.Manifest.permission.CONNECTIVITY_INTERNAL;
-import static android.Manifest.permission.DUMP;
 import static android.Manifest.permission.NETWORK_SETTINGS;
 import static android.Manifest.permission.NETWORK_STACK;
 import static android.Manifest.permission.SHUTDOWN;
 import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_DOZABLE;
 import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_DOZABLE;
-import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_NONE;
 import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_POWERSAVE;
 import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_STANDBY;
 import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NONE;
@@ -40,6 +38,7 @@
 import static android.net.NetworkStats.TAG_NONE;
 import static android.net.NetworkStats.UID_ALL;
 import static android.net.TrafficStats.UID_TETHERING;
+
 import static com.android.server.NetworkManagementService.NetdResponseCode.ClatdStatusResult;
 import static com.android.server.NetworkManagementService.NetdResponseCode.InterfaceGetCfgResult;
 import static com.android.server.NetworkManagementService.NetdResponseCode.InterfaceListResult;
@@ -53,11 +52,9 @@
 
 import android.annotation.NonNull;
 import android.app.ActivityManager;
-import android.content.ContentResolver;
 import android.content.Context;
 import android.net.ConnectivityManager;
 import android.net.INetd;
-import android.net.TetherStatsParcel;
 import android.net.INetworkManagementEventObserver;
 import android.net.ITetheringStatsProvider;
 import android.net.InterfaceConfiguration;
@@ -69,18 +66,15 @@
 import android.net.NetworkStats;
 import android.net.NetworkUtils;
 import android.net.RouteInfo;
+import android.net.TetherStatsParcel;
 import android.net.UidRange;
-import android.net.UidRangeParcel;
 import android.net.util.NetdService;
-import android.net.wifi.WifiConfiguration;
-import android.net.wifi.WifiConfiguration.KeyMgmt;
 import android.os.BatteryStats;
 import android.os.Binder;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.INetworkActivityListener;
 import android.os.INetworkManagementService;
-import android.os.PersistableBundle;
 import android.os.PowerManager;
 import android.os.Process;
 import android.os.RemoteCallbackList;
@@ -91,12 +85,7 @@
 import android.os.SystemClock;
 import android.os.SystemProperties;
 import android.os.Trace;
-import android.provider.Settings;
 import android.telephony.DataConnectionRealTimeInfo;
-import android.telephony.PhoneStateListener;
-import android.telephony.SubscriptionManager;
-import android.telephony.TelephonyManager;
-import android.text.TextUtils;
 import android.util.Log;
 import android.util.Slog;
 import android.util.SparseBooleanArray;
@@ -110,13 +99,11 @@
 import com.android.internal.util.DumpUtils;
 import com.android.internal.util.HexDump;
 import com.android.internal.util.Preconditions;
-import com.android.server.NativeDaemonConnector.Command;
-import com.android.server.NativeDaemonConnector.SensitiveArg;
+
 import com.google.android.collect.Maps;
 
 import java.io.BufferedReader;
 import java.io.DataInputStream;
-import java.io.File;
 import java.io.FileDescriptor;
 import java.io.FileInputStream;
 import java.io.IOException;
@@ -124,15 +111,11 @@
 import java.io.PrintWriter;
 import java.net.InetAddress;
 import java.net.InterfaceAddress;
-import java.net.NetworkInterface;
-import java.net.SocketException;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
-import java.util.NoSuchElementException;
-import java.util.StringTokenizer;
 import java.util.concurrent.CountDownLatch;
 
 /**
@@ -2153,28 +2136,6 @@
     }
 
     @Override
-    public void startClatd(String interfaceName) throws IllegalStateException {
-        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
-
-        try {
-            mNetdService.clatdStart(interfaceName);
-        } catch (RemoteException | ServiceSpecificException e) {
-            throw new IllegalStateException(e);
-        }
-    }
-
-    @Override
-    public void stopClatd(String interfaceName) throws IllegalStateException {
-        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
-
-        try {
-            mNetdService.clatdStop(interfaceName);
-        } catch (RemoteException | ServiceSpecificException e) {
-            throw new IllegalStateException(e);
-        }
-    }
-
-    @Override
     public void registerNetworkActivityListener(INetworkActivityListener listener) {
         mNetworkActivityListeners.register(listener);
     }
diff --git a/services/core/java/com/android/server/RescueParty.java b/services/core/java/com/android/server/RescueParty.java
index a9f190c..62da3f8 100644
--- a/services/core/java/com/android/server/RescueParty.java
+++ b/services/core/java/com/android/server/RescueParty.java
@@ -34,9 +34,9 @@
 import android.util.MathUtils;
 import android.util.Slog;
 import android.util.SparseArray;
+import android.util.StatsLog;
 
 import com.android.internal.util.ArrayUtils;
-import com.android.server.pm.PackageManagerService;
 
 import java.io.File;
 
@@ -179,6 +179,7 @@
     }
 
     private static void executeRescueLevelInternal(Context context, int level) throws Exception {
+        StatsLog.write(StatsLog.RESCUE_PARTY_RESET_REPORTED, level);
         switch (level) {
             case LEVEL_RESET_SETTINGS_UNTRUSTED_DEFAULTS:
                 resetAllSettings(context, Settings.RESET_MODE_UNTRUSTED_DEFAULTS);
diff --git a/services/core/java/com/android/server/TEST_MAPPING b/services/core/java/com/android/server/TEST_MAPPING
index 93e1dd3..16b12f1 100644
--- a/services/core/java/com/android/server/TEST_MAPPING
+++ b/services/core/java/com/android/server/TEST_MAPPING
@@ -7,7 +7,7 @@
                   "include-annotation": "android.platform.test.annotations.Presubmit"
                 },
                 {
-                  "exclude-annotation": "android.support.test.filters.FlakyTest"
+                  "exclude-annotation": "androidx.test.filters.FlakyTest"
                 }
             ]
         }
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index bab9a65..a96676e 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -516,7 +516,7 @@
                 }
                 // This app knows it is in the new model where this operation is not
                 // allowed, so tell it what has happened.
-                UidRecord uidRec = mAm.mActiveUids.get(r.appInfo.uid);
+                UidRecord uidRec = mAm.mProcessList.getUidRecordLocked(r.appInfo.uid);
                 return new ComponentName("?", "app is in background uid " + uidRec);
             }
         }
@@ -739,7 +739,7 @@
 
     private void stopServiceLocked(ServiceRecord service) {
         if (service.delayed) {
-            // If service isn't actually running, but is is being held in the
+            // If service isn't actually running, but is being held in the
             // delayed list, then we need to keep it started but note that it
             // should be stopped once no longer delayed.
             if (DEBUG_DELAYED_STARTS) Slog.v(TAG_SERVICE, "Delaying stop of pending: " + service);
diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java
index 1c04a94..dd2b33a 100644
--- a/services/core/java/com/android/server/am/ActivityManagerConstants.java
+++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java
@@ -70,6 +70,12 @@
     static final String KEY_MEMORY_INFO_THROTTLE_TIME = "memory_info_throttle_time";
     static final String KEY_TOP_TO_FGS_GRACE_DURATION = "top_to_fgs_grace_duration";
     static final String KEY_USE_COMPACTION = "use_compaction";
+    static final String KEY_COMPACT_ACTION_1 = "compact_action_1";
+    static final String KEY_COMPACT_ACTION_2 = "compact_action_2";
+    static final String KEY_COMPACT_THROTTLE_1 = "compact_throttle_1";
+    static final String KEY_COMPACT_THROTTLE_2 = "compact_throttle_2";
+    static final String KEY_COMPACT_THROTTLE_3 = "compact_throttle_3";
+    static final String KEY_COMPACT_THROTTLE_4 = "compact_throttle_4";
 
     private static final int DEFAULT_MAX_CACHED_PROCESSES = 32;
     private static final long DEFAULT_BACKGROUND_SETTLE_TIME = 60*1000;
@@ -101,6 +107,12 @@
     private static final long DEFAULT_MEMORY_INFO_THROTTLE_TIME = 5*60*1000;
     private static final long DEFAULT_TOP_TO_FGS_GRACE_DURATION = 15 * 1000;
     private static final boolean DEFAULT_USE_COMPACTION = false;
+    public static final int DEFAULT_COMPACT_ACTION_1 = 1;
+    public static final int DEFAULT_COMPACT_ACTION_2 = 3;
+    public static final long DEFAULT_COMPACT_THROTTLE_1 = 5000;
+    public static final long DEFAULT_COMPACT_THROTTLE_2 = 10000;
+    public static final long DEFAULT_COMPACT_THROTTLE_3 = 500;
+    public static final long DEFAULT_COMPACT_THROTTLE_4 = 10000;
 
     // Maximum number of cached processes we will allow.
     public int MAX_CACHED_PROCESSES = DEFAULT_MAX_CACHED_PROCESSES;
@@ -223,6 +235,20 @@
     // Use compaction for background apps.
     public boolean USE_COMPACTION = DEFAULT_USE_COMPACTION;
 
+    // Action for compactAppSome.
+    public int COMPACT_ACTION_1 = DEFAULT_COMPACT_ACTION_1;
+    // Action for compactAppFull;
+    public int COMPACT_ACTION_2 = DEFAULT_COMPACT_ACTION_2;
+
+    // How long we'll skip second compactAppSome after first compactAppSome
+    public long COMPACT_THROTTLE_1 = DEFAULT_COMPACT_THROTTLE_1;
+    // How long we'll skip compactAppSome after compactAppFull
+    public long COMPACT_THROTTLE_2 = DEFAULT_COMPACT_THROTTLE_2;
+    // How long we'll skip compactAppFull after compactAppSome
+    public long COMPACT_THROTTLE_3 = DEFAULT_COMPACT_THROTTLE_3;
+    // How long we'll skip second compactAppFull after first compactAppFull
+    public long COMPACT_THROTTLE_4 = DEFAULT_COMPACT_THROTTLE_4;
+
     // Indicates whether the activity starts logging is enabled.
     // Controlled by Settings.Global.ACTIVITY_STARTS_LOGGING_ENABLED
     volatile boolean mFlagActivityStartsLoggingEnabled;
@@ -381,6 +407,12 @@
             TOP_TO_FGS_GRACE_DURATION = mParser.getDurationMillis(KEY_TOP_TO_FGS_GRACE_DURATION,
                     DEFAULT_TOP_TO_FGS_GRACE_DURATION);
             USE_COMPACTION = mParser.getBoolean(KEY_USE_COMPACTION, DEFAULT_USE_COMPACTION);
+            COMPACT_ACTION_1 = mParser.getInt(KEY_COMPACT_ACTION_1, DEFAULT_COMPACT_ACTION_1);
+            COMPACT_ACTION_2 = mParser.getInt(KEY_COMPACT_ACTION_2, DEFAULT_COMPACT_ACTION_2);
+            COMPACT_THROTTLE_1 = mParser.getLong(KEY_COMPACT_THROTTLE_1, DEFAULT_COMPACT_THROTTLE_1);
+            COMPACT_THROTTLE_2 = mParser.getLong(KEY_COMPACT_THROTTLE_2, DEFAULT_COMPACT_THROTTLE_2);
+            COMPACT_THROTTLE_3 = mParser.getLong(KEY_COMPACT_THROTTLE_3, DEFAULT_COMPACT_THROTTLE_3);
+            COMPACT_THROTTLE_4 = mParser.getLong(KEY_COMPACT_THROTTLE_4, DEFAULT_COMPACT_THROTTLE_4);
 
             updateMaxCachedProcesses();
         }
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 26141f7..089847d 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -24,8 +24,6 @@
 import static android.Manifest.permission.REMOVE_TASKS;
 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;
@@ -34,6 +32,7 @@
 import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
 import static android.content.pm.ApplicationInfo.HIDDEN_API_ENFORCEMENT_DEFAULT;
 import static android.content.pm.PackageManager.GET_PROVIDERS;
+import static android.content.pm.PackageManager.MATCH_ALL;
 import static android.content.pm.PackageManager.MATCH_ANY_USER;
 import static android.content.pm.PackageManager.MATCH_DEBUG_TRIAGED_MISSING;
 import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE;
@@ -59,16 +58,11 @@
 import static android.os.Process.PROC_SPACE_TERM;
 import static android.os.Process.ROOT_UID;
 import static android.os.Process.SCHED_FIFO;
-import static android.os.Process.SCHED_OTHER;
 import static android.os.Process.SCHED_RESET_ON_FORK;
 import static android.os.Process.SE_UID;
 import static android.os.Process.SHELL_UID;
 import static android.os.Process.SIGNAL_USR1;
 import static android.os.Process.SYSTEM_UID;
-import static android.os.Process.THREAD_GROUP_BG_NONINTERACTIVE;
-import static android.os.Process.THREAD_GROUP_DEFAULT;
-import static android.os.Process.THREAD_GROUP_RESTRICTED;
-import static android.os.Process.THREAD_GROUP_TOP_APP;
 import static android.os.Process.THREAD_PRIORITY_FOREGROUND;
 import static android.os.Process.getTotalMemory;
 import static android.os.Process.isThreadInProcess;
@@ -79,7 +73,6 @@
 import static android.os.Process.readProcFile;
 import static android.os.Process.removeAllProcessGroups;
 import static android.os.Process.sendSignal;
-import static android.os.Process.setProcessGroup;
 import static android.os.Process.setThreadPriority;
 import static android.os.Process.setThreadScheduler;
 import static android.os.Process.zygoteProcess;
@@ -96,11 +89,9 @@
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BROADCAST;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BROADCAST_BACKGROUND;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BROADCAST_LIGHT;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_LRU;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_MU;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_NETWORK;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_OOM_ADJ;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_OOM_ADJ_REASON;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PERMISSIONS_REVIEW;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_POWER;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PROCESSES;
@@ -109,7 +100,6 @@
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PSS;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_SERVICE;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_UID_OBSERVERS;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_USAGE_STATS;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_WHITELISTS;
 import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_BACKUP;
 import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_BROADCAST;
@@ -329,7 +319,6 @@
 import com.android.internal.util.function.QuadFunction;
 import com.android.internal.util.function.TriFunction;
 import com.android.server.AlarmManagerInternal;
-import com.android.server.appop.AppOpsService;
 import com.android.server.AttributeCache;
 import com.android.server.DeviceIdleController;
 import com.android.server.DisplayThread;
@@ -348,6 +337,7 @@
 import com.android.server.Watchdog;
 import com.android.server.am.ActivityManagerServiceDumpProcessesProto.UidObserverRegistrationProto;
 import com.android.server.am.MemoryStatUtil.MemoryStat;
+import com.android.server.appop.AppOpsService;
 import com.android.server.firewall.IntentFirewall;
 import com.android.server.job.JobSchedulerInternal;
 import com.android.server.pm.Installer;
@@ -404,7 +394,7 @@
     public static final int TOP_APP_PRIORITY_BOOST = -10;
 
     static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityManagerService" : TAG_AM;
-    private static final String TAG_BACKUP = TAG + POSTFIX_BACKUP;
+    static final String TAG_BACKUP = TAG + POSTFIX_BACKUP;
     private static final String TAG_BROADCAST = TAG + POSTFIX_BROADCAST;
     private static final String TAG_CLEANUP = TAG + POSTFIX_CLEANUP;
     private static final String TAG_CONFIGURATION = TAG + POSTFIX_CONFIGURATION;
@@ -412,9 +402,9 @@
     static final String TAG_LRU = TAG + POSTFIX_LRU;
     private static final String TAG_MU = TAG + POSTFIX_MU;
     private static final String TAG_NETWORK = TAG + POSTFIX_NETWORK;
-    private static final String TAG_OOM_ADJ = TAG + POSTFIX_OOM_ADJ;
+    static final String TAG_OOM_ADJ = TAG + POSTFIX_OOM_ADJ;
     private static final String TAG_POWER = TAG + POSTFIX_POWER;
-    private static final String TAG_PROCESS_OBSERVERS = TAG + POSTFIX_PROCESS_OBSERVERS;
+    static final String TAG_PROCESS_OBSERVERS = TAG + POSTFIX_PROCESS_OBSERVERS;
     static final String TAG_PROCESSES = TAG + POSTFIX_PROCESSES;
     private static final String TAG_PROVIDER = TAG + POSTFIX_PROVIDER;
     static final String TAG_PSS = TAG + POSTFIX_PSS;
@@ -541,6 +531,8 @@
 
     private static final int NATIVE_DUMP_TIMEOUT_MS = 2000; // 2 seconds;
 
+    final OomAdjuster mOomAdjuster;
+
     /** All system services */
     SystemServiceManager mSystemServiceManager;
 
@@ -555,7 +547,7 @@
     public OomAdjProfiler mOomAdjProfiler = new OomAdjProfiler();
 
     // Whether we should use SCHED_FIFO for UI and RenderThreads.
-    private boolean mUseFifoUiScheduling = false;
+    boolean mUseFifoUiScheduling = false;
 
     BroadcastQueue mFgBroadcastQueue;
     BroadcastQueue mBgBroadcastQueue;
@@ -654,11 +646,6 @@
     final ProcessStatsService mProcessStats;
 
     /**
-     * Service for compacting background apps.
-     */
-    final AppCompactor mAppCompact;
-
-    /**
      * Non-persistent appId whitelist for background restrictions
      */
     int[] mBackgroundAppIdWhitelist = new int[] {
@@ -672,9 +659,47 @@
 
     /**
      * When an app has restrictions on the other apps that can have associations with it,
-     * it appears here with a set of the allowed apps.
+     * it appears here with a set of the allowed apps and also track debuggability of the app.
      */
-    ArrayMap<String, ArraySet<String>> mAllowedAssociations;
+    ArrayMap<String, PackageAssociationInfo> mAllowedAssociations;
+
+    /**
+     * Tracks association information for a particular package along with debuggability.
+     * <p> Associations for a package A are allowed to package B if B is part of the
+     *     allowed associations for A or if A is debuggable.
+     */
+    private final class PackageAssociationInfo {
+        private final String mSourcePackage;
+        private final ArraySet<String> mAllowedPackageAssociations;
+        private boolean mIsDebuggable;
+
+        PackageAssociationInfo(String sourcePackage, ArraySet<String> allowedPackages,
+                boolean isDebuggable) {
+            mSourcePackage = sourcePackage;
+            mAllowedPackageAssociations = allowedPackages;
+            mIsDebuggable = isDebuggable;
+        }
+
+        /**
+         * Returns true if {@code mSourcePackage} is allowed association with
+         * {@code targetPackage}.
+         */
+        boolean isPackageAssociationAllowed(String targetPackage) {
+            return mIsDebuggable || mAllowedPackageAssociations.contains(targetPackage);
+        }
+
+        boolean isDebuggable() {
+            return mIsDebuggable;
+        }
+
+        void setDebuggable(boolean isDebuggable) {
+            mIsDebuggable = isDebuggable;
+        }
+
+        ArraySet<String> getAllowedPackageAssociations() {
+            return mAllowedPackageAssociations;
+        }
+    }
 
     /**
      * All of the processes we currently have running organized by pid.
@@ -821,8 +846,6 @@
      */
     boolean mFullPssPending = false;
 
-    /** Track all uids that have actively running processes. */
-    final ActiveUids mActiveUids;
 
     /**
      * This is for verifying the UID report flow.
@@ -936,8 +959,8 @@
     /**
      * Backup/restore process management
      */
-    String mBackupAppName = null;
-    BackupRecord mBackupTarget = null;
+    @GuardedBy("this")
+    final SparseArray<BackupRecord> mBackupTargets = new SparseArray<>();
 
     final ProviderMap mProviderMap;
 
@@ -1104,32 +1127,7 @@
     /**
      * State of external calls telling us if the device is awake or asleep.
      */
-    private int mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE;
-
-    /**
-     * Current sequence id for oom_adj computation traversal.
-     */
-    int mAdjSeq = 0;
-
-    /**
-     * Keep track of the non-cached/empty process we last found, to help
-     * determine how to distribute cached/empty processes next time.
-     */
-    int mNumNonCachedProcs = 0;
-
-    /**
-     * Keep track of the number of cached hidden procs, to balance oom adj
-     * distribution between those and empty procs.
-     */
-    int mNumCachedHiddenProcs = 0;
-
-    /**
-     * Keep track of the number of service processes we last found, to
-     * determine on the next iteration which should be B services.
-     */
-    int mNumServiceProcs = 0;
-    int mNewNumAServiceProcs = 0;
-    int mNewNumServiceProcs = 0;
+    int mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE;
 
     /**
      * Allow the current computed overall memory level of the system to go down?
@@ -1256,10 +1254,6 @@
     String mTrackAllocationApp = null;
     String mNativeDebuggingApp = null;
 
-    final long[] mTmpLong = new long[3];
-
-    private final ArraySet<BroadcastQueue> mTmpBroadcastQueue = new ArraySet();
-
     private final Injector mInjector;
 
     static final class ProcessChangeItem {
@@ -1334,6 +1328,7 @@
         }
     }
 
+    // TODO: Move below 4 members and code to ProcessList
     final RemoteCallbackList<IProcessObserver> mProcessObservers = new RemoteCallbackList<>();
     ProcessChangeItem[] mActiveProcessChanges = new ProcessChangeItem[5];
 
@@ -2225,12 +2220,15 @@
         mUiContext = null;
         mAppErrors = null;
         mPackageWatchdog = null;
-        mActiveUids = new ActiveUids(this, false /* postChangesToAtm */);
         mAppOpsService = mInjector.getAppOpsService(null /* file */, null /* handler */);
         mBatteryStatsService = null;
         mHandler = hasHandlerThread ? new MainHandler(handlerThread.getLooper()) : null;
         mHandlerThread = handlerThread;
         mConstants = hasHandlerThread ? new ActivityManagerConstants(this, mHandler) : null;
+        final ActiveUids activeUids = new ActiveUids(this, false /* postChangesToAtm */);
+        mProcessList.init(this, activeUids);
+        mOomAdjuster = new OomAdjuster(this, mProcessList, activeUids);
+
         mIntentFirewall = hasHandlerThread
                 ? new IntentFirewall(new IntentFirewallInterface(), mHandler) : null;
         mProcessCpuThread = null;
@@ -2246,7 +2244,6 @@
                 ? new PendingIntentController(handlerThread.getLooper(), mUserController) : null;
         mProcStartHandlerThread = null;
         mProcStartHandler = null;
-        mAppCompact = null;
         mHiddenApiBlacklist = null;
         mFactoryTest = FACTORY_TEST_OFF;
     }
@@ -2276,8 +2273,9 @@
         mProcStartHandler = new Handler(mProcStartHandlerThread.getLooper());
 
         mConstants = new ActivityManagerConstants(this, mHandler);
-
-        mProcessList.init(this);
+        final ActiveUids activeUids = new ActiveUids(this, true /* postChangesToAtm */);
+        mProcessList.init(this, activeUids);
+        mOomAdjuster = new OomAdjuster(this, mProcessList, activeUids);
 
         mFgBroadcastQueue = new BroadcastQueue(this, mHandler,
                 "foreground", BROADCAST_FG_TIMEOUT, false);
@@ -2293,7 +2291,6 @@
         mProviderMap = new ProviderMap(this);
         mPackageWatchdog = PackageWatchdog.getInstance(mUiContext);
         mAppErrors = new AppErrors(mUiContext, this, mPackageWatchdog);
-        mActiveUids = new ActiveUids(this, true /* postChangesToAtm */);
 
         final File systemDir = SystemServiceManager.ensureSystemDir();
 
@@ -2330,8 +2327,6 @@
                 DisplayThread.get().getLooper());
         mAtmInternal = LocalServices.getService(ActivityTaskManagerInternal.class);
 
-        mAppCompact = new AppCompactor(this);
-
         mProcessCpuThread = new Thread("CpuTracker") {
             @Override
             public void run() {
@@ -2378,7 +2373,8 @@
         try {
             Process.setThreadGroupAndCpuset(BackgroundThread.get().getThreadId(),
                     Process.THREAD_GROUP_SYSTEM);
-            Process.setThreadGroupAndCpuset(mAppCompact.mCompactionThread.getThreadId(),
+            Process.setThreadGroupAndCpuset(
+                    mOomAdjuster.mAppCompact.mCompactionThread.getThreadId(),
                     Process.THREAD_GROUP_SYSTEM);
         } catch (Exception e) {
             Slog.w(TAG, "Setting background thread cpuset failed");
@@ -2435,12 +2431,10 @@
      * If it does not, give it an empty set.
      */
     void requireAllowedAssociationsLocked(String packageName) {
-        if (mAllowedAssociations == null) {
-            mAllowedAssociations = new ArrayMap<>(
-                    SystemConfig.getInstance().getAllowedAssociations());
-        }
+        ensureAllowedAssociations();
         if (mAllowedAssociations.get(packageName) == null) {
-            mAllowedAssociations.put(packageName, new ArraySet<>());
+            mAllowedAssociations.put(packageName, new PackageAssociationInfo(packageName,
+                    new ArraySet<>(), /* isDebuggable = */ false));
         }
     }
 
@@ -2451,10 +2445,7 @@
      * association is implicitly allowed.
      */
     boolean validateAssociationAllowedLocked(String pkg1, int uid1, String pkg2, int uid2) {
-        if (mAllowedAssociations == null) {
-            mAllowedAssociations = new ArrayMap<>(
-                    SystemConfig.getInstance().getAllowedAssociations());
-        }
+        ensureAllowedAssociations();
         // Interactions with the system uid are always allowed, since that is the core system
         // that everyone needs to be able to interact with. Also allow reflexive associations
         // within the same uid.
@@ -2462,24 +2453,57 @@
                 || UserHandle.getAppId(uid2) == SYSTEM_UID) {
             return true;
         }
-        // We won't allow this association if either pkg1 or pkg2 has a limit on the
-        // associations that are allowed with it, and the other package is not explicitly
-        // specified as one of those associations.
-        ArraySet<String> pkgs = mAllowedAssociations.get(pkg1);
-        if (pkgs != null) {
-            if (!pkgs.contains(pkg2)) {
-                return false;
-            }
+
+        // Check for association on both source and target packages.
+        PackageAssociationInfo pai = mAllowedAssociations.get(pkg1);
+        if (pai != null && !pai.isPackageAssociationAllowed(pkg2)) {
+            return false;
         }
-        pkgs = mAllowedAssociations.get(pkg2);
-        if (pkgs != null) {
-            return pkgs.contains(pkg1);
+        pai = mAllowedAssociations.get(pkg2);
+        if (pai != null && !pai.isPackageAssociationAllowed(pkg1)) {
+            return false;
         }
         // If no explicit associations are provided in the manifest, then assume the app is
         // allowed associations with any package.
         return true;
     }
 
+    /** Sets up allowed associations for system prebuilt packages from system config (if needed). */
+    private void ensureAllowedAssociations() {
+        if (mAllowedAssociations == null) {
+            ArrayMap<String, ArraySet<String>> allowedAssociations =
+                    SystemConfig.getInstance().getAllowedAssociations();
+            mAllowedAssociations = new ArrayMap<>(allowedAssociations.size());
+            PackageManagerInternal pm = getPackageManagerInternalLocked();
+            for (int i = 0; i < allowedAssociations.size(); i++) {
+                final String pkg = allowedAssociations.keyAt(i);
+                final ArraySet<String> asc = allowedAssociations.valueAt(i);
+
+                // Query latest debuggable flag from package-manager.
+                boolean isDebuggable = false;
+                try {
+                    ApplicationInfo ai = AppGlobals.getPackageManager()
+                            .getApplicationInfo(pkg, MATCH_ALL, 0);
+                    if (ai != null) {
+                        isDebuggable = (ai.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
+                    }
+                } catch (RemoteException e) {
+                    /* ignore */
+                }
+                mAllowedAssociations.put(pkg, new PackageAssociationInfo(pkg, asc, isDebuggable));
+            }
+        }
+    }
+
+    /** Updates allowed associations for app info (specifically, based on debuggability).  */
+    private void updateAssociationForApp(ApplicationInfo appInfo) {
+        ensureAllowedAssociations();
+        PackageAssociationInfo pai = mAllowedAssociations.get(appInfo.packageName);
+        if (pai != null) {
+            pai.setDebuggable((appInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0);
+        }
+    }
+
     @Override
     public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
             throws RemoteException {
@@ -4408,6 +4432,7 @@
         mProcessList.removeProcessLocked(app, false, true, "timeout publishing content providers");
     }
 
+    @GuardedBy("this")
     private final void processStartTimedOutLocked(ProcessRecord app) {
         final int pid = app.pid;
         boolean gone = mPidsSelfLocked.removeIfNoThread(pid);
@@ -4428,7 +4453,8 @@
                 mBatteryStatsService.removeIsolatedUid(app.uid, app.info.uid);
             }
             removeLruProcessLocked(app);
-            if (mBackupTarget != null && mBackupTarget.app.pid == pid) {
+            final BackupRecord backupTarget = mBackupTargets.get(app.userId);
+            if (backupTarget != null && backupTarget.app.pid == pid) {
                 Slog.w(TAG, "Unattached app died before backup, skipping");
                 mHandler.post(new Runnable() {
                 @Override
@@ -4436,7 +4462,7 @@
                         try {
                             IBackupManager bm = IBackupManager.Stub.asInterface(
                                     ServiceManager.getService(Context.BACKUP_SERVICE));
-                            bm.agentDisconnected(app.info.packageName);
+                            bm.agentDisconnectedForUser(app.userId, app.info.packageName);
                         } catch (RemoteException e) {
                             // Can't happen; the backup manager is local
                         }
@@ -4559,6 +4585,7 @@
         if (DEBUG_ALL) Slog.v(
             TAG, "New app record " + app
             + " thread=" + thread.asBinder() + " pid=" + pid);
+        final BackupRecord backupTarget = mBackupTargets.get(app.userId);
         try {
             int testMode = ApplicationThreadConstants.DEBUG_OFF;
             if (mDebugApp != null && mDebugApp.equals(processName)) {
@@ -4580,11 +4607,11 @@
 
             // If the app is being launched for restore or full backup, set it up specially
             boolean isRestrictedBackupMode = false;
-            if (mBackupTarget != null && mBackupAppName.equals(processName)) {
-                isRestrictedBackupMode = mBackupTarget.appInfo.uid >= FIRST_APPLICATION_UID
-                        && ((mBackupTarget.backupMode == BackupRecord.RESTORE)
-                                || (mBackupTarget.backupMode == BackupRecord.RESTORE_FULL)
-                                || (mBackupTarget.backupMode == BackupRecord.BACKUP_FULL));
+            if (backupTarget != null && backupTarget.appInfo.packageName.equals(processName)) {
+                isRestrictedBackupMode = backupTarget.appInfo.uid >= FIRST_APPLICATION_UID
+                        && ((backupTarget.backupMode == BackupRecord.RESTORE)
+                                || (backupTarget.backupMode == BackupRecord.RESTORE_FULL)
+                                || (backupTarget.backupMode == BackupRecord.BACKUP_FULL));
             }
 
             final ActiveInstrumentation instr = app.getActiveInstrumentation();
@@ -4792,15 +4819,15 @@
         }
 
         // Check whether the next backup agent is in this process...
-        if (!badApp && mBackupTarget != null && mBackupTarget.app == app) {
+        if (!badApp && backupTarget != null && backupTarget.app == app) {
             if (DEBUG_BACKUP) Slog.v(TAG_BACKUP,
                     "New app is backup target, launching agent for " + app);
-            notifyPackageUse(mBackupTarget.appInfo.packageName,
+            notifyPackageUse(backupTarget.appInfo.packageName,
                              PackageManager.NOTIFY_PACKAGE_USE_BACKUP);
             try {
-                thread.scheduleCreateBackupAgent(mBackupTarget.appInfo,
-                        compatibilityInfoForPackage(mBackupTarget.appInfo),
-                        mBackupTarget.backupMode);
+                thread.scheduleCreateBackupAgent(backupTarget.appInfo,
+                        compatibilityInfoForPackage(backupTarget.appInfo),
+                        backupTarget.backupMode);
             } catch (Exception e) {
                 Slog.wtf(TAG, "Exception thrown creating backup agent in " + app, e);
                 badApp = true;
@@ -5347,7 +5374,7 @@
 
     private boolean isAppForeground(int uid) {
         synchronized (this) {
-            UidRecord uidRec = mActiveUids.get(uid);
+            UidRecord uidRec = mProcessList.mActiveUids.get(uid);
             if (uidRec == null || uidRec.idle) {
                 return false;
             }
@@ -5359,15 +5386,10 @@
     // be guarded by permission checking.
     int getUidState(int uid) {
         synchronized (this) {
-            return getUidStateLocked(uid);
+            return mProcessList.getUidProcStateLocked(uid);
         }
     }
 
-    int getUidStateLocked(int uid) {
-        UidRecord uidRec = mActiveUids.get(uid);
-        return uidRec == null ? PROCESS_STATE_NONEXISTENT : uidRec.getCurProcState();
-    }
-
     // =========================================================
     // PROCESS INFO
     // =========================================================
@@ -5659,7 +5681,7 @@
 
     int getAppStartModeLocked(int uid, String packageName, int packageTargetSdk,
             int callingPid, boolean alwaysRestrict, boolean disabledOnly, boolean forcedStandby) {
-        UidRecord uidRec = mActiveUids.get(uid);
+        UidRecord uidRec = mProcessList.getUidRecordLocked(uid);
         if (DEBUG_BACKGROUND_CHECK) Slog.d(TAG, "checkAllowBackground: uid=" + uid + " pkg="
                 + packageName + " rec=" + uidRec + " always=" + alwaysRestrict + " idle="
                 + (uidRec != null ? uidRec.idle : false));
@@ -7880,8 +7902,7 @@
         }
 
         synchronized (this) {
-            UidRecord uidRec = mActiveUids.get(uid);
-            return uidRec != null ? uidRec.getCurProcState() : PROCESS_STATE_NONEXISTENT;
+            return mProcessList.getUidProcStateLocked(uid);
         }
     }
 
@@ -7917,7 +7938,7 @@
     }
 
     boolean isUidActiveLocked(int uid) {
-        final UidRecord uidRecord = mActiveUids.get(uid);
+        final UidRecord uidRecord = mProcessList.getUidRecordLocked(uid);
         return uidRecord != null && !uidRecord.setIdle;
     }
 
@@ -8709,7 +8730,7 @@
 
                 if (mForceBackgroundCheck) {
                     // Stop background services for idle UIDs.
-                    doStopUidForIdleUidsLocked();
+                    mProcessList.doStopUidForIdleUidsLocked();
                 }
             }
         }
@@ -9558,7 +9579,8 @@
                     proto.end(broadcastToken);
 
                     long serviceToken = proto.start(ActivityManagerServiceProto.SERVICES);
-                    mServices.writeToProto(proto, ActivityManagerServiceDumpServicesProto.ACTIVE_SERVICES);
+                    mServices.writeToProto(proto,
+                            ActivityManagerServiceDumpServicesProto.ACTIVE_SERVICES);
                     proto.end(serviceToken);
 
                     long processToken = proto.start(ActivityManagerServiceProto.PROCESSES);
@@ -10110,11 +10132,13 @@
             }
         }
 
-        if (mActiveUids.size() > 0) {
-            if (dumpUids(pw, dumpPackage, dumpAppId, mActiveUids, "UID states:", needSep)) {
+        if (mProcessList.mActiveUids.size() > 0) {
+            if (dumpUids(pw, dumpPackage, dumpAppId, mProcessList.mActiveUids,
+                    "UID states:", needSep)) {
                 needSep = true;
             }
         }
+
         if (dumpAll) {
             if (mValidateUids.size() > 0) {
                 if (dumpUids(pw, dumpPackage, dumpAppId, mValidateUids, "UID validation:",
@@ -10377,12 +10401,8 @@
                 pw.print("  mLastPowerCheckUptime=");
                         TimeUtils.formatDuration(mLastPowerCheckUptime, pw);
                         pw.println("");
-                pw.println("  mAdjSeq=" + mAdjSeq + " mLruSeq=" + mProcessList.mLruSeq);
-                pw.println("  mNumNonCachedProcs=" + mNumNonCachedProcs
-                        + " (" + mProcessList.getLruSizeLocked() + " total)"
-                        + " mNumCachedHiddenProcs=" + mNumCachedHiddenProcs
-                        + " mNumServiceProcs=" + mNumServiceProcs
-                        + " mNewNumServiceProcs=" + mNewNumServiceProcs);
+                mOomAdjuster.dumpSequenceNumbersLocked(pw);
+                mOomAdjuster.dumpProcCountsLocked(pw);
                 pw.println("  mAllowLowerMemLevel=" + mAllowLowerMemLevel
                         + " mLastMemoryLevel=" + mLastMemoryLevel
                         + " mLastNumProcesses=" + mLastNumProcesses);
@@ -10434,7 +10454,8 @@
                 if (dumpPackage != null && !r.pkgList.containsKey(dumpPackage)) {
                     continue;
                 }
-                r.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.PROCS, mProcessList.mLruProcesses.indexOf(r)
+                r.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.PROCS,
+                        mProcessList.mLruProcesses.indexOf(r)
                 );
                 if (r.isPersistent()) {
                     numPers++;
@@ -10458,19 +10479,20 @@
                     && !ai.mTargetInfo.packageName.equals(dumpPackage)) {
                 continue;
             }
-            ai.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.ACTIVE_INSTRUMENTATIONS);
+            ai.writeToProto(proto,
+                    ActivityManagerServiceDumpProcessesProto.ACTIVE_INSTRUMENTATIONS);
         }
 
         int whichAppId = getAppId(dumpPackage);
-        for (int i=0; i<mActiveUids.size(); i++) {
-            UidRecord uidRec = mActiveUids.valueAt(i);
+        for (int i = 0; i < mProcessList.mActiveUids.size(); i++) {
+            UidRecord uidRec = mProcessList.mActiveUids.valueAt(i);
             if (dumpPackage != null && UserHandle.getAppId(uidRec.uid) != whichAppId) {
                 continue;
             }
             uidRec.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.ACTIVE_UIDS);
         }
 
-        for (int i=0; i<mValidateUids.size(); i++) {
+        for (int i = 0; i < mValidateUids.size(); i++) {
             UidRecord uidRec = mValidateUids.valueAt(i);
             if (dumpPackage != null && UserHandle.getAppId(uidRec.uid) != whichAppId) {
                 continue;
@@ -10545,12 +10567,15 @@
             r.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.ON_HOLD_PROCS);
         }
 
-        writeProcessesToGcToProto(proto, ActivityManagerServiceDumpProcessesProto.GC_PROCS, dumpPackage);
-        mAppErrors.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.APP_ERRORS, dumpPackage);
+        writeProcessesToGcToProto(proto, ActivityManagerServiceDumpProcessesProto.GC_PROCS,
+                dumpPackage);
+        mAppErrors.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.APP_ERRORS,
+                dumpPackage);
         mAtmInternal.writeProcessesToProto(proto, dumpPackage, mWakefulness, mTestPssMode);
 
         if (dumpPackage == null) {
-            mUserController.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.USER_CONTROLLER);
+            mUserController.writeToProto(proto,
+            ActivityManagerServiceDumpProcessesProto.USER_CONTROLLER);
         }
 
         final int NI = mUidObservers.getRegisteredCallbackCount();
@@ -10662,11 +10687,7 @@
             proto.write(ActivityManagerServiceDumpProcessesProto.CALL_FINISH_BOOTING, mCallFinishBooting);
             proto.write(ActivityManagerServiceDumpProcessesProto.BOOT_ANIMATION_COMPLETE, mBootAnimationComplete);
             proto.write(ActivityManagerServiceDumpProcessesProto.LAST_POWER_CHECK_UPTIME_MS, mLastPowerCheckUptime);
-            proto.write(ActivityManagerServiceDumpProcessesProto.ADJ_SEQ, mAdjSeq);
-            proto.write(ActivityManagerServiceDumpProcessesProto.LRU_SEQ, mProcessList.mLruSeq);
-            proto.write(ActivityManagerServiceDumpProcessesProto.NUM_NON_CACHED_PROCS, mNumNonCachedProcs);
-            proto.write(ActivityManagerServiceDumpProcessesProto.NUM_SERVICE_PROCS, mNumServiceProcs);
-            proto.write(ActivityManagerServiceDumpProcessesProto.NEW_NUM_SERVICE_PROCS, mNewNumServiceProcs);
+            mOomAdjuster.dumpProcessListVariablesLocked(proto);
             proto.write(ActivityManagerServiceDumpProcessesProto.ALLOW_LOWER_MEM_LEVEL, mAllowLowerMemLevel);
             proto.write(ActivityManagerServiceDumpProcessesProto.LAST_MEMORY_LEVEL, mLastMemoryLevel);
             proto.write(ActivityManagerServiceDumpProcessesProto.LAST_NUM_PROCESSES, mLastNumProcesses);
@@ -10674,7 +10695,6 @@
             ProtoUtils.toDuration(proto, ActivityManagerServiceDumpProcessesProto.LAST_IDLE_TIME, mLastIdleTime, now);
             proto.write(ActivityManagerServiceDumpProcessesProto.LOW_RAM_SINCE_LAST_IDLE_MS, getLowRamTimeSinceIdle(now));
         }
-
     }
 
     void writeProcessesToGcToProto(ProtoOutputStream proto, long fieldId, String dumpPackage) {
@@ -10956,14 +10976,14 @@
     void dumpAllowedAssociationsLocked(FileDescriptor fd, PrintWriter pw, String[] args,
             int opti, boolean dumpAll, String dumpPackage) {
         boolean needSep = false;
-        boolean printedAnything = false;
 
         pw.println("ACTIVITY MANAGER ALLOWED ASSOCIATION STATE (dumpsys activity allowed-associations)");
         boolean printed = false;
         if (mAllowedAssociations != null) {
             for (int i = 0; i < mAllowedAssociations.size(); i++) {
                 final String pkg = mAllowedAssociations.keyAt(i);
-                final ArraySet<String> asc = mAllowedAssociations.valueAt(i);
+                final ArraySet<String> asc =
+                        mAllowedAssociations.valueAt(i).getAllowedPackageAssociations();
                 boolean printedHeader = false;
                 for (int j = 0; j < asc.size(); j++) {
                     if (dumpPackage == null || pkg.equals(dumpPackage)
@@ -10972,7 +10992,6 @@
                             pw.println("  Allowed associations (by restricted package):");
                             printed = true;
                             needSep = true;
-                            printedAnything = true;
                         }
                         if (!printedHeader) {
                             pw.print("  * ");
@@ -10984,6 +11003,9 @@
                         pw.println(asc.valueAt(j));
                     }
                 }
+                if (mAllowedAssociations.valueAt(i).isDebuggable()) {
+                    pw.println("      (debuggable)");
+                }
             }
         }
         if (!printed) {
@@ -13273,16 +13295,17 @@
         app.receivers.clear();
 
         // If the app is undergoing backup, tell the backup manager about it
-        if (mBackupTarget != null && app.pid == mBackupTarget.app.pid) {
+        final BackupRecord backupTarget = mBackupTargets.get(app.userId);
+        if (backupTarget != null && app.pid == backupTarget.app.pid) {
             if (DEBUG_BACKUP || DEBUG_CLEANUP) Slog.d(TAG_CLEANUP, "App "
-                    + mBackupTarget.appInfo + " died during backup");
+                    + backupTarget.appInfo + " died during backup");
             mHandler.post(new Runnable() {
                 @Override
                 public void run(){
                     try {
                         IBackupManager bm = IBackupManager.Stub.asInterface(
                                 ServiceManager.getService(Context.BACKUP_SERVICE));
-                        bm.agentDisconnected(app.info.packageName);
+                        bm.agentDisconnectedForUser(app.userId, app.info.packageName);
                     } catch (RemoteException e) {
                         // can't happen; backup manager is local
                     }
@@ -13622,7 +13645,11 @@
     // instantiated.  The backup agent will invoke backupAgentCreated() on the
     // activity manager to announce its creation.
     public boolean bindBackupAgent(String packageName, int backupMode, int userId) {
-        if (DEBUG_BACKUP) Slog.v(TAG, "bindBackupAgent: app=" + packageName + " mode=" + backupMode);
+        if (DEBUG_BACKUP) {
+            Slog.v(TAG, "bindBackupAgent: app=" + packageName + " mode="
+                    + backupMode + " userId=" + userId + " callingUid = " + Binder.getCallingUid()
+                    + " uid = " + Process.myUid());
+        }
         enforceCallingPermission("android.permission.CONFIRM_FULL_BACKUP", "bindBackupAgent");
 
         IPackageManager pm = AppGlobals.getPackageManager();
@@ -13674,10 +13701,10 @@
                 proc.inFullBackup = true;
             }
             r.app = proc;
-            oldBackupUid = mBackupTarget != null ? mBackupTarget.appInfo.uid : -1;
+            final BackupRecord backupTarget = mBackupTargets.get(userId);
+            oldBackupUid = backupTarget != null ? backupTarget.appInfo.uid : -1;
             newBackupUid = proc.inFullBackup ? r.appInfo.uid : -1;
-            mBackupTarget = r;
-            mBackupAppName = app.packageName;
+            mBackupTargets.put(userId, r);
 
             // Try not to kill the process during backup
             updateOomAdjLocked(proc, true);
@@ -13712,14 +13739,14 @@
         return true;
     }
 
-    @Override
-    public void clearPendingBackup() {
-        if (DEBUG_BACKUP) Slog.v(TAG_BACKUP, "clearPendingBackup");
-        enforceCallingPermission("android.permission.BACKUP", "clearPendingBackup");
+    private void clearPendingBackup(int userId) {
+        if (DEBUG_BACKUP) {
+            Slog.v(TAG_BACKUP, "clearPendingBackup: userId = " + userId + " callingUid = "
+                    + Binder.getCallingUid() + " uid = " + Process.myUid());
+        }
 
         synchronized (this) {
-            mBackupTarget = null;
-            mBackupAppName = null;
+            mBackupTargets.delete(userId);
         }
 
         JobSchedulerInternal js = LocalServices.getService(JobSchedulerInternal.class);
@@ -13727,12 +13754,19 @@
     }
 
     // A backup agent has just come up
+    @Override
     public void backupAgentCreated(String agentPackageName, IBinder agent) {
-        if (DEBUG_BACKUP) Slog.v(TAG_BACKUP, "backupAgentCreated: " + agentPackageName
-                + " = " + agent);
+        final int callingUserId = UserHandle.getCallingUserId();
+        if (DEBUG_BACKUP) {
+            Slog.v(TAG_BACKUP, "backupAgentCreated: " + agentPackageName + " = " + agent
+                    + " callingUserId = " + callingUserId + " callingUid = "
+                    + Binder.getCallingUid() + " uid = " + Process.myUid());
+        }
 
         synchronized(this) {
-            if (!agentPackageName.equals(mBackupAppName)) {
+            final BackupRecord backupTarget = mBackupTargets.get(callingUserId);
+            String backupAppName = backupTarget == null ? null : backupTarget.appInfo.packageName;
+            if (!agentPackageName.equals(backupAppName)) {
                 Slog.e(TAG, "Backup agent created for " + agentPackageName + " but not requested!");
                 return;
             }
@@ -13742,7 +13776,7 @@
         try {
             IBackupManager bm = IBackupManager.Stub.asInterface(
                     ServiceManager.getService(Context.BACKUP_SERVICE));
-            bm.agentConnected(agentPackageName, agent);
+            bm.agentConnectedForUser(callingUserId, agentPackageName, agent);
         } catch (RemoteException e) {
             // can't happen; the backup manager service is local
         } catch (Exception e) {
@@ -13755,7 +13789,12 @@
 
     // done with this agent
     public void unbindBackupAgent(ApplicationInfo appInfo) {
-        if (DEBUG_BACKUP) Slog.v(TAG_BACKUP, "unbindBackupAgent: " + appInfo);
+        if (DEBUG_BACKUP) {
+            Slog.v(TAG_BACKUP, "unbindBackupAgent: " + appInfo + " appInfo.uid = "
+                    + appInfo.uid + " callingUid = " + Binder.getCallingUid() + " uid = "
+                    + Process.myUid());
+        }
+
         enforceCallingPermission("android.permission.CONFIRM_FULL_BACKUP", "unbindBackupAgent");
         if (appInfo == null) {
             Slog.w(TAG, "unbind backup agent for null app");
@@ -13764,24 +13803,27 @@
 
         int oldBackupUid;
 
+        final int userId = UserHandle.getUserId(appInfo.uid);
         synchronized(this) {
+            final BackupRecord backupTarget = mBackupTargets.get(userId);
+            String backupAppName = backupTarget == null ? null : backupTarget.appInfo.packageName;
             try {
-                if (mBackupAppName == null) {
+                if (backupAppName == null) {
                     Slog.w(TAG, "Unbinding backup agent with no active backup");
                     return;
                 }
 
-                if (!mBackupAppName.equals(appInfo.packageName)) {
+                if (!backupAppName.equals(appInfo.packageName)) {
                     Slog.e(TAG, "Unbind of " + appInfo + " but is not the current backup target");
                     return;
                 }
 
                 // Not backing this app up any more; reset its OOM adjustment
-                final ProcessRecord proc = mBackupTarget.app;
+                final ProcessRecord proc = backupTarget.app;
                 updateOomAdjLocked(proc, true);
                 proc.inFullBackup = false;
 
-                oldBackupUid = mBackupTarget != null ? mBackupTarget.appInfo.uid : -1;
+                oldBackupUid = backupTarget != null ? backupTarget.appInfo.uid : -1;
 
                 // If the app crashed during backup, 'thread' will be null here
                 if (proc.thread != null) {
@@ -13794,8 +13836,7 @@
                     }
                 }
             } finally {
-                mBackupTarget = null;
-                mBackupAppName = null;
+                mBackupTargets.delete(userId);
             }
         }
 
@@ -14546,6 +14587,7 @@
                                     + " ssp=" + ssp + " data=" + data);
                             return ActivityManager.BROADCAST_SUCCESS;
                         }
+                        updateAssociationForApp(aInfo);
                         mAtmInternal.onPackageReplaced(aInfo);
                         mServices.updateServiceApplicationInfoLocked(aInfo);
                         sendPackageBroadcastLocked(ApplicationThreadConstants.PACKAGE_REPLACED,
@@ -14640,7 +14682,7 @@
                     Intent.ACTION_PACKAGE_REPLACED.equals(action)) {
                 final int uid = getUidFromIntent(intent);
                 if (uid != -1) {
-                    final UidRecord uidRec = mActiveUids.get(uid);
+                    final UidRecord uidRec = mProcessList.getUidRecordLocked(uid);
                     if (uidRec != null) {
                         uidRec.updateHasInternetPermission();
                     }
@@ -15418,7 +15460,7 @@
     // Returns whether the app is receiving broadcast.
     // If receiving, fetch all broadcast queues which the app is
     // the current [or imminent] receiver on.
-    private boolean isReceivingBroadcastLocked(ProcessRecord app,
+    boolean isReceivingBroadcastLocked(ProcessRecord app,
             ArraySet<BroadcastQueue> receivingQueues) {
         final int N = app.curReceivers.size();
         if (N > 0) {
@@ -15538,1087 +15580,6 @@
         }
     }
 
-    private final ComputeOomAdjWindowCallback mTmpComputeOomAdjWindowCallback =
-            new ComputeOomAdjWindowCallback();
-
-    /** These methods are called inline during computeOomAdjLocked(), on the same thread */
-    private final class ComputeOomAdjWindowCallback
-            implements WindowProcessController.ComputeOomAdjCallback {
-
-        ProcessRecord app;
-        int adj;
-        boolean foregroundActivities;
-        int procState;
-        int schedGroup;
-        int appUid;
-        int logUid;
-        int processStateCurTop;
-
-        void initialize(ProcessRecord app, int adj, boolean foregroundActivities,
-                int procState, int schedGroup, int appUid, int logUid, int processStateCurTop) {
-            this.app = app;
-            this.adj = adj;
-            this.foregroundActivities = foregroundActivities;
-            this.procState = procState;
-            this.schedGroup = schedGroup;
-            this.appUid = appUid;
-            this.logUid = logUid;
-            this.processStateCurTop = processStateCurTop;
-        }
-
-        @Override
-        public void onVisibleActivity() {
-            // App has a visible activity; only upgrade adjustment.
-            if (adj > ProcessList.VISIBLE_APP_ADJ) {
-                adj = ProcessList.VISIBLE_APP_ADJ;
-                app.adjType = "vis-activity";
-                if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
-                    reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise adj to vis-activity: " + app);
-                }
-            }
-            if (procState > processStateCurTop) {
-                procState = processStateCurTop;
-                app.adjType = "vis-activity";
-                if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
-                    reportOomAdjMessageLocked(TAG_OOM_ADJ,
-                            "Raise procstate to vis-activity (top): " + app);
-                }
-            }
-            if (schedGroup < ProcessList.SCHED_GROUP_DEFAULT) {
-                schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
-            }
-            app.cached = false;
-            app.empty = false;
-            foregroundActivities = true;
-        }
-
-        @Override
-        public void onPausedActivity() {
-            if (adj > ProcessList.PERCEPTIBLE_APP_ADJ) {
-                adj = ProcessList.PERCEPTIBLE_APP_ADJ;
-                app.adjType = "pause-activity";
-                if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
-                    reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise adj to pause-activity: "  + app);
-                }
-            }
-            if (procState > processStateCurTop) {
-                procState = processStateCurTop;
-                app.adjType = "pause-activity";
-                if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
-                    reportOomAdjMessageLocked(TAG_OOM_ADJ,
-                            "Raise procstate to pause-activity (top): "  + app);
-                }
-            }
-            if (schedGroup < ProcessList.SCHED_GROUP_DEFAULT) {
-                schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
-            }
-            app.cached = false;
-            app.empty = false;
-            foregroundActivities = true;
-        }
-
-        @Override
-        public void onStoppingActivity(boolean finishing) {
-            if (adj > ProcessList.PERCEPTIBLE_APP_ADJ) {
-                adj = ProcessList.PERCEPTIBLE_APP_ADJ;
-                app.adjType = "stop-activity";
-                if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
-                    reportOomAdjMessageLocked(TAG_OOM_ADJ,
-                            "Raise adj to stop-activity: "  + app);
-                }
-            }
-
-            // For the process state, we will at this point consider the process to be cached. It
-            // will be cached either as an activity or empty depending on whether the activity is
-            // finishing. We do this so that we can treat the process as cached for purposes of
-            // memory trimming (determining current memory level, trim command to send to process)
-            // since there can be an arbitrary number of stopping processes and they should soon all
-            // go into the cached state.
-            if (!finishing) {
-                if (procState > PROCESS_STATE_LAST_ACTIVITY) {
-                    procState = PROCESS_STATE_LAST_ACTIVITY;
-                    app.adjType = "stop-activity";
-                    if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
-                        reportOomAdjMessageLocked(TAG_OOM_ADJ,
-                                "Raise procstate to stop-activity: " + app);
-                    }
-                }
-            }
-            app.cached = false;
-            app.empty = false;
-            foregroundActivities = true;
-        }
-
-        @Override
-        public void onOtherActivity() {
-            if (procState > PROCESS_STATE_CACHED_ACTIVITY) {
-                procState = PROCESS_STATE_CACHED_ACTIVITY;
-                app.adjType = "cch-act";
-                if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
-                    reportOomAdjMessageLocked(TAG_OOM_ADJ,
-                            "Raise procstate to cached activity: " + app);
-                }
-            }
-        }
-    }
-
-    private final boolean computeOomAdjLocked(ProcessRecord app, int cachedAdj, ProcessRecord TOP_APP,
-            boolean doingAll, long now, boolean cycleReEval) {
-        if (mAdjSeq == app.adjSeq) {
-            if (app.adjSeq == app.completedAdjSeq) {
-                // This adjustment has already been computed successfully.
-                return false;
-            } else {
-                // The process is being computed, so there is a cycle. We cannot
-                // rely on this process's state.
-                app.containsCycle = true;
-
-                return false;
-            }
-        }
-
-        if (app.thread == null) {
-            app.adjSeq = mAdjSeq;
-            app.setCurrentSchedulingGroup(ProcessList.SCHED_GROUP_BACKGROUND);
-            app.setCurProcState(ActivityManager.PROCESS_STATE_CACHED_EMPTY);
-            app.curAdj = ProcessList.CACHED_APP_MAX_ADJ;
-            app.setCurRawAdj(ProcessList.CACHED_APP_MAX_ADJ);
-            app.completedAdjSeq = app.adjSeq;
-            return false;
-        }
-
-        app.adjTypeCode = ActivityManager.RunningAppProcessInfo.REASON_UNKNOWN;
-        app.adjSource = null;
-        app.adjTarget = null;
-        app.empty = false;
-        app.cached = false;
-
-        final WindowProcessController wpc = app.getWindowProcessController();
-        final int appUid = app.info.uid;
-        final int logUid = mCurOomAdjUid;
-
-        int prevAppAdj = app.curAdj;
-        int prevProcState = app.getCurProcState();
-
-        if (app.maxAdj <= ProcessList.FOREGROUND_APP_ADJ) {
-            // The max adjustment doesn't allow this app to be anything
-            // below foreground, so it is not worth doing work for it.
-            if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
-                reportOomAdjMessageLocked(TAG_OOM_ADJ, "Making fixed: " + app);
-            }
-            app.adjType = "fixed";
-            app.adjSeq = mAdjSeq;
-            app.setCurRawAdj(app.maxAdj);
-            app.setHasForegroundActivities(false);
-            app.setCurrentSchedulingGroup(ProcessList.SCHED_GROUP_DEFAULT);
-            app.setCurProcState(ActivityManager.PROCESS_STATE_PERSISTENT);
-            // System processes can do UI, and when they do we want to have
-            // them trim their memory after the user leaves the UI.  To
-            // facilitate this, here we need to determine whether or not it
-            // is currently showing UI.
-            app.systemNoUi = true;
-            if (app == TOP_APP) {
-                app.systemNoUi = false;
-                app.setCurrentSchedulingGroup(ProcessList.SCHED_GROUP_TOP_APP);
-                app.adjType = "pers-top-activity";
-            } else if (app.hasTopUi()) {
-                // sched group/proc state adjustment is below
-                app.systemNoUi = false;
-                app.adjType = "pers-top-ui";
-            } else if (wpc.hasVisibleActivities()) {
-                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);
-                }
-            }
-            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 || app.getCurProcState() < prevProcState;
-        }
-
-        app.systemNoUi = false;
-
-        final int PROCESS_STATE_CUR_TOP = mAtmInternal.getTopProcessState();
-
-        // Determine the importance of the process, starting with most
-        // important to least, and assign an appropriate OOM adjustment.
-        int adj;
-        int schedGroup;
-        int procState;
-        int cachedAdjSeq;
-
-        boolean foregroundActivities = false;
-        mTmpBroadcastQueue.clear();
-        if (PROCESS_STATE_CUR_TOP == ActivityManager.PROCESS_STATE_TOP && app == TOP_APP) {
-            // The last app on the list is the foreground app.
-            adj = ProcessList.FOREGROUND_APP_ADJ;
-            schedGroup = ProcessList.SCHED_GROUP_TOP_APP;
-            app.adjType = "top-activity";
-            foregroundActivities = true;
-            procState = PROCESS_STATE_CUR_TOP;
-            if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
-                reportOomAdjMessageLocked(TAG_OOM_ADJ, "Making top: " + app);
-            }
-        } else if (app.runningRemoteAnimation) {
-            adj = ProcessList.VISIBLE_APP_ADJ;
-            schedGroup = ProcessList.SCHED_GROUP_TOP_APP;
-            app.adjType = "running-remote-anim";
-            procState = PROCESS_STATE_CUR_TOP;
-            if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
-                reportOomAdjMessageLocked(TAG_OOM_ADJ, "Making running remote anim: " + app);
-            }
-        } else if (app.getActiveInstrumentation() != null) {
-            // Don't want to kill running instrumentation.
-            adj = ProcessList.FOREGROUND_APP_ADJ;
-            schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
-            app.adjType = "instrumentation";
-            procState = ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE;
-            if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
-                reportOomAdjMessageLocked(TAG_OOM_ADJ, "Making instrumentation: " + app);
-            }
-        } else if (isReceivingBroadcastLocked(app, mTmpBroadcastQueue)) {
-            // An app that is currently receiving a broadcast also
-            // counts as being in the foreground for OOM killer purposes.
-            // It's placed in a sched group based on the nature of the
-            // broadcast as reflected by which queue it's active in.
-            adj = ProcessList.FOREGROUND_APP_ADJ;
-            schedGroup = (mTmpBroadcastQueue.contains(mFgBroadcastQueue))
-                    ? ProcessList.SCHED_GROUP_DEFAULT : ProcessList.SCHED_GROUP_BACKGROUND;
-            app.adjType = "broadcast";
-            procState = ActivityManager.PROCESS_STATE_RECEIVER;
-            if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
-                reportOomAdjMessageLocked(TAG_OOM_ADJ, "Making broadcast: " + app);
-            }
-        } else if (app.executingServices.size() > 0) {
-            // An app that is currently executing a service callback also
-            // counts as being in the foreground.
-            adj = ProcessList.FOREGROUND_APP_ADJ;
-            schedGroup = app.execServicesFg ?
-                    ProcessList.SCHED_GROUP_DEFAULT : ProcessList.SCHED_GROUP_BACKGROUND;
-            app.adjType = "exec-service";
-            procState = ActivityManager.PROCESS_STATE_SERVICE;
-            if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
-                reportOomAdjMessageLocked(TAG_OOM_ADJ, "Making exec-service: " + app);
-            }
-            //Slog.i(TAG, "EXEC " + (app.execServicesFg ? "FG" : "BG") + ": " + app);
-        } else if (app == TOP_APP) {
-            adj = ProcessList.FOREGROUND_APP_ADJ;
-            schedGroup = ProcessList.SCHED_GROUP_BACKGROUND;
-            app.adjType = "top-sleeping";
-            foregroundActivities = true;
-            procState = PROCESS_STATE_CUR_TOP;
-            if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
-                reportOomAdjMessageLocked(TAG_OOM_ADJ, "Making top (sleeping): " + app);
-            }
-        } else {
-            // As far as we know the process is empty.  We may change our mind later.
-            schedGroup = ProcessList.SCHED_GROUP_BACKGROUND;
-            // At this point we don't actually know the adjustment.  Use the cached adj
-            // value that the caller wants us to.
-            adj = cachedAdj;
-            procState = ActivityManager.PROCESS_STATE_CACHED_EMPTY;
-            app.cached = true;
-            app.empty = true;
-            app.adjType = "cch-empty";
-            if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
-                reportOomAdjMessageLocked(TAG_OOM_ADJ, "Making empty: " + app);
-            }
-        }
-
-        // Examine all activities if not already foreground.
-        if (!foregroundActivities && wpc.hasActivities()) {
-            mTmpComputeOomAdjWindowCallback.initialize(app, adj, foregroundActivities, procState,
-                    schedGroup, appUid, logUid, PROCESS_STATE_CUR_TOP);
-            final int minLayer = wpc.computeOomAdjFromActivities(
-                    ProcessList.VISIBLE_APP_LAYER_MAX, mTmpComputeOomAdjWindowCallback);
-
-            adj = mTmpComputeOomAdjWindowCallback.adj;
-            foregroundActivities = mTmpComputeOomAdjWindowCallback.foregroundActivities;
-            procState = mTmpComputeOomAdjWindowCallback.procState;
-            schedGroup = mTmpComputeOomAdjWindowCallback.schedGroup;
-
-            if (adj == ProcessList.VISIBLE_APP_ADJ) {
-                adj += minLayer;
-            }
-        }
-
-        if (procState > ActivityManager.PROCESS_STATE_CACHED_RECENT && app.hasRecentTasks()) {
-            procState = ActivityManager.PROCESS_STATE_CACHED_RECENT;
-            app.adjType = "cch-rec";
-            if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
-                reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise procstate to cached recent: " + app);
-            }
-        }
-
-        if (adj > ProcessList.PERCEPTIBLE_APP_ADJ
-                || procState > ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE) {
-            if (app.hasForegroundServices()) {
-                // The user is aware of this app, so make it visible.
-                adj = ProcessList.PERCEPTIBLE_APP_ADJ;
-                procState = ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE;
-                app.cached = false;
-                app.adjType = "fg-service";
-                schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
-                if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
-                    reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise to fg service: " + app);
-                }
-            } else if (app.hasOverlayUi()) {
-                // The process is display an overlay UI.
-                adj = ProcessList.PERCEPTIBLE_APP_ADJ;
-                procState = ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND;
-                app.cached = false;
-                app.adjType = "has-overlay-ui";
-                schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
-                if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
-                    reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise to overlay ui: " + app);
-                }
-            }
-        }
-
-        // If the app was recently in the foreground and moved to a foreground service status,
-        // allow it to get a higher rank in memory for some time, compared to other foreground
-        // services so that it can finish performing any persistence/processing of in-memory state.
-        if (app.hasForegroundServices() && adj > ProcessList.PERCEPTIBLE_RECENT_FOREGROUND_APP_ADJ
-                && (app.lastTopTime + mConstants.TOP_TO_FGS_GRACE_DURATION > now
-                    || app.setProcState <= ActivityManager.PROCESS_STATE_TOP)) {
-            adj = ProcessList.PERCEPTIBLE_RECENT_FOREGROUND_APP_ADJ;
-            app.adjType = "fg-service-act";
-            if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
-                reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise to recent fg: " + app);
-            }
-        }
-
-        if (adj > ProcessList.PERCEPTIBLE_APP_ADJ
-                || procState > ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND) {
-            if (app.forcingToImportant != null) {
-                // This is currently used for toasts...  they are not interactive, and
-                // we don't want them to cause the app to become fully foreground (and
-                // thus out of background check), so we yes the best background level we can.
-                adj = ProcessList.PERCEPTIBLE_APP_ADJ;
-                procState = ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND;
-                app.cached = false;
-                app.adjType = "force-imp";
-                app.adjSource = app.forcingToImportant;
-                schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
-                if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
-                    reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise to force imp: " + app);
-                }
-            }
-        }
-
-        if (mAtmInternal.isHeavyWeightProcess(app.getWindowProcessController())) {
-            if (adj > ProcessList.HEAVY_WEIGHT_APP_ADJ) {
-                // We don't want to kill the current heavy-weight process.
-                adj = ProcessList.HEAVY_WEIGHT_APP_ADJ;
-                schedGroup = ProcessList.SCHED_GROUP_BACKGROUND;
-                app.cached = false;
-                app.adjType = "heavy";
-                if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
-                    reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise adj to heavy: " + app);
-                }
-            }
-            if (procState > ActivityManager.PROCESS_STATE_HEAVY_WEIGHT) {
-                procState = ActivityManager.PROCESS_STATE_HEAVY_WEIGHT;
-                app.adjType = "heavy";
-                if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
-                    reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise procstate to heavy: " + app);
-                }
-            }
-        }
-
-        if (wpc.isHomeProcess()) {
-            if (adj > ProcessList.HOME_APP_ADJ) {
-                // This process is hosting what we currently consider to be the
-                // home app, so we don't want to let it go into the background.
-                adj = ProcessList.HOME_APP_ADJ;
-                schedGroup = ProcessList.SCHED_GROUP_BACKGROUND;
-                app.cached = false;
-                app.adjType = "home";
-                if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
-                    reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise adj to home: " + app);
-                }
-            }
-            if (procState > ActivityManager.PROCESS_STATE_HOME) {
-                procState = ActivityManager.PROCESS_STATE_HOME;
-                app.adjType = "home";
-                if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
-                    reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise procstate to home: " + app);
-                }
-            }
-        }
-
-        if (wpc.isPreviousProcess() && app.hasActivities()) {
-            if (adj > ProcessList.PREVIOUS_APP_ADJ) {
-                // This was the previous process that showed UI to the user.
-                // We want to try to keep it around more aggressively, to give
-                // a good experience around switching between two apps.
-                adj = ProcessList.PREVIOUS_APP_ADJ;
-                schedGroup = ProcessList.SCHED_GROUP_BACKGROUND;
-                app.cached = false;
-                app.adjType = "previous";
-                if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
-                    reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise adj to prev: " + app);
-                }
-            }
-            if (procState > PROCESS_STATE_LAST_ACTIVITY) {
-                procState = PROCESS_STATE_LAST_ACTIVITY;
-                app.adjType = "previous";
-                if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
-                    reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise procstate to prev: " + app);
-                }
-            }
-        }
-
-        if (false) Slog.i(TAG, "OOM " + app + ": initial adj=" + adj
-                + " reason=" + app.adjType);
-
-        // 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. 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;
-
-        if (mBackupTarget != null && app == mBackupTarget.app) {
-            // If possible we want to avoid killing apps while they're being backed up
-            if (adj > ProcessList.BACKUP_APP_ADJ) {
-                if (DEBUG_BACKUP) Slog.v(TAG_BACKUP, "oom BACKUP_APP_ADJ for " + app);
-                adj = ProcessList.BACKUP_APP_ADJ;
-                if (procState > ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND) {
-                    procState = ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND;
-                }
-                app.adjType = "backup";
-                if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
-                    reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise adj to backup: " + app);
-                }
-                app.cached = false;
-            }
-            if (procState > ActivityManager.PROCESS_STATE_BACKUP) {
-                procState = ActivityManager.PROCESS_STATE_BACKUP;
-                app.adjType = "backup";
-                if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
-                    reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise procstate to backup: " + app);
-                }
-            }
-        }
-
-        boolean mayBeTop = false;
-        String mayBeTopType = null;
-        Object mayBeTopSource = null;
-        Object mayBeTopTarget = null;
-
-        for (int is = app.services.size()-1;
-                is >= 0 && (adj > ProcessList.FOREGROUND_APP_ADJ
-                        || schedGroup == ProcessList.SCHED_GROUP_BACKGROUND
-                        || procState > ActivityManager.PROCESS_STATE_TOP);
-                is--) {
-            ServiceRecord s = app.services.valueAt(is);
-            if (s.startRequested) {
-                app.hasStartedServices = true;
-                if (procState > ActivityManager.PROCESS_STATE_SERVICE) {
-                    procState = ActivityManager.PROCESS_STATE_SERVICE;
-                    app.adjType = "started-services";
-                    if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
-                        reportOomAdjMessageLocked(TAG_OOM_ADJ,
-                                "Raise procstate to started service: " + app);
-                    }
-                }
-                if (app.hasShownUi && !wpc.isHomeProcess()) {
-                    // If this process has shown some UI, let it immediately
-                    // go to the LRU list because it may be pretty heavy with
-                    // UI stuff.  We'll tag it with a label just to help
-                    // debug and understand what is going on.
-                    if (adj > ProcessList.SERVICE_ADJ) {
-                        app.adjType = "cch-started-ui-services";
-                    }
-                } else {
-                    if (now < (s.lastActivity + mConstants.MAX_SERVICE_INACTIVITY)) {
-                        // This service has seen some activity within
-                        // recent memory, so we will keep its process ahead
-                        // of the background processes.
-                        if (adj > ProcessList.SERVICE_ADJ) {
-                            adj = ProcessList.SERVICE_ADJ;
-                            app.adjType = "started-services";
-                            if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
-                                reportOomAdjMessageLocked(TAG_OOM_ADJ,
-                                        "Raise adj to started service: " + app);
-                            }
-                            app.cached = false;
-                        }
-                    }
-                    // If we have let the service slide into the background
-                    // state, still have some text describing what it is doing
-                    // even though the service no longer has an impact.
-                    if (adj > ProcessList.SERVICE_ADJ) {
-                        app.adjType = "cch-started-services";
-                    }
-                }
-            }
-
-            for (int conni = s.connections.size()-1;
-                    conni >= 0 && (adj > ProcessList.FOREGROUND_APP_ADJ
-                            || schedGroup == ProcessList.SCHED_GROUP_BACKGROUND
-                            || procState > ActivityManager.PROCESS_STATE_TOP);
-                    conni--) {
-                ArrayList<ConnectionRecord> clist = s.connections.valueAt(conni);
-                for (int i = 0;
-                        i < clist.size() && (adj > ProcessList.FOREGROUND_APP_ADJ
-                                || schedGroup == ProcessList.SCHED_GROUP_BACKGROUND
-                                || procState > ActivityManager.PROCESS_STATE_TOP);
-                        i++) {
-                    // XXX should compute this based on the max of
-                    // all connected clients.
-                    ConnectionRecord cr = clist.get(i);
-                    if (cr.binding.client == app) {
-                        // Binding to oneself is not interesting.
-                        continue;
-                    }
-
-                    boolean trackedProcState = false;
-                    if ((cr.flags&Context.BIND_WAIVE_PRIORITY) == 0) {
-                        ProcessRecord client = cr.binding.client;
-                        computeOomAdjLocked(client, cachedAdj, TOP_APP, doingAll, now, cycleReEval);
-
-                        if (shouldSkipDueToCycle(app, client, procState, adj, cycleReEval)) {
-                            continue;
-                        }
-
-                        int clientAdj = client.getCurRawAdj();
-                        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
-                            // doesn't propagate except under certain conditions.
-                            clientProcState = ActivityManager.PROCESS_STATE_CACHED_EMPTY;
-                        }
-                        String adjType = null;
-                        if ((cr.flags&Context.BIND_ALLOW_OOM_MANAGEMENT) != 0) {
-                            // Not doing bind OOM management, so treat
-                            // this guy more like a started service.
-                            if (app.hasShownUi && !wpc.isHomeProcess()) {
-                                // If this process has shown some UI, let it immediately
-                                // go to the LRU list because it may be pretty heavy with
-                                // UI stuff.  We'll tag it with a label just to help
-                                // debug and understand what is going on.
-                                if (adj > clientAdj) {
-                                    adjType = "cch-bound-ui-services";
-                                }
-                                app.cached = false;
-                                clientAdj = adj;
-                                clientProcState = procState;
-                            } else {
-                                if (now >= (s.lastActivity + mConstants.MAX_SERVICE_INACTIVITY)) {
-                                    // This service has not seen activity within
-                                    // recent memory, so allow it to drop to the
-                                    // LRU list if there is no other reason to keep
-                                    // it around.  We'll also tag it with a label just
-                                    // to help debug and undertand what is going on.
-                                    if (adj > clientAdj) {
-                                        adjType = "cch-bound-services";
-                                    }
-                                    clientAdj = adj;
-                                }
-                            }
-                        }
-                        if (adj > clientAdj) {
-                            // If this process has recently shown UI, and
-                            // the process that is binding to it is less
-                            // important than being visible, then we don't
-                            // care about the binding as much as we care
-                            // about letting this process get into the LRU
-                            // list to be killed and restarted if needed for
-                            // memory.
-                            if (app.hasShownUi && !wpc.isHomeProcess()
-                                    && clientAdj > ProcessList.PERCEPTIBLE_APP_ADJ) {
-                                if (adj >= ProcessList.CACHED_APP_MIN_ADJ) {
-                                    adjType = "cch-bound-ui-services";
-                                }
-                            } else {
-                                int newAdj;
-                                if ((cr.flags&(Context.BIND_ABOVE_CLIENT
-                                        |Context.BIND_IMPORTANT)) != 0) {
-                                    if (clientAdj >= ProcessList.PERSISTENT_SERVICE_ADJ) {
-                                        newAdj = clientAdj;
-                                    } else {
-                                        // make this service persistent
-                                        newAdj = ProcessList.PERSISTENT_SERVICE_ADJ;
-                                        schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
-                                        procState = ActivityManager.PROCESS_STATE_PERSISTENT;
-                                        cr.trackProcState(procState, mAdjSeq, now);
-                                        trackedProcState = true;
-                                    }
-                                } else if ((cr.flags & Context.BIND_ADJUST_BELOW_PERCEPTIBLE) != 0
-                                        && clientAdj < ProcessList.PERCEPTIBLE_APP_ADJ
-                                        && adj > ProcessList.PERCEPTIBLE_APP_ADJ + 1) {
-                                    newAdj = ProcessList.PERCEPTIBLE_APP_ADJ + 1;
-                                } else if ((cr.flags&Context.BIND_NOT_VISIBLE) != 0
-                                        && clientAdj < ProcessList.PERCEPTIBLE_APP_ADJ
-                                        && adj > ProcessList.PERCEPTIBLE_APP_ADJ) {
-                                    newAdj = ProcessList.PERCEPTIBLE_APP_ADJ;
-                                } else if (clientAdj >= ProcessList.PERCEPTIBLE_APP_ADJ) {
-                                    newAdj = clientAdj;
-                                } else {
-                                    if (adj > ProcessList.VISIBLE_APP_ADJ) {
-                                        newAdj = Math.max(clientAdj, ProcessList.VISIBLE_APP_ADJ);
-                                    } else {
-                                        newAdj = adj;
-                                    }
-                                }
-                                if (!client.cached) {
-                                    app.cached = false;
-                                }
-                                if (adj >  newAdj) {
-                                    adj = newAdj;
-                                    app.setCurRawAdj(adj);
-                                    adjType = "service";
-                                }
-                            }
-                        }
-                        if ((cr.flags & (Context.BIND_NOT_FOREGROUND
-                                | Context.BIND_IMPORTANT_BACKGROUND)) == 0) {
-                            // This will treat important bound services identically to
-                            // the top app, which may behave differently than generic
-                            // foreground work.
-                            final int curSchedGroup = client.getCurrentSchedulingGroup();
-                            if (curSchedGroup > schedGroup) {
-                                if ((cr.flags&Context.BIND_IMPORTANT) != 0) {
-                                    schedGroup = curSchedGroup;
-                                } else {
-                                    schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
-                                }
-                            }
-                            if (clientProcState <= ActivityManager.PROCESS_STATE_TOP) {
-                                if (clientProcState == ActivityManager.PROCESS_STATE_TOP) {
-                                    // Special handling of clients who are in the top state.
-                                    // We *may* want to consider this process to be in the
-                                    // top state as well, but only if there is not another
-                                    // reason for it to be running.  Being on the top is a
-                                    // special state, meaning you are specifically running
-                                    // for the current top app.  If the process is already
-                                    // running in the background for some other reason, it
-                                    // is more important to continue considering it to be
-                                    // in the background state.
-                                    mayBeTop = true;
-                                    mayBeTopType = "service";
-                                    mayBeTopSource = cr.binding.client;
-                                    mayBeTopTarget = s.instanceName;
-                                    clientProcState = ActivityManager.PROCESS_STATE_CACHED_EMPTY;
-                                } else {
-                                    // Special handling for above-top states (persistent
-                                    // processes).  These should not bring the current process
-                                    // into the top state, since they are not on top.  Instead
-                                    // give them the best state after that.
-                                    if ((cr.flags&Context.BIND_FOREGROUND_SERVICE) != 0) {
-                                        clientProcState =
-                                                ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
-                                    } else if (mWakefulness
-                                                    == PowerManagerInternal.WAKEFULNESS_AWAKE &&
-                                            (cr.flags&Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE)
-                                                    != 0) {
-                                        clientProcState =
-                                                ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
-                                    } else {
-                                        clientProcState =
-                                                ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND;
-                                    }
-                                }
-                            }
-                        } else if ((cr.flags & Context.BIND_IMPORTANT_BACKGROUND) == 0) {
-                            if (clientProcState <
-                                    ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND) {
-                                clientProcState =
-                                        ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND;
-                            }
-                        } else {
-                            if (clientProcState <
-                                    ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND) {
-                                clientProcState =
-                                        ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND;
-                            }
-                        }
-                        if (!trackedProcState) {
-                            cr.trackProcState(clientProcState, mAdjSeq, now);
-                        }
-                        if (procState > clientProcState) {
-                            procState = clientProcState;
-                            app.setCurRawProcState(procState);
-                            if (adjType == null) {
-                                adjType = "service";
-                            }
-                        }
-                        if (procState < ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND
-                                && (cr.flags & Context.BIND_SHOWING_UI) != 0) {
-                            app.setPendingUiClean(true);
-                        }
-                        if (adjType != null) {
-                            app.adjType = adjType;
-                            app.adjTypeCode = ActivityManager.RunningAppProcessInfo
-                                    .REASON_SERVICE_IN_USE;
-                            app.adjSource = cr.binding.client;
-                            app.adjSourceProcState = clientProcState;
-                            app.adjTarget = s.instanceName;
-                            if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
-                                reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise to " + adjType
-                                        + ": " + app + ", due to " + cr.binding.client
-                                        + " adj=" + adj + " procState="
-                                        + ProcessList.makeProcStateString(procState));
-                            }
-                        }
-                    }
-                    if ((cr.flags&Context.BIND_TREAT_LIKE_ACTIVITY) != 0) {
-                        app.treatLikeActivity = true;
-                    }
-                    final ActivityServiceConnectionsHolder a = cr.activity;
-                    if ((cr.flags&Context.BIND_ADJUST_WITH_ACTIVITY) != 0) {
-                        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;
-                                } else {
-                                    schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
-                                }
-                            }
-                            app.cached = false;
-                            app.adjType = "service";
-                            app.adjTypeCode = ActivityManager.RunningAppProcessInfo
-                                    .REASON_SERVICE_IN_USE;
-                            app.adjSource = a;
-                            app.adjSourceProcState = procState;
-                            app.adjTarget = s.instanceName;
-                            if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
-                                reportOomAdjMessageLocked(TAG_OOM_ADJ,
-                                        "Raise to service w/activity: " + app);
-                            }
-                        }
-                    }
-                }
-            }
-        }
-
-        for (int provi = app.pubProviders.size()-1;
-                provi >= 0 && (adj > ProcessList.FOREGROUND_APP_ADJ
-                        || schedGroup == ProcessList.SCHED_GROUP_BACKGROUND
-                        || procState > ActivityManager.PROCESS_STATE_TOP);
-                provi--) {
-            ContentProviderRecord cpr = app.pubProviders.valueAt(provi);
-            for (int i = cpr.connections.size()-1;
-                    i >= 0 && (adj > ProcessList.FOREGROUND_APP_ADJ
-                            || schedGroup == ProcessList.SCHED_GROUP_BACKGROUND
-                            || procState > ActivityManager.PROCESS_STATE_TOP);
-                    i--) {
-                ContentProviderConnection conn = cpr.connections.get(i);
-                ProcessRecord client = conn.client;
-                if (client == app) {
-                    // Being our own client is not interesting.
-                    continue;
-                }
-                computeOomAdjLocked(client, cachedAdj, TOP_APP, doingAll, now, cycleReEval);
-
-                if (shouldSkipDueToCycle(app, client, procState, adj, cycleReEval)) {
-                    continue;
-                }
-
-                int clientAdj = client.getCurRawAdj();
-                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.
-                    clientProcState = ActivityManager.PROCESS_STATE_CACHED_EMPTY;
-                }
-                String adjType = null;
-                if (adj > clientAdj) {
-                    if (app.hasShownUi && !wpc.isHomeProcess()
-                            && clientAdj > ProcessList.PERCEPTIBLE_APP_ADJ) {
-                        adjType = "cch-ui-provider";
-                    } else {
-                        adj = clientAdj > ProcessList.FOREGROUND_APP_ADJ
-                                ? clientAdj : ProcessList.FOREGROUND_APP_ADJ;
-                        app.setCurRawAdj(adj);
-                        adjType = "provider";
-                    }
-                    app.cached &= client.cached;
-                }
-                if (clientProcState <= ActivityManager.PROCESS_STATE_TOP) {
-                    if (clientProcState == ActivityManager.PROCESS_STATE_TOP) {
-                        // Special handling of clients who are in the top state.
-                        // We *may* want to consider this process to be in the
-                        // top state as well, but only if there is not another
-                        // reason for it to be running.  Being on the top is a
-                        // special state, meaning you are specifically running
-                        // for the current top app.  If the process is already
-                        // running in the background for some other reason, it
-                        // is more important to continue considering it to be
-                        // in the background state.
-                        mayBeTop = true;
-                        clientProcState = ActivityManager.PROCESS_STATE_CACHED_EMPTY;
-                        mayBeTopType = adjType = "provider-top";
-                        mayBeTopSource = client;
-                        mayBeTopTarget = cpr.name;
-                    } else {
-                        // Special handling for above-top states (persistent
-                        // processes).  These should not bring the current process
-                        // into the top state, since they are not on top.  Instead
-                        // give them the best state after that.
-                        clientProcState =
-                                ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
-                        if (adjType == null) {
-                            adjType = "provider";
-                        }
-                    }
-                }
-                conn.trackProcState(clientProcState, mAdjSeq, now);
-                if (procState > clientProcState) {
-                    procState = clientProcState;
-                    app.setCurRawProcState(procState);
-                }
-                if (client.getCurrentSchedulingGroup() > schedGroup) {
-                    schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
-                }
-                if (adjType != null) {
-                    app.adjType = adjType;
-                    app.adjTypeCode = ActivityManager.RunningAppProcessInfo
-                            .REASON_PROVIDER_IN_USE;
-                    app.adjSource = client;
-                    app.adjSourceProcState = clientProcState;
-                    app.adjTarget = cpr.name;
-                    if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
-                        reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise to " + adjType
-                                + ": " + app + ", due to " + client
-                                + " adj=" + adj + " procState="
-                                + ProcessList.makeProcStateString(procState));
-                    }
-                }
-            }
-            // If the provider has external (non-framework) process
-            // dependencies, ensure that its adjustment is at least
-            // FOREGROUND_APP_ADJ.
-            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";
-                    app.adjTarget = cpr.name;
-                    if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
-                        reportOomAdjMessageLocked(TAG_OOM_ADJ,
-                                "Raise adj to external provider: " + app);
-                    }
-                }
-                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);
-                    }
-                }
-            }
-        }
-
-        if (app.lastProviderTime > 0 &&
-                (app.lastProviderTime+mConstants.CONTENT_PROVIDER_RETAIN_TIME) > now) {
-            if (adj > ProcessList.PREVIOUS_APP_ADJ) {
-                adj = ProcessList.PREVIOUS_APP_ADJ;
-                schedGroup = ProcessList.SCHED_GROUP_BACKGROUND;
-                app.cached = false;
-                app.adjType = "recent-provider";
-                if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
-                    reportOomAdjMessageLocked(TAG_OOM_ADJ,
-                            "Raise adj to recent provider: " + app);
-                }
-            }
-            if (procState > PROCESS_STATE_LAST_ACTIVITY) {
-                procState = PROCESS_STATE_LAST_ACTIVITY;
-                app.adjType = "recent-provider";
-                if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
-                    reportOomAdjMessageLocked(TAG_OOM_ADJ,
-                            "Raise procstate to recent provider: " + app);
-                }
-            }
-        }
-
-        if (mayBeTop && procState > ActivityManager.PROCESS_STATE_TOP) {
-            // A client of one of our services or providers is in the top state.  We
-            // *may* want to be in the top state, but not if we are already running in
-            // the background for some other reason.  For the decision here, we are going
-            // to pick out a few specific states that we want to remain in when a client
-            // is top (states that tend to be longer-term) and otherwise allow it to go
-            // to the top state.
-            switch (procState) {
-                case ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE:
-                case ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE:
-                    // Something else is keeping it at this level, just leave it.
-                    break;
-                case ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND:
-                case ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND:
-                case ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND:
-                case ActivityManager.PROCESS_STATE_SERVICE:
-                    // These all are longer-term states, so pull them up to the top
-                    // of the background states, but not all the way to the top state.
-                    procState = ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
-                    app.adjType = mayBeTopType;
-                    app.adjSource = mayBeTopSource;
-                    app.adjTarget = mayBeTopTarget;
-                    if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
-                        reportOomAdjMessageLocked(TAG_OOM_ADJ, "May be top raise to " + mayBeTopType
-                                + ": " + app + ", due to " + mayBeTopSource
-                                + " adj=" + adj + " procState="
-                                + ProcessList.makeProcStateString(procState));
-                    }
-                    break;
-                default:
-                    // Otherwise, top is a better choice, so take it.
-                    procState = ActivityManager.PROCESS_STATE_TOP;
-                    app.adjType = mayBeTopType;
-                    app.adjSource = mayBeTopSource;
-                    app.adjTarget = mayBeTopTarget;
-                    if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
-                        reportOomAdjMessageLocked(TAG_OOM_ADJ, "May be top raise to " + mayBeTopType
-                                + ": " + app + ", due to " + mayBeTopSource
-                                + " adj=" + adj + " procState="
-                                + ProcessList.makeProcStateString(procState));
-                    }
-                    break;
-            }
-        }
-
-        if (procState >= ActivityManager.PROCESS_STATE_CACHED_EMPTY) {
-            if (app.hasClientActivities()) {
-                // This is a cached process, but with client activities.  Mark it so.
-                procState = ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT;
-                app.adjType = "cch-client-act";
-            } else if (app.treatLikeActivity) {
-                // This is a cached process, but somebody wants us to treat it like it has
-                // an activity, okay!
-                procState = PROCESS_STATE_CACHED_ACTIVITY;
-                app.adjType = "cch-as-act";
-            }
-        }
-
-        if (adj == ProcessList.SERVICE_ADJ) {
-            if (doingAll) {
-                app.serviceb = mNewNumAServiceProcs > (mNumServiceProcs/3);
-                mNewNumServiceProcs++;
-                //Slog.i(TAG, "ADJ " + app + " serviceb=" + app.serviceb);
-                if (!app.serviceb) {
-                    // This service isn't far enough down on the LRU list to
-                    // normally be a B service, but if we are low on RAM and it
-                    // is large we want to force it down since we would prefer to
-                    // keep launcher over it.
-                    if (mLastMemoryLevel > ProcessStats.ADJ_MEM_FACTOR_NORMAL
-                            && app.lastPss >= mProcessList.getCachedRestoreThresholdKb()) {
-                        app.serviceHighRam = true;
-                        app.serviceb = true;
-                        //Slog.i(TAG, "ADJ " + app + " high ram!");
-                    } else {
-                        mNewNumAServiceProcs++;
-                        //Slog.i(TAG, "ADJ " + app + " not high ram!");
-                    }
-                } else {
-                    app.serviceHighRam = false;
-                }
-            }
-            if (app.serviceb) {
-                adj = ProcessList.SERVICE_B_ADJ;
-            }
-        }
-
-        app.setCurRawAdj(adj);
-
-        //Slog.i(TAG, "OOM ADJ " + app + ": pid=" + app.pid +
-        //      " adj=" + adj + " curAdj=" + app.curAdj + " maxAdj=" + app.maxAdj);
-        if (adj > app.maxAdj) {
-            adj = app.maxAdj;
-            if (app.maxAdj <= ProcessList.PERCEPTIBLE_APP_ADJ) {
-                schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
-            }
-        }
-
-        // Put bound foreground services in a special sched group for additional
-        // restrictions on screen off
-        if (procState >= ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE &&
-            mWakefulness != PowerManagerInternal.WAKEFULNESS_AWAKE) {
-            if (schedGroup > ProcessList.SCHED_GROUP_RESTRICTED) {
-                schedGroup = ProcessList.SCHED_GROUP_RESTRICTED;
-            }
-        }
-
-        // Do final modification to adj.  Everything we do between here and applying
-        // the final setAdj must be done in this function, because we will also use
-        // it when computing the final cached adj later.  Note that we don't need to
-        // worry about this for max adj above, since max adj will always be used to
-        // keep it out of the cached vaues.
-        app.curAdj = app.modifyRawOomAdj(adj);
-        app.setCurrentSchedulingGroup(schedGroup);
-        app.setCurProcState(procState);
-        app.setCurRawProcState(procState);
-        app.setHasForegroundActivities(foregroundActivities);
-        app.completedAdjSeq = mAdjSeq;
-
-        // if curAdj or curProcState improved, then this process was promoted
-        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;
@@ -17016,287 +15977,6 @@
         }
     }
 
-    private final boolean applyOomAdjLocked(ProcessRecord app, boolean doingAll, long now,
-            long nowElapsed) {
-        boolean success = true;
-
-        if (app.getCurRawAdj() != app.setRawAdj) {
-            app.setRawAdj = app.getCurRawAdj();
-        }
-
-        int changes = 0;
-
-        if (app.curAdj != app.setAdj) {
-            // don't compact during bootup
-            if (mConstants.USE_COMPACTION && mBooted) {
-                // Perform a minor compaction when a perceptible app becomes the prev/home app
-                // Perform a major compaction when any app enters cached
-                // reminder: here, setAdj is previous state, curAdj is upcoming state
-                if (app.setAdj <= ProcessList.PERCEPTIBLE_APP_ADJ &&
-                    (app.curAdj == ProcessList.PREVIOUS_APP_ADJ ||
-                     app.curAdj == ProcessList.HOME_APP_ADJ)) {
-                    mAppCompact.compactAppSome(app);
-                } else if (app.setAdj < ProcessList.CACHED_APP_MIN_ADJ &&
-                           app.curAdj >= ProcessList.CACHED_APP_MIN_ADJ) {
-                    mAppCompact.compactAppFull(app);
-                }
-            }
-            ProcessList.setOomAdj(app.pid, app.uid, app.curAdj);
-            if (DEBUG_SWITCH || DEBUG_OOM_ADJ || mCurOomAdjUid == app.info.uid) {
-                String msg = "Set " + app.pid + " " + app.processName + " adj "
-                        + app.curAdj + ": " + app.adjType;
-                reportOomAdjMessageLocked(TAG_OOM_ADJ, msg);
-            }
-            app.setAdj = app.curAdj;
-            app.verifiedAdj = ProcessList.INVALID_ADJ;
-        }
-
-        final int curSchedGroup = app.getCurrentSchedulingGroup();
-        if (app.setSchedGroup != curSchedGroup) {
-            int oldSchedGroup = app.setSchedGroup;
-            app.setSchedGroup = curSchedGroup;
-            if (DEBUG_SWITCH || DEBUG_OOM_ADJ || mCurOomAdjUid == app.uid) {
-                String msg = "Setting sched group of " + app.processName
-                        + " to " + curSchedGroup + ": " + app.adjType;
-                reportOomAdjMessageLocked(TAG_OOM_ADJ, msg);
-            }
-            if (app.waitingToKill != null && app.curReceivers.isEmpty()
-                    && app.setSchedGroup == ProcessList.SCHED_GROUP_BACKGROUND) {
-                app.kill(app.waitingToKill, true);
-                success = false;
-            } else {
-                int processGroup;
-                switch (curSchedGroup) {
-                    case ProcessList.SCHED_GROUP_BACKGROUND:
-                        processGroup = THREAD_GROUP_BG_NONINTERACTIVE;
-                        break;
-                    case ProcessList.SCHED_GROUP_TOP_APP:
-                    case ProcessList.SCHED_GROUP_TOP_APP_BOUND:
-                        processGroup = THREAD_GROUP_TOP_APP;
-                        break;
-                    case ProcessList.SCHED_GROUP_RESTRICTED:
-                        processGroup = THREAD_GROUP_RESTRICTED;
-                        break;
-                    default:
-                        processGroup = THREAD_GROUP_DEFAULT;
-                        break;
-                }
-                long oldId = Binder.clearCallingIdentity();
-                try {
-                    setProcessGroup(app.pid, processGroup);
-                    if (curSchedGroup == ProcessList.SCHED_GROUP_TOP_APP) {
-                        // do nothing if we already switched to RT
-                        if (oldSchedGroup != ProcessList.SCHED_GROUP_TOP_APP) {
-                            app.getWindowProcessController().onTopProcChanged();
-                            if (mUseFifoUiScheduling) {
-                                // Switch UI pipeline for app to SCHED_FIFO
-                                app.savedPriority = Process.getThreadPriority(app.pid);
-                                scheduleAsFifoPriority(app.pid, /* suppressLogs */true);
-                                if (app.renderThreadTid != 0) {
-                                    scheduleAsFifoPriority(app.renderThreadTid,
-                                        /* suppressLogs */true);
-                                    if (DEBUG_OOM_ADJ) {
-                                        Slog.d("UI_FIFO", "Set RenderThread (TID " +
-                                            app.renderThreadTid + ") to FIFO");
-                                    }
-                                } else {
-                                    if (DEBUG_OOM_ADJ) {
-                                        Slog.d("UI_FIFO", "Not setting RenderThread TID");
-                                    }
-                                }
-                            } else {
-                                // Boost priority for top app UI and render threads
-                                setThreadPriority(app.pid, TOP_APP_PRIORITY_BOOST);
-                                if (app.renderThreadTid != 0) {
-                                    try {
-                                        setThreadPriority(app.renderThreadTid,
-                                                TOP_APP_PRIORITY_BOOST);
-                                    } catch (IllegalArgumentException e) {
-                                        // thread died, ignore
-                                    }
-                                }
-                            }
-                        }
-                    } else if (oldSchedGroup == ProcessList.SCHED_GROUP_TOP_APP &&
-                            curSchedGroup != ProcessList.SCHED_GROUP_TOP_APP) {
-                        app.getWindowProcessController().onTopProcChanged();
-                        if (mUseFifoUiScheduling) {
-                            try {
-                                // Reset UI pipeline to SCHED_OTHER
-                                setThreadScheduler(app.pid, SCHED_OTHER, 0);
-                                setThreadPriority(app.pid, app.savedPriority);
-                                if (app.renderThreadTid != 0) {
-                                    setThreadScheduler(app.renderThreadTid,
-                                        SCHED_OTHER, 0);
-                                    setThreadPriority(app.renderThreadTid, -4);
-                                }
-                            } catch (IllegalArgumentException e) {
-                                Slog.w(TAG,
-                                        "Failed to set scheduling policy, thread does not exist:\n"
-                                                + e);
-                            } catch (SecurityException e) {
-                                Slog.w(TAG, "Failed to set scheduling policy, not allowed:\n" + e);
-                            }
-                        } else {
-                            // Reset priority for top app UI and render threads
-                            setThreadPriority(app.pid, 0);
-                            if (app.renderThreadTid != 0) {
-                                setThreadPriority(app.renderThreadTid, 0);
-                            }
-                        }
-                    }
-                } catch (Exception e) {
-                    if (false) {
-                        Slog.w(TAG, "Failed setting process group of " + app.pid
-                                + " to " + app.getCurrentSchedulingGroup());
-                        Slog.w(TAG, "at location", e);
-                    }
-                } finally {
-                    Binder.restoreCallingIdentity(oldId);
-                }
-            }
-        }
-        if (app.repForegroundActivities != app.hasForegroundActivities()) {
-            app.repForegroundActivities = app.hasForegroundActivities();
-            changes |= ProcessChangeItem.CHANGE_ACTIVITIES;
-        }
-        if (app.getReportedProcState() != app.getCurProcState()) {
-            app.setReportedProcState(app.getCurProcState());
-            if (app.thread != null) {
-                try {
-                    if (false) {
-                        //RuntimeException h = new RuntimeException("here");
-                        Slog.i(TAG, "Sending new process state " + app.getReportedProcState()
-                                + " to " + app /*, h*/);
-                    }
-                    app.thread.setProcessState(app.getReportedProcState());
-                } catch (RemoteException e) {
-                }
-            }
-        }
-        if (app.setProcState == PROCESS_STATE_NONEXISTENT
-                || ProcessList.procStatesDifferForMem(app.getCurProcState(), app.setProcState)) {
-            if (false && mTestPssMode && app.setProcState >= 0 && app.lastStateTime <= (now-200)) {
-                // Experimental code to more aggressively collect pss while
-                // running test...  the problem is that this tends to collect
-                // the data right when a process is transitioning between process
-                // states, which will tend to give noisy data.
-                long start = SystemClock.uptimeMillis();
-                long startTime = SystemClock.currentThreadTimeMillis();
-                long pss = Debug.getPss(app.pid, mTmpLong, null);
-                long endTime = SystemClock.currentThreadTimeMillis();
-                recordPssSampleLocked(app, app.getCurProcState(), pss, mTmpLong[0], mTmpLong[1],
-                        mTmpLong[2], ProcessStats.ADD_PSS_INTERNAL_SINGLE, endTime-startTime, now);
-                mPendingPssProcesses.remove(app);
-                Slog.i(TAG, "Recorded pss for " + app + " state " + app.setProcState
-                        + " to " + app.getCurProcState() + ": "
-                        + (SystemClock.uptimeMillis()-start) + "ms");
-            }
-            app.lastStateTime = now;
-            app.nextPssTime = ProcessList.computeNextPssTime(app.getCurProcState(),
-                    app.procStateMemTracker, mTestPssMode, mAtmInternal.isSleeping(), now);
-            if (DEBUG_PSS) Slog.d(TAG_PSS, "Process state change from "
-                    + ProcessList.makeProcStateString(app.setProcState) + " to "
-                    + ProcessList.makeProcStateString(app.getCurProcState()) + " next pss in "
-                    + (app.nextPssTime-now) + ": " + app);
-        } else {
-            if (now > app.nextPssTime || (now > (app.lastPssTime+ProcessList.PSS_MAX_INTERVAL)
-                    && now > (app.lastStateTime+ProcessList.minTimeFromStateChange(
-                    mTestPssMode)))) {
-                if (requestPssLocked(app, app.setProcState)) {
-                    app.nextPssTime = ProcessList.computeNextPssTime(app.getCurProcState(),
-                            app.procStateMemTracker, mTestPssMode, mAtmInternal.isSleeping(), now);
-                }
-            } else if (false && DEBUG_PSS) Slog.d(TAG_PSS,
-                    "Not requesting pss of " + app + ": next=" + (app.nextPssTime-now));
-        }
-        if (app.setProcState != app.getCurProcState()) {
-            if (DEBUG_SWITCH || DEBUG_OOM_ADJ || mCurOomAdjUid == app.uid) {
-                String msg = "Proc state change of " + app.processName
-                        + " to " + ProcessList.makeProcStateString(app.getCurProcState())
-                        + " (" + app.getCurProcState() + ")" + ": " + app.adjType;
-                reportOomAdjMessageLocked(TAG_OOM_ADJ, msg);
-            }
-            boolean setImportant = app.setProcState < ActivityManager.PROCESS_STATE_SERVICE;
-            boolean curImportant = app.getCurProcState() < ActivityManager.PROCESS_STATE_SERVICE;
-            if (setImportant && !curImportant) {
-                // This app is no longer something we consider important enough to allow to use
-                // arbitrary amounts of battery power. Note its current CPU time to later know to
-                // kill it if it is not behaving well.
-                app.setWhenUnimportant(now);
-                app.lastCpuTime = 0;
-            }
-            // Inform UsageStats of important process state change
-            // Must be called before updating setProcState
-            maybeUpdateUsageStatsLocked(app, nowElapsed);
-
-            maybeUpdateLastTopTime(app, now);
-
-            app.setProcState = app.getCurProcState();
-            if (app.setProcState >= ActivityManager.PROCESS_STATE_HOME) {
-                app.notCachedSinceIdle = false;
-            }
-            if (!doingAll) {
-                setProcessTrackerStateLocked(app, mProcessStats.getMemFactorLocked(), now);
-            } else {
-                app.procStateChanged = true;
-            }
-        } else if (app.reportedInteraction && (nowElapsed - app.getInteractionEventTime())
-                > mConstants.USAGE_STATS_INTERACTION_INTERVAL) {
-            // For apps that sit around for a long time in the interactive state, we need
-            // to report this at least once a day so they don't go idle.
-            maybeUpdateUsageStatsLocked(app, nowElapsed);
-        }
-
-        if (changes != 0) {
-            if (DEBUG_PROCESS_OBSERVERS) Slog.i(TAG_PROCESS_OBSERVERS,
-                    "Changes in " + app + ": " + changes);
-            int i = mPendingProcessChanges.size()-1;
-            ProcessChangeItem item = null;
-            while (i >= 0) {
-                item = mPendingProcessChanges.get(i);
-                if (item.pid == app.pid) {
-                    if (DEBUG_PROCESS_OBSERVERS) Slog.i(TAG_PROCESS_OBSERVERS,
-                            "Re-using existing item: " + item);
-                    break;
-                }
-                i--;
-            }
-            if (i < 0) {
-                // No existing item in pending changes; need a new one.
-                final int NA = mAvailProcessChanges.size();
-                if (NA > 0) {
-                    item = mAvailProcessChanges.remove(NA-1);
-                    if (DEBUG_PROCESS_OBSERVERS) Slog.i(TAG_PROCESS_OBSERVERS,
-                            "Retrieving available item: " + item);
-                } else {
-                    item = new ProcessChangeItem();
-                    if (DEBUG_PROCESS_OBSERVERS) Slog.i(TAG_PROCESS_OBSERVERS,
-                            "Allocating new item: " + item);
-                }
-                item.changes = 0;
-                item.pid = app.pid;
-                item.uid = app.info.uid;
-                if (mPendingProcessChanges.size() == 0) {
-                    if (DEBUG_PROCESS_OBSERVERS) Slog.i(TAG_PROCESS_OBSERVERS,
-                            "*** Enqueueing dispatch processes changed!");
-                    mUiHandler.obtainMessage(DISPATCH_PROCESSES_CHANGED_UI_MSG).sendToTarget();
-                }
-                mPendingProcessChanges.add(item);
-            }
-            item.changes |= changes;
-            item.foregroundActivities = app.repForegroundActivities;
-            if (DEBUG_PROCESS_OBSERVERS) Slog.i(TAG_PROCESS_OBSERVERS,
-                    "Item " + Integer.toHexString(System.identityHashCode(item))
-                    + " " + app.toShortString() + ": changes=" + item.changes
-                    + " foreground=" + item.foregroundActivities
-                    + " type=" + app.adjType + " source=" + app.adjSource
-                    + " target=" + app.adjTarget);
-        }
-
-        return success;
-    }
-
     private boolean isEphemeralLocked(int uid) {
         String packages[] = mContext.getPackageManager().getPackagesForUid(uid);
         if (packages == null || packages.length != 1) { // Ephemeral apps cannot share uid
@@ -17410,78 +16090,13 @@
         }
     }
 
-    private void maybeUpdateUsageStatsLocked(ProcessRecord app, long nowElapsed) {
-        if (DEBUG_USAGE_STATS) {
-            Slog.d(TAG, "Checking proc [" + Arrays.toString(app.getPackageList())
-                    + "] state changes: old = " + app.setProcState + ", new = "
-                    + app.getCurProcState());
-        }
-        if (mUsageStatsService == null) {
-            return;
-        }
-        boolean isInteraction;
-        // To avoid some abuse patterns, we are going to be careful about what we consider
-        // to be an app interaction.  Being the top activity doesn't count while the display
-        // is sleeping, nor do short foreground services.
-        if (app.getCurProcState() <= ActivityManager.PROCESS_STATE_TOP) {
-            isInteraction = true;
-            app.setFgInteractionTime(0);
-        } else if (app.getCurProcState() <= ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE) {
-            if (app.getFgInteractionTime() == 0) {
-                app.setFgInteractionTime(nowElapsed);
-                isInteraction = false;
-            } else {
-                isInteraction = nowElapsed > app.getFgInteractionTime()
-                        + mConstants.SERVICE_USAGE_INTERACTION_TIME;
-            }
-        } else {
-            isInteraction =
-                    app.getCurProcState() <= ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND;
-            app.setFgInteractionTime(0);
-        }
-        if (isInteraction
-                && (!app.reportedInteraction || (nowElapsed - app.getInteractionEventTime())
-                > mConstants.USAGE_STATS_INTERACTION_INTERVAL)) {
-            app.setInteractionEventTime(nowElapsed);
-            String[] packages = app.getPackageList();
-            if (packages != null) {
-                for (int i = 0; i < packages.length; i++) {
-                    mUsageStatsService.reportEvent(packages[i], app.userId,
-                            UsageEvents.Event.SYSTEM_INTERACTION);
-                }
-            }
-        }
-        app.reportedInteraction = isInteraction;
-        if (!isInteraction) {
-            app.setInteractionEventTime(0);
-        }
-    }
-
-    private void maybeUpdateLastTopTime(ProcessRecord app, long nowUptime) {
-        if (app.setProcState <= ActivityManager.PROCESS_STATE_TOP
-                && app.getCurProcState() > ActivityManager.PROCESS_STATE_TOP) {
-            app.lastTopTime = nowUptime;
-        }
-    }
-
-    private final void setProcessTrackerStateLocked(ProcessRecord proc, int memFactor, long now) {
+    final void setProcessTrackerStateLocked(ProcessRecord proc, int memFactor, long now) {
         if (proc.thread != null && proc.baseProcessTracker != null) {
             proc.baseProcessTracker.setState(
                     proc.getReportedProcState(), memFactor, now, proc.pkgList.mPkgList);
         }
     }
 
-    private final boolean updateOomAdjLocked(ProcessRecord app, int cachedAdj,
-            ProcessRecord TOP_APP, boolean doingAll, long now) {
-        if (app.thread == null) {
-            return false;
-        }
-
-        computeOomAdjLocked(app, cachedAdj, TOP_APP, doingAll, now, false);
-
-        return applyOomAdjLocked(app, doingAll, now, SystemClock.elapsedRealtime());
-    }
-
     @GuardedBy("this")
     final void updateProcessForegroundLocked(ProcessRecord proc, boolean isForeground,
             boolean oomAdj) {
@@ -17564,29 +16179,10 @@
      */
     @GuardedBy("this")
     final boolean updateOomAdjLocked(ProcessRecord app, boolean oomAdjAll) {
-        final ProcessRecord TOP_APP = getTopAppLocked();
-        final boolean wasCached = app.cached;
-
-        mAdjSeq++;
-
-        // This is the desired cached adjusment we want to tell it to use.
-        // If our app is currently cached, we know it, and that is it.  Otherwise,
-        // we don't know it yet, and it needs to now be cached we will then
-        // need to do a complete oom adj.
-        final int cachedAdj = app.getCurRawAdj() >= ProcessList.CACHED_APP_MIN_ADJ
-                ? app.getCurRawAdj() : ProcessList.UNKNOWN_ADJ;
-        boolean success = updateOomAdjLocked(app, cachedAdj, TOP_APP, false,
-                SystemClock.uptimeMillis());
-        if (oomAdjAll
-                && (wasCached != app.cached || app.getCurRawAdj() == ProcessList.UNKNOWN_ADJ)) {
-            // Changed to/from cached state, so apps after it in the LRU
-            // list may also be changed.
-            updateOomAdjLocked();
-        }
-        return success;
+        return mOomAdjuster.updateOomAdjLocked(app, oomAdjAll);
     }
 
-    private static final class ProcStatsRunnable implements Runnable {
+    static final class ProcStatsRunnable implements Runnable {
         private final ActivityManagerService mService;
         private final ProcessStatsService mProcessStats;
 
@@ -17777,387 +16373,7 @@
 
     @GuardedBy("this")
     final void updateOomAdjLocked() {
-        mOomAdjProfiler.oomAdjStarted();
-        final ProcessRecord TOP_APP = getTopAppLocked();
-        final long now = SystemClock.uptimeMillis();
-        final long nowElapsed = SystemClock.elapsedRealtime();
-        final long oldTime = now - ProcessList.MAX_EMPTY_TIME;
-        final int N = mProcessList.getLruSizeLocked();
-
-        // Reset state in all uid records.
-        for (int i=mActiveUids.size()-1; i>=0; i--) {
-            final UidRecord uidRec = mActiveUids.valueAt(i);
-            if (false && DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS,
-                    "Starting update of " + uidRec);
-            uidRec.reset();
-        }
-
-        if (mAtmInternal != null) {
-            mAtmInternal.rankTaskLayersIfNeeded();
-        }
-
-        mAdjSeq++;
-        mNewNumServiceProcs = 0;
-        mNewNumAServiceProcs = 0;
-
-        final int emptyProcessLimit = mConstants.CUR_MAX_EMPTY_PROCESSES;
-        final int cachedProcessLimit = mConstants.CUR_MAX_CACHED_PROCESSES - emptyProcessLimit;
-
-        // Let's determine how many processes we have running vs.
-        // how many slots we have for background processes; we may want
-        // to put multiple processes in a slot of there are enough of
-        // them.
-        final int numSlots = (ProcessList.CACHED_APP_MAX_ADJ
-                - ProcessList.CACHED_APP_MIN_ADJ + 1) / 2
-                / ProcessList.CACHED_APP_IMPORTANCE_LEVELS;
-        int numEmptyProcs = N - mNumNonCachedProcs - mNumCachedHiddenProcs;
-        if (numEmptyProcs > cachedProcessLimit) {
-            // If there are more empty processes than our limit on cached
-            // processes, then use the cached process limit for the factor.
-            // This ensures that the really old empty processes get pushed
-            // down to the bottom, so if we are running low on memory we will
-            // have a better chance at keeping around more cached processes
-            // instead of a gazillion empty processes.
-            numEmptyProcs = cachedProcessLimit;
-        }
-        int emptyFactor = (numEmptyProcs + numSlots - 1) / numSlots;
-        if (emptyFactor < 1) emptyFactor = 1;
-        int cachedFactor = (mNumCachedHiddenProcs > 0 ? (mNumCachedHiddenProcs + numSlots - 1) : 1)
-                / numSlots;
-        if (cachedFactor < 1) cachedFactor = 1;
-        int stepCached = -1;
-        int stepEmpty = -1;
-        int numCached = 0;
-        int numCachedExtraGroup = 0;
-        int numEmpty = 0;
-        int numTrimming = 0;
-        int lastCachedGroup = 0;
-        int lastCachedGroupImportance = 0;
-        int lastCachedGroupUid = 0;
-
-        mNumNonCachedProcs = 0;
-        mNumCachedHiddenProcs = 0;
-
-        // First update the OOM adjustment for each of the
-        // application processes based on their current state.
-        int curCachedAdj = ProcessList.CACHED_APP_MIN_ADJ;
-        int nextCachedAdj = curCachedAdj + (ProcessList.CACHED_APP_IMPORTANCE_LEVELS * 2);
-        int curCachedImpAdj = 0;
-        int curEmptyAdj = ProcessList.CACHED_APP_MIN_ADJ + ProcessList.CACHED_APP_IMPORTANCE_LEVELS;
-        int nextEmptyAdj = curEmptyAdj + (ProcessList.CACHED_APP_IMPORTANCE_LEVELS * 2);
-
-        boolean retryCycles = false;
-
-        // need to reset cycle state before calling computeOomAdjLocked because of service connections
-        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, false);
-
-                // if any app encountered a cycle, we need to perform an additional loop later
-                retryCycles |= app.containsCycle;
-
-                // If we haven't yet assigned the final cached adj
-                // to the process, do that now.
-                if (app.curAdj >= ProcessList.UNKNOWN_ADJ) {
-                    switch (app.getCurProcState()) {
-                        case PROCESS_STATE_CACHED_ACTIVITY:
-                        case ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT:
-                        case ActivityManager.PROCESS_STATE_CACHED_RECENT:
-                            // Figure out the next cached level, taking into account groups.
-                            boolean inGroup = false;
-                            if (app.connectionGroup != 0) {
-                                if (lastCachedGroupUid == app.uid
-                                        && lastCachedGroup == app.connectionGroup) {
-                                    // This is in the same group as the last process, just tweak
-                                    // adjustment by importance.
-                                    if (app.connectionImportance > lastCachedGroupImportance) {
-                                        lastCachedGroupImportance = app.connectionImportance;
-                                        if (curCachedAdj < nextCachedAdj
-                                                && curCachedAdj < ProcessList.CACHED_APP_MAX_ADJ) {
-                                            curCachedImpAdj++;
-                                        }
-                                    }
-                                    inGroup = true;
-                                } else {
-                                    lastCachedGroupUid = app.uid;
-                                    lastCachedGroup = app.connectionGroup;
-                                    lastCachedGroupImportance = app.connectionImportance;
-                                }
-                            }
-                            if (!inGroup && curCachedAdj != nextCachedAdj) {
-                                stepCached++;
-                                curCachedImpAdj = 0;
-                                if (stepCached >= cachedFactor) {
-                                    stepCached = 0;
-                                    curCachedAdj = nextCachedAdj;
-                                    nextCachedAdj += ProcessList.CACHED_APP_IMPORTANCE_LEVELS * 2;
-                                    if (nextCachedAdj > ProcessList.CACHED_APP_MAX_ADJ) {
-                                        nextCachedAdj = ProcessList.CACHED_APP_MAX_ADJ;
-                                    }
-                                }
-                            }
-                            // This process is a cached process holding activities...
-                            // assign it the next cached value for that type, and then
-                            // step that cached level.
-                            app.setCurRawAdj(curCachedAdj + curCachedImpAdj);
-                            app.curAdj = app.modifyRawOomAdj(curCachedAdj + curCachedImpAdj);
-                            if (DEBUG_LRU && false) Slog.d(TAG_LRU, "Assigning activity LRU #" + i
-                                    + " adj: " + app.curAdj + " (curCachedAdj=" + curCachedAdj
-                                    + " curCachedImpAdj=" + curCachedImpAdj + ")");
-                            break;
-                        default:
-                            // Figure out the next cached level.
-                            if (curEmptyAdj != nextEmptyAdj) {
-                                stepEmpty++;
-                                if (stepEmpty >= emptyFactor) {
-                                    stepEmpty = 0;
-                                    curEmptyAdj = nextEmptyAdj;
-                                    nextEmptyAdj += ProcessList.CACHED_APP_IMPORTANCE_LEVELS * 2;
-                                    if (nextEmptyAdj > ProcessList.CACHED_APP_MAX_ADJ) {
-                                        nextEmptyAdj = ProcessList.CACHED_APP_MAX_ADJ;
-                                    }
-                                }
-                            }
-                            // For everything else, assign next empty cached process
-                            // level and bump that up.  Note that this means that
-                            // long-running services that have dropped down to the
-                            // cached level will be treated as empty (since their process
-                            // state is still as a service), which is what we want.
-                            app.setCurRawAdj(curEmptyAdj);
-                            app.curAdj = app.modifyRawOomAdj(curEmptyAdj);
-                            if (DEBUG_LRU && false) Slog.d(TAG_LRU, "Assigning empty LRU #" + i
-                                    + " adj: " + app.curAdj + " (curEmptyAdj=" + curEmptyAdj
-                                    + ")");
-                            break;
-                    }
-                }
-            }
-        }
-
-        // Cycle strategy:
-        // - Retry computing any process that has encountered a cycle.
-        // - Continue retrying until no process was promoted.
-        // - Iterate from least important to most important.
-        int cycleCount = 0;
-        while (retryCycles && cycleCount < 10) {
-            cycleCount++;
-            retryCycles = false;
-
-            for (int i=0; i<N; i++) {
-                ProcessRecord app = mProcessList.mLruProcesses.get(i);
-                if (!app.killedByAm && app.thread != null && app.containsCycle == true) {
-                    app.adjSeq--;
-                    app.completedAdjSeq--;
-                }
-            }
-
-            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,
-                            true)) {
-                        retryCycles = true;
-                    }
-                }
-            }
-        }
-
-        lastCachedGroup = lastCachedGroupUid = 0;
-
-        for (int i=N-1; i>=0; i--) {
-            ProcessRecord app = mProcessList.mLruProcesses.get(i);
-            if (!app.killedByAm && app.thread != null) {
-                applyOomAdjLocked(app, true, now, nowElapsed);
-
-                // Count the number of process types.
-                switch (app.getCurProcState()) {
-                    case PROCESS_STATE_CACHED_ACTIVITY:
-                    case ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT:
-                        mNumCachedHiddenProcs++;
-                        numCached++;
-                        if (app.connectionGroup != 0) {
-                            if (lastCachedGroupUid == app.uid
-                                    && lastCachedGroup == app.connectionGroup) {
-                                // If this process is the next in the same group, we don't
-                                // want it to count against our limit of the number of cached
-                                // processes, so bump up the group count to account for it.
-                                numCachedExtraGroup++;
-                            } else {
-                                lastCachedGroupUid = app.uid;
-                                lastCachedGroup = app.connectionGroup;
-                            }
-                        } else {
-                            lastCachedGroupUid = lastCachedGroup = 0;
-                        }
-                        if ((numCached - numCachedExtraGroup) > cachedProcessLimit) {
-                            app.kill("cached #" + numCached, true);
-                        }
-                        break;
-                    case ActivityManager.PROCESS_STATE_CACHED_EMPTY:
-                        if (numEmpty > mConstants.CUR_TRIM_EMPTY_PROCESSES
-                                && app.lastActivityTime < oldTime) {
-                            app.kill("empty for "
-                                    + ((oldTime + ProcessList.MAX_EMPTY_TIME - app.lastActivityTime)
-                                    / 1000) + "s", true);
-                        } else {
-                            numEmpty++;
-                            if (numEmpty > emptyProcessLimit) {
-                                app.kill("empty #" + numEmpty, true);
-                            }
-                        }
-                        break;
-                    default:
-                        mNumNonCachedProcs++;
-                        break;
-                }
-
-                if (app.isolated && app.services.size() <= 0 && app.isolatedEntryPoint == null) {
-                    // If this is an isolated process, there are no services
-                    // running in it, and it's not a special process with a
-                    // custom entry point, then the process is no longer
-                    // needed.  We agressively kill these because we can by
-                    // definition not re-use the same process again, and it is
-                    // good to avoid having whatever code was running in them
-                    // left sitting around after no longer needed.
-                    app.kill("isolated not needed", true);
-                } else {
-                    // Keeping this process, update its uid.
-                    final UidRecord uidRec = app.uidRecord;
-                    if (uidRec != null) {
-                        uidRec.ephemeral = app.info.isInstantApp();
-                        if (uidRec.getCurProcState() > app.getCurProcState()) {
-                            uidRec.setCurProcState(app.getCurProcState());
-                        }
-                        if (app.hasForegroundServices()) {
-                            uidRec.foregroundServices = true;
-                        }
-                    }
-                }
-
-                if (app.getCurProcState() >= ActivityManager.PROCESS_STATE_HOME
-                        && !app.killedByAm) {
-                    numTrimming++;
-                }
-            }
-        }
-
-        incrementProcStateSeqAndNotifyAppsLocked();
-
-        mNumServiceProcs = mNewNumServiceProcs;
-
-        boolean allChanged = updateLowMemStateLocked(numCached, numEmpty, numTrimming);
-
-        if (mAlwaysFinishActivities) {
-            // Need to do this on its own message because the stack may not
-            // be in a consistent state at this point.
-            mAtmInternal.scheduleDestroyAllActivities("always-finish");
-        }
-
-        if (allChanged) {
-            requestPssAllProcsLocked(now, false, mProcessStats.isMemFactorLowered());
-        }
-
-        ArrayList<UidRecord> becameIdle = null;
-
-        // Update from any uid changes.
-        if (mLocalPowerManager != null) {
-            mLocalPowerManager.startUidChanges();
-        }
-        for (int i=mActiveUids.size()-1; i>=0; i--) {
-            final UidRecord uidRec = mActiveUids.valueAt(i);
-            int uidChange = UidRecord.CHANGE_PROCSTATE;
-            if (uidRec.getCurProcState() != PROCESS_STATE_NONEXISTENT
-                    && (uidRec.setProcState != uidRec.getCurProcState()
-                           || uidRec.setWhitelist != uidRec.curWhitelist)) {
-                if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS, "Changes in " + uidRec
-                        + ": proc state from " + uidRec.setProcState + " to "
-                        + uidRec.getCurProcState() + ", whitelist from " + uidRec.setWhitelist
-                        + " to " + uidRec.curWhitelist);
-                if (ActivityManager.isProcStateBackground(uidRec.getCurProcState())
-                        && !uidRec.curWhitelist) {
-                    // UID is now in the background (and not on the temp whitelist).  Was it
-                    // previously in the foreground (or on the temp whitelist)?
-                    if (!ActivityManager.isProcStateBackground(uidRec.setProcState)
-                            || uidRec.setWhitelist) {
-                        uidRec.lastBackgroundTime = nowElapsed;
-                        if (!mHandler.hasMessages(IDLE_UIDS_MSG)) {
-                            // Note: the background settle time is in elapsed realtime, while
-                            // the handler time base is uptime.  All this means is that we may
-                            // stop background uids later than we had intended, but that only
-                            // happens because the device was sleeping so we are okay anyway.
-                            mHandler.sendEmptyMessageDelayed(IDLE_UIDS_MSG,
-                                    mConstants.BACKGROUND_SETTLE_TIME);
-                        }
-                    }
-                    if (uidRec.idle && !uidRec.setIdle) {
-                        uidChange = UidRecord.CHANGE_IDLE;
-                        if (becameIdle == null) {
-                            becameIdle = new ArrayList<>();
-                        }
-                        becameIdle.add(uidRec);
-                    }
-                } else {
-                    if (uidRec.idle) {
-                        uidChange = UidRecord.CHANGE_ACTIVE;
-                        EventLogTags.writeAmUidActive(uidRec.uid);
-                        uidRec.idle = false;
-                    }
-                    uidRec.lastBackgroundTime = 0;
-                }
-                final boolean wasCached = uidRec.setProcState
-                        > ActivityManager.PROCESS_STATE_RECEIVER;
-                final boolean isCached = uidRec.getCurProcState()
-                        > ActivityManager.PROCESS_STATE_RECEIVER;
-                if (wasCached != isCached || uidRec.setProcState == PROCESS_STATE_NONEXISTENT) {
-                    uidChange |= isCached ? UidRecord.CHANGE_CACHED : UidRecord.CHANGE_UNCACHED;
-                }
-                uidRec.setProcState = uidRec.getCurProcState();
-                uidRec.setWhitelist = uidRec.curWhitelist;
-                uidRec.setIdle = uidRec.idle;
-                enqueueUidChangeLocked(uidRec, -1, uidChange);
-                noteUidProcessState(uidRec.uid, uidRec.getCurProcState());
-                if (uidRec.foregroundServices) {
-                    mServices.foregroundServiceProcStateChangedLocked(uidRec);
-                }
-            }
-        }
-        if (mLocalPowerManager != null) {
-            mLocalPowerManager.finishUidChanges();
-        }
-
-        if (becameIdle != null) {
-            // If we have any new uids that became idle this time, we need to make sure
-            // they aren't left with running services.
-            for (int i = becameIdle.size() - 1; i >= 0; i--) {
-                mServices.stopInBackgroundLocked(becameIdle.get(i).uid);
-            }
-        }
-
-        if (mProcessStats.shouldWriteNowLocked(now)) {
-            mHandler.post(new ProcStatsRunnable(ActivityManagerService.this, mProcessStats));
-        }
-
-        // Run this after making sure all procstates are updated.
-        mProcessStats.updateTrackingAssociationsLocked(mAdjSeq, now);
-
-        if (DEBUG_OOM_ADJ) {
-            final long duration = SystemClock.uptimeMillis() - now;
-            if (false) {
-                Slog.d(TAG_OOM_ADJ, "Did OOM ADJ in " + duration + "ms",
-                        new RuntimeException("here").fillInStackTrace());
-            } else {
-                Slog.d(TAG_OOM_ADJ, "Did OOM ADJ in " + duration + "ms");
-            }
-        }
-        mOomAdjProfiler.oomAdjEnded();
+        mOomAdjuster.updateOomAdjLocked();
     }
 
     @Override
@@ -18192,9 +16408,9 @@
                     mLocalPowerManager.startUidChanges();
                 }
                 final int appId = UserHandle.getAppId(pkgUid);
-                final int N = mActiveUids.size();
-                for (int i=N-1; i>=0; i--) {
-                    final UidRecord uidRec = mActiveUids.valueAt(i);
+                final int N = mProcessList.mActiveUids.size();
+                for (int i = N - 1; i >= 0; i--) {
+                    final UidRecord uidRec = mProcessList.mActiveUids.valueAt(i);
                     final long bgTime = uidRec.lastBackgroundTime;
                     if (bgTime > 0 && !uidRec.idle) {
                         if (UserHandle.getAppId(uidRec.uid) == appId) {
@@ -18219,49 +16435,17 @@
         }
     }
 
+    /** Make the currently active UIDs idle after a certain grace period. */
     final void idleUids() {
         synchronized (this) {
-            final int N = mActiveUids.size();
-            if (N <= 0) {
-                return;
-            }
-            final long nowElapsed = SystemClock.elapsedRealtime();
-            final long maxBgTime = nowElapsed - mConstants.BACKGROUND_SETTLE_TIME;
-            long nextTime = 0;
-            if (mLocalPowerManager != null) {
-                mLocalPowerManager.startUidChanges();
-            }
-            for (int i=N-1; i>=0; i--) {
-                final UidRecord uidRec = mActiveUids.valueAt(i);
-                final long bgTime = uidRec.lastBackgroundTime;
-                if (bgTime > 0 && !uidRec.idle) {
-                    if (bgTime <= maxBgTime) {
-                        EventLogTags.writeAmUidIdle(uidRec.uid);
-                        uidRec.idle = true;
-                        uidRec.setIdle = true;
-                        doStopUidLocked(uidRec.uid, uidRec);
-                    } else {
-                        if (nextTime == 0 || nextTime > bgTime) {
-                            nextTime = bgTime;
-                        }
-                    }
-                }
-            }
-            if (mLocalPowerManager != null) {
-                mLocalPowerManager.finishUidChanges();
-            }
-            if (nextTime > 0) {
-                mHandler.removeMessages(IDLE_UIDS_MSG);
-                mHandler.sendEmptyMessageDelayed(IDLE_UIDS_MSG,
-                        nextTime + mConstants.BACKGROUND_SETTLE_TIME - nowElapsed);
-            }
+            mOomAdjuster.idleUidsLocked();
         }
     }
 
     /**
      * Checks if any uid is coming from background to foreground or vice versa and if so, increments
      * the {@link UidRecord#curProcStateSeq} corresponding to that uid using global seq counter
-     * {@link #mProcStateSeqCounter} and notifies the app if it needs to block.
+     * {@link ProcessList#mProcStateSeqCounter} and notifies the app if it needs to block.
      */
     @VisibleForTesting
     @GuardedBy("this")
@@ -18271,8 +16455,8 @@
         }
         // Used for identifying which uids need to block for network.
         ArrayList<Integer> blockingUids = null;
-        for (int i = mActiveUids.size() - 1; i >= 0; --i) {
-            final UidRecord uidRec = mActiveUids.valueAt(i);
+        for (int i = mProcessList.mActiveUids.size() - 1; i >= 0; --i) {
+            final UidRecord uidRec = mProcessList.mActiveUids.valueAt(i);
             // If the network is not restricted for uid, then nothing to do here.
             if (!mInjector.isNetworkRestrictedForUid(uidRec.uid)) {
                 continue;
@@ -18320,13 +16504,15 @@
                 continue;
             }
             if (!app.killedByAm && app.thread != null) {
-                final UidRecord uidRec = mActiveUids.get(app.uid);
+                final UidRecord uidRec = mProcessList.getUidRecordLocked(app.uid);
                 try {
                     if (DEBUG_NETWORK) {
                         Slog.d(TAG_NETWORK, "Informing app thread that it needs to block: "
                                 + uidRec);
                     }
-                    app.thread.setNetworkBlockSeq(uidRec.curProcStateSeq);
+                    if (uidRec != null) {
+                        app.thread.setNetworkBlockSeq(uidRec.curProcStateSeq);
+                    }
                 } catch (RemoteException ignored) {
                 }
             }
@@ -18367,7 +16553,7 @@
 
     final void runInBackgroundDisabled(int uid) {
         synchronized (this) {
-            UidRecord uidRec = mActiveUids.get(uid);
+            UidRecord uidRec = mProcessList.getUidRecordLocked(uid);
             if (uidRec != null) {
                 // This uid is actually running...  should it be considered background now?
                 if (uidRec.idle) {
@@ -18380,24 +16566,7 @@
         }
     }
 
-    /**
-     * Call {@link #doStopUidLocked} (which will also stop background services) for all idle UIDs.
-     */
-    void doStopUidForIdleUidsLocked() {
-        final int size = mActiveUids.size();
-        for (int i = 0; i < size; i++) {
-            final int uid = mActiveUids.keyAt(i);
-            if (UserHandle.isCore(uid)) {
-                continue;
-            }
-            final UidRecord uidRec = mActiveUids.valueAt(i);
-            if (!uidRec.idle) {
-                continue;
-            }
-            doStopUidLocked(uidRec.uid, uidRec);
-        }
-    }
-
+    @GuardedBy("this")
     final void doStopUidLocked(int uid, final UidRecord uidRec) {
         mServices.stopInBackgroundLocked(uid);
         enqueueUidChangeLocked(uidRec, uid, UidRecord.CHANGE_IDLE);
@@ -18481,27 +16650,12 @@
 
     @GuardedBy("this")
     final void setAppIdTempWhitelistStateLocked(int appId, boolean onWhitelist) {
-        boolean changed = false;
-        for (int i=mActiveUids.size()-1; i>=0; i--) {
-            final UidRecord uidRec = mActiveUids.valueAt(i);
-            if (UserHandle.getAppId(uidRec.uid) == appId && uidRec.curWhitelist != onWhitelist) {
-                uidRec.curWhitelist = onWhitelist;
-                changed = true;
-            }
-        }
-        if (changed) {
-            updateOomAdjLocked();
-        }
+        mOomAdjuster.setAppIdTempWhitelistStateLocked(appId, onWhitelist);
     }
 
     @GuardedBy("this")
     final void setUidTempWhitelistStateLocked(int uid, boolean onWhitelist) {
-        boolean changed = false;
-        final UidRecord uidRec = mActiveUids.get(uid);
-        if (uidRec != null && uidRec.curWhitelist != onWhitelist) {
-            uidRec.curWhitelist = onWhitelist;
-            updateOomAdjLocked();
-        }
+        mOomAdjuster.setUidTempWhitelistStateLocked(uid, onWhitelist);
     }
 
     final void trimApplications() {
@@ -19164,7 +17318,7 @@
             }
             UidRecord record;
             synchronized (ActivityManagerService.this) {
-                record = mActiveUids.get(uid);
+                record = mProcessList.getUidRecordLocked(uid);
                 if (record == null) {
                     if (DEBUG_NETWORK) {
                         Slog.d(TAG_NETWORK, "No active uidRecord for uid: " + uid
@@ -19708,6 +17862,20 @@
         public boolean isAppForeground(int uid) {
             return ActivityManagerService.this.isAppForeground(uid);
         }
+
+        @Override
+        public void clearPendingBackup(int userId) {
+            ActivityManagerService.this.clearPendingBackup(userId);
+        }
+
+        /**
+         * When power button is very long pressed, call this interface to do some pre-shutdown work
+         * like persisting database etc.
+         */
+        @Override
+        public void prepareForPossibleShutdown() {
+            ActivityManagerService.this.prepareForPossibleShutdown();
+        }
     }
 
     long inputDispatchingTimedOut(int pid, final boolean aboveSystem, String reason) {
@@ -19783,7 +17951,7 @@
         }
         UidRecord record;
         synchronized (this) {
-            record = mActiveUids.get(callingUid);
+            record = mProcessList.getUidRecordLocked(callingUid);
             if (record == null) {
                 return;
             }
@@ -19964,6 +18132,18 @@
         }
     }
 
+    /**
+     * When power button is very long pressed, call this interface to do some pre-shutdown work
+     * like persisting database etc.
+     */
+    public void prepareForPossibleShutdown() {
+        synchronized (this) {
+            if (mUsageStatsService != null) {
+                mUsageStatsService.prepareForPossibleShutdown();
+            }
+        }
+    }
+
     @VisibleForTesting
     public static class Injector {
         private NetworkManagementInternal mNmi;
diff --git a/services/core/java/com/android/server/am/AppCompactor.java b/services/core/java/com/android/server/am/AppCompactor.java
index aee16c3..fe27c49 100644
--- a/services/core/java/com/android/server/am/AppCompactor.java
+++ b/services/core/java/com/android/server/am/AppCompactor.java
@@ -44,7 +44,11 @@
      */
     final ArrayList<ProcessRecord> mPendingCompactionProcesses = new ArrayList<ProcessRecord>();
 
-    /*
+    static final int COMPACT_PROCESS_SOME = 1;
+    static final int COMPACT_PROCESS_FULL = 2;
+    static final int COMPACT_PROCESS_MSG = 1;
+
+    /**
      * This thread must be moved to the system background cpuset.
      * If that doesn't happen, it's probably going to draw a lot of power.
      * However, this has to happen after the first updateOomAdjLocked, because
@@ -53,13 +57,22 @@
      */
     final ServiceThread mCompactionThread;
 
-    static final int COMPACT_PROCESS_SOME = 1;
-    static final int COMPACT_PROCESS_FULL = 2;
-    static final int COMPACT_PROCESS_MSG = 1;
-    final Handler mCompactionHandler;
+    final private Handler mCompactionHandler;
 
-    final ActivityManagerService mAm;
-    final ActivityManagerConstants mConstants;
+    final private ActivityManagerService mAm;
+    final private ActivityManagerConstants mConstants;
+
+    final private String COMPACT_ACTION_FILE = "file";
+    final private String COMPACT_ACTION_ANON = "anon";
+    final private String COMPACT_ACTION_FULL = "full";
+
+    final private String compactActionSome;
+    final private String compactActionFull;
+
+    final private long throttleSomeSome;
+    final private long throttleSomeFull;
+    final private long throttleFullSome;
+    final private long throttleFullFull;
 
     public AppCompactor(ActivityManagerService am) {
         mAm = am;
@@ -69,6 +82,41 @@
                 THREAD_PRIORITY_FOREGROUND, true);
         mCompactionThread.start();
         mCompactionHandler = new MemCompactionHandler(this);
+
+        switch(mConstants.COMPACT_ACTION_1) {
+            case 1:
+                compactActionSome = COMPACT_ACTION_FILE;
+                break;
+            case 2:
+                compactActionSome = COMPACT_ACTION_ANON;
+                break;
+            case 3:
+                compactActionSome = COMPACT_ACTION_FULL;
+                break;
+            default:
+                compactActionSome = COMPACT_ACTION_FILE;
+                break;
+        }
+
+        switch(mConstants.COMPACT_ACTION_2) {
+            case 1:
+                compactActionFull = COMPACT_ACTION_FILE;
+                break;
+            case 2:
+                compactActionFull = COMPACT_ACTION_ANON;
+                break;
+            case 3:
+                compactActionFull = COMPACT_ACTION_FULL;
+                break;
+            default:
+                compactActionFull = COMPACT_ACTION_FULL;
+                break;
+        }
+
+        throttleSomeSome = mConstants.COMPACT_THROTTLE_1;
+        throttleSomeFull = mConstants.COMPACT_THROTTLE_2;
+        throttleFullSome = mConstants.COMPACT_THROTTLE_3;
+        throttleFullFull = mConstants.COMPACT_THROTTLE_4;
     }
 
     // Must be called while holding AMS lock.
@@ -128,18 +176,16 @@
                 }
 
                 // basic throttling
+                // use the ActivityManagerConstants knobs to determine whether current/prevous
+                // compaction combo should be throtted or not
                 if (pendingAction == COMPACT_PROCESS_SOME) {
-                    // if we're compacting some, then compact if >10s after last full
-                    // or >5s after last some
-                    if ((lastCompactAction == COMPACT_PROCESS_SOME && (start - lastCompactTime < 5000)) ||
-                        (lastCompactAction == COMPACT_PROCESS_FULL && (start - lastCompactTime < 10000))) {
+                    if ((lastCompactAction == COMPACT_PROCESS_SOME && (start - lastCompactTime < throttleSomeSome)) ||
+                        (lastCompactAction == COMPACT_PROCESS_FULL && (start - lastCompactTime < throttleSomeFull))) {
                         return;
                     }
                 } else {
-                    // if we're compacting full, then compact if >10s after last full
-                    // or >.5s after last some
-                    if ((lastCompactAction == COMPACT_PROCESS_SOME && (start - lastCompactTime < 500)) ||
-                        (lastCompactAction == COMPACT_PROCESS_FULL && (start - lastCompactTime < 10000))) {
+                    if ((lastCompactAction == COMPACT_PROCESS_SOME && (start - lastCompactTime < throttleFullSome)) ||
+                        (lastCompactAction == COMPACT_PROCESS_FULL && (start - lastCompactTime < throttleFullFull))) {
                         return;
                     }
                 }
@@ -151,9 +197,9 @@
                     long[] rssBefore = Process.getRss(pid);
                     FileOutputStream fos = new FileOutputStream("/proc/" + pid + "/reclaim");
                     if (pendingAction == COMPACT_PROCESS_SOME) {
-                        action = "file";
+                        action = compactActionSome;
                     } else {
-                        action = "all";
+                        action = compactActionFull;
                     }
                     fos.write(action.getBytes());
                     fos.close();
diff --git a/services/core/java/com/android/server/am/CoreSettingsObserver.java b/services/core/java/com/android/server/am/CoreSettingsObserver.java
index d7cb2bd..d3953b5 100644
--- a/services/core/java/com/android/server/am/CoreSettingsObserver.java
+++ b/services/core/java/com/android/server/am/CoreSettingsObserver.java
@@ -67,9 +67,10 @@
         sGlobalSettingToTypeMap.put(Settings.Global.GPU_DEBUG_LAYERS_GLES, String.class);
         sGlobalSettingToTypeMap.put(Settings.Global.GPU_DEBUG_LAYER_APP, String.class);
         sGlobalSettingToTypeMap.put(Settings.Global.SMS_ACCESS_RESTRICTION_ENABLED, int.class);
+        sGlobalSettingToTypeMap.put(Settings.Global.GUP_DEV_ALL_APPS, int.class);
         sGlobalSettingToTypeMap.put(Settings.Global.GUP_DEV_OPT_IN_APPS, String.class);
         sGlobalSettingToTypeMap.put(Settings.Global.GUP_DEV_OPT_OUT_APPS, String.class);
-        sGlobalSettingToTypeMap.put(Settings.Global.GUP_BLACK_LIST, String.class);
+        sGlobalSettingToTypeMap.put(Settings.Global.GUP_BLACKLIST, String.class);
         // add other global settings here...
     }
 
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
new file mode 100644
index 0000000..1f9362e
--- /dev/null
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -0,0 +1,2101 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.am;
+
+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.os.Process.SCHED_OTHER;
+import static android.os.Process.THREAD_GROUP_BG_NONINTERACTIVE;
+import static android.os.Process.THREAD_GROUP_DEFAULT;
+import static android.os.Process.THREAD_GROUP_RESTRICTED;
+import static android.os.Process.THREAD_GROUP_TOP_APP;
+import static android.os.Process.setProcessGroup;
+import static android.os.Process.setThreadPriority;
+import static android.os.Process.setThreadScheduler;
+
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BACKUP;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_LRU;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_OOM_ADJ;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_OOM_ADJ_REASON;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PROCESS_OBSERVERS;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PSS;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_UID_OBSERVERS;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_USAGE_STATS;
+import static com.android.server.am.ActivityManagerService.DISPATCH_OOM_ADJ_OBSERVER_MSG;
+import static com.android.server.am.ActivityManagerService.DISPATCH_PROCESSES_CHANGED_UI_MSG;
+import static com.android.server.am.ActivityManagerService.IDLE_UIDS_MSG;
+import static com.android.server.am.ActivityManagerService.TAG_BACKUP;
+import static com.android.server.am.ActivityManagerService.TAG_LRU;
+import static com.android.server.am.ActivityManagerService.TAG_OOM_ADJ;
+import static com.android.server.am.ActivityManagerService.TAG_PROCESS_OBSERVERS;
+import static com.android.server.am.ActivityManagerService.TAG_PSS;
+import static com.android.server.am.ActivityManagerService.TAG_UID_OBSERVERS;
+import static com.android.server.am.ActivityManagerService.TOP_APP_PRIORITY_BOOST;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_SWITCH;
+
+import android.app.ActivityManager;
+import android.app.usage.UsageEvents;
+import android.content.Context;
+import android.os.Binder;
+import android.os.Debug;
+import android.os.PowerManagerInternal;
+import android.os.Process;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.os.UserHandle;
+import android.util.ArraySet;
+import android.util.Slog;
+import android.util.proto.ProtoOutputStream;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.app.procstats.ProcessStats;
+import com.android.server.LocalServices;
+import com.android.server.wm.ActivityServiceConnectionsHolder;
+import com.android.server.wm.WindowProcessController;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Arrays;
+
+/**
+ * All of the code required to compute proc states and oom_adj values.
+ */
+public final class OomAdjuster {
+    private static final String TAG = "OomAdjuster";
+
+    /**
+     * For some direct access we need to power manager.
+     */
+    PowerManagerInternal mLocalPowerManager;
+
+    /**
+     * Service for compacting background apps.
+     */
+    AppCompactor mAppCompact;
+
+    ActivityManagerConstants mConstants;
+
+    final long[] mTmpLong = new long[3];
+
+    /**
+     * Current sequence id for oom_adj computation traversal.
+     */
+    int mAdjSeq = 0;
+
+    /**
+     * Keep track of the number of service processes we last found, to
+     * determine on the next iteration which should be B services.
+     */
+    int mNumServiceProcs = 0;
+    int mNewNumAServiceProcs = 0;
+    int mNewNumServiceProcs = 0;
+
+    /**
+     * Keep track of the non-cached/empty process we last found, to help
+     * determine how to distribute cached/empty processes next time.
+     */
+    int mNumNonCachedProcs = 0;
+
+    /**
+     * Keep track of the number of cached hidden procs, to balance oom adj
+     * distribution between those and empty procs.
+     */
+    int mNumCachedHiddenProcs = 0;
+
+    /** Track all uids that have actively running processes. */
+    ActiveUids mActiveUids;
+
+    private final ArraySet<BroadcastQueue> mTmpBroadcastQueue = new ArraySet();
+
+    private final ActivityManagerService mService;
+    private final ProcessList mProcessList;
+
+    OomAdjuster(ActivityManagerService service, ProcessList processList, ActiveUids activeUids) {
+        mService = service;
+        mProcessList = processList;
+        mActiveUids = activeUids;
+
+        mLocalPowerManager = LocalServices.getService(PowerManagerInternal.class);
+        mAppCompact = new AppCompactor(mService);
+        mConstants = mService.mConstants;
+    }
+
+    /**
+     * Update OomAdj for a specific process.
+     * @param app The process to update
+     * @param oomAdjAll If it's ok to call updateOomAdjLocked() for all running apps
+     *                  if necessary, or skip.
+     * @return whether updateOomAdjLocked(app) was successful.
+     */
+    @GuardedBy("mService")
+    final boolean updateOomAdjLocked(ProcessRecord app, boolean oomAdjAll) {
+        final ProcessRecord TOP_APP = mService.getTopAppLocked();
+        final boolean wasCached = app.cached;
+
+        mAdjSeq++;
+
+        // This is the desired cached adjusment we want to tell it to use.
+        // If our app is currently cached, we know it, and that is it.  Otherwise,
+        // we don't know it yet, and it needs to now be cached we will then
+        // need to do a complete oom adj.
+        final int cachedAdj = app.getCurRawAdj() >= ProcessList.CACHED_APP_MIN_ADJ
+                ? app.getCurRawAdj() : ProcessList.UNKNOWN_ADJ;
+        boolean success = updateOomAdjLocked(app, cachedAdj, TOP_APP, false,
+                SystemClock.uptimeMillis());
+        if (oomAdjAll
+                && (wasCached != app.cached || app.getCurRawAdj() == ProcessList.UNKNOWN_ADJ)) {
+            // Changed to/from cached state, so apps after it in the LRU
+            // list may also be changed.
+            updateOomAdjLocked();
+        }
+        return success;
+    }
+
+    @GuardedBy("mService")
+    private final boolean updateOomAdjLocked(ProcessRecord app, int cachedAdj,
+            ProcessRecord TOP_APP, boolean doingAll, long now) {
+        if (app.thread == null) {
+            return false;
+        }
+
+        computeOomAdjLocked(app, cachedAdj, TOP_APP, doingAll, now, false);
+
+        return applyOomAdjLocked(app, doingAll, now, SystemClock.elapsedRealtime());
+    }
+
+    @GuardedBy("mService")
+    final void updateOomAdjLocked() {
+        mService.mOomAdjProfiler.oomAdjStarted();
+        final ProcessRecord TOP_APP = mService.getTopAppLocked();
+        final long now = SystemClock.uptimeMillis();
+        final long nowElapsed = SystemClock.elapsedRealtime();
+        final long oldTime = now - ProcessList.MAX_EMPTY_TIME;
+        final int N = mProcessList.getLruSizeLocked();
+
+        // Reset state in all uid records.
+        for (int  i = mActiveUids.size() - 1; i >= 0; i--) {
+            final UidRecord uidRec = mActiveUids.valueAt(i);
+            if (false && DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS,
+                    "Starting update of " + uidRec);
+            uidRec.reset();
+        }
+
+        if (mService.mAtmInternal != null) {
+            mService.mAtmInternal.rankTaskLayersIfNeeded();
+        }
+
+        mAdjSeq++;
+        mNewNumServiceProcs = 0;
+        mNewNumAServiceProcs = 0;
+
+        final int emptyProcessLimit = mConstants.CUR_MAX_EMPTY_PROCESSES;
+        final int cachedProcessLimit = mConstants.CUR_MAX_CACHED_PROCESSES
+                - emptyProcessLimit;
+
+        // Let's determine how many processes we have running vs.
+        // how many slots we have for background processes; we may want
+        // to put multiple processes in a slot of there are enough of
+        // them.
+        final int numSlots = (ProcessList.CACHED_APP_MAX_ADJ
+                - ProcessList.CACHED_APP_MIN_ADJ + 1) / 2
+                / ProcessList.CACHED_APP_IMPORTANCE_LEVELS;
+        int numEmptyProcs = N - mNumNonCachedProcs - mNumCachedHiddenProcs;
+        if (numEmptyProcs > cachedProcessLimit) {
+            // If there are more empty processes than our limit on cached
+            // processes, then use the cached process limit for the factor.
+            // This ensures that the really old empty processes get pushed
+            // down to the bottom, so if we are running low on memory we will
+            // have a better chance at keeping around more cached processes
+            // instead of a gazillion empty processes.
+            numEmptyProcs = cachedProcessLimit;
+        }
+        int emptyFactor = (numEmptyProcs + numSlots - 1) / numSlots;
+        if (emptyFactor < 1) emptyFactor = 1;
+        int cachedFactor = (mNumCachedHiddenProcs > 0 ? (mNumCachedHiddenProcs + numSlots - 1) : 1)
+                / numSlots;
+        if (cachedFactor < 1) cachedFactor = 1;
+        int stepCached = -1;
+        int stepEmpty = -1;
+        int numCached = 0;
+        int numCachedExtraGroup = 0;
+        int numEmpty = 0;
+        int numTrimming = 0;
+        int lastCachedGroup = 0;
+        int lastCachedGroupImportance = 0;
+        int lastCachedGroupUid = 0;
+
+        mNumNonCachedProcs = 0;
+        mNumCachedHiddenProcs = 0;
+
+        // First update the OOM adjustment for each of the
+        // application processes based on their current state.
+        int curCachedAdj = ProcessList.CACHED_APP_MIN_ADJ;
+        int nextCachedAdj = curCachedAdj + (ProcessList.CACHED_APP_IMPORTANCE_LEVELS * 2);
+        int curCachedImpAdj = 0;
+        int curEmptyAdj = ProcessList.CACHED_APP_MIN_ADJ + ProcessList.CACHED_APP_IMPORTANCE_LEVELS;
+        int nextEmptyAdj = curEmptyAdj + (ProcessList.CACHED_APP_IMPORTANCE_LEVELS * 2);
+
+        boolean retryCycles = false;
+
+        // need to reset cycle state before calling computeOomAdjLocked because of service conns
+        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, false);
+
+                // if any app encountered a cycle, we need to perform an additional loop later
+                retryCycles |= app.containsCycle;
+
+                // If we haven't yet assigned the final cached adj
+                // to the process, do that now.
+                if (app.curAdj >= ProcessList.UNKNOWN_ADJ) {
+                    switch (app.getCurProcState()) {
+                        case PROCESS_STATE_CACHED_ACTIVITY:
+                        case ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT:
+                        case ActivityManager.PROCESS_STATE_CACHED_RECENT:
+                            // Figure out the next cached level, taking into account groups.
+                            boolean inGroup = false;
+                            if (app.connectionGroup != 0) {
+                                if (lastCachedGroupUid == app.uid
+                                        && lastCachedGroup == app.connectionGroup) {
+                                    // This is in the same group as the last process, just tweak
+                                    // adjustment by importance.
+                                    if (app.connectionImportance > lastCachedGroupImportance) {
+                                        lastCachedGroupImportance = app.connectionImportance;
+                                        if (curCachedAdj < nextCachedAdj
+                                                && curCachedAdj < ProcessList.CACHED_APP_MAX_ADJ) {
+                                            curCachedImpAdj++;
+                                        }
+                                    }
+                                    inGroup = true;
+                                } else {
+                                    lastCachedGroupUid = app.uid;
+                                    lastCachedGroup = app.connectionGroup;
+                                    lastCachedGroupImportance = app.connectionImportance;
+                                }
+                            }
+                            if (!inGroup && curCachedAdj != nextCachedAdj) {
+                                stepCached++;
+                                curCachedImpAdj = 0;
+                                if (stepCached >= cachedFactor) {
+                                    stepCached = 0;
+                                    curCachedAdj = nextCachedAdj;
+                                    nextCachedAdj += ProcessList.CACHED_APP_IMPORTANCE_LEVELS * 2;
+                                    if (nextCachedAdj > ProcessList.CACHED_APP_MAX_ADJ) {
+                                        nextCachedAdj = ProcessList.CACHED_APP_MAX_ADJ;
+                                    }
+                                }
+                            }
+                            // This process is a cached process holding activities...
+                            // assign it the next cached value for that type, and then
+                            // step that cached level.
+                            app.setCurRawAdj(curCachedAdj + curCachedImpAdj);
+                            app.curAdj = app.modifyRawOomAdj(curCachedAdj + curCachedImpAdj);
+                            if (DEBUG_LRU && false) Slog.d(TAG_LRU, "Assigning activity LRU #" + i
+                                    + " adj: " + app.curAdj + " (curCachedAdj=" + curCachedAdj
+                                    + " curCachedImpAdj=" + curCachedImpAdj + ")");
+                            break;
+                        default:
+                            // Figure out the next cached level.
+                            if (curEmptyAdj != nextEmptyAdj) {
+                                stepEmpty++;
+                                if (stepEmpty >= emptyFactor) {
+                                    stepEmpty = 0;
+                                    curEmptyAdj = nextEmptyAdj;
+                                    nextEmptyAdj += ProcessList.CACHED_APP_IMPORTANCE_LEVELS * 2;
+                                    if (nextEmptyAdj > ProcessList.CACHED_APP_MAX_ADJ) {
+                                        nextEmptyAdj = ProcessList.CACHED_APP_MAX_ADJ;
+                                    }
+                                }
+                            }
+                            // For everything else, assign next empty cached process
+                            // level and bump that up.  Note that this means that
+                            // long-running services that have dropped down to the
+                            // cached level will be treated as empty (since their process
+                            // state is still as a service), which is what we want.
+                            app.setCurRawAdj(curEmptyAdj);
+                            app.curAdj = app.modifyRawOomAdj(curEmptyAdj);
+                            if (DEBUG_LRU && false) Slog.d(TAG_LRU, "Assigning empty LRU #" + i
+                                    + " adj: " + app.curAdj + " (curEmptyAdj=" + curEmptyAdj
+                                    + ")");
+                            break;
+                    }
+                }
+            }
+        }
+
+        // Cycle strategy:
+        // - Retry computing any process that has encountered a cycle.
+        // - Continue retrying until no process was promoted.
+        // - Iterate from least important to most important.
+        int cycleCount = 0;
+        while (retryCycles && cycleCount < 10) {
+            cycleCount++;
+            retryCycles = false;
+
+            for (int i = 0; i < N; i++) {
+                ProcessRecord app = mProcessList.mLruProcesses.get(i);
+                if (!app.killedByAm && app.thread != null && app.containsCycle == true) {
+                    app.adjSeq--;
+                    app.completedAdjSeq--;
+                }
+            }
+
+            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,
+                            true)) {
+                        retryCycles = true;
+                    }
+                }
+            }
+        }
+
+        lastCachedGroup = lastCachedGroupUid = 0;
+
+        for (int i = N - 1; i >= 0; i--) {
+            ProcessRecord app = mProcessList.mLruProcesses.get(i);
+            if (!app.killedByAm && app.thread != null) {
+                applyOomAdjLocked(app, true, now, nowElapsed);
+
+                // Count the number of process types.
+                switch (app.getCurProcState()) {
+                    case PROCESS_STATE_CACHED_ACTIVITY:
+                    case ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT:
+                        mNumCachedHiddenProcs++;
+                        numCached++;
+                        if (app.connectionGroup != 0) {
+                            if (lastCachedGroupUid == app.uid
+                                    && lastCachedGroup == app.connectionGroup) {
+                                // If this process is the next in the same group, we don't
+                                // want it to count against our limit of the number of cached
+                                // processes, so bump up the group count to account for it.
+                                numCachedExtraGroup++;
+                            } else {
+                                lastCachedGroupUid = app.uid;
+                                lastCachedGroup = app.connectionGroup;
+                            }
+                        } else {
+                            lastCachedGroupUid = lastCachedGroup = 0;
+                        }
+                        if ((numCached - numCachedExtraGroup) > cachedProcessLimit) {
+                            app.kill("cached #" + numCached, true);
+                        }
+                        break;
+                    case ActivityManager.PROCESS_STATE_CACHED_EMPTY:
+                        if (numEmpty > mConstants.CUR_TRIM_EMPTY_PROCESSES
+                                && app.lastActivityTime < oldTime) {
+                            app.kill("empty for "
+                                    + ((oldTime + ProcessList.MAX_EMPTY_TIME - app.lastActivityTime)
+                                    / 1000) + "s", true);
+                        } else {
+                            numEmpty++;
+                            if (numEmpty > emptyProcessLimit) {
+                                app.kill("empty #" + numEmpty, true);
+                            }
+                        }
+                        break;
+                    default:
+                        mNumNonCachedProcs++;
+                        break;
+                }
+
+                if (app.isolated && app.services.size() <= 0 && app.isolatedEntryPoint == null) {
+                    // If this is an isolated process, there are no services
+                    // running in it, and it's not a special process with a
+                    // custom entry point, then the process is no longer
+                    // needed.  We agressively kill these because we can by
+                    // definition not re-use the same process again, and it is
+                    // good to avoid having whatever code was running in them
+                    // left sitting around after no longer needed.
+                    app.kill("isolated not needed", true);
+                } else {
+                    // Keeping this process, update its uid.
+                    final UidRecord uidRec = app.uidRecord;
+                    if (uidRec != null) {
+                        uidRec.ephemeral = app.info.isInstantApp();
+                        if (uidRec.getCurProcState() > app.getCurProcState()) {
+                            uidRec.setCurProcState(app.getCurProcState());
+                        }
+                        if (app.hasForegroundServices()) {
+                            uidRec.foregroundServices = true;
+                        }
+                    }
+                }
+
+                if (app.getCurProcState() >= ActivityManager.PROCESS_STATE_HOME
+                        && !app.killedByAm) {
+                    numTrimming++;
+                }
+            }
+        }
+
+        mService.incrementProcStateSeqAndNotifyAppsLocked();
+
+        mNumServiceProcs = mNewNumServiceProcs;
+
+        boolean allChanged = mService.updateLowMemStateLocked(numCached, numEmpty, numTrimming);
+
+        if (mService.mAlwaysFinishActivities) {
+            // Need to do this on its own message because the stack may not
+            // be in a consistent state at this point.
+            mService.mAtmInternal.scheduleDestroyAllActivities("always-finish");
+        }
+
+        if (allChanged) {
+            mService.requestPssAllProcsLocked(now, false,
+                    mService.mProcessStats.isMemFactorLowered());
+        }
+
+        ArrayList<UidRecord> becameIdle = null;
+
+        // Update from any uid changes.
+        if (mLocalPowerManager != null) {
+            mLocalPowerManager.startUidChanges();
+        }
+        for (int i = mActiveUids.size() - 1; i >= 0; i--) {
+            final UidRecord uidRec = mActiveUids.valueAt(i);
+            int uidChange = UidRecord.CHANGE_PROCSTATE;
+            if (uidRec.getCurProcState() != PROCESS_STATE_NONEXISTENT
+                    && (uidRec.setProcState != uidRec.getCurProcState()
+                    || uidRec.setWhitelist != uidRec.curWhitelist)) {
+                if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS, "Changes in " + uidRec
+                        + ": proc state from " + uidRec.setProcState + " to "
+                        + uidRec.getCurProcState() + ", whitelist from " + uidRec.setWhitelist
+                        + " to " + uidRec.curWhitelist);
+                if (ActivityManager.isProcStateBackground(uidRec.getCurProcState())
+                        && !uidRec.curWhitelist) {
+                    // UID is now in the background (and not on the temp whitelist).  Was it
+                    // previously in the foreground (or on the temp whitelist)?
+                    if (!ActivityManager.isProcStateBackground(uidRec.setProcState)
+                            || uidRec.setWhitelist) {
+                        uidRec.lastBackgroundTime = nowElapsed;
+                        if (!mService.mHandler.hasMessages(IDLE_UIDS_MSG)) {
+                            // Note: the background settle time is in elapsed realtime, while
+                            // the handler time base is uptime.  All this means is that we may
+                            // stop background uids later than we had intended, but that only
+                            // happens because the device was sleeping so we are okay anyway.
+                            mService.mHandler.sendEmptyMessageDelayed(IDLE_UIDS_MSG,
+                                    mConstants.BACKGROUND_SETTLE_TIME);
+                        }
+                    }
+                    if (uidRec.idle && !uidRec.setIdle) {
+                        uidChange = UidRecord.CHANGE_IDLE;
+                        if (becameIdle == null) {
+                            becameIdle = new ArrayList<>();
+                        }
+                        becameIdle.add(uidRec);
+                    }
+                } else {
+                    if (uidRec.idle) {
+                        uidChange = UidRecord.CHANGE_ACTIVE;
+                        EventLogTags.writeAmUidActive(uidRec.uid);
+                        uidRec.idle = false;
+                    }
+                    uidRec.lastBackgroundTime = 0;
+                }
+                final boolean wasCached = uidRec.setProcState
+                        > ActivityManager.PROCESS_STATE_RECEIVER;
+                final boolean isCached = uidRec.getCurProcState()
+                        > ActivityManager.PROCESS_STATE_RECEIVER;
+                if (wasCached != isCached || uidRec.setProcState == PROCESS_STATE_NONEXISTENT) {
+                    uidChange |= isCached ? UidRecord.CHANGE_CACHED : UidRecord.CHANGE_UNCACHED;
+                }
+                uidRec.setProcState = uidRec.getCurProcState();
+                uidRec.setWhitelist = uidRec.curWhitelist;
+                uidRec.setIdle = uidRec.idle;
+                mService.enqueueUidChangeLocked(uidRec, -1, uidChange);
+                mService.noteUidProcessState(uidRec.uid, uidRec.getCurProcState());
+                if (uidRec.foregroundServices) {
+                    mService.mServices.foregroundServiceProcStateChangedLocked(uidRec);
+                }
+            }
+        }
+        if (mLocalPowerManager != null) {
+            mLocalPowerManager.finishUidChanges();
+        }
+
+        if (becameIdle != null) {
+            // If we have any new uids that became idle this time, we need to make sure
+            // they aren't left with running services.
+            for (int i = becameIdle.size() - 1; i >= 0; i--) {
+                mService.mServices.stopInBackgroundLocked(becameIdle.get(i).uid);
+            }
+        }
+
+        if (mService.mProcessStats.shouldWriteNowLocked(now)) {
+            mService.mHandler.post(new ActivityManagerService.ProcStatsRunnable(mService,
+                    mService.mProcessStats));
+        }
+
+        // Run this after making sure all procstates are updated.
+        mService.mProcessStats.updateTrackingAssociationsLocked(mAdjSeq, now);
+
+        if (DEBUG_OOM_ADJ) {
+            final long duration = SystemClock.uptimeMillis() - now;
+            if (false) {
+                Slog.d(TAG_OOM_ADJ, "Did OOM ADJ in " + duration + "ms",
+                        new RuntimeException("here").fillInStackTrace());
+            } else {
+                Slog.d(TAG_OOM_ADJ, "Did OOM ADJ in " + duration + "ms");
+            }
+        }
+        mService.mOomAdjProfiler.oomAdjEnded();
+    }
+
+    private final ComputeOomAdjWindowCallback mTmpComputeOomAdjWindowCallback =
+            new ComputeOomAdjWindowCallback();
+
+    /** These methods are called inline during computeOomAdjLocked(), on the same thread */
+    private final class ComputeOomAdjWindowCallback
+            implements WindowProcessController.ComputeOomAdjCallback {
+
+        ProcessRecord app;
+        int adj;
+        boolean foregroundActivities;
+        int procState;
+        int schedGroup;
+        int appUid;
+        int logUid;
+        int processStateCurTop;
+
+        void initialize(ProcessRecord app, int adj, boolean foregroundActivities,
+                int procState, int schedGroup, int appUid, int logUid, int processStateCurTop) {
+            this.app = app;
+            this.adj = adj;
+            this.foregroundActivities = foregroundActivities;
+            this.procState = procState;
+            this.schedGroup = schedGroup;
+            this.appUid = appUid;
+            this.logUid = logUid;
+            this.processStateCurTop = processStateCurTop;
+        }
+
+        @Override
+        public void onVisibleActivity() {
+            // App has a visible activity; only upgrade adjustment.
+            if (adj > ProcessList.VISIBLE_APP_ADJ) {
+                adj = ProcessList.VISIBLE_APP_ADJ;
+                app.adjType = "vis-activity";
+                if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
+                    reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise adj to vis-activity: " + app);
+                }
+            }
+            if (procState > processStateCurTop) {
+                procState = processStateCurTop;
+                app.adjType = "vis-activity";
+                if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
+                    reportOomAdjMessageLocked(TAG_OOM_ADJ,
+                            "Raise procstate to vis-activity (top): " + app);
+                }
+            }
+            if (schedGroup < ProcessList.SCHED_GROUP_DEFAULT) {
+                schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
+            }
+            app.cached = false;
+            app.empty = false;
+            foregroundActivities = true;
+        }
+
+        @Override
+        public void onPausedActivity() {
+            if (adj > ProcessList.PERCEPTIBLE_APP_ADJ) {
+                adj = ProcessList.PERCEPTIBLE_APP_ADJ;
+                app.adjType = "pause-activity";
+                if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
+                    reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise adj to pause-activity: "  + app);
+                }
+            }
+            if (procState > processStateCurTop) {
+                procState = processStateCurTop;
+                app.adjType = "pause-activity";
+                if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
+                    reportOomAdjMessageLocked(TAG_OOM_ADJ,
+                            "Raise procstate to pause-activity (top): "  + app);
+                }
+            }
+            if (schedGroup < ProcessList.SCHED_GROUP_DEFAULT) {
+                schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
+            }
+            app.cached = false;
+            app.empty = false;
+            foregroundActivities = true;
+        }
+
+        @Override
+        public void onStoppingActivity(boolean finishing) {
+            if (adj > ProcessList.PERCEPTIBLE_APP_ADJ) {
+                adj = ProcessList.PERCEPTIBLE_APP_ADJ;
+                app.adjType = "stop-activity";
+                if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
+                    reportOomAdjMessageLocked(TAG_OOM_ADJ,
+                            "Raise adj to stop-activity: "  + app);
+                }
+            }
+
+            // For the process state, we will at this point consider the process to be cached. It
+            // will be cached either as an activity or empty depending on whether the activity is
+            // finishing. We do this so that we can treat the process as cached for purposes of
+            // memory trimming (determining current memory level, trim command to send to process)
+            // since there can be an arbitrary number of stopping processes and they should soon all
+            // go into the cached state.
+            if (!finishing) {
+                if (procState > PROCESS_STATE_LAST_ACTIVITY) {
+                    procState = PROCESS_STATE_LAST_ACTIVITY;
+                    app.adjType = "stop-activity";
+                    if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
+                        reportOomAdjMessageLocked(TAG_OOM_ADJ,
+                                "Raise procstate to stop-activity: " + app);
+                    }
+                }
+            }
+            app.cached = false;
+            app.empty = false;
+            foregroundActivities = true;
+        }
+
+        @Override
+        public void onOtherActivity() {
+            if (procState > PROCESS_STATE_CACHED_ACTIVITY) {
+                procState = PROCESS_STATE_CACHED_ACTIVITY;
+                app.adjType = "cch-act";
+                if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
+                    reportOomAdjMessageLocked(TAG_OOM_ADJ,
+                            "Raise procstate to cached activity: " + app);
+                }
+            }
+        }
+    }
+
+    private final boolean computeOomAdjLocked(ProcessRecord app, int cachedAdj,
+            ProcessRecord TOP_APP, boolean doingAll, long now, boolean cycleReEval) {
+        if (mAdjSeq == app.adjSeq) {
+            if (app.adjSeq == app.completedAdjSeq) {
+                // This adjustment has already been computed successfully.
+                return false;
+            } else {
+                // The process is being computed, so there is a cycle. We cannot
+                // rely on this process's state.
+                app.containsCycle = true;
+
+                return false;
+            }
+        }
+
+        if (app.thread == null) {
+            app.adjSeq = mAdjSeq;
+            app.setCurrentSchedulingGroup(ProcessList.SCHED_GROUP_BACKGROUND);
+            app.setCurProcState(ActivityManager.PROCESS_STATE_CACHED_EMPTY);
+            app.curAdj = ProcessList.CACHED_APP_MAX_ADJ;
+            app.setCurRawAdj(ProcessList.CACHED_APP_MAX_ADJ);
+            app.completedAdjSeq = app.adjSeq;
+            return false;
+        }
+
+        app.adjTypeCode = ActivityManager.RunningAppProcessInfo.REASON_UNKNOWN;
+        app.adjSource = null;
+        app.adjTarget = null;
+        app.empty = false;
+        app.cached = false;
+
+        final WindowProcessController wpc = app.getWindowProcessController();
+        final int appUid = app.info.uid;
+        final int logUid = mService.mCurOomAdjUid;
+
+        int prevAppAdj = app.curAdj;
+        int prevProcState = app.getCurProcState();
+
+        if (app.maxAdj <= ProcessList.FOREGROUND_APP_ADJ) {
+            // The max adjustment doesn't allow this app to be anything
+            // below foreground, so it is not worth doing work for it.
+            if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
+                mService.reportOomAdjMessageLocked(TAG_OOM_ADJ, "Making fixed: " + app);
+            }
+            app.adjType = "fixed";
+            app.adjSeq = mAdjSeq;
+            app.setCurRawAdj(app.maxAdj);
+            app.setHasForegroundActivities(false);
+            app.setCurrentSchedulingGroup(ProcessList.SCHED_GROUP_DEFAULT);
+            app.setCurProcState(ActivityManager.PROCESS_STATE_PERSISTENT);
+            // System processes can do UI, and when they do we want to have
+            // them trim their memory after the user leaves the UI.  To
+            // facilitate this, here we need to determine whether or not it
+            // is currently showing UI.
+            app.systemNoUi = true;
+            if (app == TOP_APP) {
+                app.systemNoUi = false;
+                app.setCurrentSchedulingGroup(ProcessList.SCHED_GROUP_TOP_APP);
+                app.adjType = "pers-top-activity";
+            } else if (app.hasTopUi()) {
+                // sched group/proc state adjustment is below
+                app.systemNoUi = false;
+                app.adjType = "pers-top-ui";
+            } else if (wpc.hasVisibleActivities()) {
+                app.systemNoUi = false;
+            }
+            if (!app.systemNoUi) {
+                if (mService.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 || app.getCurProcState() < prevProcState;
+        }
+
+        app.systemNoUi = false;
+
+        final int PROCESS_STATE_CUR_TOP = mService.mAtmInternal.getTopProcessState();
+
+        // Determine the importance of the process, starting with most
+        // important to least, and assign an appropriate OOM adjustment.
+        int adj;
+        int schedGroup;
+        int procState;
+        int cachedAdjSeq;
+
+        boolean foregroundActivities = false;
+        mTmpBroadcastQueue.clear();
+        if (PROCESS_STATE_CUR_TOP == ActivityManager.PROCESS_STATE_TOP && app == TOP_APP) {
+            // The last app on the list is the foreground app.
+            adj = ProcessList.FOREGROUND_APP_ADJ;
+            schedGroup = ProcessList.SCHED_GROUP_TOP_APP;
+            app.adjType = "top-activity";
+            foregroundActivities = true;
+            procState = PROCESS_STATE_CUR_TOP;
+            if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
+                reportOomAdjMessageLocked(TAG_OOM_ADJ, "Making top: " + app);
+            }
+        } else if (app.runningRemoteAnimation) {
+            adj = ProcessList.VISIBLE_APP_ADJ;
+            schedGroup = ProcessList.SCHED_GROUP_TOP_APP;
+            app.adjType = "running-remote-anim";
+            procState = PROCESS_STATE_CUR_TOP;
+            if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
+                reportOomAdjMessageLocked(TAG_OOM_ADJ, "Making running remote anim: " + app);
+            }
+        } else if (app.getActiveInstrumentation() != null) {
+            // Don't want to kill running instrumentation.
+            adj = ProcessList.FOREGROUND_APP_ADJ;
+            schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
+            app.adjType = "instrumentation";
+            procState = ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE;
+            if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
+                reportOomAdjMessageLocked(TAG_OOM_ADJ, "Making instrumentation: " + app);
+            }
+        } else if (mService.isReceivingBroadcastLocked(app, mTmpBroadcastQueue)) {
+            // An app that is currently receiving a broadcast also
+            // counts as being in the foreground for OOM killer purposes.
+            // It's placed in a sched group based on the nature of the
+            // broadcast as reflected by which queue it's active in.
+            adj = ProcessList.FOREGROUND_APP_ADJ;
+            schedGroup = (mTmpBroadcastQueue.contains(mService.mFgBroadcastQueue))
+                    ? ProcessList.SCHED_GROUP_DEFAULT : ProcessList.SCHED_GROUP_BACKGROUND;
+            app.adjType = "broadcast";
+            procState = ActivityManager.PROCESS_STATE_RECEIVER;
+            if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
+                reportOomAdjMessageLocked(TAG_OOM_ADJ, "Making broadcast: " + app);
+            }
+        } else if (app.executingServices.size() > 0) {
+            // An app that is currently executing a service callback also
+            // counts as being in the foreground.
+            adj = ProcessList.FOREGROUND_APP_ADJ;
+            schedGroup = app.execServicesFg ?
+                    ProcessList.SCHED_GROUP_DEFAULT : ProcessList.SCHED_GROUP_BACKGROUND;
+            app.adjType = "exec-service";
+            procState = ActivityManager.PROCESS_STATE_SERVICE;
+            if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
+                reportOomAdjMessageLocked(TAG_OOM_ADJ, "Making exec-service: " + app);
+            }
+            //Slog.i(TAG, "EXEC " + (app.execServicesFg ? "FG" : "BG") + ": " + app);
+        } else if (app == TOP_APP) {
+            adj = ProcessList.FOREGROUND_APP_ADJ;
+            schedGroup = ProcessList.SCHED_GROUP_BACKGROUND;
+            app.adjType = "top-sleeping";
+            foregroundActivities = true;
+            procState = PROCESS_STATE_CUR_TOP;
+            if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
+                reportOomAdjMessageLocked(TAG_OOM_ADJ, "Making top (sleeping): " + app);
+            }
+        } else {
+            // As far as we know the process is empty.  We may change our mind later.
+            schedGroup = ProcessList.SCHED_GROUP_BACKGROUND;
+            // At this point we don't actually know the adjustment.  Use the cached adj
+            // value that the caller wants us to.
+            adj = cachedAdj;
+            procState = ActivityManager.PROCESS_STATE_CACHED_EMPTY;
+            app.cached = true;
+            app.empty = true;
+            app.adjType = "cch-empty";
+            if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
+                reportOomAdjMessageLocked(TAG_OOM_ADJ, "Making empty: " + app);
+            }
+        }
+
+        // Examine all activities if not already foreground.
+        if (!foregroundActivities && wpc.hasActivities()) {
+            mTmpComputeOomAdjWindowCallback.initialize(app, adj, foregroundActivities, procState,
+                    schedGroup, appUid, logUid, PROCESS_STATE_CUR_TOP);
+            final int minLayer = wpc.computeOomAdjFromActivities(
+                    ProcessList.VISIBLE_APP_LAYER_MAX, mTmpComputeOomAdjWindowCallback);
+
+            adj = mTmpComputeOomAdjWindowCallback.adj;
+            foregroundActivities = mTmpComputeOomAdjWindowCallback.foregroundActivities;
+            procState = mTmpComputeOomAdjWindowCallback.procState;
+            schedGroup = mTmpComputeOomAdjWindowCallback.schedGroup;
+
+            if (adj == ProcessList.VISIBLE_APP_ADJ) {
+                adj += minLayer;
+            }
+        }
+
+        if (procState > ActivityManager.PROCESS_STATE_CACHED_RECENT && app.hasRecentTasks()) {
+            procState = ActivityManager.PROCESS_STATE_CACHED_RECENT;
+            app.adjType = "cch-rec";
+            if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
+                reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise procstate to cached recent: " + app);
+            }
+        }
+
+        if (adj > ProcessList.PERCEPTIBLE_APP_ADJ
+                || procState > ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE) {
+            if (app.hasForegroundServices()) {
+                // The user is aware of this app, so make it visible.
+                adj = ProcessList.PERCEPTIBLE_APP_ADJ;
+                procState = ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE;
+                app.cached = false;
+                app.adjType = "fg-service";
+                schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
+                if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
+                    reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise to fg service: " + app);
+                }
+            } else if (app.hasOverlayUi()) {
+                // The process is display an overlay UI.
+                adj = ProcessList.PERCEPTIBLE_APP_ADJ;
+                procState = ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND;
+                app.cached = false;
+                app.adjType = "has-overlay-ui";
+                schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
+                if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
+                    reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise to overlay ui: " + app);
+                }
+            }
+        }
+
+        // If the app was recently in the foreground and moved to a foreground service status,
+        // allow it to get a higher rank in memory for some time, compared to other foreground
+        // services so that it can finish performing any persistence/processing of in-memory state.
+        if (app.hasForegroundServices() && adj > ProcessList.PERCEPTIBLE_RECENT_FOREGROUND_APP_ADJ
+                && (app.lastTopTime + mConstants.TOP_TO_FGS_GRACE_DURATION > now
+                || app.setProcState <= ActivityManager.PROCESS_STATE_TOP)) {
+            adj = ProcessList.PERCEPTIBLE_RECENT_FOREGROUND_APP_ADJ;
+            app.adjType = "fg-service-act";
+            if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
+                reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise to recent fg: " + app);
+            }
+        }
+
+        if (adj > ProcessList.PERCEPTIBLE_APP_ADJ
+                || procState > ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND) {
+            if (app.forcingToImportant != null) {
+                // This is currently used for toasts...  they are not interactive, and
+                // we don't want them to cause the app to become fully foreground (and
+                // thus out of background check), so we yes the best background level we can.
+                adj = ProcessList.PERCEPTIBLE_APP_ADJ;
+                procState = ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND;
+                app.cached = false;
+                app.adjType = "force-imp";
+                app.adjSource = app.forcingToImportant;
+                schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
+                if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
+                    reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise to force imp: " + app);
+                }
+            }
+        }
+
+        if (mService.mAtmInternal.isHeavyWeightProcess(app.getWindowProcessController())) {
+            if (adj > ProcessList.HEAVY_WEIGHT_APP_ADJ) {
+                // We don't want to kill the current heavy-weight process.
+                adj = ProcessList.HEAVY_WEIGHT_APP_ADJ;
+                schedGroup = ProcessList.SCHED_GROUP_BACKGROUND;
+                app.cached = false;
+                app.adjType = "heavy";
+                if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
+                    reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise adj to heavy: " + app);
+                }
+            }
+            if (procState > ActivityManager.PROCESS_STATE_HEAVY_WEIGHT) {
+                procState = ActivityManager.PROCESS_STATE_HEAVY_WEIGHT;
+                app.adjType = "heavy";
+                if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
+                    reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise procstate to heavy: " + app);
+                }
+            }
+        }
+
+        if (wpc.isHomeProcess()) {
+            if (adj > ProcessList.HOME_APP_ADJ) {
+                // This process is hosting what we currently consider to be the
+                // home app, so we don't want to let it go into the background.
+                adj = ProcessList.HOME_APP_ADJ;
+                schedGroup = ProcessList.SCHED_GROUP_BACKGROUND;
+                app.cached = false;
+                app.adjType = "home";
+                if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
+                    reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise adj to home: " + app);
+                }
+            }
+            if (procState > ActivityManager.PROCESS_STATE_HOME) {
+                procState = ActivityManager.PROCESS_STATE_HOME;
+                app.adjType = "home";
+                if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
+                    reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise procstate to home: " + app);
+                }
+            }
+        }
+
+        if (wpc.isPreviousProcess() && app.hasActivities()) {
+            if (adj > ProcessList.PREVIOUS_APP_ADJ) {
+                // This was the previous process that showed UI to the user.
+                // We want to try to keep it around more aggressively, to give
+                // a good experience around switching between two apps.
+                adj = ProcessList.PREVIOUS_APP_ADJ;
+                schedGroup = ProcessList.SCHED_GROUP_BACKGROUND;
+                app.cached = false;
+                app.adjType = "previous";
+                if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
+                    reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise adj to prev: " + app);
+                }
+            }
+            if (procState > PROCESS_STATE_LAST_ACTIVITY) {
+                procState = PROCESS_STATE_LAST_ACTIVITY;
+                app.adjType = "previous";
+                if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
+                    reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise procstate to prev: " + app);
+                }
+            }
+        }
+
+        if (false) Slog.i(TAG, "OOM " + app + ": initial adj=" + adj
+                + " reason=" + app.adjType);
+
+        // 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. 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;
+
+        final BackupRecord backupTarget = mService.mBackupTargets.get(app.userId);
+        if (backupTarget != null && app == backupTarget.app) {
+            // If possible we want to avoid killing apps while they're being backed up
+            if (adj > ProcessList.BACKUP_APP_ADJ) {
+                if (DEBUG_BACKUP) Slog.v(TAG_BACKUP, "oom BACKUP_APP_ADJ for " + app);
+                adj = ProcessList.BACKUP_APP_ADJ;
+                if (procState > ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND) {
+                    procState = ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND;
+                }
+                app.adjType = "backup";
+                if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
+                    reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise adj to backup: " + app);
+                }
+                app.cached = false;
+            }
+            if (procState > ActivityManager.PROCESS_STATE_BACKUP) {
+                procState = ActivityManager.PROCESS_STATE_BACKUP;
+                app.adjType = "backup";
+                if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
+                    reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise procstate to backup: " + app);
+                }
+            }
+        }
+
+        boolean mayBeTop = false;
+        String mayBeTopType = null;
+        Object mayBeTopSource = null;
+        Object mayBeTopTarget = null;
+
+        for (int is = app.services.size() - 1;
+                is >= 0 && (adj > ProcessList.FOREGROUND_APP_ADJ
+                        || schedGroup == ProcessList.SCHED_GROUP_BACKGROUND
+                        || procState > ActivityManager.PROCESS_STATE_TOP);
+                is--) {
+            ServiceRecord s = app.services.valueAt(is);
+            if (s.startRequested) {
+                app.hasStartedServices = true;
+                if (procState > ActivityManager.PROCESS_STATE_SERVICE) {
+                    procState = ActivityManager.PROCESS_STATE_SERVICE;
+                    app.adjType = "started-services";
+                    if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
+                        reportOomAdjMessageLocked(TAG_OOM_ADJ,
+                                "Raise procstate to started service: " + app);
+                    }
+                }
+                if (app.hasShownUi && !wpc.isHomeProcess()) {
+                    // If this process has shown some UI, let it immediately
+                    // go to the LRU list because it may be pretty heavy with
+                    // UI stuff.  We'll tag it with a label just to help
+                    // debug and understand what is going on.
+                    if (adj > ProcessList.SERVICE_ADJ) {
+                        app.adjType = "cch-started-ui-services";
+                    }
+                } else {
+                    if (now < (s.lastActivity + mConstants.MAX_SERVICE_INACTIVITY)) {
+                        // This service has seen some activity within
+                        // recent memory, so we will keep its process ahead
+                        // of the background processes.
+                        if (adj > ProcessList.SERVICE_ADJ) {
+                            adj = ProcessList.SERVICE_ADJ;
+                            app.adjType = "started-services";
+                            if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
+                                reportOomAdjMessageLocked(TAG_OOM_ADJ,
+                                        "Raise adj to started service: " + app);
+                            }
+                            app.cached = false;
+                        }
+                    }
+                    // If we have let the service slide into the background
+                    // state, still have some text describing what it is doing
+                    // even though the service no longer has an impact.
+                    if (adj > ProcessList.SERVICE_ADJ) {
+                        app.adjType = "cch-started-services";
+                    }
+                }
+            }
+
+            for (int conni = s.connections.size() - 1;
+                    conni >= 0 && (adj > ProcessList.FOREGROUND_APP_ADJ
+                            || schedGroup == ProcessList.SCHED_GROUP_BACKGROUND
+                            || procState > ActivityManager.PROCESS_STATE_TOP);
+                    conni--) {
+                ArrayList<ConnectionRecord> clist = s.connections.valueAt(conni);
+                for (int i = 0;
+                        i < clist.size() && (adj > ProcessList.FOREGROUND_APP_ADJ
+                                || schedGroup == ProcessList.SCHED_GROUP_BACKGROUND
+                                || procState > ActivityManager.PROCESS_STATE_TOP);
+                        i++) {
+                    // XXX should compute this based on the max of
+                    // all connected clients.
+                    ConnectionRecord cr = clist.get(i);
+                    if (cr.binding.client == app) {
+                        // Binding to oneself is not interesting.
+                        continue;
+                    }
+
+                    boolean trackedProcState = false;
+                    if ((cr.flags& Context.BIND_WAIVE_PRIORITY) == 0) {
+                        ProcessRecord client = cr.binding.client;
+                        computeOomAdjLocked(client, cachedAdj, TOP_APP, doingAll, now, cycleReEval);
+
+                        if (shouldSkipDueToCycle(app, client, procState, adj, cycleReEval)) {
+                            continue;
+                        }
+
+                        int clientAdj = client.getCurRawAdj();
+                        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
+                            // doesn't propagate except under certain conditions.
+                            clientProcState = ActivityManager.PROCESS_STATE_CACHED_EMPTY;
+                        }
+                        String adjType = null;
+                        if ((cr.flags&Context.BIND_ALLOW_OOM_MANAGEMENT) != 0) {
+                            // Not doing bind OOM management, so treat
+                            // this guy more like a started service.
+                            if (app.hasShownUi && !wpc.isHomeProcess()) {
+                                // If this process has shown some UI, let it immediately
+                                // go to the LRU list because it may be pretty heavy with
+                                // UI stuff.  We'll tag it with a label just to help
+                                // debug and understand what is going on.
+                                if (adj > clientAdj) {
+                                    adjType = "cch-bound-ui-services";
+                                }
+                                app.cached = false;
+                                clientAdj = adj;
+                                clientProcState = procState;
+                            } else {
+                                if (now >= (s.lastActivity
+                                        + mConstants.MAX_SERVICE_INACTIVITY)) {
+                                    // This service has not seen activity within
+                                    // recent memory, so allow it to drop to the
+                                    // LRU list if there is no other reason to keep
+                                    // it around.  We'll also tag it with a label just
+                                    // to help debug and undertand what is going on.
+                                    if (adj > clientAdj) {
+                                        adjType = "cch-bound-services";
+                                    }
+                                    clientAdj = adj;
+                                }
+                            }
+                        }
+                        if (adj > clientAdj) {
+                            // If this process has recently shown UI, and
+                            // the process that is binding to it is less
+                            // important than being visible, then we don't
+                            // care about the binding as much as we care
+                            // about letting this process get into the LRU
+                            // list to be killed and restarted if needed for
+                            // memory.
+                            if (app.hasShownUi && !wpc.isHomeProcess()
+                                    && clientAdj > ProcessList.PERCEPTIBLE_APP_ADJ) {
+                                if (adj >= ProcessList.CACHED_APP_MIN_ADJ) {
+                                    adjType = "cch-bound-ui-services";
+                                }
+                            } else {
+                                int newAdj;
+                                if ((cr.flags&(Context.BIND_ABOVE_CLIENT
+                                        |Context.BIND_IMPORTANT)) != 0) {
+                                    if (clientAdj >= ProcessList.PERSISTENT_SERVICE_ADJ) {
+                                        newAdj = clientAdj;
+                                    } else {
+                                        // make this service persistent
+                                        newAdj = ProcessList.PERSISTENT_SERVICE_ADJ;
+                                        schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
+                                        procState = ActivityManager.PROCESS_STATE_PERSISTENT;
+                                        cr.trackProcState(procState, mAdjSeq, now);
+                                        trackedProcState = true;
+                                    }
+                                } else if ((cr.flags & Context.BIND_ADJUST_BELOW_PERCEPTIBLE) != 0
+                                        && clientAdj < ProcessList.PERCEPTIBLE_APP_ADJ
+                                        && adj > ProcessList.PERCEPTIBLE_APP_ADJ + 1) {
+                                    newAdj = ProcessList.PERCEPTIBLE_APP_ADJ + 1;
+                                } else if ((cr.flags&Context.BIND_NOT_VISIBLE) != 0
+                                        && clientAdj < ProcessList.PERCEPTIBLE_APP_ADJ
+                                        && adj > ProcessList.PERCEPTIBLE_APP_ADJ) {
+                                    newAdj = ProcessList.PERCEPTIBLE_APP_ADJ;
+                                } else if (clientAdj >= ProcessList.PERCEPTIBLE_APP_ADJ) {
+                                    newAdj = clientAdj;
+                                } else {
+                                    if (adj > ProcessList.VISIBLE_APP_ADJ) {
+                                        newAdj = Math.max(clientAdj, ProcessList.VISIBLE_APP_ADJ);
+                                    } else {
+                                        newAdj = adj;
+                                    }
+                                }
+                                if (!client.cached) {
+                                    app.cached = false;
+                                }
+                                if (adj >  newAdj) {
+                                    adj = newAdj;
+                                    app.setCurRawAdj(adj);
+                                    adjType = "service";
+                                }
+                            }
+                        }
+                        if ((cr.flags & (Context.BIND_NOT_FOREGROUND
+                                | Context.BIND_IMPORTANT_BACKGROUND)) == 0) {
+                            // This will treat important bound services identically to
+                            // the top app, which may behave differently than generic
+                            // foreground work.
+                            final int curSchedGroup = client.getCurrentSchedulingGroup();
+                            if (curSchedGroup > schedGroup) {
+                                if ((cr.flags&Context.BIND_IMPORTANT) != 0) {
+                                    schedGroup = curSchedGroup;
+                                } else {
+                                    schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
+                                }
+                            }
+                            if (clientProcState <= ActivityManager.PROCESS_STATE_TOP) {
+                                if (clientProcState == ActivityManager.PROCESS_STATE_TOP) {
+                                    // Special handling of clients who are in the top state.
+                                    // We *may* want to consider this process to be in the
+                                    // top state as well, but only if there is not another
+                                    // reason for it to be running.  Being on the top is a
+                                    // special state, meaning you are specifically running
+                                    // for the current top app.  If the process is already
+                                    // running in the background for some other reason, it
+                                    // is more important to continue considering it to be
+                                    // in the background state.
+                                    mayBeTop = true;
+                                    mayBeTopType = "service";
+                                    mayBeTopSource = cr.binding.client;
+                                    mayBeTopTarget = s.instanceName;
+                                    clientProcState = ActivityManager.PROCESS_STATE_CACHED_EMPTY;
+                                } else {
+                                    // Special handling for above-top states (persistent
+                                    // processes).  These should not bring the current process
+                                    // into the top state, since they are not on top.  Instead
+                                    // give them the best state after that.
+                                    if ((cr.flags&Context.BIND_FOREGROUND_SERVICE) != 0) {
+                                        clientProcState =
+                                                ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
+                                    } else if (mService.mWakefulness
+                                            == PowerManagerInternal.WAKEFULNESS_AWAKE &&
+                                            (cr.flags&Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE)
+                                                    != 0) {
+                                        clientProcState =
+                                                ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
+                                    } else {
+                                        clientProcState =
+                                                ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND;
+                                    }
+                                }
+                            }
+                        } else if ((cr.flags & Context.BIND_IMPORTANT_BACKGROUND) == 0) {
+                            if (clientProcState <
+                                    ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND) {
+                                clientProcState =
+                                        ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND;
+                            }
+                        } else {
+                            if (clientProcState <
+                                    ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND) {
+                                clientProcState =
+                                        ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND;
+                            }
+                        }
+                        if (!trackedProcState) {
+                            cr.trackProcState(clientProcState, mAdjSeq, now);
+                        }
+                        if (procState > clientProcState) {
+                            procState = clientProcState;
+                            app.setCurRawProcState(procState);
+                            if (adjType == null) {
+                                adjType = "service";
+                            }
+                        }
+                        if (procState < ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND
+                                && (cr.flags & Context.BIND_SHOWING_UI) != 0) {
+                            app.setPendingUiClean(true);
+                        }
+                        if (adjType != null) {
+                            app.adjType = adjType;
+                            app.adjTypeCode = ActivityManager.RunningAppProcessInfo
+                                    .REASON_SERVICE_IN_USE;
+                            app.adjSource = cr.binding.client;
+                            app.adjSourceProcState = clientProcState;
+                            app.adjTarget = s.instanceName;
+                            if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
+                                reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise to " + adjType
+                                        + ": " + app + ", due to " + cr.binding.client
+                                        + " adj=" + adj + " procState="
+                                        + ProcessList.makeProcStateString(procState));
+                            }
+                        }
+                    }
+                    if ((cr.flags&Context.BIND_TREAT_LIKE_ACTIVITY) != 0) {
+                        app.treatLikeActivity = true;
+                    }
+                    final ActivityServiceConnectionsHolder a = cr.activity;
+                    if ((cr.flags&Context.BIND_ADJUST_WITH_ACTIVITY) != 0) {
+                        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;
+                                } else {
+                                    schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
+                                }
+                            }
+                            app.cached = false;
+                            app.adjType = "service";
+                            app.adjTypeCode = ActivityManager.RunningAppProcessInfo
+                                    .REASON_SERVICE_IN_USE;
+                            app.adjSource = a;
+                            app.adjSourceProcState = procState;
+                            app.adjTarget = s.instanceName;
+                            if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
+                                reportOomAdjMessageLocked(TAG_OOM_ADJ,
+                                        "Raise to service w/activity: " + app);
+                            }
+                        }
+                    }
+                }
+            }
+        }
+
+        for (int provi = app.pubProviders.size() - 1;
+                provi >= 0 && (adj > ProcessList.FOREGROUND_APP_ADJ
+                        || schedGroup == ProcessList.SCHED_GROUP_BACKGROUND
+                        || procState > ActivityManager.PROCESS_STATE_TOP);
+                provi--) {
+            ContentProviderRecord cpr = app.pubProviders.valueAt(provi);
+            for (int i = cpr.connections.size() - 1;
+                    i >= 0 && (adj > ProcessList.FOREGROUND_APP_ADJ
+                            || schedGroup == ProcessList.SCHED_GROUP_BACKGROUND
+                            || procState > ActivityManager.PROCESS_STATE_TOP);
+                    i--) {
+                ContentProviderConnection conn = cpr.connections.get(i);
+                ProcessRecord client = conn.client;
+                if (client == app) {
+                    // Being our own client is not interesting.
+                    continue;
+                }
+                computeOomAdjLocked(client, cachedAdj, TOP_APP, doingAll, now, cycleReEval);
+
+                if (shouldSkipDueToCycle(app, client, procState, adj, cycleReEval)) {
+                    continue;
+                }
+
+                int clientAdj = client.getCurRawAdj();
+                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.
+                    clientProcState = ActivityManager.PROCESS_STATE_CACHED_EMPTY;
+                }
+                String adjType = null;
+                if (adj > clientAdj) {
+                    if (app.hasShownUi && !wpc.isHomeProcess()
+                            && clientAdj > ProcessList.PERCEPTIBLE_APP_ADJ) {
+                        adjType = "cch-ui-provider";
+                    } else {
+                        adj = clientAdj > ProcessList.FOREGROUND_APP_ADJ
+                                ? clientAdj : ProcessList.FOREGROUND_APP_ADJ;
+                        app.setCurRawAdj(adj);
+                        adjType = "provider";
+                    }
+                    app.cached &= client.cached;
+                }
+                if (clientProcState <= ActivityManager.PROCESS_STATE_TOP) {
+                    if (clientProcState == ActivityManager.PROCESS_STATE_TOP) {
+                        // Special handling of clients who are in the top state.
+                        // We *may* want to consider this process to be in the
+                        // top state as well, but only if there is not another
+                        // reason for it to be running.  Being on the top is a
+                        // special state, meaning you are specifically running
+                        // for the current top app.  If the process is already
+                        // running in the background for some other reason, it
+                        // is more important to continue considering it to be
+                        // in the background state.
+                        mayBeTop = true;
+                        clientProcState = ActivityManager.PROCESS_STATE_CACHED_EMPTY;
+                        mayBeTopType = adjType = "provider-top";
+                        mayBeTopSource = client;
+                        mayBeTopTarget = cpr.name;
+                    } else {
+                        // Special handling for above-top states (persistent
+                        // processes).  These should not bring the current process
+                        // into the top state, since they are not on top.  Instead
+                        // give them the best state after that.
+                        clientProcState =
+                                ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
+                        if (adjType == null) {
+                            adjType = "provider";
+                        }
+                    }
+                }
+                conn.trackProcState(clientProcState, mAdjSeq, now);
+                if (procState > clientProcState) {
+                    procState = clientProcState;
+                    app.setCurRawProcState(procState);
+                }
+                if (client.getCurrentSchedulingGroup() > schedGroup) {
+                    schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
+                }
+                if (adjType != null) {
+                    app.adjType = adjType;
+                    app.adjTypeCode = ActivityManager.RunningAppProcessInfo
+                            .REASON_PROVIDER_IN_USE;
+                    app.adjSource = client;
+                    app.adjSourceProcState = clientProcState;
+                    app.adjTarget = cpr.name;
+                    if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
+                        reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise to " + adjType
+                                + ": " + app + ", due to " + client
+                                + " adj=" + adj + " procState="
+                                + ProcessList.makeProcStateString(procState));
+                    }
+                }
+            }
+            // If the provider has external (non-framework) process
+            // dependencies, ensure that its adjustment is at least
+            // FOREGROUND_APP_ADJ.
+            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";
+                    app.adjTarget = cpr.name;
+                    if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
+                        reportOomAdjMessageLocked(TAG_OOM_ADJ,
+                                "Raise adj to external provider: " + app);
+                    }
+                }
+                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);
+                    }
+                }
+            }
+        }
+
+        if (app.lastProviderTime > 0 &&
+                (app.lastProviderTime + mConstants.CONTENT_PROVIDER_RETAIN_TIME) > now) {
+            if (adj > ProcessList.PREVIOUS_APP_ADJ) {
+                adj = ProcessList.PREVIOUS_APP_ADJ;
+                schedGroup = ProcessList.SCHED_GROUP_BACKGROUND;
+                app.cached = false;
+                app.adjType = "recent-provider";
+                if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
+                    reportOomAdjMessageLocked(TAG_OOM_ADJ,
+                            "Raise adj to recent provider: " + app);
+                }
+            }
+            if (procState > PROCESS_STATE_LAST_ACTIVITY) {
+                procState = PROCESS_STATE_LAST_ACTIVITY;
+                app.adjType = "recent-provider";
+                if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
+                    reportOomAdjMessageLocked(TAG_OOM_ADJ,
+                            "Raise procstate to recent provider: " + app);
+                }
+            }
+        }
+
+        if (mayBeTop && procState > ActivityManager.PROCESS_STATE_TOP) {
+            // A client of one of our services or providers is in the top state.  We
+            // *may* want to be in the top state, but not if we are already running in
+            // the background for some other reason.  For the decision here, we are going
+            // to pick out a few specific states that we want to remain in when a client
+            // is top (states that tend to be longer-term) and otherwise allow it to go
+            // to the top state.
+            switch (procState) {
+                case ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE:
+                case ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE:
+                    // Something else is keeping it at this level, just leave it.
+                    break;
+                case ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND:
+                case ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND:
+                case ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND:
+                case ActivityManager.PROCESS_STATE_SERVICE:
+                    // These all are longer-term states, so pull them up to the top
+                    // of the background states, but not all the way to the top state.
+                    procState = ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
+                    app.adjType = mayBeTopType;
+                    app.adjSource = mayBeTopSource;
+                    app.adjTarget = mayBeTopTarget;
+                    if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
+                        reportOomAdjMessageLocked(TAG_OOM_ADJ, "May be top raise to " + mayBeTopType
+                                + ": " + app + ", due to " + mayBeTopSource
+                                + " adj=" + adj + " procState="
+                                + ProcessList.makeProcStateString(procState));
+                    }
+                    break;
+                default:
+                    // Otherwise, top is a better choice, so take it.
+                    procState = ActivityManager.PROCESS_STATE_TOP;
+                    app.adjType = mayBeTopType;
+                    app.adjSource = mayBeTopSource;
+                    app.adjTarget = mayBeTopTarget;
+                    if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
+                        reportOomAdjMessageLocked(TAG_OOM_ADJ, "May be top raise to " + mayBeTopType
+                                + ": " + app + ", due to " + mayBeTopSource
+                                + " adj=" + adj + " procState="
+                                + ProcessList.makeProcStateString(procState));
+                    }
+                    break;
+            }
+        }
+
+        if (procState >= ActivityManager.PROCESS_STATE_CACHED_EMPTY) {
+            if (app.hasClientActivities()) {
+                // This is a cached process, but with client activities.  Mark it so.
+                procState = ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT;
+                app.adjType = "cch-client-act";
+            } else if (app.treatLikeActivity) {
+                // This is a cached process, but somebody wants us to treat it like it has
+                // an activity, okay!
+                procState = PROCESS_STATE_CACHED_ACTIVITY;
+                app.adjType = "cch-as-act";
+            }
+        }
+
+        if (adj == ProcessList.SERVICE_ADJ) {
+            if (doingAll) {
+                app.serviceb = mNewNumAServiceProcs > (mNumServiceProcs/3);
+                mNewNumServiceProcs++;
+                //Slog.i(TAG, "ADJ " + app + " serviceb=" + app.serviceb);
+                if (!app.serviceb) {
+                    // This service isn't far enough down on the LRU list to
+                    // normally be a B service, but if we are low on RAM and it
+                    // is large we want to force it down since we would prefer to
+                    // keep launcher over it.
+                    if (mService.mLastMemoryLevel > ProcessStats.ADJ_MEM_FACTOR_NORMAL
+                            && app.lastPss >= mProcessList.getCachedRestoreThresholdKb()) {
+                        app.serviceHighRam = true;
+                        app.serviceb = true;
+                        //Slog.i(TAG, "ADJ " + app + " high ram!");
+                    } else {
+                        mNewNumAServiceProcs++;
+                        //Slog.i(TAG, "ADJ " + app + " not high ram!");
+                    }
+                } else {
+                    app.serviceHighRam = false;
+                }
+            }
+            if (app.serviceb) {
+                adj = ProcessList.SERVICE_B_ADJ;
+            }
+        }
+
+        app.setCurRawAdj(adj);
+
+        //Slog.i(TAG, "OOM ADJ " + app + ": pid=" + app.pid +
+        //      " adj=" + adj + " curAdj=" + app.curAdj + " maxAdj=" + app.maxAdj);
+        if (adj > app.maxAdj) {
+            adj = app.maxAdj;
+            if (app.maxAdj <= ProcessList.PERCEPTIBLE_APP_ADJ) {
+                schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
+            }
+        }
+
+        // Put bound foreground services in a special sched group for additional
+        // restrictions on screen off
+        if (procState >= ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE &&
+                mService.mWakefulness != PowerManagerInternal.WAKEFULNESS_AWAKE) {
+            if (schedGroup > ProcessList.SCHED_GROUP_RESTRICTED) {
+                schedGroup = ProcessList.SCHED_GROUP_RESTRICTED;
+            }
+        }
+
+        // Do final modification to adj.  Everything we do between here and applying
+        // the final setAdj must be done in this function, because we will also use
+        // it when computing the final cached adj later.  Note that we don't need to
+        // worry about this for max adj above, since max adj will always be used to
+        // keep it out of the cached vaues.
+        app.curAdj = app.modifyRawOomAdj(adj);
+        app.setCurrentSchedulingGroup(schedGroup);
+        app.setCurProcState(procState);
+        app.setCurRawProcState(procState);
+        app.setHasForegroundActivities(foregroundActivities);
+        app.completedAdjSeq = mAdjSeq;
+
+        // if curAdj or curProcState improved, then this process was promoted
+        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;
+    }
+
+    /** Inform the oomadj observer of changes to oomadj. Used by tests. */
+    @GuardedBy("mService")
+    void reportOomAdjMessageLocked(String tag, String msg) {
+        Slog.d(tag, msg);
+        if (mService.mCurOomAdjObserver != null) {
+            mService.mUiHandler.obtainMessage(DISPATCH_OOM_ADJ_OBSERVER_MSG, msg).sendToTarget();
+        }
+    }
+
+    /** Applies the computed oomadj, procstate and sched group values and freezes them in set* */
+    @GuardedBy("mService")
+    private final boolean applyOomAdjLocked(ProcessRecord app, boolean doingAll, long now,
+            long nowElapsed) {
+        boolean success = true;
+
+        if (app.getCurRawAdj() != app.setRawAdj) {
+            app.setRawAdj = app.getCurRawAdj();
+        }
+
+        int changes = 0;
+
+        if (app.curAdj != app.setAdj) {
+            // don't compact during bootup
+            if (mConstants.USE_COMPACTION && mService.mBooted) {
+                // Perform a minor compaction when a perceptible app becomes the prev/home app
+                // Perform a major compaction when any app enters cached
+                // reminder: here, setAdj is previous state, curAdj is upcoming state
+                if (app.setAdj <= ProcessList.PERCEPTIBLE_APP_ADJ &&
+                        (app.curAdj == ProcessList.PREVIOUS_APP_ADJ ||
+                                app.curAdj == ProcessList.HOME_APP_ADJ)) {
+                    mAppCompact.compactAppSome(app);
+                } else if (app.setAdj < ProcessList.CACHED_APP_MIN_ADJ &&
+                        app.curAdj >= ProcessList.CACHED_APP_MIN_ADJ) {
+                    mAppCompact.compactAppFull(app);
+                }
+            }
+            ProcessList.setOomAdj(app.pid, app.uid, app.curAdj);
+            if (DEBUG_SWITCH || DEBUG_OOM_ADJ || mService.mCurOomAdjUid == app.info.uid) {
+                String msg = "Set " + app.pid + " " + app.processName + " adj "
+                        + app.curAdj + ": " + app.adjType;
+                reportOomAdjMessageLocked(TAG_OOM_ADJ, msg);
+            }
+            app.setAdj = app.curAdj;
+            app.verifiedAdj = ProcessList.INVALID_ADJ;
+        }
+
+        final int curSchedGroup = app.getCurrentSchedulingGroup();
+        if (app.setSchedGroup != curSchedGroup) {
+            int oldSchedGroup = app.setSchedGroup;
+            app.setSchedGroup = curSchedGroup;
+            if (DEBUG_SWITCH || DEBUG_OOM_ADJ || mService.mCurOomAdjUid == app.uid) {
+                String msg = "Setting sched group of " + app.processName
+                        + " to " + curSchedGroup + ": " + app.adjType;
+                reportOomAdjMessageLocked(TAG_OOM_ADJ, msg);
+            }
+            if (app.waitingToKill != null && app.curReceivers.isEmpty()
+                    && app.setSchedGroup == ProcessList.SCHED_GROUP_BACKGROUND) {
+                app.kill(app.waitingToKill, true);
+                success = false;
+            } else {
+                int processGroup;
+                switch (curSchedGroup) {
+                    case ProcessList.SCHED_GROUP_BACKGROUND:
+                        processGroup = THREAD_GROUP_BG_NONINTERACTIVE;
+                        break;
+                    case ProcessList.SCHED_GROUP_TOP_APP:
+                    case ProcessList.SCHED_GROUP_TOP_APP_BOUND:
+                        processGroup = THREAD_GROUP_TOP_APP;
+                        break;
+                    case ProcessList.SCHED_GROUP_RESTRICTED:
+                        processGroup = THREAD_GROUP_RESTRICTED;
+                        break;
+                    default:
+                        processGroup = THREAD_GROUP_DEFAULT;
+                        break;
+                }
+                long oldId = Binder.clearCallingIdentity();
+                try {
+                    setProcessGroup(app.pid, processGroup);
+                    if (curSchedGroup == ProcessList.SCHED_GROUP_TOP_APP) {
+                        // do nothing if we already switched to RT
+                        if (oldSchedGroup != ProcessList.SCHED_GROUP_TOP_APP) {
+                            app.getWindowProcessController().onTopProcChanged();
+                            if (mService.mUseFifoUiScheduling) {
+                                // Switch UI pipeline for app to SCHED_FIFO
+                                app.savedPriority = Process.getThreadPriority(app.pid);
+                                mService.scheduleAsFifoPriority(app.pid, /* suppressLogs */true);
+                                if (app.renderThreadTid != 0) {
+                                    mService.scheduleAsFifoPriority(app.renderThreadTid,
+                                            /* suppressLogs */true);
+                                    if (DEBUG_OOM_ADJ) {
+                                        Slog.d("UI_FIFO", "Set RenderThread (TID " +
+                                                app.renderThreadTid + ") to FIFO");
+                                    }
+                                } else {
+                                    if (DEBUG_OOM_ADJ) {
+                                        Slog.d("UI_FIFO", "Not setting RenderThread TID");
+                                    }
+                                }
+                            } else {
+                                // Boost priority for top app UI and render threads
+                                setThreadPriority(app.pid, TOP_APP_PRIORITY_BOOST);
+                                if (app.renderThreadTid != 0) {
+                                    try {
+                                        setThreadPriority(app.renderThreadTid,
+                                                TOP_APP_PRIORITY_BOOST);
+                                    } catch (IllegalArgumentException e) {
+                                        // thread died, ignore
+                                    }
+                                }
+                            }
+                        }
+                    } else if (oldSchedGroup == ProcessList.SCHED_GROUP_TOP_APP &&
+                            curSchedGroup != ProcessList.SCHED_GROUP_TOP_APP) {
+                        app.getWindowProcessController().onTopProcChanged();
+                        if (mService.mUseFifoUiScheduling) {
+                            try {
+                                // Reset UI pipeline to SCHED_OTHER
+                                setThreadScheduler(app.pid, SCHED_OTHER, 0);
+                                setThreadPriority(app.pid, app.savedPriority);
+                                if (app.renderThreadTid != 0) {
+                                    setThreadScheduler(app.renderThreadTid,
+                                            SCHED_OTHER, 0);
+                                    setThreadPriority(app.renderThreadTid, -4);
+                                }
+                            } catch (IllegalArgumentException e) {
+                                Slog.w(TAG,
+                                        "Failed to set scheduling policy, thread does not exist:\n"
+                                                + e);
+                            } catch (SecurityException e) {
+                                Slog.w(TAG, "Failed to set scheduling policy, not allowed:\n" + e);
+                            }
+                        } else {
+                            // Reset priority for top app UI and render threads
+                            setThreadPriority(app.pid, 0);
+                            if (app.renderThreadTid != 0) {
+                                setThreadPriority(app.renderThreadTid, 0);
+                            }
+                        }
+                    }
+                } catch (Exception e) {
+                    if (false) {
+                        Slog.w(TAG, "Failed setting process group of " + app.pid
+                                + " to " + app.getCurrentSchedulingGroup());
+                        Slog.w(TAG, "at location", e);
+                    }
+                } finally {
+                    Binder.restoreCallingIdentity(oldId);
+                }
+            }
+        }
+        if (app.repForegroundActivities != app.hasForegroundActivities()) {
+            app.repForegroundActivities = app.hasForegroundActivities();
+            changes |= ActivityManagerService.ProcessChangeItem.CHANGE_ACTIVITIES;
+        }
+        if (app.getReportedProcState() != app.getCurProcState()) {
+            app.setReportedProcState(app.getCurProcState());
+            if (app.thread != null) {
+                try {
+                    if (false) {
+                        //RuntimeException h = new RuntimeException("here");
+                        Slog.i(TAG, "Sending new process state " + app.getReportedProcState()
+                                + " to " + app /*, h*/);
+                    }
+                    app.thread.setProcessState(app.getReportedProcState());
+                } catch (RemoteException e) {
+                }
+            }
+        }
+        if (app.setProcState == PROCESS_STATE_NONEXISTENT
+                || ProcessList.procStatesDifferForMem(app.getCurProcState(), app.setProcState)) {
+            if (false && mService.mTestPssMode
+                    && app.setProcState >= 0 && app.lastStateTime <= (now-200)) {
+                // Experimental code to more aggressively collect pss while
+                // running test...  the problem is that this tends to collect
+                // the data right when a process is transitioning between process
+                // states, which will tend to give noisy data.
+                long start = SystemClock.uptimeMillis();
+                long startTime = SystemClock.currentThreadTimeMillis();
+                long pss = Debug.getPss(app.pid, mTmpLong, null);
+                long endTime = SystemClock.currentThreadTimeMillis();
+                mService.recordPssSampleLocked(app, app.getCurProcState(), pss,
+                        mTmpLong[0], mTmpLong[1], mTmpLong[2],
+                        ProcessStats.ADD_PSS_INTERNAL_SINGLE, endTime-startTime, now);
+                mService.mPendingPssProcesses.remove(app);
+                Slog.i(TAG, "Recorded pss for " + app + " state " + app.setProcState
+                        + " to " + app.getCurProcState() + ": "
+                        + (SystemClock.uptimeMillis()-start) + "ms");
+            }
+            app.lastStateTime = now;
+            app.nextPssTime = ProcessList.computeNextPssTime(app.getCurProcState(),
+                    app.procStateMemTracker, mService.mTestPssMode,
+                    mService.mAtmInternal.isSleeping(), now);
+            if (DEBUG_PSS) Slog.d(TAG_PSS, "Process state change from "
+                    + ProcessList.makeProcStateString(app.setProcState) + " to "
+                    + ProcessList.makeProcStateString(app.getCurProcState()) + " next pss in "
+                    + (app.nextPssTime-now) + ": " + app);
+        } else {
+            if (now > app.nextPssTime || (now > (app.lastPssTime+ProcessList.PSS_MAX_INTERVAL)
+                    && now > (app.lastStateTime+ProcessList.minTimeFromStateChange(
+                    mService.mTestPssMode)))) {
+                if (mService.requestPssLocked(app, app.setProcState)) {
+                    app.nextPssTime = ProcessList.computeNextPssTime(app.getCurProcState(),
+                            app.procStateMemTracker, mService.mTestPssMode,
+                            mService.mAtmInternal.isSleeping(), now);
+                }
+            } else if (false && DEBUG_PSS) {
+                Slog.d(TAG_PSS,
+                        "Not requesting pss of " + app + ": next=" + (app.nextPssTime-now));
+            }
+        }
+        if (app.setProcState != app.getCurProcState()) {
+            if (DEBUG_SWITCH || DEBUG_OOM_ADJ || mService.mCurOomAdjUid == app.uid) {
+                String msg = "Proc state change of " + app.processName
+                        + " to " + ProcessList.makeProcStateString(app.getCurProcState())
+                        + " (" + app.getCurProcState() + ")" + ": " + app.adjType;
+                reportOomAdjMessageLocked(TAG_OOM_ADJ, msg);
+            }
+            boolean setImportant = app.setProcState < ActivityManager.PROCESS_STATE_SERVICE;
+            boolean curImportant = app.getCurProcState() < ActivityManager.PROCESS_STATE_SERVICE;
+            if (setImportant && !curImportant) {
+                // This app is no longer something we consider important enough to allow to use
+                // arbitrary amounts of battery power. Note its current CPU time to later know to
+                // kill it if it is not behaving well.
+                app.setWhenUnimportant(now);
+                app.lastCpuTime = 0;
+            }
+            // Inform UsageStats of important process state change
+            // Must be called before updating setProcState
+            maybeUpdateUsageStatsLocked(app, nowElapsed);
+
+            maybeUpdateLastTopTime(app, now);
+
+            app.setProcState = app.getCurProcState();
+            if (app.setProcState >= ActivityManager.PROCESS_STATE_HOME) {
+                app.notCachedSinceIdle = false;
+            }
+            if (!doingAll) {
+                mService.setProcessTrackerStateLocked(app,
+                        mService.mProcessStats.getMemFactorLocked(), now);
+            } else {
+                app.procStateChanged = true;
+            }
+        } else if (app.reportedInteraction && (nowElapsed - app.getInteractionEventTime())
+                > mConstants.USAGE_STATS_INTERACTION_INTERVAL) {
+            // For apps that sit around for a long time in the interactive state, we need
+            // to report this at least once a day so they don't go idle.
+            maybeUpdateUsageStatsLocked(app, nowElapsed);
+        }
+
+        if (changes != 0) {
+            if (DEBUG_PROCESS_OBSERVERS) Slog.i(TAG_PROCESS_OBSERVERS,
+                    "Changes in " + app + ": " + changes);
+            int i = mService.mPendingProcessChanges.size()-1;
+            ActivityManagerService.ProcessChangeItem item = null;
+            while (i >= 0) {
+                item = mService.mPendingProcessChanges.get(i);
+                if (item.pid == app.pid) {
+                    if (DEBUG_PROCESS_OBSERVERS) Slog.i(TAG_PROCESS_OBSERVERS,
+                            "Re-using existing item: " + item);
+                    break;
+                }
+                i--;
+            }
+            if (i < 0) {
+                // No existing item in pending changes; need a new one.
+                final int NA = mService.mAvailProcessChanges.size();
+                if (NA > 0) {
+                    item = mService.mAvailProcessChanges.remove(NA-1);
+                    if (DEBUG_PROCESS_OBSERVERS) Slog.i(TAG_PROCESS_OBSERVERS,
+                            "Retrieving available item: " + item);
+                } else {
+                    item = new ActivityManagerService.ProcessChangeItem();
+                    if (DEBUG_PROCESS_OBSERVERS) Slog.i(TAG_PROCESS_OBSERVERS,
+                            "Allocating new item: " + item);
+                }
+                item.changes = 0;
+                item.pid = app.pid;
+                item.uid = app.info.uid;
+                if (mService.mPendingProcessChanges.size() == 0) {
+                    if (DEBUG_PROCESS_OBSERVERS) Slog.i(TAG_PROCESS_OBSERVERS,
+                            "*** Enqueueing dispatch processes changed!");
+                    mService.mUiHandler.obtainMessage(DISPATCH_PROCESSES_CHANGED_UI_MSG)
+                            .sendToTarget();
+                }
+                mService.mPendingProcessChanges.add(item);
+            }
+            item.changes |= changes;
+            item.foregroundActivities = app.repForegroundActivities;
+            if (DEBUG_PROCESS_OBSERVERS) Slog.i(TAG_PROCESS_OBSERVERS,
+                    "Item " + Integer.toHexString(System.identityHashCode(item))
+                            + " " + app.toShortString() + ": changes=" + item.changes
+                            + " foreground=" + item.foregroundActivities
+                            + " type=" + app.adjType + " source=" + app.adjSource
+                            + " target=" + app.adjTarget);
+        }
+
+        return success;
+    }
+
+    @GuardedBy("mService")
+    private void maybeUpdateUsageStatsLocked(ProcessRecord app, long nowElapsed) {
+        if (DEBUG_USAGE_STATS) {
+            Slog.d(TAG, "Checking proc [" + Arrays.toString(app.getPackageList())
+                    + "] state changes: old = " + app.setProcState + ", new = "
+                    + app.getCurProcState());
+        }
+        if (mService.mUsageStatsService == null) {
+            return;
+        }
+        boolean isInteraction;
+        // To avoid some abuse patterns, we are going to be careful about what we consider
+        // to be an app interaction.  Being the top activity doesn't count while the display
+        // is sleeping, nor do short foreground services.
+        if (app.getCurProcState() <= ActivityManager.PROCESS_STATE_TOP) {
+            isInteraction = true;
+            app.setFgInteractionTime(0);
+        } else if (app.getCurProcState() <= ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE) {
+            if (app.getFgInteractionTime() == 0) {
+                app.setFgInteractionTime(nowElapsed);
+                isInteraction = false;
+            } else {
+                isInteraction = nowElapsed > app.getFgInteractionTime()
+                        + mConstants.SERVICE_USAGE_INTERACTION_TIME;
+            }
+        } else {
+            isInteraction =
+                    app.getCurProcState() <= ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND;
+            app.setFgInteractionTime(0);
+        }
+        if (isInteraction
+                && (!app.reportedInteraction || (nowElapsed - app.getInteractionEventTime())
+                > mConstants.USAGE_STATS_INTERACTION_INTERVAL)) {
+            app.setInteractionEventTime(nowElapsed);
+            String[] packages = app.getPackageList();
+            if (packages != null) {
+                for (int i = 0; i < packages.length; i++) {
+                    mService.mUsageStatsService.reportEvent(packages[i], app.userId,
+                            UsageEvents.Event.SYSTEM_INTERACTION);
+                }
+            }
+        }
+        app.reportedInteraction = isInteraction;
+        if (!isInteraction) {
+            app.setInteractionEventTime(0);
+        }
+    }
+
+    private void maybeUpdateLastTopTime(ProcessRecord app, long nowUptime) {
+        if (app.setProcState <= ActivityManager.PROCESS_STATE_TOP
+                && app.getCurProcState() > ActivityManager.PROCESS_STATE_TOP) {
+            app.lastTopTime = nowUptime;
+        }
+    }
+
+    /**
+     * Look for recently inactive apps and mark them idle after a grace period. If idled, stop
+     * any background services and inform listeners.
+     */
+    @GuardedBy("mService")
+    void idleUidsLocked() {
+        final int N = mActiveUids.size();
+        if (N <= 0) {
+            return;
+        }
+        final long nowElapsed = SystemClock.elapsedRealtime();
+        final long maxBgTime = nowElapsed - mConstants.BACKGROUND_SETTLE_TIME;
+        long nextTime = 0;
+        if (mLocalPowerManager != null) {
+            mLocalPowerManager.startUidChanges();
+        }
+        for (int i = N - 1; i >= 0; i--) {
+            final UidRecord uidRec = mActiveUids.valueAt(i);
+            final long bgTime = uidRec.lastBackgroundTime;
+            if (bgTime > 0 && !uidRec.idle) {
+                if (bgTime <= maxBgTime) {
+                    EventLogTags.writeAmUidIdle(uidRec.uid);
+                    uidRec.idle = true;
+                    uidRec.setIdle = true;
+                    mService.doStopUidLocked(uidRec.uid, uidRec);
+                } else {
+                    if (nextTime == 0 || nextTime > bgTime) {
+                        nextTime = bgTime;
+                    }
+                }
+            }
+        }
+        if (mLocalPowerManager != null) {
+            mLocalPowerManager.finishUidChanges();
+        }
+        if (nextTime > 0) {
+            mService.mHandler.removeMessages(IDLE_UIDS_MSG);
+            mService.mHandler.sendEmptyMessageDelayed(IDLE_UIDS_MSG,
+                    nextTime + mConstants.BACKGROUND_SETTLE_TIME - nowElapsed);
+        }
+    }
+
+    @GuardedBy("mService")
+    final void setAppIdTempWhitelistStateLocked(int appId, boolean onWhitelist) {
+        boolean changed = false;
+        for (int i = mActiveUids.size() - 1; i >= 0; i--) {
+            final UidRecord uidRec = mActiveUids.valueAt(i);
+            if (UserHandle.getAppId(uidRec.uid) == appId && uidRec.curWhitelist != onWhitelist) {
+                uidRec.curWhitelist = onWhitelist;
+                changed = true;
+            }
+        }
+        if (changed) {
+            updateOomAdjLocked();
+        }
+    }
+
+    @GuardedBy("mService")
+    final void setUidTempWhitelistStateLocked(int uid, boolean onWhitelist) {
+        boolean changed = false;
+        final UidRecord uidRec = mActiveUids.get(uid);
+        if (uidRec != null && uidRec.curWhitelist != onWhitelist) {
+            uidRec.curWhitelist = onWhitelist;
+            updateOomAdjLocked();
+        }
+    }
+
+    @GuardedBy("mService")
+    void dumpProcessListVariablesLocked(ProtoOutputStream proto) {
+        proto.write(ActivityManagerServiceDumpProcessesProto.ADJ_SEQ, mAdjSeq);
+        proto.write(ActivityManagerServiceDumpProcessesProto.LRU_SEQ, mProcessList.mLruSeq);
+        proto.write(ActivityManagerServiceDumpProcessesProto.NUM_NON_CACHED_PROCS,
+                mNumNonCachedProcs);
+        proto.write(ActivityManagerServiceDumpProcessesProto.NUM_SERVICE_PROCS, mNumServiceProcs);
+        proto.write(ActivityManagerServiceDumpProcessesProto.NEW_NUM_SERVICE_PROCS,
+                mNewNumServiceProcs);
+
+    }
+
+    @GuardedBy("mService")
+    void dumpSequenceNumbersLocked(PrintWriter pw) {
+        pw.println("  mAdjSeq=" + mAdjSeq + " mLruSeq=" + mProcessList.mLruSeq);
+    }
+
+    @GuardedBy("mService")
+    void dumpProcCountsLocked(PrintWriter pw) {
+        pw.println("  mNumNonCachedProcs=" + mNumNonCachedProcs
+                + " (" + mProcessList.getLruSizeLocked() + " total)"
+                + " mNumCachedHiddenProcs=" + mNumCachedHiddenProcs
+                + " mNumServiceProcs=" + mNumServiceProcs
+                + " mNewNumServiceProcs=" + mNewNumServiceProcs);
+    }
+
+}
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 9898d06..5bc8845 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -17,6 +17,7 @@
 package com.android.server.am;
 
 import static android.app.ActivityManager.PROCESS_STATE_CACHED_ACTIVITY;
+import static android.app.ActivityManager.PROCESS_STATE_NONEXISTENT;
 import static android.app.ActivityThread.PROC_START_SEQ_IDENT;
 import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AUTO;
 import static android.os.Process.SYSTEM_UID;
@@ -123,7 +124,7 @@
  * </ul>
  */
 public final class ProcessList {
-    private static final String TAG = TAG_WITH_CLASS_NAME ? "ProcessList" : TAG_AM;
+    static final String TAG = TAG_WITH_CLASS_NAME ? "ProcessList" : TAG_AM;
 
     // The minimum time we allow between crashes, for us to consider this
     // application to be bad and stop and its services and reject broadcasts.
@@ -352,6 +353,8 @@
      */
     int mLruSeq = 0;
 
+    ActiveUids mActiveUids;
+
     /**
      * The currently running isolated processes.
      */
@@ -549,8 +552,10 @@
         updateOomLevels(0, 0, false);
     }
 
-    void init(ActivityManagerService service) {
+    void init(ActivityManagerService service, ActiveUids activeUids) {
         mService = service;
+        mActiveUids = activeUids;
+
         if (sKillHandler == null) {
             sKillThread = new ServiceThread(TAG + ":kill",
                     THREAD_PRIORITY_BACKGROUND, true /* allowIo */);
@@ -2176,7 +2181,7 @@
         } else if (old != null) {
             Slog.wtf(TAG, "Already have existing proc " + old + " when adding " + proc);
         }
-        UidRecord uidRec = mService.mActiveUids.get(proc.uid);
+        UidRecord uidRec = mActiveUids.get(proc.uid);
         if (uidRec == null) {
             uidRec = new UidRecord(proc.uid, mService.mAtmInternal);
             // This is the first appearance of the uid, report it now!
@@ -2188,7 +2193,7 @@
                 uidRec.setWhitelist = uidRec.curWhitelist = true;
             }
             uidRec.updateHasInternetPermission();
-            mService.mActiveUids.put(proc.uid, uidRec);
+            mActiveUids.put(proc.uid, uidRec);
             EventLogTags.writeAmUidRunning(uidRec.uid);
             mService.noteUidProcessState(uidRec.uid, uidRec.getCurProcState());
         }
@@ -2290,7 +2295,7 @@
                         "No more processes in " + old.uidRecord);
                 mService.enqueueUidChangeLocked(old.uidRecord, -1, UidRecord.CHANGE_GONE);
                 EventLogTags.writeAmUidStopped(uid);
-                mService.mActiveUids.remove(uid);
+                mActiveUids.remove(uid);
                 mService.noteUidProcessState(uid, ActivityManager.PROCESS_STATE_NONEXISTENT);
             }
             old.uidRecord = null;
@@ -3050,4 +3055,37 @@
             }
         }
     }
+
+    /** Returns the uid's process state or PROCESS_STATE_NONEXISTENT if not running */
+    @GuardedBy("mService")
+    int getUidProcStateLocked(int uid) {
+        UidRecord uidRec = mActiveUids.get(uid);
+        return uidRec == null ? PROCESS_STATE_NONEXISTENT : uidRec.getCurProcState();
+    }
+
+    /** Returns the UidRecord for the given uid, if it exists. */
+    @GuardedBy("mService")
+    UidRecord getUidRecordLocked(int uid) {
+        return mActiveUids.get(uid);
+    }
+
+    /**
+     * Call {@link ActivityManagerService#doStopUidLocked}
+     * (which will also stop background services) for all idle UIDs.
+     */
+    @GuardedBy("mService")
+    void doStopUidForIdleUidsLocked() {
+        final int size = mActiveUids.size();
+        for (int i = 0; i < size; i++) {
+            final int uid = mActiveUids.keyAt(i);
+            if (UserHandle.isCore(uid)) {
+                continue;
+            }
+            final UidRecord uidRec = mActiveUids.valueAt(i);
+            if (!uidRec.idle) {
+                continue;
+            }
+            mService.doStopUidLocked(uidRec.uid, uidRec);
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
index d5ede5b..c2117a7 100644
--- a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
+++ b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
@@ -77,6 +77,7 @@
     // permission in the corresponding .te file your feature belongs to.
     @VisibleForTesting
     static final String[] sDeviceConfigScopes = new String[] {
+        DeviceConfig.NAMESPACE_INPUT_NATIVE_BOOT,
     };
 
     private final String[] mGlobalSettings;
diff --git a/services/core/java/com/android/server/appop/HistoricalRegistry.java b/services/core/java/com/android/server/appop/HistoricalRegistry.java
index 8d7811f..714a807 100644
--- a/services/core/java/com/android/server/appop/HistoricalRegistry.java
+++ b/services/core/java/com/android/server/appop/HistoricalRegistry.java
@@ -1454,8 +1454,8 @@
                 if (accessCount > 0) {
                     if (!printedUidState) {
                         mWriter.print(mUidStatePrefix);
-                        mWriter.print(AppOpsManager.uidStateToString(uidState));
-                        mWriter.print("[");
+                        mWriter.print(AppOpsService.UID_STATE_NAMES[uidState]);
+                        mWriter.print(" = ");
                         printedUidState = true;
                     }
                     mWriter.print("access=");
@@ -1465,11 +1465,11 @@
                 if (rejectCount > 0) {
                     if (!printedUidState) {
                         mWriter.print(mUidStatePrefix);
-                        mWriter.print(AppOpsManager.uidStateToString(uidState));
-                        mWriter.print("[");
+                        mWriter.print(AppOpsService.UID_STATE_NAMES[uidState]);
+                        mWriter.print(" = ");
                         printedUidState = true;
                     } else {
-                        mWriter.print(",");
+                        mWriter.print(", ");
                     }
                     mWriter.print("reject=");
                     mWriter.print(rejectCount);
@@ -1478,16 +1478,17 @@
                 if (accessDuration > 0) {
                     if (!printedUidState) {
                         mWriter.print(mUidStatePrefix);
-                        mWriter.print(AppOpsManager.uidStateToString(uidState));
+                        mWriter.print(AppOpsService.UID_STATE_NAMES[uidState]);
+                        mWriter.print(" = ");
                         printedUidState = true;
                     } else {
-                        mWriter.print(",");
+                        mWriter.print(", ");
                     }
                     mWriter.print("duration=");
-                    mWriter.print(accessDuration);
+                    TimeUtils.formatDuration(accessDuration, mWriter);
                 }
                 if (printedUidState) {
-                    mWriter.println("]");
+                    mWriter.println("");
                 }
             }
         }
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index d704a3e..9a4ca1f 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -7824,8 +7824,10 @@
 
     /** see AudioPolicy.setUidDeviceAffinity() */
     public int setUidDeviceAffinity(IAudioPolicyCallback pcb, int uid,
-            @NonNull int[] deviceTypes,
-            @NonNull String[] deviceAddresses) {
+            @NonNull int[] deviceTypes, @NonNull String[] deviceAddresses) {
+        if (DEBUG_AP) {
+            Log.d(TAG, "setUidDeviceAffinity for " + pcb.asBinder() + " uid:" + uid);
+        }
         synchronized (mAudioPolicies) {
             final AudioPolicyProxy app =
                     checkUpdateForPolicy(pcb, "Cannot change device affinity in audio policy");
@@ -7835,21 +7837,23 @@
             if (!app.hasMixRoutedToDevices(deviceTypes, deviceAddresses)) {
                 return AudioManager.ERROR;
             }
+            return app.setUidDeviceAffinities(uid, deviceTypes, deviceAddresses);
         }
-        return AudioManager.SUCCESS;
     }
 
     /** see AudioPolicy.removeUidDeviceAffinity() */
     public int removeUidDeviceAffinity(IAudioPolicyCallback pcb, int uid) {
+        if (DEBUG_AP) {
+            Log.d(TAG, "removeUidDeviceAffinity for " + pcb.asBinder() + " uid:" + uid);
+        }
         synchronized (mAudioPolicies) {
             final AudioPolicyProxy app =
                     checkUpdateForPolicy(pcb, "Cannot remove device affinity in audio policy");
             if (app == null) {
                 return AudioManager.ERROR;
             }
-
+            return app.removeUidDeviceAffinities(uid);
         }
-        return AudioManager.SUCCESS;
     }
 
     public int setFocusPropertiesForPolicy(int duckingBehavior, IAudioPolicyCallback pcb) {
@@ -8160,27 +8164,41 @@
             Binder.restoreCallingIdentity(identity);
         }
 
-        void setUidDeviceAffinities(int uid, @NonNull int[] types, @NonNull String[] addresses) {
+        int setUidDeviceAffinities(int uid, @NonNull int[] types, @NonNull String[] addresses) {
             final Integer Uid = new Integer(uid);
+            int res;
             if (mUidDeviceAffinities.remove(Uid) != null) {
                 final long identity = Binder.clearCallingIdentity();
-                AudioSystem.removeUidDeviceAffinities(uid);
+                res = AudioSystem.removeUidDeviceAffinities(uid);
                 Binder.restoreCallingIdentity(identity);
+                if (res != AudioSystem.SUCCESS) {
+                    Log.e(TAG, "AudioSystem. removeUidDeviceAffinities(" + uid + ") failed, "
+                            + " cannot call AudioSystem.setUidDeviceAffinities");
+                    return AudioManager.ERROR;
+                }
             }
             final long identity = Binder.clearCallingIdentity();
-            final int res = AudioSystem.setUidDeviceAffinities(uid, types, addresses);
+            res = AudioSystem.setUidDeviceAffinities(uid, types, addresses);
             Binder.restoreCallingIdentity(identity);
             if (res == AudioSystem.SUCCESS) {
                 mUidDeviceAffinities.put(Uid, new AudioDeviceArray(types, addresses));
+                return AudioManager.SUCCESS;
             }
+            Log.e(TAG, "AudioSystem. setUidDeviceAffinities(" + uid + ") failed");
+            return AudioManager.ERROR;
         }
 
-        void removeUidDeviceAffinities(int uid, @NonNull int[] types, @NonNull String[] addresses) {
+        int removeUidDeviceAffinities(int uid) {
             if (mUidDeviceAffinities.remove(new Integer(uid)) != null) {
                 final long identity = Binder.clearCallingIdentity();
-                AudioSystem.removeUidDeviceAffinities(uid);
+                final int res = AudioSystem.removeUidDeviceAffinities(uid);
                 Binder.restoreCallingIdentity(identity);
+                if (res == AudioSystem.SUCCESS) {
+                    return AudioManager.SUCCESS;
+                }
             }
+            Log.e(TAG, "AudioSystem. removeUidDeviceAffinities failed");
+            return AudioManager.ERROR;
         }
     };
 
diff --git a/services/core/java/com/android/server/biometrics/BiometricServiceBase.java b/services/core/java/com/android/server/biometrics/BiometricServiceBase.java
index ecc3d2d..174ecfa 100644
--- a/services/core/java/com/android/server/biometrics/BiometricServiceBase.java
+++ b/services/core/java/com/android/server/biometrics/BiometricServiceBase.java
@@ -116,6 +116,7 @@
     protected HashMap<Integer, PerformanceStats> mPerformanceMap = new HashMap<>();
     // Transactions that make use of CryptoObjects are tracked by mCryptoPerformaceMap.
     protected HashMap<Integer, PerformanceStats> mCryptoPerformanceMap = new HashMap<>();
+    protected int mHALDeathCount;
 
     protected class PerformanceStats {
         public int accept; // number of accepted biometrics
@@ -596,6 +597,7 @@
     public void serviceDied(long cookie) {
         Slog.e(getTag(), "HAL died");
         mMetricsLogger.count(getMetrics().tagHalDied(), 1);
+        mHALDeathCount++;
         handleError(getHalDeviceId(), BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE,
                 0 /*vendorCode */);
     }
diff --git a/services/core/java/com/android/server/biometrics/face/FaceService.java b/services/core/java/com/android/server/biometrics/face/FaceService.java
index 5a9c1ac..a2aacdd 100644
--- a/services/core/java/com/android/server/biometrics/face/FaceService.java
+++ b/services/core/java/com/android/server/biometrics/face/FaceService.java
@@ -675,6 +675,12 @@
     }
 
     @Override
+    public void serviceDied(long cookie) {
+        super.serviceDied(cookie);
+        mDaemon = null;
+    }
+
+    @Override
     protected void updateActiveGroup(int userId, String clientPackage) {
         IBiometricsFace daemon = getFaceDaemon();
 
@@ -864,6 +870,8 @@
             Slog.e(TAG, "dump formatting failure", e);
         }
         pw.println(dump);
+        pw.println("HAL Deaths: " + mHALDeathCount);
+        mHALDeathCount = 0;
     }
 
     private void dumpProto(FileDescriptor fd) {
diff --git a/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java b/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java
index 1613dc9..3db6a74 100644
--- a/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java
@@ -777,6 +777,12 @@
     }
 
     @Override
+    public void serviceDied(long cookie) {
+        super.serviceDied(cookie);
+        mDaemon = null;
+    }
+
+    @Override
     protected void updateActiveGroup(int userId, String clientPackage) {
         IBiometricsFingerprint daemon = getFingerprintDaemon();
 
@@ -1074,6 +1080,8 @@
             Slog.e(TAG, "dump formatting failure", e);
         }
         pw.println(dump);
+        pw.println("HAL Deaths: " + mHALDeathCount);
+        mHALDeathCount = 0;
     }
 
     private void dumpProto(FileDescriptor fd) {
diff --git a/services/core/java/com/android/server/connectivity/Nat464Xlat.java b/services/core/java/com/android/server/connectivity/Nat464Xlat.java
index 6596d27..9d9b1cf 100644
--- a/services/core/java/com/android/server/connectivity/Nat464Xlat.java
+++ b/services/core/java/com/android/server/connectivity/Nat464Xlat.java
@@ -16,8 +16,9 @@
 
 package com.android.server.connectivity;
 
-import android.net.InterfaceConfiguration;
 import android.net.ConnectivityManager;
+import android.net.INetd;
+import android.net.InterfaceConfiguration;
 import android.net.LinkAddress;
 import android.net.LinkProperties;
 import android.net.NetworkInfo;
@@ -59,6 +60,7 @@
         NetworkInfo.State.SUSPENDED,
     };
 
+    private final INetd mNetd;
     private final INetworkManagementService mNMService;
 
     // The network we're running on, and its type.
@@ -76,7 +78,8 @@
     private String mIface;
     private State mState = State.IDLE;
 
-    public Nat464Xlat(INetworkManagementService nmService, NetworkAgentInfo nai) {
+    public Nat464Xlat(NetworkAgentInfo nai, INetd netd, INetworkManagementService nmService) {
+        mNetd = netd;
         mNMService = nmService;
         mNetwork = nai;
     }
@@ -140,7 +143,7 @@
             return;
         }
         try {
-            mNMService.startClatd(baseIface);
+            mNetd.clatdStart(baseIface);
         } catch(RemoteException|IllegalStateException e) {
             Slog.e(TAG, "Error starting clatd on " + baseIface, e);
         }
@@ -162,7 +165,7 @@
      */
     private void enterStoppingState() {
         try {
-            mNMService.stopClatd(mBaseIface);
+            mNetd.clatdStop(mBaseIface);
         } catch(RemoteException|IllegalStateException e) {
             Slog.e(TAG, "Error stopping clatd on " + mBaseIface, e);
         }
@@ -204,7 +207,7 @@
             Slog.e(TAG, "startClat: Can't start clat on null interface");
             return;
         }
-        // TODO: should we only do this if mNMService.startClatd() succeeds?
+        // TODO: should we only do this if mNetd.clatdStart() succeeds?
         Slog.i(TAG, "Starting clatd on " + baseIface);
         enterStartingState(baseIface);
     }
diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
index 54c89aa..9ea73fb 100644
--- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
+++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
@@ -17,6 +17,7 @@
 package com.android.server.connectivity;
 
 import android.content.Context;
+import android.net.INetd;
 import android.net.INetworkMonitor;
 import android.net.LinkProperties;
 import android.net.Network;
@@ -239,12 +240,15 @@
     private static final String TAG = ConnectivityService.class.getSimpleName();
     private static final boolean VDBG = false;
     private final ConnectivityService mConnService;
+    private final INetd mNetd;
+    private final INetworkManagementService mNMS;
     private final Context mContext;
     private final Handler mHandler;
 
     public NetworkAgentInfo(Messenger messenger, AsyncChannel ac, Network net, NetworkInfo info,
             LinkProperties lp, NetworkCapabilities nc, int score, Context context, Handler handler,
-            NetworkMisc misc, ConnectivityService connService) {
+            NetworkMisc misc, ConnectivityService connService, INetd netd,
+            INetworkManagementService nms) {
         this.messenger = messenger;
         asyncChannel = ac;
         network = net;
@@ -253,6 +257,8 @@
         networkCapabilities = nc;
         currentScore = score;
         mConnService = connService;
+        mNetd = netd;
+        mNMS = nms;
         mContext = context;
         mHandler = handler;
         networkMisc = misc;
@@ -587,18 +593,18 @@
 
     public void updateClat(INetworkManagementService netd) {
         if (Nat464Xlat.requiresClat(this)) {
-            maybeStartClat(netd);
+            maybeStartClat();
         } else {
             maybeStopClat();
         }
     }
 
     /** Ensure clat has started for this network. */
-    public void maybeStartClat(INetworkManagementService netd) {
+    public void maybeStartClat() {
         if (clatd != null && clatd.isStarted()) {
             return;
         }
-        clatd = new Nat464Xlat(netd, this);
+        clatd = new Nat464Xlat(this, mNetd, mNMS);
         clatd.start();
     }
 
diff --git a/services/core/java/com/android/server/content/SyncJobService.java b/services/core/java/com/android/server/content/SyncJobService.java
index bfcc629..aaf9cbc 100644
--- a/services/core/java/com/android/server/content/SyncJobService.java
+++ b/services/core/java/com/android/server/content/SyncJobService.java
@@ -141,10 +141,7 @@
             final long runtime = nowUptime - startUptime;
 
 
-            if (startUptime == 0) {
-                wtf("Job " + jobId + " start uptime not found: "
-                        + " params=" + jobParametersToString(params));
-            } else if (runtime > 60 * 1000) {
+            if (runtime > 60 * 1000) {
                 // WTF if startSyncH() hasn't happened, *unless* onStopJob() was called too soon.
                 // (1 minute threshold.)
                 // Also don't wtf when it's not ready to sync.
diff --git a/services/core/java/com/android/server/content/SyncLogger.java b/services/core/java/com/android/server/content/SyncLogger.java
index 5cbe5b9..fc20ef20 100644
--- a/services/core/java/com/android/server/content/SyncLogger.java
+++ b/services/core/java/com/android/server/content/SyncLogger.java
@@ -16,6 +16,7 @@
 
 package com.android.server.content;
 
+import android.accounts.Account;
 import android.app.job.JobParameters;
 import android.os.Build;
 import android.os.Environment;
@@ -31,6 +32,8 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.util.IntPair;
 import com.android.server.IoThread;
+import com.android.server.content.SyncManager.ActiveSyncContext;
+import com.android.server.content.SyncStorageEngine.EndPoint;
 
 import libcore.io.IoUtils;
 
@@ -309,4 +312,20 @@
             }
         }
     }
+
+    static String logSafe(Account account) {
+        return account == null ? "[null]" : account.toSafeString();
+    }
+
+    static String logSafe(EndPoint endPoint) {
+        return endPoint == null ? "[null]" : endPoint.toSafeString();
+    }
+
+    static String logSafe(SyncOperation operation) {
+        return operation == null ? "[null]" : operation.toSafeString();
+    }
+
+    static String logSafe(ActiveSyncContext asc) {
+        return asc == null ? "[null]" : asc.toSafeString();
+    }
 }
diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java
index 8b93e04..7096477 100644
--- a/services/core/java/com/android/server/content/SyncManager.java
+++ b/services/core/java/com/android/server/content/SyncManager.java
@@ -16,6 +16,8 @@
 
 package com.android.server.content;
 
+import static com.android.server.content.SyncLogger.logSafe;
+
 import android.accounts.Account;
 import android.accounts.AccountAndUser;
 import android.accounts.AccountManager;
@@ -1140,7 +1142,7 @@
             /* ignore - local call */
         }
         if (checkAccountAccess && !canAccessAccount(account, owningPackage, owningUid)) {
-            Log.w(TAG, "Access to " + account + " denied for package "
+            Log.w(TAG, "Access to " + logSafe(account) + " denied for package "
                     + owningPackage + " in UID " + syncAdapterInfo.uid);
             return AuthorityInfo.SYNCABLE_NO_ACCOUNT_ACCESS;
         }
@@ -1474,7 +1476,8 @@
         if (!syncOperation.ignoreBackoff()) {
             Pair<Long, Long> backoff = mSyncStorageEngine.getBackoff(syncOperation.target);
             if (backoff == null) {
-                Slog.e(TAG, "Couldn't find backoff values for " + syncOperation.target);
+                Slog.e(TAG, "Couldn't find backoff values for "
+                        + logSafe(syncOperation.target));
                 backoff = new Pair<Long, Long>(SyncStorageEngine.NOT_IN_BACKOFF_MODE,
                         SyncStorageEngine.NOT_IN_BACKOFF_MODE);
             }
@@ -1745,8 +1748,8 @@
             scheduleSyncOperationH(operation);
         } else {
             // Otherwise do not reschedule.
-            Log.d(TAG, "not retrying sync operation because the error is a hard error: "
-                    + operation);
+            Log.e(TAG, "not retrying sync operation because the error is a hard error: "
+                    + logSafe(operation));
         }
     }
 
@@ -1881,11 +1884,12 @@
             sendSyncFinishedOrCanceledMessage(this, result);
         }
 
-        public void toString(StringBuilder sb) {
+        public void toString(StringBuilder sb, boolean logSafe) {
             sb.append("startTime ").append(mStartTime)
                     .append(", mTimeoutStartTime ").append(mTimeoutStartTime)
                     .append(", mHistoryRowId ").append(mHistoryRowId)
-                    .append(", syncOperation ").append(mSyncOperation);
+                    .append(", syncOperation ").append(
+                        logSafe ? logSafe(mSyncOperation) : mSyncOperation);
         }
 
         public void onServiceConnected(ComponentName name, IBinder service) {
@@ -1947,7 +1951,13 @@
 
         public String toString() {
             StringBuilder sb = new StringBuilder();
-            toString(sb);
+            toString(sb, false);
+            return sb.toString();
+        }
+
+        public String toSafeString() {
+            StringBuilder sb = new StringBuilder();
+            toString(sb, true);
             return sb.toString();
         }
 
@@ -2036,7 +2046,7 @@
         int count = 0;
         for (SyncOperation op: pendingSyncs) {
             if (!op.isPeriodic) {
-                pw.println(op.dump(null, false, buckets));
+                pw.println(op.dump(null, false, buckets, /*logSafe=*/ false));
                 count++;
             }
         }
@@ -2053,7 +2063,7 @@
         int count = 0;
         for (SyncOperation op: pendingSyncs) {
             if (op.isPeriodic) {
-                pw.println(op.dump(null, false, buckets));
+                pw.println(op.dump(null, false, buckets, /*logSafe=*/ false));
                 count++;
             }
         }
@@ -2186,7 +2196,7 @@
             sb.setLength(0);
             pw.print(formatDurationHMS(sb, durationInSeconds));
             pw.print(" - ");
-            pw.print(activeSyncContext.mSyncOperation.dump(pm, false, buckets));
+            pw.print(activeSyncContext.mSyncOperation.dump(pm, false, buckets, /*logSafe=*/ false));
             pw.println();
         }
         pw.println();
@@ -2974,7 +2984,7 @@
                     case SyncHandler.MESSAGE_CANCEL:
                         SyncStorageEngine.EndPoint endpoint = (SyncStorageEngine.EndPoint) msg.obj;
                         Bundle extras = msg.peekData();
-                        if (Log.isLoggable(TAG, Log.DEBUG)) {
+                        if (isLoggable) {
                             Log.d(TAG, "handleSyncHandlerMessage: MESSAGE_CANCEL: "
                                     + endpoint + " bundle: " + extras);
                         }
@@ -2985,9 +2995,11 @@
                         SyncFinishedOrCancelledMessagePayload payload =
                                 (SyncFinishedOrCancelledMessagePayload) msg.obj;
                         if (!isSyncStillActiveH(payload.activeSyncContext)) {
-                            Log.d(TAG, "handleSyncHandlerMessage: dropping since the "
-                                    + "sync is no longer active: "
-                                    + payload.activeSyncContext);
+                            if (isLoggable) {
+                                Log.d(TAG, "handleSyncHandlerMessage: dropping since the "
+                                        + "sync is no longer active: "
+                                        + payload.activeSyncContext);
+                            }
                             break;
                         }
                         if (isLoggable) {
@@ -3002,7 +3014,7 @@
 
                     case SyncHandler.MESSAGE_SERVICE_CONNECTED: {
                         ServiceConnectionData msgData = (ServiceConnectionData) msg.obj;
-                        if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                        if (isLoggable) {
                             Log.d(TAG, "handleSyncHandlerMessage: MESSAGE_SERVICE_CONNECTED: "
                                     + msgData.activeSyncContext);
                         }
@@ -3018,7 +3030,7 @@
                     case SyncHandler.MESSAGE_SERVICE_DISCONNECTED: {
                         final ActiveSyncContext currentSyncContext =
                                 ((ServiceConnectionData) msg.obj).activeSyncContext;
-                        if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                        if (isLoggable) {
                             Log.d(TAG, "handleSyncHandlerMessage: MESSAGE_SERVICE_DISCONNECTED: "
                                     + currentSyncContext);
                         }
@@ -3053,7 +3065,7 @@
 
                     case SyncHandler.MESSAGE_MONITOR_SYNC:
                         ActiveSyncContext monitoredSyncContext = (ActiveSyncContext) msg.obj;
-                        if (Log.isLoggable(TAG, Log.DEBUG)) {
+                        if (isLoggable) {
                             Log.d(TAG, "handleSyncHandlerMessage: MESSAGE_MONITOR_SYNC: " +
                                     monitoredSyncContext.mSyncOperation.target);
                         }
@@ -3061,7 +3073,7 @@
                         if (isSyncNotUsingNetworkH(monitoredSyncContext)) {
                             Log.w(TAG, String.format(
                                     "Detected sync making no progress for %s. cancelling.",
-                                    monitoredSyncContext));
+                                    logSafe(monitoredSyncContext)));
                             SyncJobService.callJobFinished(
                                     monitoredSyncContext.mSyncOperation.jobId, false,
                                     "no network activity");
@@ -3558,7 +3570,8 @@
             } catch (RuntimeException exc) {
                 mLogger.log("Sync failed with RuntimeException: ", exc.toString());
                 closeActiveSyncContext(activeSyncContext);
-                Slog.e(TAG, "Caught RuntimeException while starting the sync " + syncOperation, exc);
+                Slog.e(TAG, "Caught RuntimeException while starting the sync "
+                        + logSafe(syncOperation), exc);
             }
         }
 
@@ -3658,7 +3671,8 @@
                         reschedulePeriodicSyncH(syncOperation);
                     }
                 } else {
-                    Log.w(TAG, "failed sync operation " + syncOperation + ", " + syncResult);
+                    Log.w(TAG, "failed sync operation "
+                            + logSafe(syncOperation) + ", " + syncResult);
 
                     syncOperation.retries++;
                     if (syncOperation.retries > mConstants.getMaxRetriesWithAppStandbyExemption()) {
@@ -4042,11 +4056,6 @@
         getJobScheduler().cancel(op.jobId);
     }
 
-    private void wtfWithLog(String message) {
-        Slog.wtf(TAG, message);
-        mLogger.log("WTF: ", message);
-    }
-
     public void resetTodayStats() {
         mSyncStorageEngine.resetTodayStats(/*force=*/ true);
     }
diff --git a/services/core/java/com/android/server/content/SyncOperation.java b/services/core/java/com/android/server/content/SyncOperation.java
index 25edf40..2abc2e6 100644
--- a/services/core/java/com/android/server/content/SyncOperation.java
+++ b/services/core/java/com/android/server/content/SyncOperation.java
@@ -363,14 +363,19 @@
 
     @Override
     public String toString() {
-        return dump(null, true, null);
+        return dump(null, true, null, false);
     }
 
-    String dump(PackageManager pm, boolean shorter, SyncAdapterStateFetcher appStates) {
+    public String toSafeString() {
+        return dump(null, true, null, true);
+    }
+
+    String dump(PackageManager pm, boolean shorter, SyncAdapterStateFetcher appStates,
+            boolean logSafe) {
         StringBuilder sb = new StringBuilder();
         sb.append("JobId=").append(jobId)
                 .append(" ")
-                .append(target.account.name)
+                .append(logSafe ? "***" : target.account.name)
                 .append("/")
                 .append(target.account.type)
                 .append(" u")
diff --git a/services/core/java/com/android/server/content/SyncStorageEngine.java b/services/core/java/com/android/server/content/SyncStorageEngine.java
index bfd1791..6b441a0 100644
--- a/services/core/java/com/android/server/content/SyncStorageEngine.java
+++ b/services/core/java/com/android/server/content/SyncStorageEngine.java
@@ -16,6 +16,8 @@
 
 package com.android.server.content;
 
+import static com.android.server.content.SyncLogger.logSafe;
+
 import android.accounts.Account;
 import android.accounts.AccountAndUser;
 import android.accounts.AccountManager;
@@ -225,6 +227,15 @@
             sb.append(":u" + userId);
             return sb.toString();
         }
+
+        public String toSafeString() {
+            StringBuilder sb = new StringBuilder();
+            sb.append(account == null ? "ALL ACCS" : logSafe(account))
+                    .append("/")
+                    .append(provider == null ? "ALL PDRS" : provider);
+            sb.append(":u" + userId);
+            return sb.toString();
+        }
     }
 
     public static class AuthorityInfo {
@@ -1861,8 +1872,8 @@
 
                 }
             } else {
-                Slog.w(TAG, "Failure adding authority: account="
-                        + accountName + " auth=" + authorityName
+                Slog.w(TAG, "Failure adding authority:"
+                        + " auth=" + authorityName
                         + " enabled=" + enabled
                         + " syncable=" + syncable);
             }
diff --git a/services/core/java/com/android/server/display/ColorDisplayService.java b/services/core/java/com/android/server/display/ColorDisplayService.java
index b332c47..2fe17d8 100644
--- a/services/core/java/com/android/server/display/ColorDisplayService.java
+++ b/services/core/java/com/android/server/display/ColorDisplayService.java
@@ -34,8 +34,10 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.pm.PackageManager;
+import android.content.res.Resources;
 import android.database.ContentObserver;
 import android.hardware.display.ColorDisplayManager;
+import android.graphics.ColorSpace;
 import android.hardware.display.IColorDisplayManager;
 import android.net.Uri;
 import android.opengl.Matrix;
@@ -59,6 +61,7 @@
 import com.android.server.twilight.TwilightManager;
 import com.android.server.twilight.TwilightState;
 
+import java.io.PrintWriter;
 import java.time.DateTimeException;
 import java.time.Instant;
 import java.time.LocalDateTime;
@@ -148,26 +151,204 @@
     };
 
     private final TintController mDisplayWhiteBalanceTintController = new TintController() {
+        // Three chromaticity coordinates per color: X, Y, and Z
+        private final int NUM_VALUES_PER_PRIMARY = 3;
+        // Four colors: red, green, blue, and white
+        private final int NUM_DISPLAY_PRIMARIES_VALS = 4 * NUM_VALUES_PER_PRIMARY;
 
+        private final Object mLock = new Object();
+        private int mTemperatureMin;
+        private int mTemperatureMax;
+        private int mTemperatureDefault;
+        private float[] mDisplayNominalWhiteXYZ = new float[NUM_VALUES_PER_PRIMARY];
+        private ColorSpace.Rgb mDisplayColorSpaceRGB;
+        private float[] mChromaticAdaptationMatrix;
+        private int mCurrentColorTemperature;
+        private float[] mCurrentColorTemperatureXYZ;
+        private boolean mSetUp = false;
         private float[] mMatrixDisplayWhiteBalance = new float[16];
 
         @Override
         public void setUp(Context context, boolean needsLinear) {
+            mSetUp = false;
+
+            final Resources res = getContext().getResources();
+            final String[] displayPrimariesValues = res.getStringArray(
+                    R.array.config_displayWhiteBalanceDisplayPrimaries);
+            final String[] nominalWhiteValues = res.getStringArray(
+                    R.array.config_displayWhiteBalanceDisplayNominalWhite);
+
+            if (displayPrimariesValues.length != NUM_DISPLAY_PRIMARIES_VALS) {
+                Slog.e(TAG, "Unexpected display white balance primaries resource length " +
+                        displayPrimariesValues.length);
+                return;
+            }
+
+            if (nominalWhiteValues.length != NUM_VALUES_PER_PRIMARY) {
+                Slog.e(TAG, "Unexpected display white balance nominal white resource length " +
+                        nominalWhiteValues.length);
+                return;
+            }
+
+            float[] displayRedGreenBlueXYZ =
+                new float[NUM_DISPLAY_PRIMARIES_VALS - NUM_VALUES_PER_PRIMARY];
+            float[] displayWhiteXYZ = new float[NUM_VALUES_PER_PRIMARY];
+            for (int i = 0; i < displayRedGreenBlueXYZ.length; i++) {
+                displayRedGreenBlueXYZ[i] = Float.parseFloat(displayPrimariesValues[i]);
+            }
+            for (int i = 0; i < displayWhiteXYZ.length; i++) {
+                displayWhiteXYZ[i] = Float.parseFloat(
+                        displayPrimariesValues[displayRedGreenBlueXYZ.length + i]);
+            }
+
+            final ColorSpace.Rgb displayColorSpaceRGB = new ColorSpace.Rgb(
+                "Display Color Space",
+                displayRedGreenBlueXYZ,
+                displayWhiteXYZ,
+                2.2f // gamma, unused for display white balance
+            );
+
+            float[] displayNominalWhiteXYZ = new float[NUM_VALUES_PER_PRIMARY];
+            for (int i = 0; i < nominalWhiteValues.length; i++) {
+                displayNominalWhiteXYZ[i] = Float.parseFloat(nominalWhiteValues[i]);
+            }
+
+            final int colorTemperatureMin = res.getInteger(
+                    R.integer.config_displayWhiteBalanceColorTemperatureMin);
+            if (colorTemperatureMin <= 0) {
+                Slog.e(TAG, "display white balance minimum temperature must be greater than 0");
+                return;
+            }
+
+            final int colorTemperatureMax = res.getInteger(
+                    R.integer.config_displayWhiteBalanceColorTemperatureMax);
+            if (colorTemperatureMax < colorTemperatureMin) {
+                Slog.e(TAG, "display white balance max temp must be greater or equal to min");
+                return;
+            }
+
+            final int colorTemperature = res.getInteger(
+                    R.integer.config_displayWhiteBalanceColorTemperatureDefault);
+
+            synchronized (mLock) {
+                mDisplayColorSpaceRGB = displayColorSpaceRGB;
+                mDisplayNominalWhiteXYZ = displayNominalWhiteXYZ;
+                mTemperatureMin = colorTemperatureMin;
+                mTemperatureMax = colorTemperatureMax;
+                mTemperatureDefault = colorTemperature;
+                mSetUp = true;
+            }
+
+            setMatrix(mTemperatureDefault);
         }
 
         @Override
         public float[] getMatrix() {
-            return isActivated() ? mMatrixDisplayWhiteBalance : MATRIX_IDENTITY;
+            return mSetUp && isActivated() ? mMatrixDisplayWhiteBalance : MATRIX_IDENTITY;
         }
 
         @Override
         public void setMatrix(int cct) {
+            if (!mSetUp) {
+                Slog.w(TAG, "Can't set display white balance temperature: uninitialized");
+                return;
+            }
+
+            if (cct < mTemperatureMin) {
+                Slog.w(TAG, "Requested display color temperature is below allowed minimum");
+                cct = mTemperatureMin;
+            } else if (cct > mTemperatureMax) {
+                Slog.w(TAG, "Requested display color temperature is above allowed maximum");
+                cct = mTemperatureMax;
+            }
+
+            Slog.d(TAG, "setDisplayWhiteBalanceTemperatureMatrix: cct = " + cct);
+
+            synchronized (mLock) {
+                mCurrentColorTemperature = cct;
+
+                // Adapt the display's nominal white point to match the requested CCT value
+                mCurrentColorTemperatureXYZ = ColorSpace.cctToIlluminantdXyz(cct);
+
+                mChromaticAdaptationMatrix =
+                    ColorSpace.chromaticAdaptation(ColorSpace.Adaptation.BRADFORD,
+                            mDisplayNominalWhiteXYZ, mCurrentColorTemperatureXYZ);
+
+                // Convert the adaptation matrix to RGB space
+                float[] result = ColorSpace.mul3x3(mChromaticAdaptationMatrix,
+                        mDisplayColorSpaceRGB.getTransform());
+                result = ColorSpace.mul3x3(mDisplayColorSpaceRGB.getInverseTransform(), result);
+
+                // Normalize the transform matrix to peak white value in RGB space
+                final float adaptedMaxR = result[0] + result[3] + result[6];
+                final float adaptedMaxG = result[1] + result[4] + result[7];
+                final float adaptedMaxB = result[2] + result[5] + result[8];
+                final float denum = Math.max(Math.max(adaptedMaxR, adaptedMaxG), adaptedMaxB);
+                for (int i = 0; i < result.length; i++) {
+                    result[i] /= denum;
+                }
+
+                Matrix.setIdentityM(mMatrixDisplayWhiteBalance, 0);
+                java.lang.System.arraycopy(result, 0, mMatrixDisplayWhiteBalance, 0, 3);
+                java.lang.System.arraycopy(result, 3, mMatrixDisplayWhiteBalance, 4, 3);
+                java.lang.System.arraycopy(result, 6, mMatrixDisplayWhiteBalance, 8, 3);
+            }
         }
 
         @Override
         public int getLevel() {
             return LEVEL_COLOR_MATRIX_DISPLAY_WHITE_BALANCE;
         }
+
+        /**
+         * Format a given matrix into a string.
+         *
+         * @param matrix the matrix to format
+         * @param cols number of columns in the matrix
+         */
+        private String matrixToString(float[] matrix, int cols) {
+            if (matrix == null || cols <= 0) {
+                Slog.e(TAG, "Invalid arguments when formatting matrix to string");
+                return "";
+            }
+
+            StringBuilder sb = new StringBuilder("");
+            for (int i = 0; i < matrix.length; i++) {
+                if (i % cols == 0) {
+                    sb.append("\n    ");
+                }
+                sb.append(String.format("%9.6f ", matrix[i]));
+            }
+            return sb.toString();
+        }
+
+        @Override
+        public void dump(PrintWriter pw) {
+            synchronized (mLock) {
+                pw.println("ColorDisplayService");
+                pw.println("  mSetUp = " + mSetUp);
+
+                if (!mSetUp) {
+                    return;
+                }
+
+                pw.println("  isActivated = " + isActivated());
+                pw.println("  mTemperatureMin = " + mTemperatureMin);
+                pw.println("  mTemperatureMax = " + mTemperatureMax);
+                pw.println("  mTemperatureDefault = " + mTemperatureDefault);
+                pw.println("  mCurrentColorTemperature = " + mCurrentColorTemperature);
+                pw.println("  mCurrentColorTemperatureXYZ = " +
+                        matrixToString(mCurrentColorTemperatureXYZ, 3));
+                pw.println("  mDisplayColorSpaceRGB RGB-to-XYZ = " +
+                        matrixToString(mDisplayColorSpaceRGB.getTransform(), 3));
+                pw.println("  mChromaticAdaptationMatrix = " +
+                        matrixToString(mChromaticAdaptationMatrix, 3));
+                pw.println("  mDisplayColorSpaceRGB XYZ-to-RGB = " +
+                        matrixToString(mDisplayColorSpaceRGB.getInverseTransform(), 3));
+                pw.println("  mMatrixDisplayWhiteBalance = " +
+                        matrixToString(mMatrixDisplayWhiteBalance, 4));
+            }
+        }
     };
 
     private final TintController mGlobalSaturationTintController = new TintController() {
@@ -230,8 +411,6 @@
 
     private NightDisplayAutoMode mNightDisplayAutoMode;
 
-    private Integer mDisplayWhiteBalanceColorTemperature;
-
     public ColorDisplayService(Context context) {
         super(context);
         mHandler = new TintHandler(Looper.getMainLooper());
@@ -363,7 +542,7 @@
                                 onAccessibilityTransformChanged();
                                 break;
                             case Secure.DISPLAY_WHITE_BALANCE_ENABLED:
-                                onDisplayWhiteBalanceEnabled(isDisplayWhiteBalanceSettingEnabled());
+                                updateDisplayWhiteBalanceStatus();
                                 break;
                         }
                     }
@@ -416,14 +595,9 @@
 
         if (ColorDisplayManager.isDisplayWhiteBalanceAvailable(getContext())) {
             // Prepare the display white balance transform matrix.
-            mDisplayWhiteBalanceTintController
-                    .setUp(getContext(), DisplayTransformManager.needsLinearColorMatrix());
-            if (mDisplayWhiteBalanceColorTemperature != null) {
-                mDisplayWhiteBalanceTintController
-                        .setMatrix(mDisplayWhiteBalanceColorTemperature);
-            }
+            mDisplayWhiteBalanceTintController.setUp(getContext(), true /* needsLinear */);
 
-            onDisplayWhiteBalanceEnabled(isDisplayWhiteBalanceSettingEnabled());
+            updateDisplayWhiteBalanceStatus();
         }
     }
 
@@ -460,6 +634,8 @@
                 mNightDisplayAutoMode.onActivated(activated);
             }
 
+            updateDisplayWhiteBalanceStatus();
+
             applyTint(mNightDisplayTintController, false);
         }
     }
@@ -516,11 +692,7 @@
                 .setUp(getContext(), DisplayTransformManager.needsLinearColorMatrix(mode));
         mNightDisplayTintController.setMatrix(mNightDisplayController.getColorTemperature());
 
-        mDisplayWhiteBalanceTintController
-                .setUp(getContext(), DisplayTransformManager.needsLinearColorMatrix(mode));
-        if (mDisplayWhiteBalanceColorTemperature != null) {
-            mDisplayWhiteBalanceTintController.setMatrix(mDisplayWhiteBalanceColorTemperature);
-        }
+        updateDisplayWhiteBalanceStatus();
 
         final DisplayTransformManager dtm = getLocalService(DisplayTransformManager.class);
         dtm.setColorMode(mode, mNightDisplayTintController.getMatrix());
@@ -611,10 +783,15 @@
         return ldt.isBefore(compareTime) ? ldt.plusDays(1) : ldt;
     }
 
-    private void onDisplayWhiteBalanceEnabled(boolean enabled) {
-        mDisplayWhiteBalanceTintController.setActivated(enabled);
-        if (mDisplayWhiteBalanceListener != null) {
-            mDisplayWhiteBalanceListener.onDisplayWhiteBalanceStatusChanged(enabled);
+    private void updateDisplayWhiteBalanceStatus() {
+        boolean oldActivated = mDisplayWhiteBalanceTintController.isActivated();
+        mDisplayWhiteBalanceTintController.setActivated(isDisplayWhiteBalanceSettingEnabled() &&
+                !mNightDisplayTintController.isActivated() &&
+                DisplayTransformManager.needsLinearColorMatrix());
+        boolean activated = mDisplayWhiteBalanceTintController.isActivated();
+
+        if (mDisplayWhiteBalanceListener != null && oldActivated != activated) {
+            mDisplayWhiteBalanceListener.onDisplayWhiteBalanceStatusChanged(activated);
         }
     }
 
@@ -896,6 +1073,12 @@
         }
 
         /**
+         * Dump debug information.
+         */
+        public void dump(PrintWriter pw) {
+        }
+
+        /**
          * Set up any constants needed for computing the matrix.
          */
         public abstract void setUp(Context context, boolean needsLinear);
@@ -929,11 +1112,10 @@
          */
         public boolean setDisplayWhiteBalanceColorTemperature(int cct) {
             // Update the transform matrix even if it can't be applied.
-            mDisplayWhiteBalanceColorTemperature = cct;
             mDisplayWhiteBalanceTintController.setMatrix(cct);
 
             if (mDisplayWhiteBalanceTintController.isActivated()) {
-                applyTint(mDisplayWhiteBalanceTintController, true);
+                applyTint(mDisplayWhiteBalanceTintController, false);
                 return true;
             }
             return false;
@@ -946,6 +1128,10 @@
             mDisplayWhiteBalanceListener = listener;
             return mDisplayWhiteBalanceTintController.isActivated();
         }
+
+        public void dump(PrintWriter pw) {
+            mDisplayWhiteBalanceTintController.dump(pw);
+        }
     }
 
     /**
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 4a17c65..cb3f91b 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -95,6 +95,7 @@
 import com.android.server.UiThread;
 import com.android.server.wm.SurfaceAnimationThread;
 import com.android.server.wm.WindowManagerInternal;
+import com.android.server.display.ColorDisplayService.ColorDisplayServiceInternal;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -791,6 +792,16 @@
         }
     }
 
+    private void setVirtualDisplayStateInternal(IBinder appToken, boolean isOn) {
+        synchronized (mSyncRoot) {
+            if (mVirtualDisplayAdapter == null) {
+                return;
+            }
+
+            mVirtualDisplayAdapter.setVirtualDisplayStateLocked(appToken, isOn);
+        }
+    }
+
     private void registerDefaultDisplayAdapters() {
         // Register default display adapters.
         synchronized (mSyncRoot) {
@@ -1459,6 +1470,13 @@
 
             pw.println();
             mPersistentDataStore.dump(pw);
+
+            final ColorDisplayServiceInternal cds = LocalServices.getService(
+                    ColorDisplayServiceInternal.class);
+            if (cds != null) {
+                pw.println();
+                cds.dump(pw);
+            }
         }
     }
 
@@ -1922,6 +1940,16 @@
         }
 
         @Override // Binder call
+        public void setVirtualDisplayState(IVirtualDisplayCallback callback, boolean isOn) {
+            final long token = Binder.clearCallingIdentity();
+            try {
+                setVirtualDisplayStateInternal(callback.asBinder(), isOn);
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        @Override // Binder call
         public void dump(FileDescriptor fd, final PrintWriter pw, String[] args) {
             if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
 
diff --git a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
index 5aa585f..1ca8dd3 100644
--- a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
@@ -147,6 +147,13 @@
         return device;
     }
 
+    void setVirtualDisplayStateLocked(IBinder appToken, boolean isOn) {
+        VirtualDisplayDevice device = mVirtualDisplayDevices.get(appToken);
+        if (device != null) {
+            device.setDisplayState(isOn);
+        }
+    }
+
     /**
      * Returns the next unique index for the uniqueIdPrefix
      */
@@ -206,6 +213,7 @@
         private int mPendingChanges;
         private int mUniqueIndex;
         private Display.Mode mMode;
+        private boolean mIsDisplayOn;
 
         public VirtualDisplayDevice(IBinder displayToken, IBinder appToken,
                 int ownerUid, String ownerPackageName,
@@ -226,6 +234,7 @@
             mDisplayState = Display.STATE_UNKNOWN;
             mPendingChanges |= PENDING_SURFACE_CHANGE;
             mUniqueIndex = uniqueIndex;
+            mIsDisplayOn = surface != null;
         }
 
         @Override
@@ -304,6 +313,14 @@
             }
         }
 
+        void setDisplayState(boolean isOn) {
+            if (mIsDisplayOn != isOn) {
+                mIsDisplayOn = isOn;
+                mInfo = null;
+                sendDisplayDeviceEventLocked(this, DISPLAY_DEVICE_EVENT_CHANGED);
+            }
+        }
+
         public void stopLocked() {
             setSurfaceLocked(null);
             mStopped = true;
@@ -375,7 +392,9 @@
                 mInfo.type = Display.TYPE_VIRTUAL;
                 mInfo.touch = ((mFlags & VIRTUAL_DISPLAY_FLAG_SUPPORTS_TOUCH) == 0) ?
                         DisplayDeviceInfo.TOUCH_NONE : DisplayDeviceInfo.TOUCH_VIRTUAL;
-                mInfo.state = mSurface != null ? Display.STATE_ON : Display.STATE_OFF;
+
+                mInfo.state = mIsDisplayOn ? Display.STATE_ON : Display.STATE_OFF;
+
                 mInfo.ownerUid = mOwnerUid;
                 mInfo.ownerPackageName = mOwnerPackageName;
             }
diff --git a/services/core/java/com/android/server/hdmi/ArcInitiationActionFromAvr.java b/services/core/java/com/android/server/hdmi/ArcInitiationActionFromAvr.java
index 18d328d..137833c 100644
--- a/services/core/java/com/android/server/hdmi/ArcInitiationActionFromAvr.java
+++ b/services/core/java/com/android/server/hdmi/ArcInitiationActionFromAvr.java
@@ -51,6 +51,13 @@
         }
         switch (cmd.getOpcode()) {
             case Constants.MESSAGE_FEATURE_ABORT:
+                if ((cmd.getParams()[0] & 0xFF) == Constants.MESSAGE_INITIATE_ARC) {
+                    audioSystem().setArcStatus(false);
+                    finish();
+                    return true;
+                } else {
+                    return false;
+                }
             case Constants.MESSAGE_REPORT_ARC_TERMINATED:
                 audioSystem().setArcStatus(false);
                 finish();
diff --git a/services/core/java/com/android/server/hdmi/Constants.java b/services/core/java/com/android/server/hdmi/Constants.java
index 6f5a196..297a418 100644
--- a/services/core/java/com/android/server/hdmi/Constants.java
+++ b/services/core/java/com/android/server/hdmi/Constants.java
@@ -17,6 +17,7 @@
 package com.android.server.hdmi;
 
 import android.annotation.IntDef;
+import android.annotation.StringDef;
 import android.hardware.hdmi.HdmiDeviceInfo;
 
 import java.lang.annotation.Retention;
@@ -222,6 +223,15 @@
     static final int AUDIO_CODEC_WMAPRO = 0xE; // Support WMA-Pro
     static final int AUDIO_CODEC_MAX = 0xF;
 
+    @StringDef({
+        AUDIO_DEVICE_ARC_IN,
+        AUDIO_DEVICE_SPDIF,
+    })
+    public @interface AudioDevice {}
+
+    static final String AUDIO_DEVICE_ARC_IN = "ARC_IN";
+    static final String AUDIO_DEVICE_SPDIF = "SPDIF";
+
     // Bit mask used to get the routing path of the top level device.
     // When &'d with the path 1.2.2.0 (0x1220), for instance, gives 1.0.0.0.
     static final int ROUTING_PATH_TOP_MASK = 0xF000;
@@ -343,14 +353,6 @@
     static final String PROPERTY_HDMI_CEC_NEVER_ASSIGN_LOGICAL_ADDRESSES =
             "ro.hdmi.property_hdmi_cec_never_assign_logical_addresses";
 
-    /**
-     * Property to indicate if the current device is a cec switch device.
-     *
-     * <p> Default is false.
-     */
-    static final String PROPERTY_HDMI_IS_DEVICE_HDMI_CEC_SWITCH =
-            "ro.hdmi.property_is_device_hdmi_cec_switch";
-
     // Set to false to allow playback device to go to suspend mode even
     // when it's an active source. True by default.
     static final String PROPERTY_KEEP_AWAKE = "persist.sys.hdmi.keep_awake";
diff --git a/services/core/java/com/android/server/hdmi/DetectTvSystemAudioModeSupportAction.java b/services/core/java/com/android/server/hdmi/DetectTvSystemAudioModeSupportAction.java
index 0495ff8..7187319 100644
--- a/services/core/java/com/android/server/hdmi/DetectTvSystemAudioModeSupportAction.java
+++ b/services/core/java/com/android/server/hdmi/DetectTvSystemAudioModeSupportAction.java
@@ -50,8 +50,10 @@
             if (mState != STATE_WAITING_FOR_FEATURE_ABORT) {
                 return false;
             }
-            finishAction(false);
-            return true;
+            if ((cmd.getParams()[0] & 0xFF) == Constants.MESSAGE_SET_SYSTEM_AUDIO_MODE) {
+                finishAction(false);
+                return true;
+            }
         }
         return false;
     }
diff --git a/services/core/java/com/android/server/hdmi/DeviceDiscoveryAction.java b/services/core/java/com/android/server/hdmi/DeviceDiscoveryAction.java
index b75e75f..df0dc77 100755
--- a/services/core/java/com/android/server/hdmi/DeviceDiscoveryAction.java
+++ b/services/core/java/com/android/server/hdmi/DeviceDiscoveryAction.java
@@ -16,6 +16,7 @@
 
 package com.android.server.hdmi;
 
+import android.hardware.hdmi.HdmiControlManager;
 import android.hardware.hdmi.HdmiDeviceInfo;
 import android.util.Slog;
 
@@ -28,7 +29,7 @@
 
 /**
  * Feature action that handles device discovery sequences.
- * Device discovery is launched when TV device is woken from "Standby" state
+ * Device discovery is launched when device is woken from "Standby" state
  * or enabled "Control for Hdmi" from disabled state.
  *
  * <p>Device discovery goes through the following steps.
@@ -51,6 +52,10 @@
     private static final int STATE_WAITING_FOR_OSD_NAME = 3;
     // State in which the action is waiting for gathering vendor id of non-local devices.
     private static final int STATE_WAITING_FOR_VENDOR_ID = 4;
+    // State in which the action is waiting for devices to be ready.
+    private static final int STATE_WAITING_FOR_DEVICES = 5;
+    // State in which the action is waiting for gathering power status of non-local devices.
+    private static final int STATE_WAITING_FOR_POWER = 6;
 
     /**
      * Interface used to report result of device discovery.
@@ -72,6 +77,7 @@
         private int mPhysicalAddress = Constants.INVALID_PHYSICAL_ADDRESS;
         private int mPortId = Constants.INVALID_PORT_ID;
         private int mVendorId = Constants.UNKNOWN_VENDOR_ID;
+        private int mPowerStatus = HdmiControlManager.POWER_STATUS_UNKNOWN;
         private String mDisplayName = "";
         private int mDeviceType = HdmiDeviceInfo.DEVICE_INACTIVE;
 
@@ -81,7 +87,7 @@
 
         private HdmiDeviceInfo toHdmiDeviceInfo() {
             return new HdmiDeviceInfo(mLogicalAddress, mPhysicalAddress, mPortId, mDeviceType,
-                    mVendorId, mDisplayName);
+                    mVendorId, mDisplayName, mPowerStatus);
         }
     }
 
@@ -89,6 +95,20 @@
     private final DeviceDiscoveryCallback mCallback;
     private int mProcessedDeviceCount = 0;
     private int mTimeoutRetry = 0;
+    private boolean mIsTvDevice = localDevice().mService.isTvDevice();
+    private final int mDelayPeriod;
+
+    /**
+     * Constructor.
+     *
+     * @param source an instance of {@link HdmiCecLocalDevice}.
+     * @param delay delay action for this period between query Physical Address and polling
+     */
+    DeviceDiscoveryAction(HdmiCecLocalDevice source, DeviceDiscoveryCallback callback, int delay) {
+        super(source);
+        mCallback = Preconditions.checkNotNull(callback);
+        mDelayPeriod = delay;
+    }
 
     /**
      * Constructor.
@@ -96,8 +116,7 @@
      * @param source an instance of {@link HdmiCecLocalDevice}.
      */
     DeviceDiscoveryAction(HdmiCecLocalDevice source, DeviceDiscoveryCallback callback) {
-        super(source);
-        mCallback = Preconditions.checkNotNull(callback);
+        this(source, callback, 0);
     }
 
     @Override
@@ -116,7 +135,11 @@
 
                 Slog.v(TAG, "Device detected: " + ackedAddress);
                 allocateDevices(ackedAddress);
-                startPhysicalAddressStage();
+                if (mDelayPeriod > 0) {
+                    startToDelayAction();
+                } else {
+                    startPhysicalAddressStage();
+                }
             }
         }, Constants.POLL_ITERATION_REVERSE_ORDER
             | Constants.POLL_STRATEGY_REMOTES_DEVICES, HdmiConfig.DEVICE_POLLING_RETRY);
@@ -130,6 +153,13 @@
         }
     }
 
+    private void startToDelayAction() {
+        Slog.v(TAG, "Waiting for connected devices to be ready");
+        mState = STATE_WAITING_FOR_DEVICES;
+
+        checkAndProceedStage();
+    }
+
     private void startPhysicalAddressStage() {
         Slog.v(TAG, "Start [Physical Address Stage]:" + mDevices.size());
         mProcessedDeviceCount = 0;
@@ -158,6 +188,11 @@
         addTimer(mState, HdmiConfig.TIMEOUT_MS);
     }
 
+    private void delayActionWithTimePeriod(int timeDelay) {
+        mActionTimer.clearTimerMessage();
+        addTimer(mState, timeDelay);
+    }
+
     private void startOsdNameStage() {
         Slog.v(TAG, "Start [Osd Name Stage]:" + mDevices.size());
         mProcessedDeviceCount = 0;
@@ -206,6 +241,29 @@
         addTimer(mState, HdmiConfig.TIMEOUT_MS);
     }
 
+    private void startPowerStatusStage() {
+        Slog.v(TAG, "Start [Power Status Stage]:" + mDevices.size());
+        mProcessedDeviceCount = 0;
+        mState = STATE_WAITING_FOR_POWER;
+
+        checkAndProceedStage();
+    }
+
+    private void queryPowerStatus(int address) {
+        if (!verifyValidLogicalAddress(address)) {
+            checkAndProceedStage();
+            return;
+        }
+
+        mActionTimer.clearTimerMessage();
+
+        if (mayProcessMessageIfCached(address, Constants.MESSAGE_REPORT_POWER_STATUS)) {
+            return;
+        }
+        sendCommand(HdmiCecMessageBuilder.buildGiveDevicePowerStatus(getSourceAddress(), address));
+        addTimer(mState, HdmiConfig.TIMEOUT_MS);
+    }
+
     private boolean mayProcessMessageIfCached(int address, int opcode) {
         HdmiCecMessage message = getCecMessageCache().getMessage(address, opcode);
         if (message != null) {
@@ -244,6 +302,16 @@
                     return true;
                 }
                 return false;
+            case STATE_WAITING_FOR_POWER:
+                if (cmd.getOpcode() == Constants.MESSAGE_REPORT_POWER_STATUS) {
+                    handleReportPowerStatus(cmd);
+                    return true;
+                } else if ((cmd.getOpcode() == Constants.MESSAGE_FEATURE_ABORT)
+                        && ((cmd.getParams()[0] & 0xFF) == Constants.MESSAGE_REPORT_POWER_STATUS)) {
+                    handleReportPowerStatus(cmd);
+                    return true;
+                }
+                return false;
             case STATE_WAITING_FOR_DEVICE_POLLING:
                 // Fall through.
             default:
@@ -265,16 +333,21 @@
         current.mPhysicalAddress = HdmiUtils.twoBytesToInt(params);
         current.mPortId = getPortId(current.mPhysicalAddress);
         current.mDeviceType = params[2] & 0xFF;
+        current.mDisplayName = HdmiUtils.getDefaultDeviceName(current.mDeviceType);
 
-        tv().updateCecSwitchInfo(current.mLogicalAddress, current.mDeviceType,
+        // TODO(amyjojo): check if non-TV device needs to update cec switch info.
+        // This is to manager CEC device separately in case they don't have address.
+        if (mIsTvDevice) {
+            tv().updateCecSwitchInfo(current.mLogicalAddress, current.mDeviceType,
                     current.mPhysicalAddress);
-
+        }
         increaseProcessedDeviceCount();
         checkAndProceedStage();
     }
 
     private int getPortId(int physicalAddress) {
-        return tv().getPortId(physicalAddress);
+        return mIsTvDevice ? tv().getPortId(physicalAddress)
+            : source().getPortId(physicalAddress);
     }
 
     private void handleSetOsdName(HdmiCecMessage cmd) {
@@ -324,6 +397,26 @@
         checkAndProceedStage();
     }
 
+    private void handleReportPowerStatus(HdmiCecMessage cmd) {
+        Preconditions.checkState(mProcessedDeviceCount < mDevices.size());
+
+        DeviceInfo current = mDevices.get(mProcessedDeviceCount);
+        if (current.mLogicalAddress != cmd.getSource()) {
+            Slog.w(TAG, "Unmatched address[expected:" + current.mLogicalAddress + ", actual:"
+                    + cmd.getSource());
+            return;
+        }
+
+        if (cmd.getOpcode() != Constants.MESSAGE_FEATURE_ABORT) {
+            byte[] params = cmd.getParams();
+            int powerStatus = params[0] & 0xFF;
+            current.mPowerStatus = powerStatus;
+        }
+
+        increaseProcessedDeviceCount();
+        checkAndProceedStage();
+    }
+
     private void increaseProcessedDeviceCount() {
         mProcessedDeviceCount++;
         mTimeoutRetry = 0;
@@ -345,7 +438,9 @@
         mCallback.onDeviceDiscoveryDone(result);
         finish();
         // Process any commands buffered while device discovery action was in progress.
-        tv().processAllDelayedMessages();
+        if (mIsTvDevice) {
+            tv().processAllDelayedMessages();
+        }
     }
 
     private void checkAndProceedStage() {
@@ -365,6 +460,9 @@
                     startVendorIdStage();
                     return;
                 case STATE_WAITING_FOR_VENDOR_ID:
+                    startPowerStatusStage();
+                    return;
+                case STATE_WAITING_FOR_POWER:
                     wrapUpAndFinish();
                     return;
                 default:
@@ -378,6 +476,9 @@
     private void sendQueryCommand() {
         int address = mDevices.get(mProcessedDeviceCount).mLogicalAddress;
         switch (mState) {
+            case STATE_WAITING_FOR_DEVICES:
+                delayActionWithTimePeriod(mDelayPeriod);
+                return;
             case STATE_WAITING_FOR_PHYSICAL_ADDRESS:
                 queryPhysicalAddress(address);
                 return;
@@ -387,6 +488,9 @@
             case STATE_WAITING_FOR_VENDOR_ID:
                 queryVendorId(address);
                 return;
+            case STATE_WAITING_FOR_POWER:
+                queryPowerStatus(address);
+                return;
             default:
                 return;
         }
@@ -398,13 +502,24 @@
             return;
         }
 
+        if (mState == STATE_WAITING_FOR_DEVICES) {
+            startPhysicalAddressStage();
+            return;
+        }
         if (++mTimeoutRetry < HdmiConfig.TIMEOUT_RETRY) {
             sendQueryCommand();
             return;
         }
         mTimeoutRetry = 0;
         Slog.v(TAG, "Timeout[State=" + mState + ", Processed=" + mProcessedDeviceCount);
-        removeDevice(mProcessedDeviceCount);
+        if (mState != STATE_WAITING_FOR_POWER && mState != STATE_WAITING_FOR_OSD_NAME) {
+            // We don't need to remove the device info if the power status is unknown.
+            // Some device does not have preferred OSD name and does not respond to Give OSD name.
+            // Like LG TV. We can give it default device name and not remove it.
+            removeDevice(mProcessedDeviceCount);
+        } else {
+            increaseProcessedDeviceCount();
+        }
         checkAndProceedStage();
     }
 }
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecController.java b/services/core/java/com/android/server/hdmi/HdmiCecController.java
index e777ce8..86be585 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecController.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecController.java
@@ -77,7 +77,7 @@
 
     private static final int NUM_LOGICAL_ADDRESS = 16;
 
-    private static final int MAX_CEC_MESSAGE_HISTORY = 20;
+    private static final int MAX_CEC_MESSAGE_HISTORY = 200;
 
     // Predicate for whether the given logical address is remote device's one or not.
     private final Predicate<Integer> mRemoteDeviceAddressPredicate = new Predicate<Integer>() {
@@ -682,7 +682,7 @@
 
     void dump(final IndentingPrintWriter pw) {
         for (int i = 0; i < mLocalDevices.size(); ++i) {
-            pw.println("HdmiCecLocalDevice #" + i + ":");
+            pw.println("HdmiCecLocalDevice #" + mLocalDevices.keyAt(i) + ":");
             pw.increaseIndent();
             mLocalDevices.valueAt(i).dump(pw);
             pw.decreaseIndent();
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
index 6e1b018..414f6bb 100755
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
@@ -56,16 +56,12 @@
     // Within the timer, a received <User Control Pressed> will start "Press and Hold" behavior.
     // When it expires, we can assume <User Control Release> is received.
     private static final int FOLLOWER_SAFETY_TIMEOUT = 550;
-    /**
-     * Return value of {@link #getLocalPortFromPhysicalAddress(int)}
-     */
-    private static final int TARGET_NOT_UNDER_LOCAL_DEVICE = -1;
-    private static final int TARGET_SAME_PHYSICAL_ADDRESS = 0;
 
     protected final HdmiControlService mService;
     protected final int mDeviceType;
     protected int mAddress;
     protected int mPreferredAddress;
+    @GuardedBy("mLock")
     protected HdmiDeviceInfo mDeviceInfo;
     protected int mLastKeycode = HdmiCecKeycode.UNSUPPORTED_KEYCODE;
     protected int mLastKeyRepeatCount = 0;
@@ -717,16 +713,18 @@
         return mDeviceType;
     }
 
-    @ServiceThreadOnly
+    @GuardedBy("mLock")
     HdmiDeviceInfo getDeviceInfo() {
-        assertRunOnServiceThread();
-        return mDeviceInfo;
+        synchronized (mLock) {
+            return mDeviceInfo;
+        }
     }
 
-    @ServiceThreadOnly
+    @GuardedBy("mLock")
     void setDeviceInfo(HdmiDeviceInfo info) {
-        assertRunOnServiceThread();
-        mDeviceInfo = info;
+        synchronized (mLock) {
+            mDeviceInfo = info;
+        }
     }
 
     // Returns true if the logical address is same as the argument.
@@ -916,6 +914,11 @@
         setActivePath(mService.portIdToPath(portId));
     }
 
+    // Returns the id of the port that the target device is connected to.
+    int getPortId(int physicalAddress) {
+        return mService.pathToPortId(physicalAddress);
+    }
+
     @ServiceThreadOnly
     HdmiCecMessageCache getCecMessageCache() {
         assertRunOnServiceThread();
@@ -1053,47 +1056,6 @@
         pw.println(String.format("mActiveRoutingPath: 0x%04x", mActiveRoutingPath));
     }
 
-    /**
-     * Method to parse target physical address to the port number on the current device.
-     *
-     * <p>This check assumes target address is valid.
-     * @param targetPhysicalAddress is the physical address of the target device
-     * @return
-     * If the target device is under the current device, return the port number of current device
-     * that the target device is connected to.
-     *
-     * <p>If the target device has the same physical address as the current device, return
-     * {@link #TARGET_SAME_PHYSICAL_ADDRESS}.
-     *
-     * <p>If the target device is not under the current device, return
-     * {@link #TARGET_NOT_UNDER_LOCAL_DEVICE}.
-     */
-    protected int getLocalPortFromPhysicalAddress(int targetPhysicalAddress) {
-        int myPhysicalAddress = mService.getPhysicalAddress();
-        if (myPhysicalAddress == targetPhysicalAddress) {
-            return TARGET_SAME_PHYSICAL_ADDRESS;
-        }
-        int finalMask = 0xF000;
-        int mask;
-        int port = 0;
-        for (mask = 0x0F00; mask > 0x000F;  mask >>= 4) {
-            if ((myPhysicalAddress & mask) == 0)  {
-                port = mask & targetPhysicalAddress;
-                break;
-            } else {
-                finalMask |= mask;
-            }
-        }
-        if (finalMask != 0xFFFF && (finalMask & targetPhysicalAddress) == myPhysicalAddress) {
-            while (mask != 0x000F) {
-                mask >>= 4;
-                port >>= 4;
-            }
-            return port;
-        }
-        return TARGET_NOT_UNDER_LOCAL_DEVICE;
-    }
-
     /** Calculates the physical address for {@code activePortId}.
      *
      * <p>This method assumes current device physical address is valid.
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
index 8cfe47f..63214ed 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
@@ -23,19 +23,41 @@
 import android.content.Intent;
 import android.hardware.hdmi.HdmiControlManager;
 import android.hardware.hdmi.HdmiDeviceInfo;
+import android.hardware.hdmi.HdmiPortInfo;
 import android.hardware.hdmi.IHdmiControlCallback;
 import android.media.AudioDeviceInfo;
+import android.media.AudioFormat;
 import android.media.AudioManager;
 import android.media.AudioSystem;
 import android.media.tv.TvContract;
 import android.os.SystemProperties;
+import android.provider.Settings.Global;
+import android.util.Slog;
+import android.util.SparseArray;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.IndentingPrintWriter;
 import com.android.server.hdmi.Constants.AudioCodec;
+import com.android.server.hdmi.DeviceDiscoveryAction.DeviceDiscoveryCallback;
 import com.android.server.hdmi.HdmiAnnotations.ServiceThreadOnly;
+import com.android.server.hdmi.HdmiUtils.CodecSad;
+import com.android.server.hdmi.HdmiUtils.DeviceConfig;
 
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.UnsupportedEncodingException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
 import java.util.HashMap;
+import java.util.List;
+import java.util.stream.Collectors;
+
 
 /**
  * Represent a logical device of type {@link HdmiDeviceInfo#DEVICE_AUDIO_SYSTEM} residing in Android
@@ -71,19 +93,172 @@
     // processing.
     private final HashMap<Integer, String> mTvInputs = new HashMap<>();
 
+    // Copy of mDeviceInfos to guarantee thread-safety.
+    @GuardedBy("mLock")
+    private List<HdmiDeviceInfo> mSafeAllDeviceInfos = Collections.emptyList();
+
+    // Map-like container of all cec devices.
+    // device id is used as key of container.
+    private final SparseArray<HdmiDeviceInfo> mDeviceInfos = new SparseArray<>();
+
     protected HdmiCecLocalDeviceAudioSystem(HdmiControlService service) {
         super(service, HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM);
-        mSystemAudioControlFeatureEnabled = true;
-        // TODO(amyjojo) make System Audio Control controllable by users
-        /*mSystemAudioControlFeatureEnabled =
-        mService.readBooleanSetting(Global.HDMI_SYSTEM_AUDIO_CONTROL_ENABLED, true);*/
-        // TODO(amyjojo): make the map ro property.
-        mTvInputs.put(Constants.CEC_SWITCH_HDMI1,
-                "com.droidlogic.tvinput/.services.Hdmi1InputService/HW5");
-        mTvInputs.put(Constants.CEC_SWITCH_HDMI2,
-                "com.droidlogic.tvinput/.services.Hdmi2InputService/HW6");
-        mTvInputs.put(Constants.CEC_SWITCH_HDMI3,
-                "com.droidlogic.tvinput/.services.Hdmi3InputService/HW7");
+        mRoutingControlFeatureEnabled =
+            mService.readBooleanSetting(Global.HDMI_CEC_SWITCH_ENABLED, false);
+        mSystemAudioControlFeatureEnabled =
+            mService.readBooleanSetting(Global.HDMI_SYSTEM_AUDIO_CONTROL_ENABLED, true);
+        // TODO(amyjojo): Maintain a portId to TvinputId map.
+        mTvInputs.put(2, "com.droidlogic.tvinput/.services.Hdmi1InputService/HW5");
+        mTvInputs.put(4, "com.droidlogic.tvinput/.services.Hdmi2InputService/HW6");
+        mTvInputs.put(1, "com.droidlogic.tvinput/.services.Hdmi3InputService/HW7");
+    }
+
+    private static final String SHORT_AUDIO_DESCRIPTOR_CONFIG_PATH = "/vendor/etc/sadConfig.xml";
+
+    /**
+     * Called when a device is newly added or a new device is detected or
+     * an existing device is updated.
+     *
+     * @param info device info of a new device.
+     */
+    @ServiceThreadOnly
+    final void addCecDevice(HdmiDeviceInfo info) {
+        assertRunOnServiceThread();
+        HdmiDeviceInfo old = addDeviceInfo(info);
+        if (info.getPhysicalAddress() == mService.getPhysicalAddress()) {
+            // The addition of the device itself should not be notified.
+            // Note that different logical address could still be the same local device.
+            return;
+        }
+        if (old == null) {
+            invokeDeviceEventListener(info, HdmiControlManager.DEVICE_EVENT_ADD_DEVICE);
+        } else if (!old.equals(info)) {
+            invokeDeviceEventListener(old, HdmiControlManager.DEVICE_EVENT_REMOVE_DEVICE);
+            invokeDeviceEventListener(info, HdmiControlManager.DEVICE_EVENT_ADD_DEVICE);
+        }
+    }
+
+    /**
+     * Called when a device is removed or removal of device is detected.
+     *
+     * @param address a logical address of a device to be removed
+     */
+    @ServiceThreadOnly
+    final void removeCecDevice(int address) {
+        assertRunOnServiceThread();
+        HdmiDeviceInfo info = removeDeviceInfo(HdmiDeviceInfo.idForCecDevice(address));
+
+        mCecMessageCache.flushMessagesFrom(address);
+        invokeDeviceEventListener(info, HdmiControlManager.DEVICE_EVENT_REMOVE_DEVICE);
+    }
+
+    /**
+     * Called when a device is updated.
+     *
+     * @param info device info of the updating device.
+     */
+    @ServiceThreadOnly
+    final void updateCecDevice(HdmiDeviceInfo info) {
+        assertRunOnServiceThread();
+        HdmiDeviceInfo old = addDeviceInfo(info);
+
+        if (old == null) {
+            invokeDeviceEventListener(info, HdmiControlManager.DEVICE_EVENT_ADD_DEVICE);
+        } else if (!old.equals(info)) {
+            invokeDeviceEventListener(info, HdmiControlManager.DEVICE_EVENT_UPDATE_DEVICE);
+        }
+    }
+
+    /**
+    * Add a new {@link HdmiDeviceInfo}. It returns old device info which has the same
+     * logical address as new device info's.
+     *
+     * @param deviceInfo a new {@link HdmiDeviceInfo} to be added.
+     * @return {@code null} if it is new device. Otherwise, returns old {@HdmiDeviceInfo}
+     *         that has the same logical address as new one has.
+     */
+    @ServiceThreadOnly
+    @VisibleForTesting
+    protected HdmiDeviceInfo addDeviceInfo(HdmiDeviceInfo deviceInfo) {
+        assertRunOnServiceThread();
+        HdmiDeviceInfo oldDeviceInfo = getCecDeviceInfo(deviceInfo.getLogicalAddress());
+        if (oldDeviceInfo != null) {
+            removeDeviceInfo(deviceInfo.getId());
+        }
+        mDeviceInfos.append(deviceInfo.getId(), deviceInfo);
+        updateSafeDeviceInfoList();
+        return oldDeviceInfo;
+    }
+
+    /**
+     * Remove a device info corresponding to the given {@code logicalAddress}.
+     * It returns removed {@link HdmiDeviceInfo} if exists.
+     *
+     * @param id id of device to be removed
+     * @return removed {@link HdmiDeviceInfo} it exists. Otherwise, returns {@code null}
+     */
+    @ServiceThreadOnly
+    private HdmiDeviceInfo removeDeviceInfo(int id) {
+        assertRunOnServiceThread();
+        HdmiDeviceInfo deviceInfo = mDeviceInfos.get(id);
+        if (deviceInfo != null) {
+            mDeviceInfos.remove(id);
+        }
+        updateSafeDeviceInfoList();
+        return deviceInfo;
+    }
+
+    /**
+     * Return a {@link HdmiDeviceInfo} corresponding to the given {@code logicalAddress}.
+     *
+     * @param logicalAddress logical address of the device to be retrieved
+     * @return {@link HdmiDeviceInfo} matched with the given {@code logicalAddress}.
+     *         Returns null if no logical address matched
+     */
+    @ServiceThreadOnly
+    HdmiDeviceInfo getCecDeviceInfo(int logicalAddress) {
+        assertRunOnServiceThread();
+        return mDeviceInfos.get(HdmiDeviceInfo.idForCecDevice(logicalAddress));
+    }
+
+    @ServiceThreadOnly
+    private void updateSafeDeviceInfoList() {
+        assertRunOnServiceThread();
+        List<HdmiDeviceInfo> copiedDevices = HdmiUtils.sparseArrayToList(mDeviceInfos);
+        synchronized (mLock) {
+            mSafeAllDeviceInfos = copiedDevices;
+        }
+    }
+
+    @GuardedBy("mLock")
+    List<HdmiDeviceInfo> getSafeCecDevicesLocked() {
+        ArrayList<HdmiDeviceInfo> infoList = new ArrayList<>();
+        for (HdmiDeviceInfo info : mSafeAllDeviceInfos) {
+            infoList.add(info);
+        }
+        return infoList;
+    }
+
+    private void invokeDeviceEventListener(HdmiDeviceInfo info, int status) {
+        mService.invokeDeviceEventListeners(info, status);
+    }
+
+    @Override
+    @ServiceThreadOnly
+    void onHotplug(int portId, boolean connected) {
+        assertRunOnServiceThread();
+        if (connected) {
+            mService.wakeUp();
+        }
+        if (mService.getPortInfo(portId).getType() == HdmiPortInfo.PORT_OUTPUT) {
+            mCecMessageCache.flushAll();
+        } else {
+            if (connected) {
+                launchDeviceDiscovery();
+            } else {
+                // TODO(amyjojo): remove device from mDeviceInfo
+            }
+        }
     }
 
     @Override
@@ -93,7 +268,7 @@
         mTvSystemAudioModeSupport = false;
         // Record the last state of System Audio Control before going to standby
         synchronized (mLock) {
-            SystemProperties.set(
+            mService.writeStringSetting(
                     Constants.PROPERTY_LAST_SYSTEM_AUDIO_CONTROL,
                     mSystemAudioActivated ? "true" : "false");
         }
@@ -104,6 +279,10 @@
     @ServiceThreadOnly
     protected void onAddressAllocated(int logicalAddress, int reason) {
         assertRunOnServiceThread();
+        if (reason == mService.INITIATED_BY_ENABLE_CEC) {
+            mService.setAndBroadcastActiveSource(mService.getPhysicalAddress(),
+                    getDeviceInfo().getDeviceType(), Constants.ADDR_BROADCAST);
+        }
         mService.sendCecCommand(
                 HdmiCecMessageBuilder.buildReportPhysicalAddressCommand(
                         mAddress, mService.getPhysicalAddress(), mDeviceType));
@@ -116,12 +295,17 @@
         boolean lastSystemAudioControlStatus =
                 SystemProperties.getBoolean(Constants.PROPERTY_LAST_SYSTEM_AUDIO_CONTROL, true);
         systemAudioControlOnPowerOn(systemAudioControlOnPowerOnProp, lastSystemAudioControlStatus);
+        clearDeviceInfoList();
+        launchDeviceDiscovery();
         startQueuedActions();
     }
 
     @Override
     protected int findKeyReceiverAddress() {
-        return Constants.ADDR_TV;
+        if (getActiveSource().isValid()) {
+            return getActiveSource().logicalAddress;
+        }
+        return Constants.ADDR_INVALID;
     }
 
     @VisibleForTesting
@@ -129,7 +313,7 @@
             int systemAudioOnPowerOnProp, boolean lastSystemAudioControlStatus) {
         if ((systemAudioOnPowerOnProp == ALWAYS_SYSTEM_AUDIO_CONTROL_ON_POWER_ON)
                 || ((systemAudioOnPowerOnProp == USE_LAST_STATE_SYSTEM_AUDIO_CONTROL_ON_POWER_ON)
-                && lastSystemAudioControlStatus)) {
+                && lastSystemAudioControlStatus && isSystemAudioControlFeatureEnabled())) {
             addAndStartAction(new SystemAudioInitiationActionFromAvr(this));
         }
     }
@@ -146,12 +330,84 @@
     @ServiceThreadOnly
     protected void setPreferredAddress(int addr) {
         assertRunOnServiceThread();
-        SystemProperties.set(
+        mService.writeStringSetting(
                 Constants.PROPERTY_PREFERRED_ADDRESS_AUDIO_SYSTEM, String.valueOf(addr));
     }
 
     @Override
     @ServiceThreadOnly
+    protected boolean handleReportPhysicalAddress(HdmiCecMessage message) {
+        assertRunOnServiceThread();
+        int path = HdmiUtils.twoBytesToInt(message.getParams());
+        int address = message.getSource();
+        int type = message.getParams()[2];
+
+        // Ignore if [Device Discovery Action] is going on.
+        if (hasAction(DeviceDiscoveryAction.class)) {
+            Slog.i(TAG, "Ignored while Device Discovery Action is in progress: " + message);
+            return true;
+        }
+
+        // Update the device info with TIF, note that the same device info could have added in
+        // device discovery and we do not want to override it with default OSD name. Therefore we
+        // need the following check to skip redundant device info updating.
+        HdmiDeviceInfo oldDevice = getCecDeviceInfo(address);
+        if (oldDevice == null || oldDevice.getPhysicalAddress() != path) {
+            addCecDevice(new HdmiDeviceInfo(
+                    address, path, mService.pathToPortId(path), type,
+                    Constants.UNKNOWN_VENDOR_ID, HdmiUtils.getDefaultDeviceName(address)));
+            // if we are adding a new device info, send out a give osd name command
+            // to update the name of the device in TIF
+            mService.sendCecCommand(
+                    HdmiCecMessageBuilder.buildGiveOsdNameCommand(mAddress, address));
+            return true;
+        }
+
+        Slog.w(TAG, "Device info exists. Not updating on Physical Address.");
+        return true;
+    }
+
+    @Override
+    protected boolean handleReportPowerStatus(HdmiCecMessage command) {
+        int newStatus = command.getParams()[0] & 0xFF;
+        updateDevicePowerStatus(command.getSource(), newStatus);
+        return true;
+    }
+
+    @Override
+    @ServiceThreadOnly
+    protected boolean handleSetOsdName(HdmiCecMessage message) {
+        int source = message.getSource();
+        String osdName;
+        HdmiDeviceInfo deviceInfo = getCecDeviceInfo(source);
+        // If the device is not in device list, ignore it.
+        if (deviceInfo == null) {
+            Slog.i(TAG, "No source device info for <Set Osd Name>." + message);
+            return true;
+        }
+        try {
+            osdName = new String(message.getParams(), "US-ASCII");
+        } catch (UnsupportedEncodingException e) {
+            Slog.e(TAG, "Invalid <Set Osd Name> request:" + message, e);
+            return true;
+        }
+
+        if (deviceInfo.getDisplayName().equals(osdName)) {
+            Slog.d(TAG, "Ignore incoming <Set Osd Name> having same osd name:" + message);
+            return true;
+        }
+
+        Slog.d(TAG, "Updating device OSD name from "
+                + deviceInfo.getDisplayName()
+                + " to " + osdName);
+        updateCecDevice(new HdmiDeviceInfo(deviceInfo.getLogicalAddress(),
+                deviceInfo.getPhysicalAddress(), deviceInfo.getPortId(),
+                deviceInfo.getDeviceType(), deviceInfo.getVendorId(), osdName));
+        return true;
+    }
+
+    @Override
+    @ServiceThreadOnly
     protected boolean handleReportAudioStatus(HdmiCecMessage message) {
         assertRunOnServiceThread();
         // TODO(amyjojo): implement report audio status handler
@@ -190,8 +446,11 @@
     @ServiceThreadOnly
     protected boolean handleGiveAudioStatus(HdmiCecMessage message) {
         assertRunOnServiceThread();
-
-        reportAudioStatus(message);
+        if (isSystemAudioControlFeatureEnabled()) {
+            reportAudioStatus(message.getSource());
+        } else {
+            mService.maySendFeatureAbortCommand(message, Constants.ABORT_REFUSED);
+        }
         return true;
     }
 
@@ -210,7 +469,7 @@
     protected boolean handleRequestArcInitiate(HdmiCecMessage message) {
         assertRunOnServiceThread();
         removeAction(ArcInitiationActionFromAvr.class);
-        if (!SystemProperties.getBoolean(Constants.PROPERTY_ARC_SUPPORT, true)) {
+        if (!mService.readBooleanSetting(Constants.PROPERTY_ARC_SUPPORT, true)) {
             mService.maySendFeatureAbortCommand(message, Constants.ABORT_UNRECOGNIZED_OPCODE);
         } else if (!isDirectConnectToTv()) {
             HdmiLogger.debug("AVR device is not directly connected with TV");
@@ -249,13 +508,35 @@
             mService.maySendFeatureAbortCommand(message, Constants.ABORT_NOT_IN_CORRECT_MODE);
             return true;
         }
-        AudioDeviceInfo deviceInfo = getSystemAudioDeviceInfo();
-        if (deviceInfo == null) {
-            mService.maySendFeatureAbortCommand(message, Constants.ABORT_UNABLE_TO_DETERMINE);
-            return true;
+
+        List<DeviceConfig> config = null;
+        File file = new File(SHORT_AUDIO_DESCRIPTOR_CONFIG_PATH);
+        if (file.exists()) {
+            try {
+                InputStream in = new FileInputStream(file);
+                config = HdmiUtils.ShortAudioDescriptorXmlParser.parse(in);
+                in.close();
+            } catch (IOException e) {
+                Slog.e(TAG, "Error reading file: " + file, e);
+            } catch (XmlPullParserException e) {
+                Slog.e(TAG, "Unable to parse file: " + file, e);
+            }
         }
+
         @AudioCodec int[] audioFormatCodes = parseAudioFormatCodes(message.getParams());
-        byte[] sadBytes = getSupportedShortAudioDescriptors(deviceInfo, audioFormatCodes);
+        byte[] sadBytes;
+        if (config != null && config.size() > 0) {
+            sadBytes = getSupportedShortAudioDescriptorsFromConfig(config, audioFormatCodes);
+        } else {
+            AudioDeviceInfo deviceInfo = getSystemAudioDeviceInfo();
+            if (deviceInfo == null) {
+                mService.maySendFeatureAbortCommand(message, Constants.ABORT_UNABLE_TO_DETERMINE);
+                return true;
+            }
+
+            sadBytes = getSupportedShortAudioDescriptors(deviceInfo, audioFormatCodes);
+        }
+
         if (sadBytes.length == 0) {
             mService.maySendFeatureAbortCommand(message, Constants.ABORT_INVALID_OPERAND);
         } else {
@@ -268,14 +549,127 @@
 
     private byte[] getSupportedShortAudioDescriptors(
             AudioDeviceInfo deviceInfo, @AudioCodec int[] audioFormatCodes) {
-        // TODO(b/80297701) implement
-        return new byte[] {};
+        ArrayList<byte[]> sads = new ArrayList<>(audioFormatCodes.length);
+        for (@AudioCodec int audioFormatCode : audioFormatCodes) {
+            byte[] sad = getSupportedShortAudioDescriptor(deviceInfo, audioFormatCode);
+            if (sad != null) {
+                if (sad.length == 3) {
+
+                    sads.add(sad);
+                } else {
+                    HdmiLogger.warning(
+                            "Dropping Short Audio Descriptor with length %d for requested codec %x",
+                            sad.length, audioFormatCode);
+                }
+            }
+        }
+        return getShortAudioDescriptorBytes(sads);
+    }
+
+    private byte[] getSupportedShortAudioDescriptorsFromConfig(
+            List<DeviceConfig> deviceConfig, @AudioCodec int[] audioFormatCodes) {
+        DeviceConfig deviceConfigToUse = null;
+        for (DeviceConfig device : deviceConfig) {
+            // TODO(amyjojo) use PROPERTY_SYSTEM_AUDIO_MODE_AUDIO_PORT to get the audio device name
+            if (device.name.equals("VX_AUDIO_DEVICE_IN_HDMI_ARC")) {
+                deviceConfigToUse = device;
+                break;
+            }
+        }
+        if (deviceConfigToUse == null) {
+            // TODO(amyjojo) use PROPERTY_SYSTEM_AUDIO_MODE_AUDIO_PORT to get the audio device name
+            Slog.w(TAG, "sadConfig.xml does not have required device info for "
+                        + "VX_AUDIO_DEVICE_IN_HDMI_ARC");
+            return new byte[0];
+        }
+        HashMap<Integer, byte[]> map = new HashMap<>();
+        ArrayList<byte[]> sads = new ArrayList<>(audioFormatCodes.length);
+        for (CodecSad codecSad : deviceConfigToUse.supportedCodecs) {
+            map.put(codecSad.audioCodec, codecSad.sad);
+        }
+        for (int i = 0; i < audioFormatCodes.length; i++) {
+            if (map.containsKey(audioFormatCodes[i])) {
+                byte[] sad = map.get(audioFormatCodes[i]);
+                if (sad != null && sad.length == 3) {
+                    sads.add(sad);
+                }
+            }
+        }
+        return getShortAudioDescriptorBytes(sads);
+    }
+
+    private byte[] getShortAudioDescriptorBytes(ArrayList<byte[]> sads) {
+        // Short Audio Descriptors are always 3 bytes long.
+        byte[] bytes = new byte[sads.size() * 3];
+        int index = 0;
+        for (byte[] sad : sads) {
+            System.arraycopy(sad, 0, bytes, index, 3);
+            index += 3;
+        }
+        return bytes;
+    }
+
+    /**
+     * Returns a 3 byte short audio descriptor as described in CEC 1.4 table 29 or null if the
+     * audioFormatCode is not supported.
+     */
+    @Nullable
+    private byte[] getSupportedShortAudioDescriptor(
+            AudioDeviceInfo deviceInfo, @AudioCodec int audioFormatCode) {
+        switch (audioFormatCode) {
+            case Constants.AUDIO_CODEC_NONE: {
+                return null;
+            }
+            case Constants.AUDIO_CODEC_LPCM: {
+                return getLpcmShortAudioDescriptor(deviceInfo);
+            }
+            // TODO(b/80297701): implement the rest of the codecs
+            case Constants.AUDIO_CODEC_DD:
+            case Constants.AUDIO_CODEC_MPEG1:
+            case Constants.AUDIO_CODEC_MP3:
+            case Constants.AUDIO_CODEC_MPEG2:
+            case Constants.AUDIO_CODEC_AAC:
+            case Constants.AUDIO_CODEC_DTS:
+            case Constants.AUDIO_CODEC_ATRAC:
+            case Constants.AUDIO_CODEC_ONEBITAUDIO:
+            case Constants.AUDIO_CODEC_DDP:
+            case Constants.AUDIO_CODEC_DTSHD:
+            case Constants.AUDIO_CODEC_TRUEHD:
+            case Constants.AUDIO_CODEC_DST:
+            case Constants.AUDIO_CODEC_WMAPRO:
+            default: {
+                return null;
+            }
+        }
+    }
+
+    @Nullable
+    private byte[] getLpcmShortAudioDescriptor(AudioDeviceInfo deviceInfo) {
+        // TODO(b/80297701): implement
+        return null;
     }
 
     @Nullable
     private AudioDeviceInfo getSystemAudioDeviceInfo() {
-        // TODO(b/80297701) implement
-        // Get the audio device used for system audio mode.
+        AudioManager audioManager = mService.getContext().getSystemService(AudioManager.class);
+        if (audioManager == null) {
+            HdmiLogger.error(
+                    "Error getting system audio device because AudioManager not available.");
+            return null;
+        }
+        AudioDeviceInfo[] devices = audioManager.getDevices(AudioManager.GET_DEVICES_INPUTS);
+        HdmiLogger.debug("Found %d audio input devices", devices.length);
+        for (AudioDeviceInfo device : devices) {
+            HdmiLogger.debug("%s at port %s", device.getProductName(), device.getPort());
+            HdmiLogger.debug("Supported encodings are %s",
+                    Arrays.stream(device.getEncodings()).mapToObj(
+                            AudioFormat::toLogFriendlyEncoding
+                    ).collect(Collectors.joining(", ")));
+            // TODO(b/80297701) use the actual device type that system audio mode is connected to.
+            if (device.getType() == AudioDeviceInfo.TYPE_HDMI_ARC) {
+                return device;
+            }
+        }
         return null;
     }
 
@@ -373,17 +767,20 @@
             .setWiredDeviceConnectionState(AudioSystem.DEVICE_IN_HDMI, enabled ? 1 : 0, "", "");
     }
 
-    private void reportAudioStatus(HdmiCecMessage message) {
+    void reportAudioStatus(int source) {
         assertRunOnServiceThread();
 
         int volume = mService.getAudioManager().getStreamVolume(AudioManager.STREAM_MUSIC);
         boolean mute = mService.getAudioManager().isStreamMute(AudioManager.STREAM_MUSIC);
         int maxVolume = mService.getAudioManager().getStreamMaxVolume(AudioManager.STREAM_MUSIC);
+        int minVolume = mService.getAudioManager().getStreamMinVolume(AudioManager.STREAM_MUSIC);
         int scaledVolume = VolumeControlAction.scaleToCecVolume(volume, maxVolume);
+        HdmiLogger.debug("Reporting volume %i (%i-%i) as CEC volume %i", volume,
+                minVolume, maxVolume, scaledVolume);
 
         mService.sendCecCommand(
                 HdmiCecMessageBuilder.buildReportAudioStatus(
-                        mAddress, message.getSource(), scaledVolume, mute));
+                        mAddress, source, scaledVolume, mute));
     }
 
     /**
@@ -407,8 +804,8 @@
         HdmiLogger.debug(
                 "System Audio Mode change[old:%b new:%b]",
                 mSystemAudioActivated, newSystemAudioMode);
-        // Wake up device if System Audio Control is turned on but device is still on standby
-        if (newSystemAudioMode && mService.isPowerStandbyOrTransient()) {
+        // Wake up device if System Audio Control is turned on
+        if (newSystemAudioMode) {
             mService.wakeUp();
         }
         setSystemAudioMode(newSystemAudioMode);
@@ -423,7 +820,7 @@
      */
     private void setSystemAudioMode(boolean newSystemAudioMode) {
         int targetPhysicalAddress = getActiveSource().physicalAddress;
-        int port = getLocalPortFromPhysicalAddress(targetPhysicalAddress);
+        int port = mService.pathToPortId(targetPhysicalAddress);
         if (newSystemAudioMode && port >= 0) {
             switchToAudioInput();
         }
@@ -431,16 +828,18 @@
         // PROPERTY_SYSTEM_AUDIO_MODE_MUTING_ENABLE is false when device never needs to be muted.
         boolean currentMuteStatus =
                 mService.getAudioManager().isStreamMute(AudioManager.STREAM_MUSIC);
-        if (SystemProperties.getBoolean(
-                Constants.PROPERTY_SYSTEM_AUDIO_MODE_MUTING_ENABLE, true)
-                && currentMuteStatus == newSystemAudioMode) {
-            mService.getAudioManager()
-                    .adjustStreamVolume(
-                            AudioManager.STREAM_MUSIC,
-                            newSystemAudioMode
-                                    ? AudioManager.ADJUST_UNMUTE
-                                    : AudioManager.ADJUST_MUTE,
-                                    0);
+        if (currentMuteStatus == newSystemAudioMode) {
+            if (mService.readBooleanSetting(
+                    Constants.PROPERTY_SYSTEM_AUDIO_MODE_MUTING_ENABLE, true)
+                            || newSystemAudioMode) {
+                mService.getAudioManager()
+                        .adjustStreamVolume(
+                                AudioManager.STREAM_MUSIC,
+                                newSystemAudioMode
+                                        ? AudioManager.ADJUST_UNMUTE
+                                        : AudioManager.ADJUST_MUTE,
+                                0);
+            }
         }
         updateAudioManagerForSystemAudio(newSystemAudioMode);
         synchronized (mLock) {
@@ -478,6 +877,13 @@
         HdmiLogger.debug("[A]UpdateSystemAudio mode[on=%b] output=[%X]", on, device);
     }
 
+    void onSystemAduioControlFeatureSupportChanged(boolean enabled) {
+        setSystemAudioControlFeatureEnabled(enabled);
+        if (enabled) {
+            addAndStartAction(new SystemAudioInitiationActionFromAvr(this));
+        }
+    }
+
     @ServiceThreadOnly
     void setSystemAudioControlFeatureEnabled(boolean enabled) {
         assertRunOnServiceThread();
@@ -487,6 +893,14 @@
     }
 
     @ServiceThreadOnly
+    void setRoutingControlFeatureEnables(boolean enabled) {
+        assertRunOnServiceThread();
+        synchronized (mLock) {
+            mRoutingControlFeatureEnabled = enabled;
+        }
+    }
+
+    @ServiceThreadOnly
     void doManualPortSwitching(int portId, IHdmiControlCallback callback) {
         assertRunOnServiceThread();
         // TODO: validate port ID
@@ -494,19 +908,20 @@
             invokeCallback(callback, HdmiControlManager.RESULT_SUCCESS);
             return;
         }
-        getActiveSource().invalidate();
         if (!mService.isControlEnabled()) {
+            setRoutingPort(portId);
             setLocalActivePort(portId);
             invokeCallback(callback, HdmiControlManager.RESULT_INCORRECT_MODE);
             return;
         }
-        int oldPath = getLocalActivePort() != Constants.CEC_SWITCH_HOME
-                ? getActivePathOnSwitchFromActivePortId(getLocalActivePort())
+        int oldPath = getRoutingPort() != Constants.CEC_SWITCH_HOME
+                ? mService.portIdToPath(getRoutingPort())
                 : getDeviceInfo().getPhysicalAddress();
-        int newPath = getActivePathOnSwitchFromActivePortId(portId);
+        int newPath = mService.portIdToPath(portId);
         if (oldPath == newPath) {
             return;
         }
+        setRoutingPort(portId);
         setLocalActivePort(portId);
         HdmiCecMessage routingChange =
                 HdmiCecMessageBuilder.buildRoutingChange(mAddress, oldPath, newPath);
@@ -575,10 +990,8 @@
             mService.maySendFeatureAbortCommand(message, Constants.ABORT_REFUSED);
             return;
         }
-        // Wake up device if it is still on standby
-        if (mService.isPowerStandbyOrTransient()) {
-            mService.wakeUp();
-        }
+        // Wake up device
+        mService.wakeUp();
         // Check if TV supports System Audio Control.
         // Handle broadcasting setSystemAudioMode on or aborting message on callback.
         queryTvSystemAudioModeSupport(new TvSystemAudioModeSupportedCallback() {
@@ -608,7 +1021,7 @@
 
     @Override
     protected void switchInputOnReceivingNewActivePath(int physicalAddress) {
-        int port = getLocalPortFromPhysicalAddress(physicalAddress);
+        int port = mService.pathToPortId(physicalAddress);
         if (isSystemAudioActivated() && port < 0) {
             // If system audio mode is on and the new active source is not under the current device,
             // Will switch to ARC input.
@@ -622,6 +1035,10 @@
     }
 
     protected void routeToInputFromPortId(int portId) {
+        if (!isRoutingControlFeatureEnabled()) {
+            HdmiLogger.debug("Routing Control Feature is not enabled.");
+            return;
+        }
         if (mArcIntentUsed) {
             routeToTvInputFromPortId(portId);
         } else {
@@ -635,9 +1052,7 @@
             return;
         }
         // Wake up if the current device if ready to route.
-        if (mService.isPowerStandbyOrTransient()) {
-            mService.wakeUp();
-        }
+        mService.wakeUp();
         if (portId == Constants.CEC_SWITCH_HOME && mService.isPlaybackDevice()) {
             switchToHomeTvInput();
         } else if (portId == Constants.CEC_SWITCH_ARC) {
@@ -678,7 +1093,7 @@
 
     @Override
     protected void handleRoutingChangeAndInformation(int physicalAddress, HdmiCecMessage message) {
-        int port = getLocalPortFromPhysicalAddress(physicalAddress);
+        int port = mService.pathToPortId(physicalAddress);
         // Routing change or information sent from switches under the current device can be ignored.
         if (port > 0) {
             return;
@@ -711,8 +1126,7 @@
             return;
         }
 
-        int routingInformationPath =
-                getActivePathOnSwitchFromActivePortId(getRoutingPort());
+        int routingInformationPath = mService.portIdToPath(getRoutingPort());
         // If current device is already the leaf of the whole HDMI system, will do nothing.
         if (routingInformationPath == mService.getPhysicalAddress()) {
             HdmiLogger.debug("Current device can't assign valid physical address"
@@ -725,4 +1139,74 @@
                 mAddress, routingInformationPath));
         routeToInputFromPortId(getRoutingPort());
     }
+
+    protected void updateDevicePowerStatus(int logicalAddress, int newPowerStatus) {
+        HdmiDeviceInfo info = getCecDeviceInfo(logicalAddress);
+        if (info == null) {
+            Slog.w(TAG, "Can not update power status of non-existing device:" + logicalAddress);
+            return;
+        }
+
+        if (info.getDevicePowerStatus() == newPowerStatus) {
+            return;
+        }
+
+        HdmiDeviceInfo newInfo = HdmiUtils.cloneHdmiDeviceInfo(info, newPowerStatus);
+        // addDeviceInfo replaces old device info with new one if exists.
+        addDeviceInfo(newInfo);
+
+        invokeDeviceEventListener(newInfo, HdmiControlManager.DEVICE_EVENT_UPDATE_DEVICE);
+    }
+
+    @ServiceThreadOnly
+    private void launchDeviceDiscovery() {
+        assertRunOnServiceThread();
+        if (hasAction(DeviceDiscoveryAction.class)) {
+            Slog.i(TAG, "Device Discovery Action is in progress. Restarting.");
+            removeAction(DeviceDiscoveryAction.class);
+        }
+        DeviceDiscoveryAction action = new DeviceDiscoveryAction(this,
+                new DeviceDiscoveryCallback() {
+                    @Override
+                    public void onDeviceDiscoveryDone(List<HdmiDeviceInfo> deviceInfos) {
+                        for (HdmiDeviceInfo info : deviceInfos) {
+                            addCecDevice(info);
+                        }
+                    }
+                });
+        addAndStartAction(action);
+    }
+
+    // Clear all device info.
+    @ServiceThreadOnly
+    private void clearDeviceInfoList() {
+        assertRunOnServiceThread();
+        for (HdmiDeviceInfo info : HdmiUtils.sparseArrayToList(mDeviceInfos)) {
+            if (info.getPhysicalAddress() == mService.getPhysicalAddress()) {
+                continue;
+            }
+            invokeDeviceEventListener(info, HdmiControlManager.DEVICE_EVENT_REMOVE_DEVICE);
+        }
+        mDeviceInfos.clear();
+        updateSafeDeviceInfoList();
+    }
+
+    @Override
+    protected void dump(IndentingPrintWriter pw) {
+        pw.println("HdmiCecLocalDeviceAudioSystem:");
+        pw.increaseIndent();
+        pw.println("mSystemAudioActivated: " + mSystemAudioActivated);
+        pw.println("isRoutingFeatureEnabled " + isRoutingControlFeatureEnabled());
+        pw.println("mSystemAudioControlFeatureEnabled: " + mSystemAudioControlFeatureEnabled);
+        pw.println("mTvSystemAudioModeSupport: " + mTvSystemAudioModeSupport);
+        pw.println("mArcEstablished: " + mArcEstablished);
+        pw.println("mArcIntentUsed: " + mArcIntentUsed);
+        pw.println("mRoutingPort: " + getRoutingPort());
+        pw.println("mLocalActivePort: " + getLocalActivePort());
+        HdmiUtils.dumpMap(pw, "mTvInputs:", mTvInputs);
+        HdmiUtils.dumpSparseArray(pw, "mDeviceInfos:", mDeviceInfos);
+        pw.decreaseIndent();
+        super.dump(pw);
+    }
+
 }
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
index 07db971..7a0c279 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
@@ -77,6 +77,10 @@
     @ServiceThreadOnly
     protected void onAddressAllocated(int logicalAddress, int reason) {
         assertRunOnServiceThread();
+        if (reason == mService.INITIATED_BY_ENABLE_CEC) {
+            mService.setAndBroadcastActiveSource(mService.getPhysicalAddress(),
+                    getDeviceInfo().getDeviceType(), Constants.ADDR_BROADCAST);
+        }
         mService.sendCecCommand(HdmiCecMessageBuilder.buildReportPhysicalAddressCommand(
                 mAddress, mService.getPhysicalAddress(), mDeviceType));
         mService.sendCecCommand(HdmiCecMessageBuilder.buildDeviceVendorIdCommand(
@@ -96,7 +100,7 @@
     @ServiceThreadOnly
     protected void setPreferredAddress(int addr) {
         assertRunOnServiceThread();
-        SystemProperties.set(Constants.PROPERTY_PREFERRED_ADDRESS_PLAYBACK,
+        mService.writeStringSetting(Constants.PROPERTY_PREFERRED_ADDRESS_PLAYBACK,
                 String.valueOf(addr));
     }
 
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java
index 6532e16..ae008b4 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java
@@ -16,7 +16,10 @@
 
 package com.android.server.hdmi;
 
+import static com.android.internal.os.RoSystemProperties.PROPERTY_HDMI_IS_DEVICE_HDMI_CEC_SWITCH;
+
 import android.hardware.hdmi.HdmiControlManager;
+import android.hardware.hdmi.HdmiPortInfo;
 import android.hardware.hdmi.IHdmiControlCallback;
 import android.os.SystemProperties;
 import android.util.Slog;
@@ -42,7 +45,7 @@
     // Device has cec switch functionality or not.
     // Default is false.
     protected boolean mIsSwitchDevice = SystemProperties.getBoolean(
-            Constants.PROPERTY_HDMI_IS_DEVICE_HDMI_CEC_SWITCH, false);
+            PROPERTY_HDMI_IS_DEVICE_HDMI_CEC_SWITCH, false);
 
     // Routing port number used for Routing Control.
     // This records the default routing port or the previous valid routing port.
@@ -63,6 +66,10 @@
     @LocalActivePort
     protected int mLocalActivePort = Constants.CEC_SWITCH_HOME;
 
+    // Whether the Routing Coutrol feature is enabled or not. False by default.
+    @GuardedBy("mLock")
+    protected boolean mRoutingControlFeatureEnabled;
+
     protected HdmiCecLocalDeviceSource(HdmiControlService service, int deviceType) {
         super(service, deviceType);
     }
@@ -71,9 +78,11 @@
     @ServiceThreadOnly
     void onHotplug(int portId, boolean connected) {
         assertRunOnServiceThread();
-        mCecMessageCache.flushAll();
+        if (mService.getPortInfo(portId).getType() == HdmiPortInfo.PORT_OUTPUT) {
+            mCecMessageCache.flushAll();
+        }
         // We'll not clear mIsActiveSource on the hotplug event to pass CETC 11.2.2-2 ~ 3.
-        if (mService.isPowerStandbyOrTransient()) {
+        if (connected) {
             mService.wakeUp();
         }
     }
@@ -117,7 +126,10 @@
             setActiveSource(activeSource);
         }
         setIsActiveSource(physicalAddress == mService.getPhysicalAddress());
-        switchInputOnReceivingNewActivePath(physicalAddress);
+        updateDevicePowerStatus(logicalAddress, HdmiControlManager.POWER_STATUS_ON);
+        if (isRoutingControlFeatureEnabled()) {
+            switchInputOnReceivingNewActivePath(physicalAddress);
+        }
         return true;
     }
 
@@ -147,6 +159,10 @@
     @ServiceThreadOnly
     protected boolean handleRoutingChange(HdmiCecMessage message) {
         assertRunOnServiceThread();
+        if (!isRoutingControlFeatureEnabled()) {
+            mService.maySendFeatureAbortCommand(message, Constants.ABORT_REFUSED);
+            return true;
+        }
         int newPath = HdmiUtils.twoBytesToInt(message.getParams(), 2);
         // if the current device is a pure playback device
         if (!mIsSwitchDevice
@@ -162,6 +178,10 @@
     @ServiceThreadOnly
     protected boolean handleRoutingInformation(HdmiCecMessage message) {
         assertRunOnServiceThread();
+        if (!isRoutingControlFeatureEnabled()) {
+            mService.maySendFeatureAbortCommand(message, Constants.ABORT_REFUSED);
+            return true;
+        }
         int physicalAddress = HdmiUtils.twoBytesToInt(message.getParams());
         // if the current device is a pure playback device
         if (!mIsSwitchDevice
@@ -185,13 +205,20 @@
         // do nothing
     }
 
+    // Update the power status of the devices connected to the current device.
+    // This only works if the current device is a switch and keeps tracking the device info
+    // of the device connected to it.
+    protected void updateDevicePowerStatus(int logicalAddress, int newPowerStatus) {
+        // do nothing
+    }
+
     // Active source claiming needs to be handled in Service
     // since service can decide who will be the active source when the device supports
     // multiple device types in this method.
     // This method should only be called when the device can be the active source.
     protected void setAndBroadcastActiveSource(HdmiCecMessage message, int physicalAddress) {
         mService.setAndBroadcastActiveSource(
-                message, physicalAddress, getDeviceInfo().getDeviceType());
+                physicalAddress, getDeviceInfo().getDeviceType(), message.getSource());
     }
 
     @ServiceThreadOnly
@@ -204,10 +231,8 @@
         if (!mIsActiveSource) {
             return;
         }
-        // Wake up the device if the power is in standby mode
-        if (mService.isPowerStandbyOrTransient()) {
-            mService.wakeUp();
-        }
+        // Wake up the device
+        mService.wakeUp();
         return;
     }
 
@@ -268,6 +293,12 @@
         }
     }
 
+    boolean isRoutingControlFeatureEnabled() {
+        synchronized (mLock) {
+            return mRoutingControlFeatureEnabled;
+        }
+    }
+
     // Check if the device is trying to switch to the same input that is active right now.
     // This can help avoid redundant port switching.
     protected boolean isSwitchingToTheSameInput(@LocalActivePort int activePort) {
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
index b91d8c6..a8c4350 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
@@ -346,10 +346,6 @@
         }
     }
 
-    int getPortId(int physicalAddress) {
-        return mService.pathToPortId(physicalAddress);
-    }
-
     /**
      * Returns the previous port id kept to handle input switching on <Inactive Source>.
      */
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecMessage.java b/services/core/java/com/android/server/hdmi/HdmiCecMessage.java
index c005615..f8b3962 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecMessage.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecMessage.java
@@ -17,6 +17,7 @@
 package com.android.server.hdmi;
 
 import android.annotation.Nullable;
+
 import libcore.util.EmptyArray;
 
 import java.util.Arrays;
@@ -111,12 +112,11 @@
     @Override
     public String toString() {
         StringBuffer s = new StringBuffer();
-        s.append(String.format("<%s> src: %d, dst: %d",
-                opcodeToString(mOpcode), mSource, mDestination));
+        s.append(String.format("<%s> %X%X:%02X",
+                opcodeToString(mOpcode), mSource, mDestination, mOpcode));
         if (mParams.length > 0) {
-            s.append(", params:");
             for (byte data : mParams) {
-                s.append(String.format(" %02X", data));
+                s.append(String.format(":%02X", data));
             }
         }
         return s.toString();
@@ -133,7 +133,7 @@
             case Constants.MESSAGE_TUNER_STEP_DECREMENT:
                 return "Tuner Step Decrement";
             case Constants.MESSAGE_TUNER_DEVICE_STATUS:
-                return "Tuner Device Staus";
+                return "Tuner Device Status";
             case Constants.MESSAGE_GIVE_TUNER_DEVICE_STATUS:
                 return "Give Tuner Device Status";
             case Constants.MESSAGE_RECORD_ON:
@@ -207,7 +207,7 @@
             case Constants.MESSAGE_DEVICE_VENDOR_ID:
                 return "Device Vendor Id";
             case Constants.MESSAGE_VENDOR_COMMAND:
-                return "Vendor Commandn";
+                return "Vendor Command";
             case Constants.MESSAGE_VENDOR_REMOTE_BUTTON_DOWN:
                 return "Vendor Remote Button Down";
             case Constants.MESSAGE_VENDOR_REMOTE_BUTTON_UP:
@@ -215,7 +215,7 @@
             case Constants.MESSAGE_GIVE_DEVICE_VENDOR_ID:
                 return "Give Device Vendor Id";
             case Constants.MESSAGE_MENU_REQUEST:
-                return "Menu REquest";
+                return "Menu Request";
             case Constants.MESSAGE_MENU_STATUS:
                 return "Menu Status";
             case Constants.MESSAGE_GIVE_DEVICE_POWER_STATUS:
@@ -247,7 +247,7 @@
             case Constants.MESSAGE_SET_EXTERNAL_TIMER:
                 return "Set External Timer";
             case Constants.MESSAGE_REPORT_SHORT_AUDIO_DESCRIPTOR:
-                return "Repot Short Audio Descriptor";
+                return "Report Short Audio Descriptor";
             case Constants.MESSAGE_REQUEST_SHORT_AUDIO_DESCRIPTOR:
                 return "Request Short Audio Descriptor";
             case Constants.MESSAGE_INITIATE_ARC:
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index 833091d..46219d5 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -19,12 +19,15 @@
 import static android.hardware.hdmi.HdmiControlManager.DEVICE_EVENT_ADD_DEVICE;
 import static android.hardware.hdmi.HdmiControlManager.DEVICE_EVENT_REMOVE_DEVICE;
 
+import static com.android.internal.os.RoSystemProperties.PROPERTY_HDMI_IS_DEVICE_HDMI_CEC_SWITCH;
+import static com.android.server.hdmi.Constants.ADDR_UNREGISTERED;
 import static com.android.server.hdmi.Constants.DISABLED;
 import static com.android.server.hdmi.Constants.ENABLED;
 import static com.android.server.hdmi.Constants.OPTION_MHL_ENABLE;
 import static com.android.server.hdmi.Constants.OPTION_MHL_INPUT_SWITCHING;
 import static com.android.server.hdmi.Constants.OPTION_MHL_POWER_CHARGE;
 import static com.android.server.hdmi.Constants.OPTION_MHL_SERVICE_CONTROL;
+import static com.android.server.power.ShutdownThread.SHUTDOWN_ACTION_PROPERTY;
 
 import android.annotation.Nullable;
 import android.content.BroadcastReceiver;
@@ -183,9 +186,10 @@
         @Override
         public void onReceive(Context context, Intent intent) {
             assertRunOnServiceThread();
+            boolean isReboot = SystemProperties.get(SHUTDOWN_ACTION_PROPERTY).contains("1");
             switch (intent.getAction()) {
                 case Intent.ACTION_SCREEN_OFF:
-                    if (isPowerOnOrTransient()) {
+                    if (isPowerOnOrTransient() && !isReboot) {
                         onStandby(STANDBY_SCREEN_OFF);
                     }
                     break;
@@ -201,7 +205,7 @@
                     }
                     break;
                 case Intent.ACTION_SHUTDOWN:
-                    if (isPowerOnOrTransient()) {
+                    if (isPowerOnOrTransient() && !isReboot) {
                         onStandby(STANDBY_SHUTDOWN);
                     }
                     break;
@@ -344,6 +348,10 @@
     @Nullable
     private Looper mIoLooper;
 
+    // Thread safe physical address
+    @GuardedBy("mLock")
+    private int mPhysicalAddress = Constants.INVALID_PHYSICAL_ADDRESS;
+
     // Last input port before switching to the MHL port. Should switch back to this port
     // when the mobile device sends the request one touch play with off.
     // Gets invalidated if we go to other port/input.
@@ -563,7 +571,8 @@
                 Global.HDMI_CONTROL_AUTO_DEVICE_OFF_ENABLED,
                 Global.HDMI_SYSTEM_AUDIO_CONTROL_ENABLED,
                 Global.MHL_INPUT_SWITCHING_ENABLED,
-                Global.MHL_POWER_CHARGE_ENABLED
+                Global.MHL_POWER_CHARGE_ENABLED,
+                Global.HDMI_CEC_SWITCH_ENABLED
         };
         for (String s : settings) {
             resolver.registerContentObserver(Global.getUriFor(s), false, mSettingsObserver,
@@ -604,6 +613,24 @@
                     if (isTvDeviceEnabled()) {
                         tv().setSystemAudioControlFeatureEnabled(enabled);
                     }
+                    if (isAudioSystemDevice()) {
+                        if (audioSystem() == null) {
+                            Slog.e(TAG, "Audio System device has not registered yet."
+                                    + " Can't turn system audio mode on.");
+                            break;
+                        }
+                        audioSystem().onSystemAduioControlFeatureSupportChanged(enabled);
+                    }
+                    break;
+                case Global.HDMI_CEC_SWITCH_ENABLED:
+                    if (isAudioSystemDevice()) {
+                        if (audioSystem() == null) {
+                            Slog.w(TAG, "Switch device has not registered yet."
+                                    + " Can't turn routing on.");
+                            break;
+                        }
+                        audioSystem().setRoutingControlFeatureEnables(enabled);
+                    }
                     break;
                 case Global.MHL_INPUT_SWITCHING_ENABLED:
                     setMhlInputChangeEnabled(enabled);
@@ -619,6 +646,7 @@
         return enabled ? ENABLED : DISABLED;
     }
 
+    @VisibleForTesting
     boolean readBooleanSetting(String key, boolean defVal) {
         ContentResolver cr = getContext().getContentResolver();
         return Global.getInt(cr, key, toInt(defVal)) == ENABLED;
@@ -629,6 +657,11 @@
         Global.putInt(cr, key, toInt(value));
     }
 
+    void writeStringSetting(String key, String value) {
+        ContentResolver cr = getContext().getContentResolver();
+        Global.putString(cr, key, value);
+    }
+
     private void initializeCec(int initiatedBy) {
         mAddressAllocated = false;
         mCecController.setOption(OptionKey.SYSTEM_CEC_CONTROL, true);
@@ -733,6 +766,10 @@
         assertRunOnServiceThread();
         HdmiPortInfo[] cecPortInfo = null;
 
+        synchronized (mLock) {
+            mPhysicalAddress = getPhysicalAddress();
+        }
+
         // CEC HAL provides majority of the info while MHL does only MHL support flag for
         // each port. Return empty array if CEC HAL didn't provide the info.
         if (mCecController != null) {
@@ -754,6 +791,9 @@
         mPortInfoMap = new UnmodifiableSparseArray<>(portInfoMap);
         mPortDeviceMap = new UnmodifiableSparseArray<>(portDeviceMap);
 
+        if (mMhlController == null) {
+            return;
+        }
         HdmiPortInfo[] mhlPortInfo = mMhlController.getPortInfos();
         ArraySet<Integer> mhlSupportedPorts = new ArraySet<Integer>(mhlPortInfo.length);
         for (HdmiPortInfo info : mhlPortInfo) {
@@ -808,13 +848,34 @@
     }
 
     /**
-     * Returns the id of HDMI port located at the top of the hierarchy of
-     * the specified routing path. For the routing path 0x1220 (1.2.2.0), for instance,
-     * the port id to be returned is the ID associated with the port address
-     * 0x1000 (1.0.0.0) which is the topmost path of the given routing path.
+     * Returns the id of HDMI port located at the current device that runs this method.
+     *
+     * For TV with physical address 0x0000, target device 0x1120, we want port physical address
+     * 0x1000 to get the correct port id from {@link #mPortIdMap}. For device with Physical Address
+     * 0x2000, target device 0x2420, we want port address 0x24000 to get the port id.
+     *
+     * <p>Return {@link Constants#INVALID_PORT_ID} if target device does not connect to.
+     *
+     * @param path the target device's physical address.
+     * @return the id of the port that the target device eventually connects to
+     * on the current device.
      */
     int pathToPortId(int path) {
-        int portAddress = path & Constants.ROUTING_PATH_TOP_MASK;
+        int mask = 0xF000;
+        int finalMask = 0xF000;
+        int physicalAddress;
+        synchronized (mLock) {
+            physicalAddress = mPhysicalAddress;
+        }
+        int maskedAddress = physicalAddress;
+
+        while (maskedAddress != 0) {
+            maskedAddress = physicalAddress & mask;
+            finalMask |= mask;
+            mask >>= 4;
+        }
+
+        int portAddress = path & finalMask;
         return mPortIdMap.get(portAddress, Constants.INVALID_PORT_ID);
     }
 
@@ -1007,8 +1068,9 @@
     void onHotplug(int portId, boolean connected) {
         assertRunOnServiceThread();
 
-        if (connected && !isTvDevice()) {
-            if (getPortInfo(portId).getType() == HdmiPortInfo.PORT_OUTPUT && isSwitchDevice()) {
+        if (connected && !isTvDevice()
+                && getPortInfo(portId).getType() == HdmiPortInfo.PORT_OUTPUT) {
+            if (isSwitchDevice()) {
                 initPortInfo();
                 HdmiLogger.debug("initPortInfo for switch device when onHotplug from tx.");
             }
@@ -1112,7 +1174,7 @@
         String displayName = Build.MODEL;
         return new HdmiDeviceInfo(logicalAddress,
                 getPhysicalAddress(), pathToPortId(getPhysicalAddress()), deviceType,
-                getVendorId(), displayName);
+                getVendorId(), displayName, powerStatus);
     }
 
     @ServiceThreadOnly
@@ -1331,6 +1393,33 @@
             HdmiCecLocalDeviceTv tv = tv();
             if (tv == null) {
                 Slog.w(TAG, "Local tv device not available");
+                if (isPlaybackDevice()) {
+                    // if playback device itself is the active source,
+                    // return its own device info.
+                    if (playback() != null && playback().mIsActiveSource) {
+                        return playback().getDeviceInfo();
+                    }
+                    // Otherwise get the active source and look for it from the device list
+                    ActiveSource activeSource = mActiveSource;
+                    // If the active source is not set yet, return null
+                    if (!activeSource.isValid()) {
+                        return null;
+                    }
+                    if (audioSystem() != null) {
+                        HdmiCecLocalDeviceAudioSystem audioSystem = audioSystem();
+                        for (HdmiDeviceInfo info : audioSystem.getSafeCecDevicesLocked()) {
+                            if (info.getLogicalAddress() == activeSource.logicalAddress) {
+                                return info;
+                            }
+                        }
+                    }
+                    // If the device info is not in the list yet, return a device info with minimum
+                    // information from mActiveSource.
+                    return new HdmiDeviceInfo(activeSource.logicalAddress,
+                        activeSource.physicalAddress, pathToPortId(activeSource.physicalAddress),
+                        HdmiUtils.getTypeFromAddress(activeSource.logicalAddress), 0,
+                        HdmiUtils.getDefaultDeviceName(activeSource.logicalAddress));
+                }
                 return null;
             }
             ActiveSource activeSource = tv.getActiveSource();
@@ -1509,6 +1598,14 @@
         }
 
         @Override
+        public int getPhysicalAddress() {
+            enforceAccessPermission();
+            synchronized (mLock) {
+                return mPhysicalAddress;
+            }
+        }
+
+        @Override
         public void setSystemAudioMode(final boolean enabled, final IHdmiControlCallback callback) {
             enforceAccessPermission();
             runOnServiceThread(new Runnable() {
@@ -1565,14 +1662,62 @@
         public List<HdmiDeviceInfo> getDeviceList() {
             enforceAccessPermission();
             HdmiCecLocalDeviceTv tv = tv();
-            synchronized (mLock) {
-                return (tv == null)
+            if (tv != null) {
+                synchronized (mLock) {
+                    return tv.getSafeCecDevicesLocked();
+                }
+            } else {
+                HdmiCecLocalDeviceAudioSystem audioSystem = audioSystem();
+                synchronized (mLock) {
+                    return (audioSystem == null)
                         ? Collections.<HdmiDeviceInfo>emptyList()
-                        : tv.getSafeCecDevicesLocked();
+                        : audioSystem.getSafeCecDevicesLocked();
+                }
             }
         }
 
         @Override
+        public void powerOffRemoteDevice(int logicalAddress, int powerStatus) {
+            enforceAccessPermission();
+            runOnServiceThread(new Runnable() {
+                @Override
+                public void run() {
+                    Slog.w(TAG, "Device "
+                            + logicalAddress + " power status is " + powerStatus
+                            + " before standby command sent out");
+                    sendCecCommand(HdmiCecMessageBuilder.buildStandby(
+                            getRemoteControlSourceAddress(), logicalAddress));
+                }
+            });
+        }
+
+        @Override
+        public void powerOnRemoteDevice(int logicalAddress, int powerStatus) {
+            // TODO(amyjojo): implement the method
+        }
+
+        @Override
+        // TODO(AMYJOJO): add a result callback
+        public void askRemoteDeviceToBecomeActiveSource(int physicalAddress) {
+            enforceAccessPermission();
+            runOnServiceThread(new Runnable() {
+                @Override
+                public void run() {
+                    HdmiCecMessage setStreamPath = HdmiCecMessageBuilder.buildSetStreamPath(
+                            getRemoteControlSourceAddress(), physicalAddress);
+                    if (pathToPortId(physicalAddress) != Constants.INVALID_PORT_ID) {
+                        if (getSwitchDevice() != null) {
+                            getSwitchDevice().handleSetStreamPath(setStreamPath);
+                        } else {
+                            Slog.e(TAG, "Can't get the correct local device to handle routing.");
+                        }
+                    }
+                    sendCecCommand(setStreamPath);
+                }
+            });
+        }
+
+        @Override
         public void setSystemAudioVolume(final int oldIndex, final int newIndex,
                 final int maxIndex) {
             enforceAccessPermission();
@@ -1811,14 +1956,32 @@
                         Slog.w(TAG, "audio system is not in system audio mode");
                         return;
                     }
-                    int scaledVolume = VolumeControlAction.scaleToCecVolume(volume, maxVolume);
+                    audioSystem().reportAudioStatus(Constants.ADDR_TV);
+                }
+            });
+        }
 
-                    sendCecCommand(HdmiCecMessageBuilder
-                            .buildReportAudioStatus(
-                                    device.getDeviceInfo().getLogicalAddress(),
-                                    Constants.ADDR_TV,
-                                    scaledVolume,
-                                    isMute));
+        @Override
+        public void setSystemAudioModeOnForAudioOnlySource() {
+            enforceAccessPermission();
+            runOnServiceThread(new Runnable() {
+                @Override
+                public void run() {
+                    if (!isAudioSystemDevice()) {
+                        Slog.e(TAG, "Not an audio system device. Won't set system audio mode on");
+                        return;
+                    }
+                    if (audioSystem() == null) {
+                        Slog.e(TAG, "Audio System local device is not registered");
+                        return;
+                    }
+                    if (!audioSystem().checkSupportAndSetSystemAudioMode(true)) {
+                        Slog.e(TAG, "System Audio Mode is not supported.");
+                        return;
+                    }
+                    sendCecCommand(
+                            HdmiCecMessageBuilder.buildSetSystemAudioMode(
+                                    audioSystem().mAddress, Constants.ADDR_BROADCAST, true));
                 }
             });
         }
@@ -1828,30 +1991,54 @@
             if (!DumpUtils.checkDumpPermission(getContext(), TAG, writer)) return;
             final IndentingPrintWriter pw = new IndentingPrintWriter(writer, "  ");
 
-            pw.println("mHdmiControlEnabled: " + mHdmiControlEnabled);
             pw.println("mProhibitMode: " + mProhibitMode);
-            if (mCecController != null) {
-                pw.println("mCecController: ");
-                pw.increaseIndent();
-                mCecController.dump(pw);
-                pw.decreaseIndent();
-            }
+            pw.println("mPowerStatus: " + mPowerStatus);
+
+            // System settings
+            pw.println("System_settings:");
+            pw.increaseIndent();
+            pw.println("mHdmiControlEnabled: " + mHdmiControlEnabled);
+            pw.println("mMhlInputChangeEnabled: " + mMhlInputChangeEnabled);
+            pw.decreaseIndent();
 
             pw.println("mMhlController: ");
             pw.increaseIndent();
             mMhlController.dump(pw);
             pw.decreaseIndent();
 
-            pw.println("mPortInfo: ");
-            pw.increaseIndent();
-            for (HdmiPortInfo hdmiPortInfo : mPortInfo) {
-                pw.println("- " + hdmiPortInfo);
+            HdmiUtils.dumpIterable(pw, "mPortInfo:", mPortInfo);
+            if (mCecController != null) {
+                pw.println("mCecController: ");
+                pw.increaseIndent();
+                mCecController.dump(pw);
+                pw.decreaseIndent();
             }
-            pw.decreaseIndent();
-            pw.println("mPowerStatus: " + mPowerStatus);
         }
     }
 
+    // Get the source address to send out commands to devices connected to the current device
+    // when other services interact with HdmiControlService.
+    private int getRemoteControlSourceAddress() {
+        if (isAudioSystemDevice()) {
+            return audioSystem().getDeviceInfo().getLogicalAddress();
+        } else if (isPlaybackDevice()) {
+            return playback().getDeviceInfo().getLogicalAddress();
+        }
+        return ADDR_UNREGISTERED;
+    }
+
+    // Get the switch device to do CEC routing control
+    @Nullable
+    private HdmiCecLocalDeviceSource getSwitchDevice() {
+        if (isAudioSystemDevice()) {
+            return audioSystem();
+        }
+        if (isPlaybackDevice()) {
+            return playback();
+        }
+        return null;
+    }
+
     @ServiceThreadOnly
     private void oneTouchPlay(final IHdmiControlCallback callback) {
         assertRunOnServiceThread();
@@ -2130,7 +2317,7 @@
 
     boolean isSwitchDevice() {
         return SystemProperties.getBoolean(
-            Constants.PROPERTY_HDMI_IS_DEVICE_HDMI_CEC_SWITCH, false);
+            PROPERTY_HDMI_IS_DEVICE_HDMI_CEC_SWITCH, false);
     }
 
     boolean isTvDeviceEnabled() {
@@ -2526,14 +2713,14 @@
     // For example, when receiving broadcast messages, all the device types will call this
     // method but only one of them will be the Active Source.
     protected void setAndBroadcastActiveSource(
-            HdmiCecMessage message, int physicalAddress, int deviceType) {
+            int physicalAddress, int deviceType, int source) {
         // If the device has both playback and audio system logical addresses,
         // playback will claim active source. Otherwise audio system will.
         if (deviceType == HdmiDeviceInfo.DEVICE_PLAYBACK) {
             HdmiCecLocalDevicePlayback playback = playback();
             playback.setIsActiveSource(true);
             playback.wakeUpIfActiveSource();
-            playback.maySendActiveSource(message.getSource());
+            playback.maySendActiveSource(source);
             setActiveSource(playback.mAddress, physicalAddress);
         }
 
@@ -2544,7 +2731,7 @@
             } else {
                 audioSystem.setIsActiveSource(true);
                 audioSystem.wakeUpIfActiveSource();
-                audioSystem.maySendActiveSource(message.getSource());
+                audioSystem.maySendActiveSource(source);
                 setActiveSource(audioSystem.mAddress, physicalAddress);
             }
         }
diff --git a/services/core/java/com/android/server/hdmi/HdmiUtils.java b/services/core/java/com/android/server/hdmi/HdmiUtils.java
index 2a8117f..e44f1d1 100644
--- a/services/core/java/com/android/server/hdmi/HdmiUtils.java
+++ b/services/core/java/com/android/server/hdmi/HdmiUtils.java
@@ -16,19 +16,35 @@
 
 package com.android.server.hdmi;
 
+import android.annotation.Nullable;
 import android.hardware.hdmi.HdmiDeviceInfo;
 import android.util.Slog;
 import android.util.SparseArray;
+import android.util.Xml;
 
+import com.android.internal.util.HexDump;
+import com.android.internal.util.IndentingPrintWriter;
+import com.android.server.hdmi.Constants.AudioCodec;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.io.InputStream;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
+import java.util.Map;
+import java.util.Objects;
 
 /**
  * Various utilities to handle HDMI CEC messages.
  */
 final class HdmiUtils {
 
+    private static final String TAG = "HdmiUtils";
+
     private static final int[] ADDRESS_TO_TYPE = {
         HdmiDeviceInfo.DEVICE_TV,  // ADDR_TV
         HdmiDeviceInfo.DEVICE_RECORDER,  // ADDR_RECORDER_1
@@ -65,6 +81,12 @@
         "Secondary_TV",
     };
 
+    /**
+     * Return value of {@link #getLocalPortFromPhysicalAddress(int, int)}
+     */
+    static final int TARGET_NOT_UNDER_LOCAL_DEVICE = -1;
+    static final int TARGET_SAME_PHYSICAL_ADDRESS = 0;
+
     private HdmiUtils() { /* cannot be instantiated */ }
 
     /**
@@ -317,4 +339,339 @@
                 info.getPhysicalAddress(), info.getPortId(), info.getDeviceType(),
                 info.getVendorId(), info.getDisplayName(), newPowerStatus);
     }
+
+    /**
+     * Dump a {@link SparseArray} to the print writer.
+     *
+     * <p>The dump is formatted:
+     * <pre>
+     *     name:
+     *        key = value
+     *        key = value
+     *        ...
+     * </pre>
+     */
+    static <T> void dumpSparseArray(IndentingPrintWriter pw, String name,
+            SparseArray<T> sparseArray) {
+        printWithTrailingColon(pw, name);
+        pw.increaseIndent();
+        int size = sparseArray.size();
+        for (int i = 0; i < size; i++) {
+            int key = sparseArray.keyAt(i);
+            T value = sparseArray.get(key);
+            pw.printPair(Integer.toString(key), value);
+            pw.println();
+        }
+        pw.decreaseIndent();
+    }
+
+    private static void printWithTrailingColon(IndentingPrintWriter pw, String name) {
+        pw.println(name.endsWith(":") ? name : name.concat(":"));
+    }
+
+    /**
+     * Dump a {@link Map} to the print writer.
+     *
+     * <p>The dump is formatted:
+     * <pre>
+     *     name:
+     *        key = value
+     *        key = value
+     *        ...
+     * </pre>
+     */
+    static <K, V> void dumpMap(IndentingPrintWriter pw, String name, Map<K, V> map) {
+        printWithTrailingColon(pw, name);
+        pw.increaseIndent();
+        for (Map.Entry<K, V> entry: map.entrySet()) {
+            pw.printPair(entry.getKey().toString(), entry.getValue());
+            pw.println();
+        }
+        pw.decreaseIndent();
+    }
+
+    /**
+     * Dump a {@link Map} to the print writer.
+     *
+     * <p>The dump is formatted:
+     * <pre>
+     *     name:
+     *        value
+     *        value
+     *        ...
+     * </pre>
+     */
+    static <T> void dumpIterable(IndentingPrintWriter pw, String name, Iterable<T> values) {
+        printWithTrailingColon(pw, name);
+        pw.increaseIndent();
+        for (T value : values) {
+            pw.println(value);
+        }
+        pw.decreaseIndent();
+    }
+
+    /**
+     * Method to parse target physical address to the port number on the current device.
+     *
+     * <p>This check assumes target address is valid.
+     *
+     * @param targetPhysicalAddress is the physical address of the target device
+     * @param myPhysicalAddress is the physical address of the current device
+     * @return
+     * If the target device is under the current device, return the port number of current device
+     * that the target device is connected to. This also applies to the devices that are indirectly
+     * connected to the current device.
+     *
+     * <p>If the target device has the same physical address as the current device, return
+     * {@link #TARGET_SAME_PHYSICAL_ADDRESS}.
+     *
+     * <p>If the target device is not under the current device, return
+     * {@link #TARGET_NOT_UNDER_LOCAL_DEVICE}.
+     */
+    public static int getLocalPortFromPhysicalAddress(
+            int targetPhysicalAddress, int myPhysicalAddress) {
+        if (myPhysicalAddress == targetPhysicalAddress) {
+            return TARGET_SAME_PHYSICAL_ADDRESS;
+        }
+
+        int mask = 0xF000;
+        int finalMask = 0xF000;
+        int maskedAddress = myPhysicalAddress;
+
+        while (maskedAddress != 0) {
+            maskedAddress = myPhysicalAddress & mask;
+            finalMask |= mask;
+            mask >>= 4;
+        }
+
+        int portAddress = targetPhysicalAddress & finalMask;
+        if ((portAddress & (finalMask << 4)) != myPhysicalAddress) {
+            return TARGET_NOT_UNDER_LOCAL_DEVICE;
+        }
+
+        mask <<= 4;
+        int port = portAddress & mask;
+        while ((port >> 4) != 0) {
+            port >>= 4;
+        }
+        return port;
+    }
+
+    public static class ShortAudioDescriptorXmlParser {
+        // We don't use namespaces
+        private static final String NS = null;
+
+        // return a list of devices config
+        public static List<DeviceConfig> parse(InputStream in)
+                throws XmlPullParserException, IOException {
+            XmlPullParser parser = Xml.newPullParser();
+            parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false);
+            parser.setInput(in, null);
+            parser.nextTag();
+            return readDevices(parser);
+        }
+
+        private static void skip(XmlPullParser parser) throws XmlPullParserException, IOException {
+            if (parser.getEventType() != XmlPullParser.START_TAG) {
+                throw new IllegalStateException();
+            }
+            int depth = 1;
+            while (depth != 0) {
+                switch (parser.next()) {
+                    case XmlPullParser.END_TAG:
+                        depth--;
+                        break;
+                    case XmlPullParser.START_TAG:
+                        depth++;
+                        break;
+                }
+            }
+        }
+
+        private static List<DeviceConfig> readDevices(XmlPullParser parser)
+                throws XmlPullParserException, IOException {
+            List<DeviceConfig> devices = new ArrayList<>();
+
+            parser.require(XmlPullParser.START_TAG, NS, "config");
+            while (parser.next() != XmlPullParser.END_TAG) {
+                if (parser.getEventType() != XmlPullParser.START_TAG) {
+                    continue;
+                }
+                String name = parser.getName();
+                // Starts by looking for the device tag
+                if (name.equals("device")) {
+                    String deviceType = parser.getAttributeValue(null, "type");
+                    DeviceConfig config = null;
+                    if (deviceType != null) {
+                        config = readDeviceConfig(parser, deviceType);
+                    }
+                    if (config != null) {
+                        devices.add(config);
+                    }
+                } else {
+                    skip(parser);
+                }
+            }
+            return devices;
+        }
+
+        // Processes device tags in the config.
+        @Nullable
+        private static DeviceConfig readDeviceConfig(XmlPullParser parser, String deviceType)
+                throws XmlPullParserException, IOException {
+            List<CodecSad> codecSads = new ArrayList<>();
+            int format;
+            byte[] descriptor;
+
+            parser.require(XmlPullParser.START_TAG, NS, "device");
+            while (parser.next() != XmlPullParser.END_TAG) {
+                if (parser.getEventType() != XmlPullParser.START_TAG) {
+                    continue;
+                }
+                String tagName = parser.getName();
+
+                // Starts by looking for the supportedFormat tag
+                if (tagName.equals("supportedFormat")) {
+                    String codecAttriValue = parser.getAttributeValue(null, "format");
+                    String sadAttriValue = parser.getAttributeValue(null, "descriptor");
+                    format = (codecAttriValue) == null
+                            ? Constants.AUDIO_CODEC_NONE : formatNameToNum(codecAttriValue);
+                    descriptor = readSad(sadAttriValue);
+                    if (format != Constants.AUDIO_CODEC_NONE && descriptor != null) {
+                        codecSads.add(new CodecSad(format, descriptor));
+                    }
+                    parser.nextTag();
+                    parser.require(XmlPullParser.END_TAG, NS, "supportedFormat");
+                } else {
+                    skip(parser);
+                }
+            }
+            if (codecSads.size() == 0) {
+                return null;
+            }
+            return new DeviceConfig(deviceType, codecSads);
+        }
+
+        // Processes sad attribute in the supportedFormat.
+        @Nullable
+        private static byte[] readSad(String sad) {
+            if (sad == null || sad.length() == 0) {
+                return null;
+            }
+            byte[] sadBytes = HexDump.hexStringToByteArray(sad);
+            if (sadBytes.length != 3) {
+                Slog.w(TAG, "SAD byte array length is not 3. Length = " + sadBytes.length);
+                return null;
+            }
+            return sadBytes;
+        }
+
+        @AudioCodec
+        private static int formatNameToNum(String codecAttriValue) {
+            switch (codecAttriValue) {
+                case "AUDIO_FORMAT_NONE":
+                    return Constants.AUDIO_CODEC_NONE;
+                case "AUDIO_FORMAT_LPCM":
+                    return Constants.AUDIO_CODEC_LPCM;
+                case "AUDIO_FORMAT_DD":
+                    return Constants.AUDIO_CODEC_DD;
+                case "AUDIO_FORMAT_MPEG1":
+                    return Constants.AUDIO_CODEC_MPEG1;
+                case "AUDIO_FORMAT_MP3":
+                    return Constants.AUDIO_CODEC_MP3;
+                case "AUDIO_FORMAT_MPEG2":
+                    return Constants.AUDIO_CODEC_MPEG2;
+                case "AUDIO_FORMAT_AAC":
+                    return Constants.AUDIO_CODEC_AAC;
+                case "AUDIO_FORMAT_DTS":
+                    return Constants.AUDIO_CODEC_DTS;
+                case "AUDIO_FORMAT_ATRAC":
+                    return Constants.AUDIO_CODEC_ATRAC;
+                case "AUDIO_FORMAT_ONEBITAUDIO":
+                    return Constants.AUDIO_CODEC_ONEBITAUDIO;
+                case "AUDIO_FORMAT_DDP":
+                    return Constants.AUDIO_CODEC_DDP;
+                case "AUDIO_FORMAT_DTSHD":
+                    return Constants.AUDIO_CODEC_DTSHD;
+                case "AUDIO_FORMAT_TRUEHD":
+                    return Constants.AUDIO_CODEC_TRUEHD;
+                case "AUDIO_FORMAT_DST":
+                    return Constants.AUDIO_CODEC_DST;
+                case "AUDIO_FORMAT_WMAPRO":
+                    return Constants.AUDIO_CODEC_WMAPRO;
+                case "AUDIO_FORMAT_MAX":
+                    return Constants.AUDIO_CODEC_MAX;
+                default:
+                    return Constants.AUDIO_CODEC_NONE;
+            }
+        }
+    }
+
+    // Device configuration of its supported Codecs and their Short Audio Descriptors.
+    public static class DeviceConfig {
+        /** Name of the device. Should be {@link Constants.AudioDevice}. **/
+        public final String name;
+        /** List of a {@link CodecSad}. **/
+        public final List<CodecSad> supportedCodecs;
+
+        public DeviceConfig(String name, List<CodecSad> supportedCodecs) {
+            this.name = name;
+            this.supportedCodecs = supportedCodecs;
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (obj instanceof DeviceConfig) {
+                DeviceConfig that = (DeviceConfig) obj;
+                return that.name.equals(this.name)
+                    && that.supportedCodecs.equals(this.supportedCodecs);
+            }
+            return false;
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(
+                name,
+                supportedCodecs.hashCode());
+        }
+    }
+
+    // Short Audio Descriptor of a specific Codec
+    public static class CodecSad {
+        /** Audio Codec. Should be {@link Constants.AudioCodec}. **/
+        public final int audioCodec;
+        /**
+         * Three-byte Short Audio Descriptor. See HDMI Specification 1.4b CEC 13.15.3 and
+         * ANSI-CTA-861-F-FINAL 7.5.2 Audio Data Block for more details.
+         */
+        public final byte[] sad;
+
+        public CodecSad(int audioCodec, byte[] sad) {
+            this.audioCodec = audioCodec;
+            this.sad = sad;
+        }
+
+        public CodecSad(int audioCodec, String sad) {
+            this.audioCodec = audioCodec;
+            this.sad = HexDump.hexStringToByteArray(sad);
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (obj instanceof CodecSad) {
+                CodecSad that = (CodecSad) obj;
+                return that.audioCodec == this.audioCodec
+                    && Arrays.equals(that.sad, this.sad);
+            }
+            return false;
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(
+                audioCodec,
+                Arrays.hashCode(sad));
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/hdmi/OneTouchPlayAction.java b/services/core/java/com/android/server/hdmi/OneTouchPlayAction.java
index 41bf01f..354d8d1 100644
--- a/services/core/java/com/android/server/hdmi/OneTouchPlayAction.java
+++ b/services/core/java/com/android/server/hdmi/OneTouchPlayAction.java
@@ -92,6 +92,9 @@
         if (source.mService.audioSystem() != null) {
             source = source.mService.audioSystem();
         }
+        if (source.getLocalActivePort() != Constants.CEC_SWITCH_HOME) {
+            source.switchInputOnReceivingNewActivePath(getSourcePath());
+        }
         source.setRoutingPort(Constants.CEC_SWITCH_HOME);
         source.setLocalActivePort(Constants.CEC_SWITCH_HOME);
     }
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index df28f30..979de66 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -1761,12 +1761,15 @@
 
     // Native callback
     private void notifyFocusChanged(IBinder oldToken, IBinder newToken) {
-        if (mFocusedWindow.asBinder() == newToken) {
-            Log.w(TAG, "notifyFocusChanged called with unchanged mFocusedWindow=" + mFocusedWindow);
-            return;
+        if (mFocusedWindow != null) {
+            if (mFocusedWindow.asBinder() == newToken) {
+                Slog.w(TAG, "notifyFocusChanged called with unchanged mFocusedWindow="
+                        + mFocusedWindow);
+                return;
+            }
+            setPointerCapture(false);
         }
 
-        setPointerCapture(false);
         mFocusedWindow = IWindow.Stub.asInterface(newToken);
     }
 
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 5fa3f52..7ff6a2f 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -89,6 +89,7 @@
 import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.os.UserManager;
+import android.os.UserManagerInternal;
 import android.provider.Settings;
 import android.service.vr.IVrManager;
 import android.service.vr.IVrStateCallbacks;
@@ -309,6 +310,7 @@
     private final HardKeyboardListener mHardKeyboardListener;
     private final AppOpsManager mAppOpsManager;
     private final UserManager mUserManager;
+    private final UserManagerInternal mUserManagerInternal;
 
     // All known input methods.  mMethodMap also serves as the global
     // lock for this class.
@@ -1405,6 +1407,7 @@
         }, true /*asyncHandler*/);
         mAppOpsManager = mContext.getSystemService(AppOpsManager.class);
         mUserManager = mContext.getSystemService(UserManager.class);
+        mUserManagerInternal = LocalServices.getService(UserManagerInternal.class);
         mHardKeyboardListener = new HardKeyboardListener();
         mHasFeature = context.getPackageManager().hasSystemFeature(
                 PackageManager.FEATURE_INPUT_METHODS);
@@ -1489,7 +1492,7 @@
         // If the system is not ready or the device is not yed unlocked by the user, then we use
         // copy-on-write settings.
         final boolean useCopyOnWriteSettings =
-                !mSystemReady || !mUserManager.isUserUnlockingOrUnlocked(newUserId);
+                !mSystemReady || !mUserManagerInternal.isUserUnlockingOrUnlocked(newUserId);
         mSettings.switchCurrentUser(newUserId, useCopyOnWriteSettings);
         updateCurrentProfileIds();
         // Additional subtypes should be reset when the user is changed
@@ -1562,7 +1565,7 @@
                 mLastSystemLocales = mRes.getConfiguration().getLocales();
                 final int currentUserId = mSettings.getCurrentUserId();
                 mSettings.switchCurrentUser(currentUserId,
-                        !mUserManager.isUserUnlockingOrUnlocked(currentUserId));
+                        !mUserManagerInternal.isUserUnlockingOrUnlocked(currentUserId));
                 mKeyguardManager = mContext.getSystemService(KeyguardManager.class);
                 mNotificationManager = mContext.getSystemService(NotificationManager.class);
                 mStatusBar = statusBar;
diff --git a/services/core/java/com/android/server/job/controllers/QuotaController.java b/services/core/java/com/android/server/job/controllers/QuotaController.java
index ac2dbdf..c16d1b4 100644
--- a/services/core/java/com/android/server/job/controllers/QuotaController.java
+++ b/services/core/java/com/android/server/job/controllers/QuotaController.java
@@ -26,7 +26,10 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.UserIdInt;
+import android.app.ActivityManager;
+import android.app.ActivityManagerInternal;
 import android.app.AlarmManager;
+import android.app.IUidObserver;
 import android.app.usage.UsageStatsManagerInternal;
 import android.app.usage.UsageStatsManagerInternal.AppIdleStateChangeListener;
 import android.content.BroadcastReceiver;
@@ -38,12 +41,14 @@
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
+import android.os.RemoteException;
 import android.os.UserHandle;
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.Log;
 import android.util.Slog;
 import android.util.SparseArray;
+import android.util.SparseBooleanArray;
 import android.util.proto.ProtoOutputStream;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -69,6 +74,11 @@
  * bucket, it will be eligible to run. When a job's bucket changes, its new quota is immediately
  * applied to it.
  *
+ * Jobs are throttled while an app is not in a foreground state. All jobs are allowed to run
+ * freely when an app enters the foreground state and are restricted when the app leaves the
+ * foreground state. However, jobs that are started while the app is in the TOP state are not
+ * restricted regardless of the app's state change.
+ *
  * Test: atest com.android.server.job.controllers.QuotaControllerTest
  */
 public final class QuotaController extends StateController {
@@ -97,6 +107,12 @@
             data.put(packageName, obj);
         }
 
+        public void clear() {
+            for (int i = 0; i < mData.size(); ++i) {
+                mData.valueAt(i).clear();
+            }
+        }
+
         /** Removes all the data for the user, if there was any. */
         public void delete(int userId) {
             mData.delete(userId);
@@ -119,6 +135,11 @@
             return null;
         }
 
+        /** @see SparseArray#indexOfKey */
+        public int indexOfKey(int userId) {
+            return mData.indexOfKey(userId);
+        }
+
         /** Returns the userId at the given index. */
         public int keyAt(int index) {
             return mData.keyAt(index);
@@ -294,6 +315,17 @@
     /** Cached calculation results for each app, with the standby buckets as the array indices. */
     private final UserPackageMap<ExecutionStats[]> mExecutionStatsCache = new UserPackageMap<>();
 
+    /** List of UIDs currently in the foreground. */
+    private final SparseBooleanArray mForegroundUids = new SparseBooleanArray();
+
+    /**
+     * List of jobs that started while the UID was in the TOP state. There will be no more than
+     * 16 ({@link JobSchedulerService.MAX_JOB_CONTEXTS_COUNT}) running at once, so an ArraySet is
+     * fine.
+     */
+    private final ArraySet<JobStatus> mTopStartedJobs = new ArraySet<>();
+
+    private final ActivityManagerInternal mActivityManagerInternal;
     private final AlarmManager mAlarmManager;
     private final ChargingTracker mChargeTracker;
     private final Handler mHandler;
@@ -343,6 +375,29 @@
                 }
             };
 
+    private final IUidObserver mUidObserver = new IUidObserver.Stub() {
+        @Override
+        public void onUidStateChanged(int uid, int procState, long procStateSeq) {
+            mHandler.obtainMessage(MSG_UID_PROCESS_STATE_CHANGED, uid, procState).sendToTarget();
+        }
+
+        @Override
+        public void onUidGone(int uid, boolean disabled) {
+        }
+
+        @Override
+        public void onUidActive(int uid) {
+        }
+
+        @Override
+        public void onUidIdle(int uid, boolean disabled) {
+        }
+
+        @Override
+        public void onUidCachedChanged(int uid, boolean cached) {
+        }
+    };
+
     /**
      * The rolling window size for each standby bucket. Within each window, an app will have 10
      * minutes to run its jobs.
@@ -363,12 +418,15 @@
     private static final int MSG_CLEAN_UP_SESSIONS = 1;
     /** Check if a package is now within its quota. */
     private static final int MSG_CHECK_PACKAGE = 2;
+    /** Process state for a UID has changed. */
+    private static final int MSG_UID_PROCESS_STATE_CHANGED = 3;
 
     public QuotaController(JobSchedulerService service) {
         super(service);
         mHandler = new QcHandler(mContext.getMainLooper());
         mChargeTracker = new ChargingTracker();
         mChargeTracker.startTracking();
+        mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class);
         mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
 
         // Set up the app standby bucketing tracker
@@ -376,6 +434,14 @@
                 UsageStatsManagerInternal.class);
         usageStats.addAppIdleStateChangeListener(new StandbyTracker());
 
+        try {
+            ActivityManager.getService().registerUidObserver(mUidObserver,
+                    ActivityManager.UID_OBSERVER_PROCSTATE,
+                    ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE, null);
+        } catch (RemoteException e) {
+            // ignored; both services live in system_server
+        }
+
         onConstantsUpdatedLocked();
     }
 
@@ -399,11 +465,15 @@
         if (DEBUG) Slog.d(TAG, "Prepping for " + jobStatus.toShortString());
         final int userId = jobStatus.getSourceUserId();
         final String packageName = jobStatus.getSourcePackageName();
+        final int uid = jobStatus.getSourceUid();
         Timer timer = mPkgTimers.get(userId, packageName);
         if (timer == null) {
-            timer = new Timer(userId, packageName);
+            timer = new Timer(uid, userId, packageName);
             mPkgTimers.add(userId, packageName, timer);
         }
+        if (mActivityManagerInternal.getUidProcessState(uid) == ActivityManager.PROCESS_STATE_TOP) {
+            mTopStartedJobs.add(jobStatus);
+        }
         timer.startTrackingJob(jobStatus);
     }
 
@@ -421,6 +491,7 @@
             if (jobs != null) {
                 jobs.remove(jobStatus);
             }
+            mTopStartedJobs.remove(jobStatus);
         }
     }
 
@@ -511,6 +582,7 @@
             mInQuotaAlarmListeners.delete(userId, packageName);
         }
         mExecutionStatsCache.delete(userId, packageName);
+        mForegroundUids.delete(uid);
     }
 
     @Override
@@ -522,6 +594,20 @@
         mExecutionStatsCache.delete(userId);
     }
 
+    private boolean isUidInForeground(int uid) {
+        if (UserHandle.isCore(uid)) {
+            return true;
+        }
+        synchronized (mLock) {
+            return mForegroundUids.get(uid);
+        }
+    }
+
+    /** @return true if the job was started while the app was in the TOP state. */
+    private boolean isTopStartedJob(@NonNull final JobStatus jobStatus) {
+        return mTopStartedJobs.contains(jobStatus);
+    }
+
     /**
      * Returns an appropriate standby bucket for the job, taking into account any standby
      * exemptions.
@@ -537,9 +623,15 @@
 
     private boolean isWithinQuotaLocked(@NonNull final JobStatus jobStatus) {
         final int standbyBucket = getEffectiveStandbyBucket(jobStatus);
-        // Jobs for the active app should always be able to run.
-        return jobStatus.uidActive || isWithinQuotaLocked(
-                jobStatus.getSourceUserId(), jobStatus.getSourcePackageName(), standbyBucket);
+        Timer timer = mPkgTimers.get(jobStatus.getSourceUserId(), jobStatus.getSourcePackageName());
+        // A job is within quota if one of the following is true:
+        //   1. it was started while the app was in the TOP state
+        //   2. the app is currently in the foreground
+        //   3. the app overall is within its quota
+        return isTopStartedJob(jobStatus)
+                || isUidInForeground(jobStatus.getSourceUid())
+                || isWithinQuotaLocked(
+                      jobStatus.getSourceUserId(), jobStatus.getSourcePackageName(), standbyBucket);
     }
 
     private boolean isWithinQuotaLocked(final int userId, @NonNull final String packageName,
@@ -800,7 +892,7 @@
         final boolean isCharging = mChargeTracker.isCharging();
         if (DEBUG) Slog.d(TAG, "handleNewChargingStateLocked: " + isCharging);
         // Deal with Timers first.
-        mPkgTimers.forEach((t) -> t.onChargingChanged(nowElapsed, isCharging));
+        mPkgTimers.forEach((t) -> t.onStateChanged(nowElapsed, isCharging));
         // Now update jobs.
         maybeUpdateAllConstraintsLocked();
     }
@@ -837,10 +929,15 @@
         boolean changed = false;
         for (int i = jobs.size() - 1; i >= 0; --i) {
             final JobStatus js = jobs.valueAt(i);
-            if (js.uidActive) {
-                // Jobs for the active app should always be able to run.
+            if (isTopStartedJob(js)) {
+                // Job was started while the app was in the TOP state so we should allow it to
+                // finish.
                 changed |= js.setQuotaConstraintSatisfied(true);
-            } else if (realStandbyBucket == getEffectiveStandbyBucket(js)) {
+            } else if (realStandbyBucket != ACTIVE_INDEX
+                    && realStandbyBucket == getEffectiveStandbyBucket(js)) {
+                // An app in the ACTIVE bucket may be out of quota while the job could be in quota
+                // for some reason. Therefore, avoid setting the real value here and check each job
+                // individually.
                 changed |= js.setQuotaConstraintSatisfied(realInQuota);
             } else {
                 // This job is somehow exempted. Need to determine its own quota status.
@@ -854,7 +951,7 @@
             maybeScheduleStartAlarmLocked(userId, packageName, realStandbyBucket);
         } else {
             QcAlarmListener alarmListener = mInQuotaAlarmListeners.get(userId, packageName);
-            if (alarmListener != null) {
+            if (alarmListener != null && alarmListener.isWaiting()) {
                 mAlarmManager.cancel(alarmListener);
                 // Set the trigger time to 0 so that the alarm doesn't think it's still waiting.
                 alarmListener.setTriggerTime(0);
@@ -863,6 +960,56 @@
         return changed;
     }
 
+    private class UidConstraintUpdater implements Consumer<JobStatus> {
+        private final UserPackageMap<Integer> mToScheduleStartAlarms = new UserPackageMap<>();
+        public boolean wasJobChanged;
+
+        @Override
+        public void accept(JobStatus jobStatus) {
+            wasJobChanged |= jobStatus.setQuotaConstraintSatisfied(isWithinQuotaLocked(jobStatus));
+            final int userId = jobStatus.getSourceUserId();
+            final String packageName = jobStatus.getSourcePackageName();
+            final int realStandbyBucket = jobStatus.getStandbyBucket();
+            if (isWithinQuotaLocked(userId, packageName, realStandbyBucket)) {
+                QcAlarmListener alarmListener = mInQuotaAlarmListeners.get(userId, packageName);
+                if (alarmListener != null && alarmListener.isWaiting()) {
+                    mAlarmManager.cancel(alarmListener);
+                    // Set the trigger time to 0 so that the alarm doesn't think it's still waiting.
+                    alarmListener.setTriggerTime(0);
+                }
+            } else {
+                mToScheduleStartAlarms.add(userId, packageName, realStandbyBucket);
+            }
+        }
+
+        void postProcess() {
+            for (int u = 0; u < mToScheduleStartAlarms.numUsers(); ++u) {
+                final int userId = mToScheduleStartAlarms.keyAt(u);
+                for (int p = 0; p < mToScheduleStartAlarms.numPackagesForUser(userId); ++p) {
+                    final String packageName = mToScheduleStartAlarms.keyAt(u, p);
+                    final int standbyBucket = mToScheduleStartAlarms.get(userId, packageName);
+                    maybeScheduleStartAlarmLocked(userId, packageName, standbyBucket);
+                }
+            }
+        }
+
+        void reset() {
+            wasJobChanged = false;
+            mToScheduleStartAlarms.clear();
+        }
+    }
+
+    private final UidConstraintUpdater mUpdateUidConstraints = new UidConstraintUpdater();
+
+    private boolean maybeUpdateConstraintForUidLocked(final int uid) {
+        mService.getJobStore().forEachJobForSourceUid(uid, mUpdateUidConstraints);
+
+        mUpdateUidConstraints.postProcess();
+        boolean changed = mUpdateUidConstraints.wasJobChanged;
+        mUpdateUidConstraints.reset();
+        return changed;
+    }
+
     /**
      * Maybe schedule a non-wakeup alarm for the next time this package will have quota to run
      * again. This should only be called if the package is already out of quota.
@@ -1052,6 +1199,7 @@
 
     private final class Timer {
         private final Package mPkg;
+        private final int mUid;
 
         // List of jobs currently running for this app that started when the app wasn't in the
         // foreground.
@@ -1059,16 +1207,18 @@
         private long mStartTimeElapsed;
         private int mBgJobCount;
 
-        Timer(int userId, String packageName) {
+        Timer(int uid, int userId, String packageName) {
             mPkg = new Package(userId, packageName);
+            mUid = uid;
         }
 
         void startTrackingJob(@NonNull JobStatus jobStatus) {
-            if (jobStatus.uidActive) {
-                // We intentionally don't pay attention to fg state changes after a job has started.
+            if (isTopStartedJob(jobStatus)) {
+                // We intentionally don't pay attention to fg state changes after a TOP job has
+                // started.
                 if (DEBUG) {
                     Slog.v(TAG,
-                            "Timer ignoring " + jobStatus.toShortString() + " because uidActive");
+                            "Timer ignoring " + jobStatus.toShortString() + " because isTop");
                 }
                 return;
             }
@@ -1076,7 +1226,7 @@
             synchronized (mLock) {
                 // Always track jobs, even when charging.
                 mRunningBgJobs.add(jobStatus);
-                if (!mChargeTracker.isCharging()) {
+                if (shouldTrackLocked()) {
                     mBgJobCount++;
                     if (mRunningBgJobs.size() == 1) {
                         // Started tracking the first job.
@@ -1142,6 +1292,10 @@
             }
         }
 
+        boolean isRunning(JobStatus jobStatus) {
+            return mRunningBgJobs.contains(jobStatus);
+        }
+
         long getCurrentDuration(long nowElapsed) {
             synchronized (mLock) {
                 return !isActive() ? 0 : nowElapsed - mStartTimeElapsed;
@@ -1154,17 +1308,21 @@
             }
         }
 
-        void onChargingChanged(long nowElapsed, boolean isCharging) {
+        private boolean shouldTrackLocked() {
+            return !mChargeTracker.isCharging() && !mForegroundUids.get(mUid);
+        }
+
+        void onStateChanged(long nowElapsed, boolean isQuotaFree) {
             synchronized (mLock) {
-                if (isCharging) {
+                if (isQuotaFree) {
                     emitSessionLocked(nowElapsed);
-                } else {
+                } else if (shouldTrackLocked()) {
                     // Start timing from unplug.
                     if (mRunningBgJobs.size() > 0) {
                         mStartTimeElapsed = nowElapsed;
                         // NOTE: this does have the unfortunate consequence that if the device is
-                        // repeatedly plugged in and unplugged, the job count for a package may be
-                        // artificially high.
+                        // repeatedly plugged in and unplugged, or an app changes foreground state
+                        // very frequently, the job count for a package may be artificially high.
                         mBgJobCount = mRunningBgJobs.size();
                         // Starting the timer means that all cached execution stats are now
                         // incorrect.
@@ -1371,6 +1529,38 @@
                         }
                         break;
                     }
+                    case MSG_UID_PROCESS_STATE_CHANGED: {
+                        final int uid = msg.arg1;
+                        final int procState = msg.arg2;
+                        final int userId = UserHandle.getUserId(uid);
+                        final long nowElapsed = sElapsedRealtimeClock.millis();
+
+                        synchronized (mLock) {
+                            boolean isQuotaFree;
+                            if (procState <= ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE) {
+                                mForegroundUids.put(uid, true);
+                                isQuotaFree = true;
+                            } else {
+                                mForegroundUids.delete(uid);
+                                isQuotaFree = false;
+                            }
+                            // Update Timers first.
+                            final int userIndex = mPkgTimers.indexOfKey(userId);
+                            if (userIndex != -1) {
+                                final int numPkgs = mPkgTimers.numPackagesForUser(userId);
+                                for (int p = 0; p < numPkgs; ++p) {
+                                    Timer t = mPkgTimers.valueAt(userIndex, p);
+                                    if (t != null) {
+                                        t.onStateChanged(nowElapsed, isQuotaFree);
+                                    }
+                                }
+                            }
+                            if (maybeUpdateConstraintForUidLocked(uid)) {
+                                mStateChangedListener.onControllerStateChanged();
+                            }
+                        }
+                        break;
+                    }
                 }
             }
         }
@@ -1420,6 +1610,12 @@
 
     @VisibleForTesting
     @NonNull
+    SparseBooleanArray getForegroundUids() {
+        return mForegroundUids;
+    }
+
+    @VisibleForTesting
+    @NonNull
     Handler getHandler() {
         return mHandler;
     }
@@ -1450,6 +1646,10 @@
         pw.println("In parole: " + mInParole);
         pw.println();
 
+        pw.print("Foreground UIDs: ");
+        pw.println(mForegroundUids.toString());
+        pw.println();
+
         mTrackedJobs.forEach((jobs) -> {
             for (int j = 0; j < jobs.size(); j++) {
                 final JobStatus js = jobs.valueAt(j);
@@ -1460,6 +1660,9 @@
                 js.printUniqueId(pw);
                 pw.print(" from ");
                 UserHandle.formatUid(pw, js.getSourceUid());
+                if (mTopStartedJobs.contains(js)) {
+                    pw.print(" (TOP)");
+                }
                 pw.println();
 
                 pw.increaseIndent();
@@ -1511,6 +1714,11 @@
         proto.write(StateControllerProto.QuotaController.IS_CHARGING, mChargeTracker.isCharging());
         proto.write(StateControllerProto.QuotaController.IS_IN_PAROLE, mInParole);
 
+        for (int i = 0; i < mForegroundUids.size(); ++i) {
+            proto.write(StateControllerProto.QuotaController.FOREGROUND_UIDS,
+                    mForegroundUids.keyAt(i));
+        }
+
         mTrackedJobs.forEach((jobs) -> {
             for (int j = 0; j < jobs.size(); j++) {
                 final JobStatus js = jobs.valueAt(j);
@@ -1526,6 +1734,8 @@
                 proto.write(
                         StateControllerProto.QuotaController.TrackedJob.EFFECTIVE_STANDBY_BUCKET,
                         getEffectiveStandbyBucket(js));
+                proto.write(StateControllerProto.QuotaController.TrackedJob.IS_TOP_STARTED_JOB,
+                        mTopStartedJobs.contains(js));
                 proto.write(StateControllerProto.QuotaController.TrackedJob.HAS_QUOTA,
                         js.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA));
                 proto.write(StateControllerProto.QuotaController.TrackedJob.REMAINING_QUOTA_MS,
diff --git a/services/core/java/com/android/server/location/GnssConfiguration.java b/services/core/java/com/android/server/location/GnssConfiguration.java
index 29465ad..9e33943 100644
--- a/services/core/java/com/android/server/location/GnssConfiguration.java
+++ b/services/core/java/com/android/server/location/GnssConfiguration.java
@@ -29,7 +29,10 @@
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
 import java.util.Properties;
@@ -66,6 +69,7 @@
             "USE_EMERGENCY_PDN_FOR_EMERGENCY_SUPL";
     private static final String CONFIG_GPS_LOCK = "GPS_LOCK";
     private static final String CONFIG_ES_EXTENSION_SEC = "ES_EXTENSION_SEC";
+    public static final String CONFIG_NFW_PROXY_APPS = "NFW_PROXY_APPS";
 
     // Limit on NI emergency mode time extension after emergency sessions ends
     private static final int MAX_EMERGENCY_MODE_EXTENSION_SECONDS = 300;  // 5 minute maximum
@@ -171,6 +175,32 @@
     }
 
     /**
+     * Returns the list of proxy apps from the value of config parameter NFW_PROXY_APPS or
+     * {@Collections.EMPTY_LIST} if no value is provided.
+     */
+    List<String> getProxyApps() {
+        // Space separated list of Android proxy app package names.
+        String proxyAppsStr = mProperties.getProperty(CONFIG_NFW_PROXY_APPS);
+        if (TextUtils.isEmpty(proxyAppsStr)) {
+            return Collections.EMPTY_LIST;
+        }
+
+        String[] proxyAppsArray = proxyAppsStr.trim().split("\\s+");
+        if (proxyAppsArray.length == 0) {
+            return Collections.EMPTY_LIST;
+        }
+
+        // TODO(b/122856486): Validate proxy app names so that a system app or some popular app
+        //                    with location permission is not specified as a proxy app.
+        ArrayList proxyApps = new ArrayList(proxyAppsArray.length);
+        for (String proxyApp : proxyAppsArray) {
+            proxyApps.add(proxyApp);
+        }
+
+        return proxyApps;
+    }
+
+    /**
      * Updates the GNSS HAL satellite blacklist.
      */
     void setSatelliteBlacklist(int[] constellations, int[] svids) {
diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java
index 269767a..d346ddc 100644
--- a/services/core/java/com/android/server/location/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/GnssLocationProvider.java
@@ -64,6 +64,7 @@
 import android.telephony.gsm.GsmCellLocation;
 import android.text.TextUtils;
 import android.util.Log;
+import android.util.StatsLog;
 
 import com.android.internal.app.IBatteryStats;
 import com.android.internal.location.GpsNetInitiatedHandler;
@@ -371,6 +372,7 @@
     private boolean mSuplEsEnabled = false;
 
     private final Context mContext;
+    private final Looper mLooper;
     private final LocationExtras mLocationExtras = new LocationExtras();
     private final GnssStatusListenerHelper mGnssStatusListenerHelper;
     private final GnssSatelliteBlacklistHelper mGnssSatelliteBlacklistHelper;
@@ -381,6 +383,7 @@
     private final NtpTimeHelper mNtpTimeHelper;
     private final GnssBatchingProvider mGnssBatchingProvider;
     private final GnssGeofenceProvider mGnssGeofenceProvider;
+    private GnssVisibilityControl mGnssVisibilityControl;
 
     // Handler for processing events
     private Handler mHandler;
@@ -555,6 +558,9 @@
         mC2KServerPort = mGnssConfiguration.getC2KPort(TCP_MIN_PORT);
         mNIHandler.setEmergencyExtensionSeconds(mGnssConfiguration.getEsExtensionSec());
         mSuplEsEnabled = mGnssConfiguration.getSuplEs(0) == 1;
+        if (mGnssVisibilityControl != null) {
+            mGnssVisibilityControl.updateProxyApps(mGnssConfiguration.getProxyApps());
+        }
     }
 
     public GnssLocationProvider(Context context, LocationProviderManager locationProviderManager,
@@ -562,6 +568,7 @@
         super(locationProviderManager);
 
         mContext = context;
+        mLooper = looper;
 
         // Create a wake lock
         mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
@@ -1704,6 +1711,24 @@
                         ", response: " + userResponse);
             }
             native_send_ni_response(notificationId, userResponse);
+
+            StatsLog.write(StatsLog.GNSS_NI_EVENT_REPORTED,
+                    StatsLog.GNSS_NI_EVENT_REPORTED__EVENT_TYPE__NI_RESPONSE,
+                    notificationId,
+                    /* niType= */ 0,
+                    /* needNotify= */ false,
+                    /* needVerify= */ false,
+                    /* privacyOverride= */ false,
+                    /* timeout= */ 0,
+                    /* defaultResponse= */ 0,
+                    /* requestorId= */ null,
+                    /* text= */ null,
+                    /* requestorIdEncoding= */ 0,
+                    /* textEncoding= */ 0,
+                    mSuplEsEnabled,
+                    mEnabled,
+                    userResponse);
+
             return true;
         }
     };
@@ -1753,6 +1778,22 @@
         notification.textEncoding = textEncoding;
 
         mNIHandler.handleNiNotification(notification);
+        StatsLog.write(StatsLog.GNSS_NI_EVENT_REPORTED,
+                StatsLog.GNSS_NI_EVENT_REPORTED__EVENT_TYPE__NI_REQUEST,
+                notification.notificationId,
+                notification.niType,
+                notification.needNotify,
+                notification.needVerify,
+                notification.privacyOverride,
+                notification.timeout,
+                notification.defaultResponse,
+                notification.requestorId,
+                notification.text,
+                notification.requestorIdEncoding,
+                notification.textEncoding,
+                mSuplEsEnabled,
+                mEnabled,
+                /* userResponse= */ 0);
     }
 
     /**
@@ -1834,6 +1875,27 @@
         }
     }
 
+    // Implements method nfwNotifyCb() in IGnssVisibilityControlCallback.hal.
+    @NativeEntryPoint
+    private void reportNfwNotification(String proxyAppPackageName, byte protocolStack,
+            String otherProtocolStackName, byte requestor, String requestorId, byte responseType,
+            boolean inEmergencyMode, boolean isCachedLocation) {
+        if (mGnssVisibilityControl == null) {
+            Log.e(TAG, "reportNfwNotification: mGnssVisibilityControl is not initialized.");
+            return;
+        }
+
+        mGnssVisibilityControl.reportNfwNotification(proxyAppPackageName, protocolStack,
+                otherProtocolStackName, requestor, requestorId, responseType, inEmergencyMode,
+                isCachedLocation);
+    }
+
+    // Implements method isInEmergencySession() in IGnssVisibilityControlCallback.hal.
+    @NativeEntryPoint
+    boolean isInEmergencySession() {
+        return mNIHandler.getInEmergency();
+    }
+
     private void sendMessage(int message, int arg, Object obj) {
         // hold a wake lock until this message is delivered
         // note that this assumes the message will not be removed from the queue before
@@ -1922,6 +1984,10 @@
                 native_cleanup();
             }
 
+            if (native_is_gnss_visibility_control_supported()) {
+                mGnssVisibilityControl = new GnssVisibilityControl(mContext, mLooper);
+            }
+
             // load default GPS configuration
             // (this configuration might change in the future based on SIM changes)
             reloadGpsProperties();
@@ -2079,6 +2145,8 @@
 
     private static native boolean native_is_supported();
 
+    private static native boolean native_is_gnss_visibility_control_supported();
+
     private static native void native_init_once();
 
     private native boolean native_init();
diff --git a/services/core/java/com/android/server/location/GnssVisibilityControl.java b/services/core/java/com/android/server/location/GnssVisibilityControl.java
new file mode 100644
index 0000000..845aa9d
--- /dev/null
+++ b/services/core/java/com/android/server/location/GnssVisibilityControl.java
@@ -0,0 +1,362 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.location;
+
+import android.annotation.SuppressLint;
+import android.app.AppOpsManager;
+import android.content.ActivityNotFoundException;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.PowerManager;
+import android.os.UserHandle;
+import android.text.TextUtils;
+import android.util.Log;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Handles GNSS non-framework location access user visibility and control.
+ *
+ * The state of the GnssVisibilityControl object must be accessed/modified through the Handler
+ * thread only.
+ */
+class GnssVisibilityControl {
+    private static final String TAG = "GnssVisibilityControl";
+    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+    // Constants related to non-framework (NFW) location access permission proxy apps.
+    private static final String NFW_PROXY_APP_PKG_ACTIVITY_NAME_SUFFIX =
+            ".NonFrameworkLocationAccessActivity";
+    private static final String NFW_INTENT_ACTION_NFW_LOCATION_ACCESS_SUFFIX =
+            ".intent.action.NON_FRAMEWORK_LOCATION_ACCESS";
+    private static final String NFW_INTENT_TYPE = "text/plain";
+
+    private static final String LOCATION_PERMISSION_NAME =
+            "android.permission.ACCESS_FINE_LOCATION";
+
+    // Wakelocks
+    private static final String WAKELOCK_KEY = TAG;
+    private static final long WAKELOCK_TIMEOUT_MILLIS = 60 * 1000;
+    private final PowerManager.WakeLock mWakeLock;
+
+    private final AppOpsManager mAppOps;
+    private final PackageManager mPackageManager;
+
+    private final Handler mHandler;
+    private final Context mContext;
+
+    // Number of non-framework location access proxy apps is expected to be small (< 5).
+    private static final int HASH_MAP_INITIAL_CAPACITY_PROXY_APP_TO_LOCATION_PERMISSIONS = 7;
+    private HashMap<String, Boolean> mProxyAppToLocationPermissions = new HashMap<>(
+            HASH_MAP_INITIAL_CAPACITY_PROXY_APP_TO_LOCATION_PERMISSIONS);
+
+    private PackageManager.OnPermissionsChangedListener mOnPermissionsChangedListener =
+            uid -> postEvent(() -> handlePermissionsChanged(uid));
+
+    GnssVisibilityControl(Context context, Looper looper) {
+        mContext = context;
+        PowerManager powerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
+        mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_KEY);
+        mHandler = new Handler(looper);
+        mAppOps = mContext.getSystemService(AppOpsManager.class);
+        mPackageManager = mContext.getPackageManager();
+        // TODO(b/122855984): Handle global location settings on/off.
+        // TODO(b/122856189): Handle roaming case.
+    }
+
+    void updateProxyApps(List<String> nfwLocationAccessProxyApps) {
+        // NOTE: This class doesn't explicitly register and listen for SIM_STATE_CHANGED event
+        //       but rather piggy backs on the GnssLocationProvider SIM_STATE_CHANGED handling
+        //       so that the order of processing is preserved. GnssLocationProvider should
+        //       first load the new config parameters for the new SIM and then call this method.
+        postEvent(() -> handleSubscriptionOrSimChanged(nfwLocationAccessProxyApps));
+    }
+
+    void reportNfwNotification(String proxyAppPackageName, byte protocolStack,
+            String otherProtocolStackName, byte requestor, String requestorId, byte responseType,
+            boolean inEmergencyMode, boolean isCachedLocation) {
+        postEvent(() -> handleNfwNotification(
+                new NfwNotification(proxyAppPackageName, protocolStack, otherProtocolStackName,
+                        requestor, requestorId, responseType, inEmergencyMode, isCachedLocation)));
+    }
+
+    private void handleSubscriptionOrSimChanged(List<String> nfwLocationAccessProxyApps) {
+        if (nfwLocationAccessProxyApps.isEmpty()) {
+            // Stop listening for app permission changes. Clear the app list in GNSS HAL.
+            if (!mProxyAppToLocationPermissions.isEmpty()) {
+                mPackageManager.removeOnPermissionsChangeListener(mOnPermissionsChangedListener);
+                mProxyAppToLocationPermissions.clear();
+                updateNfwLocationAccessProxyAppsInGnssHal();
+            }
+            return;
+        }
+
+        if (mProxyAppToLocationPermissions.isEmpty()) {
+            mPackageManager.addOnPermissionsChangeListener(mOnPermissionsChangedListener);
+        } else {
+            mProxyAppToLocationPermissions.clear();
+        }
+
+        for (String proxApp : nfwLocationAccessProxyApps) {
+            mProxyAppToLocationPermissions.put(proxApp, hasLocationPermission(proxApp));
+        }
+
+        updateNfwLocationAccessProxyAppsInGnssHal();
+    }
+
+    // Represents NfwNotification structure in IGnssVisibilityControlCallback.hal
+    private static class NfwNotification {
+        private static final String KEY_PROTOCOL_STACK = "ProtocolStack";
+        private static final String KEY_OTHER_PROTOCOL_STACK_NAME = "OtherProtocolStackName";
+        private static final String KEY_REQUESTOR = "Requestor";
+        private static final String KEY_REQUESTOR_ID = "RequestorId";
+        private static final String KEY_RESPONSE_TYPE = "ResponseType";
+        private static final String KEY_IN_EMERGENCY_MODE = "InEmergencyMode";
+        private static final String KEY_IS_CACHED_LOCATION = "IsCachedLocation";
+
+        // This must match with NfwResponseType enum in IGnssVisibilityControlCallback.hal.
+        private static final byte NFW_RESPONSE_TYPE_REJECTED = 0;
+        private static final byte NFW_RESPONSE_TYPE_ACCEPTED_NO_LOCATION_PROVIDED = 1;
+        private static final byte NFW_RESPONSE_TYPE_ACCEPTED_LOCATION_PROVIDED = 2;
+
+        private final String mProxyAppPackageName;
+        private final byte mProtocolStack;
+        private final String mOtherProtocolStackName;
+        private final byte mRequestor;
+        private final String mRequestorId;
+        private final byte mResponseType;
+        private final boolean mInEmergencyMode;
+        private final boolean mIsCachedLocation;
+
+        NfwNotification(String proxyAppPackageName, byte protocolStack,
+                String otherProtocolStackName, byte requestor, String requestorId,
+                byte responseType, boolean inEmergencyMode, boolean isCachedLocation) {
+            mProxyAppPackageName = proxyAppPackageName;
+            mProtocolStack = protocolStack;
+            mOtherProtocolStackName = otherProtocolStackName;
+            mRequestor = requestor;
+            mRequestorId = requestorId;
+            mResponseType = responseType;
+            mInEmergencyMode = inEmergencyMode;
+            mIsCachedLocation = isCachedLocation;
+        }
+
+        void copyFieldsToIntent(Intent intent) {
+            intent.putExtra(KEY_PROTOCOL_STACK, mProtocolStack);
+            if (!TextUtils.isEmpty(mOtherProtocolStackName)) {
+                intent.putExtra(KEY_OTHER_PROTOCOL_STACK_NAME, mOtherProtocolStackName);
+            }
+            intent.putExtra(KEY_REQUESTOR, mRequestor);
+            if (!TextUtils.isEmpty(mRequestorId)) {
+                intent.putExtra(KEY_REQUESTOR_ID, mRequestorId);
+            }
+            intent.putExtra(KEY_RESPONSE_TYPE, mResponseType);
+            intent.putExtra(KEY_IN_EMERGENCY_MODE, mInEmergencyMode);
+            if (mResponseType == NFW_RESPONSE_TYPE_ACCEPTED_LOCATION_PROVIDED) {
+                intent.putExtra(KEY_IS_CACHED_LOCATION, mIsCachedLocation);
+            }
+        }
+
+        @SuppressLint("DefaultLocale")
+        public String toString() {
+            return String.format(
+                    "[Notification] proxyAppPackageName: %s, protocolStack: %d"
+                            + ", otherProtocolStackName: %s, requestor: %d, requestorId: %s"
+                            + ", responseType: %d, inEmergencyMode: %b, isCachedLocation: %b",
+                    mProxyAppPackageName, mProtocolStack, mOtherProtocolStackName,
+                    mRequestor, mRequestorId, mResponseType, mInEmergencyMode, mIsCachedLocation);
+        }
+
+        String getResponseTypeAsString() {
+            switch (mResponseType) {
+                case NFW_RESPONSE_TYPE_REJECTED:
+                    return "REJECTED";
+                case NFW_RESPONSE_TYPE_ACCEPTED_NO_LOCATION_PROVIDED:
+                    return "ACCEPTED_NO_LOCATION_PROVIDED";
+                case NFW_RESPONSE_TYPE_ACCEPTED_LOCATION_PROVIDED:
+                    return "ACCEPTED_LOCATION_PROVIDED";
+                default:
+                    return "<Unknown>";
+            }
+        }
+    }
+
+    private void handlePermissionsChanged(int uid) {
+        if (mProxyAppToLocationPermissions.isEmpty()) {
+            return;
+        }
+
+        for (Map.Entry<String, Boolean> entry : mProxyAppToLocationPermissions.entrySet()) {
+            // Cannot cache uid since the application could be uninstalled and reinstalled.
+            final String proxyApp = entry.getKey();
+            final Integer nfwProxyAppUid = getApplicationUid(proxyApp);
+            if (nfwProxyAppUid == null || nfwProxyAppUid != uid) {
+                continue;
+            }
+
+            final boolean isLocationPermissionEnabled = hasLocationPermission(proxyApp);
+            if (isLocationPermissionEnabled != entry.getValue()) {
+                Log.i(TAG, "Location permission setting is changed to "
+                        + (isLocationPermissionEnabled ? "enabled" : "disabled")
+                        + " for non-framework location access proxy app "
+                        + proxyApp);
+                entry.setValue(isLocationPermissionEnabled);
+                updateNfwLocationAccessProxyAppsInGnssHal();
+                return;
+            }
+        }
+    }
+
+    private Integer getApplicationUid(String pkgName) {
+        try {
+            return mPackageManager.getApplicationInfo(pkgName, 0).uid;
+        } catch (PackageManager.NameNotFoundException e) {
+            if (DEBUG) {
+                Log.d(TAG, "Non-framework location access proxy app "
+                        + pkgName + " is not found.");
+            }
+            return null;
+        }
+    }
+
+    private boolean hasLocationPermission(String pkgName) {
+        return mPackageManager.checkPermission(LOCATION_PERMISSION_NAME, pkgName)
+                == PackageManager.PERMISSION_GRANTED;
+    }
+
+    private void updateNfwLocationAccessProxyAppsInGnssHal() {
+        // Get a count of proxy apps with location permission enabled to array creation size.
+        int countLocationPermissionEnabledProxyApps = 0;
+        for (Boolean hasLocationPermissionEnabled : mProxyAppToLocationPermissions.values()) {
+            if (hasLocationPermissionEnabled) {
+                ++countLocationPermissionEnabledProxyApps;
+            }
+        }
+
+        int i = 0;
+        String[] locationPermissionEnabledProxyApps =
+                new String[countLocationPermissionEnabledProxyApps];
+        for (Map.Entry<String, Boolean> entry : mProxyAppToLocationPermissions.entrySet()) {
+            final String proxyApp = entry.getKey();
+            final boolean hasLocationPermissionEnabled = entry.getValue();
+            if (hasLocationPermissionEnabled) {
+                locationPermissionEnabledProxyApps[i++] = proxyApp;
+            }
+        }
+
+        String proxyAppsStr = Arrays.toString(locationPermissionEnabledProxyApps);
+        Log.i(TAG, "Updating non-framework location access proxy apps in the GNSS HAL to: "
+                + proxyAppsStr);
+        boolean result = native_enable_nfw_location_access(locationPermissionEnabledProxyApps);
+        if (!result) {
+            Log.e(TAG, "Failed to update non-framework location access proxy apps in the"
+                    + " GNSS HAL to: " + proxyAppsStr);
+        }
+    }
+
+    private void handleNfwNotification(NfwNotification nfwNotification) {
+        if (DEBUG) Log.d(TAG, nfwNotification.toString());
+
+        final String proxyAppPackageName = nfwNotification.mProxyAppPackageName;
+        if (TextUtils.isEmpty(proxyAppPackageName)) {
+            Log.e(TAG, "ProxyAppPackageName field is not set. Not sending intent to proxy app for "
+                    + nfwNotification);
+            return;
+        }
+
+        Boolean isLocationPermissionEnabled = mProxyAppToLocationPermissions.get(
+                proxyAppPackageName);
+        if (isLocationPermissionEnabled == null) {
+            // App is not in the configured list.
+            Log.e(TAG, "Could not find proxy app with name: " + proxyAppPackageName + " in the "
+                    + "value specified for config parameter: "
+                    + GnssConfiguration.CONFIG_NFW_PROXY_APPS + ". Not sending intent to proxy app"
+                    + " for " + nfwNotification);
+            return;
+        }
+
+        // Send intent to non-framework location proxy app with notification information.
+        final Intent intent = new Intent(
+                proxyAppPackageName + NFW_INTENT_ACTION_NFW_LOCATION_ACCESS_SUFFIX);
+        final String proxAppActivityName =
+                proxyAppPackageName + NFW_PROXY_APP_PKG_ACTIVITY_NAME_SUFFIX;
+        intent.setClassName(proxyAppPackageName, proxAppActivityName);
+        intent.setType(NFW_INTENT_TYPE);
+        nfwNotification.copyFieldsToIntent(intent);
+
+        // Check if the proxy app is still installed.
+        final Integer clsAppUid = getApplicationUid(proxyAppPackageName);
+        if (clsAppUid == null || intent.resolveActivity(mPackageManager) == null) {
+            Log.i(TAG, "Proxy application " + proxyAppPackageName + " and/or activity "
+                    + proxAppActivityName + " is not found. Not sending"
+                    + " intent to proxy app for " + nfwNotification);
+            return;
+        }
+
+        // Display location icon attributed to this proxy app.
+        mAppOps.noteOpNoThrow(AppOpsManager.OP_FINE_LOCATION, clsAppUid, proxyAppPackageName);
+
+        // Log proxy app permission mismatch between framework and GNSS HAL.
+        boolean isLocationRequestAccepted =
+                nfwNotification.mResponseType != NfwNotification.NFW_RESPONSE_TYPE_REJECTED;
+        if (isLocationPermissionEnabled != isLocationRequestAccepted) {
+            Log.w(TAG, "Permission mismatch. Framework proxy app " + proxyAppPackageName
+                    + " location permission is set to " + isLocationPermissionEnabled
+                    + " but GNSS non-framework location access response type is "
+                    + nfwNotification.getResponseTypeAsString() + " for " + nfwNotification);
+        }
+
+        // Notify proxy app.
+        try {
+            if (DEBUG) {
+                Log.d(TAG, "Sending non-framework location access notification intent: " + intent);
+            }
+            mContext.startActivityAsUser(intent, UserHandle.getUserHandleForUid(clsAppUid));
+        } catch (ActivityNotFoundException e) {
+            Log.w(TAG, "Activity not found. Failed to send non-framework location access"
+                    + " notification intent to proxy app activity: " + proxAppActivityName);
+        }
+    }
+
+    private void postEvent(Runnable event) {
+        // Hold a wake lock until this message is delivered.
+        // Note that this assumes the message will not be removed from the queue before
+        // it is handled (otherwise the wake lock would be leaked).
+        mWakeLock.acquire(WAKELOCK_TIMEOUT_MILLIS);
+        if (!mHandler.post(runEventAndReleaseWakeLock(event))) {
+            mWakeLock.release();
+        }
+    }
+
+    private Runnable runEventAndReleaseWakeLock(Runnable event) {
+        return () -> {
+            try {
+                event.run();
+            } finally {
+                mWakeLock.release();
+            }
+        };
+    }
+
+    private native boolean native_enable_nfw_location_access(String[] proxyApps);
+}
diff --git a/services/core/java/com/android/server/location/MockProvider.java b/services/core/java/com/android/server/location/MockProvider.java
index 86bc9f3..fe91c63 100644
--- a/services/core/java/com/android/server/location/MockProvider.java
+++ b/services/core/java/com/android/server/location/MockProvider.java
@@ -52,7 +52,6 @@
         mExtras = null;
 
         setProperties(properties);
-        setEnabled(true);
     }
 
     /** Sets the enabled state of this mock provider. */
diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java
index d611a17..7fffe8e 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecord.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecord.java
@@ -80,7 +80,7 @@
     private final ControllerStub mController;
     private final SessionStub mSession;
     private final SessionCb mSessionCb;
-    private final MediaSessionService mService;
+    private final MediaSessionService.ServiceImpl mService;
     private final Context mContext;
 
     private final Object mLock = new Object();
@@ -120,7 +120,8 @@
     private String mMetadataDescription;
 
     public MediaSessionRecord(int ownerPid, int ownerUid, int userId, String ownerPackageName,
-            SessionCallbackLink cb, String tag, MediaSessionService service, Looper handlerLooper) {
+            SessionCallbackLink cb, String tag, MediaSessionService.ServiceImpl service,
+            Looper handlerLooper) {
         mOwnerPid = ownerPid;
         mOwnerUid = ownerUid;
         mUserId = userId;
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index ce0e72b..d20ed8c 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -16,76 +16,15 @@
 
 package com.android.server.media;
 
-import android.annotation.Nullable;
-import android.app.ActivityManager;
-import android.app.INotificationManager;
-import android.app.KeyguardManager;
-import android.app.PendingIntent;
-import android.app.PendingIntent.CanceledException;
-import android.content.ActivityNotFoundException;
-import android.content.BroadcastReceiver;
-import android.content.ComponentName;
-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;
-import android.media.AudioManagerInternal;
-import android.media.AudioPlaybackConfiguration;
-import android.media.AudioSystem;
-import android.media.IAudioService;
-import android.media.IRemoteVolumeController;
-import android.media.MediaController2;
-import android.media.Session2CommandGroup;
 import android.media.Session2Token;
-import android.media.session.IActiveSessionsListener;
-import android.media.session.ICallback;
-import android.media.session.IOnMediaKeyListener;
-import android.media.session.IOnVolumeKeyLongPressListener;
-import android.media.session.ISession;
-import android.media.session.ISessionManager;
-import android.media.session.MediaSession;
-import android.media.session.MediaSessionManager;
-import android.media.session.SessionCallbackLink;
-import android.net.Uri;
-import android.os.Binder;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.HandlerExecutor;
 import android.os.IBinder;
-import android.os.Message;
-import android.os.PowerManager;
-import android.os.Process;
-import android.os.RemoteException;
-import android.os.ResultReceiver;
-import android.os.ServiceManager;
-import android.os.UserHandle;
-import android.os.UserManager;
-import android.provider.Settings;
-import android.speech.RecognizerIntent;
-import android.text.TextUtils;
 import android.util.Log;
-import android.util.Slog;
-import android.util.SparseArray;
-import android.util.SparseIntArray;
-import android.view.KeyEvent;
-import android.view.ViewConfiguration;
 
-import com.android.internal.annotations.GuardedBy;
-import com.android.internal.util.DumpUtils;
-import com.android.server.LocalServices;
 import com.android.server.SystemService;
 import com.android.server.Watchdog;
 import com.android.server.Watchdog.Monitor;
 
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.util.ArrayList;
 import java.util.List;
 
 /**
@@ -94,1933 +33,124 @@
 public class MediaSessionService extends SystemService implements Monitor {
     private static final String TAG = "MediaSessionService";
     static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
-    // Leave log for key event always.
-    private static final boolean DEBUG_KEY_EVENT = true;
 
-    private static final int WAKELOCK_TIMEOUT = 5000;
-    private static final int MEDIA_KEY_LISTENER_TIMEOUT = 1000;
-
-    private final SessionManagerImpl mSessionManagerImpl;
-    private final MessageHandler mHandler = new MessageHandler();
-    private final PowerManager.WakeLock mMediaEventWakeLock;
-    private final int mLongPressTimeout;
-    private final INotificationManager mNotificationManager;
-    private final Object mLock = new Object();
-    // Keeps the full user id for each user.
-    @GuardedBy("mLock")
-    private final SparseIntArray mFullUserIds = new SparseIntArray();
-    @GuardedBy("mLock")
-    private final SparseArray<FullUserRecord> mUserRecords = new SparseArray<FullUserRecord>();
-    @GuardedBy("mLock")
-    private final ArrayList<SessionsListenerRecord> mSessionsListeners
-            = new ArrayList<SessionsListenerRecord>();
-    // Map user id as index to list of Session2Tokens
-    // TODO: Keep session2 info in MediaSessionStack for prioritizing both session1 and session2 in
-    //       one place.
-    @GuardedBy("mLock")
-    private final SparseArray<List<Session2Token>> mSession2TokensPerUser = new SparseArray<>();
-
-    private KeyguardManager mKeyguardManager;
-    private IAudioService mAudioService;
-    private AudioManagerInternal mAudioManagerInternal;
-    private ActivityManager mActivityManager;
-    private ContentResolver mContentResolver;
-    private SettingsObserver mSettingsObserver;
-    private boolean mHasFeatureLeanback;
-
-    // The FullUserRecord of the current users. (i.e. The foreground user that isn't a profile)
-    // It's always not null after the MediaSessionService is started.
-    private FullUserRecord mCurrentFullUserRecord;
-    private MediaSessionRecord mGlobalPrioritySession;
-    private AudioPlayerStateMonitor mAudioPlayerStateMonitor;
-
-    // Used to notify system UI when remote volume was changed. TODO find a
-    // better way to handle this.
-    private IRemoteVolumeController mRvc;
+    private final ServiceImpl mImpl;
 
     public MediaSessionService(Context context) {
         super(context);
-        mSessionManagerImpl = new SessionManagerImpl();
-        PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
-        mMediaEventWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "handleMediaEvent");
-        mLongPressTimeout = ViewConfiguration.getLongPressTimeout();
-        mNotificationManager = INotificationManager.Stub.asInterface(
-                ServiceManager.getService(Context.NOTIFICATION_SERVICE));
+        mImpl = new MediaSessionServiceImpl(context);
     }
 
     @Override
     public void onStart() {
-        publishBinderService(Context.MEDIA_SESSION_SERVICE, mSessionManagerImpl);
+        publishBinderService(Context.MEDIA_SESSION_SERVICE, mImpl.getServiceBinder());
         Watchdog.getInstance().addMonitor(this);
-        mKeyguardManager =
-                (KeyguardManager) getContext().getSystemService(Context.KEYGUARD_SERVICE);
-        mAudioService = getAudioService();
-        mAudioManagerInternal = LocalServices.getService(AudioManagerInternal.class);
-        mActivityManager =
-                (ActivityManager) getContext().getSystemService(Context.ACTIVITY_SERVICE);
-        mAudioPlayerStateMonitor = AudioPlayerStateMonitor.getInstance();
-        mAudioPlayerStateMonitor.registerListener(
-                (config, isRemoved) -> {
-                    if (isRemoved || !config.isActive() || config.getPlayerType()
-                            == AudioPlaybackConfiguration.PLAYER_TYPE_JAM_SOUNDPOOL) {
-                        return;
-                    }
-                    synchronized (mLock) {
-                        FullUserRecord user = getFullUserRecordLocked(
-                                UserHandle.getUserId(config.getClientUid()));
-                        if (user != null) {
-                            user.mPriorityStack.updateMediaButtonSessionIfNeeded();
-                        }
-                    }
-                }, null /* handler */);
-        mAudioPlayerStateMonitor.registerSelfIntoAudioServiceIfNeeded(mAudioService);
-        mContentResolver = getContext().getContentResolver();
-        mSettingsObserver = new SettingsObserver();
-        mSettingsObserver.observe();
-        mHasFeatureLeanback = getContext().getPackageManager().hasSystemFeature(
-                PackageManager.FEATURE_LEANBACK);
 
-        updateUser();
+        mImpl.onStart();
     }
 
-    private IAudioService getAudioService() {
-        IBinder b = ServiceManager.getService(Context.AUDIO_SERVICE);
-        return IAudioService.Stub.asInterface(b);
+    @Override
+    public void onStartUser(int userId) {
+        mImpl.onStartUser(userId);
     }
 
-    private boolean isGlobalPriorityActiveLocked() {
-        return mGlobalPrioritySession != null && mGlobalPrioritySession.isActive();
+    @Override
+    public void onSwitchUser(int userId) {
+        mImpl.onSwitchUser(userId);
     }
 
+    // Called when the user with the userId is removed.
+    @Override
+    public void onStopUser(int userId) {
+        mImpl.onStopUser(userId);
+    }
+
+    @Override
+    public void monitor() {
+        mImpl.monitor();
+    }
+
+    /**
+     * Updates session.
+     */
     public void updateSession(MediaSessionRecord record) {
-        synchronized (mLock) {
-            FullUserRecord user = getFullUserRecordLocked(record.getUserId());
-            if (user == null) {
-                Log.w(TAG, "Unknown session updated. Ignoring.");
-                return;
-            }
-            if ((record.getFlags() & MediaSession.FLAG_EXCLUSIVE_GLOBAL_PRIORITY) != 0) {
-                if (DEBUG_KEY_EVENT) {
-                    Log.d(TAG, "Global priority session is updated, active=" + record.isActive());
-                }
-                user.pushAddressedPlayerChangedLocked();
-            } else {
-                if (!user.mPriorityStack.contains(record)) {
-                    Log.w(TAG, "Unknown session updated. Ignoring.");
-                    return;
-                }
-                user.mPriorityStack.onSessionStateChange(record);
-            }
-            mHandler.postSessionsChanged(record.getUserId());
-        }
+        mImpl.updateSession(record);
     }
 
+    /**
+     * Sets global priority session.
+     */
     public void setGlobalPrioritySession(MediaSessionRecord record) {
-        synchronized (mLock) {
-            FullUserRecord user = getFullUserRecordLocked(record.getUserId());
-            if (mGlobalPrioritySession != record) {
-                Log.d(TAG, "Global priority session is changed from " + mGlobalPrioritySession
-                        + " to " + record);
-                mGlobalPrioritySession = record;
-                if (user != null && user.mPriorityStack.contains(record)) {
-                    // Handle the global priority session separately.
-                    // Otherwise, it can be the media button session regardless of the active state
-                    // because it or other system components might have been the lastly played media
-                    // app.
-                    user.mPriorityStack.removeSession(record);
-                }
-            }
-        }
+        mImpl.setGlobalPrioritySession(record);
     }
 
-    private List<MediaSessionRecord> getActiveSessionsLocked(int userId) {
-        List<MediaSessionRecord> records = new ArrayList<>();
-        if (userId == UserHandle.USER_ALL) {
-            int size = mUserRecords.size();
-            for (int i = 0; i < size; i++) {
-                records.addAll(mUserRecords.valueAt(i).mPriorityStack.getActiveSessions(userId));
-            }
-        } else {
-            FullUserRecord user = getFullUserRecordLocked(userId);
-            if (user == null) {
-                Log.w(TAG, "getSessions failed. Unknown user " + userId);
-                return records;
-            }
-            records.addAll(user.mPriorityStack.getActiveSessions(userId));
-        }
-
-        // Return global priority session at the first whenever it's asked.
-        if (isGlobalPriorityActiveLocked()
-                && (userId == UserHandle.USER_ALL
-                    || userId == mGlobalPrioritySession.getUserId())) {
-            records.add(0, mGlobalPrioritySession);
-        }
-        return records;
+    List<Session2Token> getSession2TokensLocked(int userId) {
+        return mImpl.getSession2TokensLocked(userId);
     }
 
     /**
      * Tells the system UI that volume has changed on an active remote session.
      */
     public void notifyRemoteVolumeChanged(int flags, MediaSessionRecord session) {
-        if (mRvc == null || !session.isActive()) {
-            return;
-        }
-        try {
-            mRvc.remoteVolumeChanged(session.getControllerBinder(), flags);
-        } catch (Exception e) {
-            Log.wtf(TAG, "Error sending volume change to system UI.", e);
-        }
+        mImpl.notifyRemoteVolumeChanged(flags, session);
     }
 
+    /**
+     * Called when session playstate is changed.
+     */
     public void onSessionPlaystateChanged(MediaSessionRecord record, int oldState, int newState) {
-        synchronized (mLock) {
-            FullUserRecord user = getFullUserRecordLocked(record.getUserId());
-            if (user == null || !user.mPriorityStack.contains(record)) {
-                Log.d(TAG, "Unknown session changed playback state. Ignoring.");
-                return;
-            }
-            user.mPriorityStack.onPlaystateChanged(record, oldState, newState);
-        }
+        mImpl.onSessionPlaystateChanged(record, oldState, newState);
     }
 
+    /**
+     * Called when session playback type is changed.
+     */
     public void onSessionPlaybackTypeChanged(MediaSessionRecord record) {
-        synchronized (mLock) {
-            FullUserRecord user = getFullUserRecordLocked(record.getUserId());
-            if (user == null || !user.mPriorityStack.contains(record)) {
-                Log.d(TAG, "Unknown session changed playback type. Ignoring.");
-                return;
-            }
-            pushRemoteVolumeUpdateLocked(record.getUserId());
-        }
-    }
-
-    @Override
-    public void onStartUser(int userId) {
-        if (DEBUG) Log.d(TAG, "onStartUser: " + userId);
-        updateUser();
-    }
-
-    @Override
-    public void onSwitchUser(int userId) {
-        if (DEBUG) Log.d(TAG, "onSwitchUser: " + userId);
-        updateUser();
-    }
-
-    // Called when the user with the userId is removed.
-    @Override
-    public void onStopUser(int userId) {
-        if (DEBUG) Log.d(TAG, "onStopUser: " + userId);
-        synchronized (mLock) {
-            // TODO: Also handle removing user in updateUser() because adding/switching user is
-            //       handled in updateUser().
-            FullUserRecord user = getFullUserRecordLocked(userId);
-            if (user != null) {
-                if (user.mFullUserId == userId) {
-                    user.destroySessionsForUserLocked(UserHandle.USER_ALL);
-                    mUserRecords.remove(userId);
-                } else {
-                    user.destroySessionsForUserLocked(userId);
-                }
-            }
-            mSession2TokensPerUser.remove(userId);
-            updateUser();
-        }
-    }
-
-    @Override
-    public void monitor() {
-        synchronized (mLock) {
-            // Check for deadlock
-        }
+        mImpl.onSessionPlaybackTypeChanged(record);
     }
 
     protected void enforcePhoneStatePermission(int pid, int uid) {
-        if (getContext().checkPermission(android.Manifest.permission.MODIFY_PHONE_STATE, pid, uid)
-                != PackageManager.PERMISSION_GRANTED) {
-            throw new SecurityException("Must hold the MODIFY_PHONE_STATE permission.");
-        }
+        mImpl.enforcePhoneStatePermission(pid, uid);
     }
 
     void sessionDied(MediaSessionRecord session) {
-        synchronized (mLock) {
-            destroySessionLocked(session);
-        }
+        mImpl.sessionDied(session);
     }
 
     void destroySession(MediaSessionRecord session) {
-        synchronized (mLock) {
-            destroySessionLocked(session);
-        }
+        mImpl.destroySession(session);
     }
 
-    private void updateUser() {
-        synchronized (mLock) {
-            UserManager manager = (UserManager) getContext().getSystemService(Context.USER_SERVICE);
-            mFullUserIds.clear();
-            List<UserInfo> allUsers = manager.getUsers();
-            if (allUsers != null) {
-                for (UserInfo userInfo : allUsers) {
-                    if (userInfo.isManagedProfile()) {
-                        mFullUserIds.put(userInfo.id, userInfo.profileGroupId);
-                    } else {
-                        mFullUserIds.put(userInfo.id, userInfo.id);
-                        if (mUserRecords.get(userInfo.id) == null) {
-                            mUserRecords.put(userInfo.id, new FullUserRecord(userInfo.id));
-                        }
-                    }
-                    if (mSession2TokensPerUser.get(userInfo.id) == null) {
-                        mSession2TokensPerUser.put(userInfo.id, new ArrayList<>());
-                    }
-                }
-            }
-            // Ensure that the current full user exists.
-            int currentFullUserId = ActivityManager.getCurrentUser();
-            mCurrentFullUserRecord = mUserRecords.get(currentFullUserId);
-            if (mCurrentFullUserRecord == null) {
-                Log.w(TAG, "Cannot find FullUserInfo for the current user " + currentFullUserId);
-                mCurrentFullUserRecord = new FullUserRecord(currentFullUserId);
-                mUserRecords.put(currentFullUserId, mCurrentFullUserRecord);
-                if (mSession2TokensPerUser.get(currentFullUserId) == null) {
-                    mSession2TokensPerUser.put(currentFullUserId, new ArrayList<>());
-                }
-            }
-            mFullUserIds.put(currentFullUserId, currentFullUserId);
-        }
-    }
-
-    private void updateActiveSessionListeners() {
-        synchronized (mLock) {
-            for (int i = mSessionsListeners.size() - 1; i >= 0; i--) {
-                SessionsListenerRecord listener = mSessionsListeners.get(i);
-                try {
-                    enforceMediaPermissions(listener.mComponentName, listener.mPid, listener.mUid,
-                            listener.mUserId);
-                } catch (SecurityException e) {
-                    Log.i(TAG, "ActiveSessionsListener " + listener.mComponentName
-                            + " is no longer authorized. Disconnecting.");
-                    mSessionsListeners.remove(i);
-                    try {
-                        listener.mListener
-                                .onActiveSessionsChanged(new ArrayList<MediaSession.Token>());
-                    } catch (Exception e1) {
-                        // ignore
-                    }
-                }
-            }
-        }
-    }
-
-    /*
-     * When a session is removed several things need to happen.
-     * 1. We need to remove it from the relevant user.
-     * 2. We need to remove it from the priority stack.
-     * 3. We need to remove it from all sessions.
-     * 4. If this is the system priority session we need to clear it.
-     * 5. We need to unlink to death from the cb binder
-     * 6. We need to tell the session to do any final cleanup (onDestroy)
-     */
-    private void destroySessionLocked(MediaSessionRecord session) {
-        if (DEBUG) {
-            Log.d(TAG, "Destroying " + session);
-        }
-        FullUserRecord user = getFullUserRecordLocked(session.getUserId());
-        if (mGlobalPrioritySession == session) {
-            mGlobalPrioritySession = null;
-            if (session.isActive() && user != null) {
-                user.pushAddressedPlayerChangedLocked();
-            }
-        } else {
-            if (user != null) {
-                user.mPriorityStack.removeSession(session);
-            }
-        }
-
-        try {
-            session.getCallback().getBinder().unlinkToDeath(session, 0);
-        } catch (Exception e) {
-            // ignore exceptions while destroying a session.
-        }
-        session.onDestroy();
-        mHandler.postSessionsChanged(session.getUserId());
-    }
-
-    private void enforcePackageName(String packageName, int uid) {
-        if (TextUtils.isEmpty(packageName)) {
-            throw new IllegalArgumentException("packageName may not be empty");
-        }
-        String[] packages = getContext().getPackageManager().getPackagesForUid(uid);
-        final int packageCount = packages.length;
-        for (int i = 0; i < packageCount; i++) {
-            if (packageName.equals(packages[i])) {
-                return;
-            }
-        }
-        throw new IllegalArgumentException("packageName is not owned by the calling process");
+    void pushSession2TokensChangedLocked(int userId) {
+        mImpl.pushSession2TokensChangedLocked(userId);
     }
 
     /**
-     * Checks a caller's authorization to register an IRemoteControlDisplay.
-     * Authorization is granted if one of the following is true:
-     * <ul>
-     * <li>the caller has android.Manifest.permission.MEDIA_CONTENT_CONTROL
-     * permission</li>
-     * <li>the caller's listener is one of the enabled notification listeners
-     * for the caller's user</li>
-     * </ul>
-     */
-    private void enforceMediaPermissions(ComponentName compName, int pid, int uid,
-            int resolvedUserId) {
-        if (isCurrentVolumeController(pid, uid)) return;
-        if (getContext()
-                .checkPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL, pid, uid)
-                    != PackageManager.PERMISSION_GRANTED
-                && !isEnabledNotificationListener(compName, UserHandle.getUserId(uid),
-                        resolvedUserId)) {
-            throw new SecurityException("Missing permission to control media.");
-        }
-    }
-
-    private boolean isCurrentVolumeController(int pid, int uid) {
-        return getContext().checkPermission(android.Manifest.permission.STATUS_BAR_SERVICE,
-                pid, uid) == PackageManager.PERMISSION_GRANTED;
-    }
-
-    private void enforceSystemUiPermission(String action, int pid, int uid) {
-        if (!isCurrentVolumeController(pid, uid)) {
-            throw new SecurityException("Only system ui may " + action);
-        }
-    }
-
-    /**
-     * This checks if the component is an enabled notification listener for the
-     * specified user. Enabled components may only operate on behalf of the user
-     * they're running as.
-     *
-     * @param compName The component that is enabled.
-     * @param userId The user id of the caller.
-     * @param forUserId The user id they're making the request on behalf of.
-     * @return True if the component is enabled, false otherwise
-     */
-    private boolean isEnabledNotificationListener(ComponentName compName, int userId,
-            int forUserId) {
-        if (userId != forUserId) {
-            // You may not access another user's content as an enabled listener.
-            return false;
-        }
-        if (DEBUG) {
-            Log.d(TAG, "Checking if enabled notification listener " + compName);
-        }
-        if (compName != null) {
-            try {
-                return mNotificationManager.isNotificationListenerAccessGrantedForUser(
-                        compName, userId);
-            } catch(RemoteException e) {
-                Log.w(TAG, "Dead NotificationManager in isEnabledNotificationListener", e);
-            }
-        }
-        return false;
-    }
-
-    private MediaSessionRecord createSessionInternal(int callerPid, int callerUid, int userId,
-            String callerPackageName, SessionCallbackLink cb, String tag) throws RemoteException {
-        synchronized (mLock) {
-            return createSessionLocked(callerPid, callerUid, userId, callerPackageName, cb, tag);
-        }
-    }
-
-    /*
-     * When a session is created the following things need to happen.
-     * 1. Its callback binder needs a link to death
-     * 2. It needs to be added to all sessions.
-     * 3. It needs to be added to the priority stack.
-     * 4. It needs to be added to the relevant user record.
-     */
-    private MediaSessionRecord createSessionLocked(int callerPid, int callerUid, int userId,
-            String callerPackageName, SessionCallbackLink cb, String tag) {
-        FullUserRecord user = getFullUserRecordLocked(userId);
-        if (user == null) {
-            Log.wtf(TAG, "Request from invalid user: " +  userId);
-            throw new RuntimeException("Session request from invalid user.");
-        }
-
-        final MediaSessionRecord session = new MediaSessionRecord(callerPid, callerUid, userId,
-                callerPackageName, cb, tag, this, mHandler.getLooper());
-        try {
-            cb.getBinder().linkToDeath(session, 0);
-        } catch (RemoteException e) {
-            throw new RuntimeException("Media Session owner died prematurely.", e);
-        }
-
-        user.mPriorityStack.addSession(session);
-        mHandler.postSessionsChanged(userId);
-
-        if (DEBUG) {
-            Log.d(TAG, "Created session for " + callerPackageName + " with tag " + tag);
-        }
-        return session;
-    }
-
-    private int findIndexOfSessionsListenerLocked(IActiveSessionsListener listener) {
-        for (int i = mSessionsListeners.size() - 1; i >= 0; i--) {
-            if (mSessionsListeners.get(i).mListener.asBinder() == listener.asBinder()) {
-                return i;
-            }
-        }
-        return -1;
-    }
-
-    private void pushSessionsChanged(int userId) {
-        synchronized (mLock) {
-            FullUserRecord user = getFullUserRecordLocked(userId);
-            if (user == null) {
-                Log.w(TAG, "pushSessionsChanged failed. No user with id=" + userId);
-                return;
-            }
-            List<MediaSessionRecord> records = getActiveSessionsLocked(userId);
-            int size = records.size();
-            ArrayList<MediaSession.Token> tokens = new ArrayList<MediaSession.Token>();
-            for (int i = 0; i < size; i++) {
-                tokens.add(new MediaSession.Token(records.get(i).getControllerBinder()));
-            }
-            pushRemoteVolumeUpdateLocked(userId);
-            for (int i = mSessionsListeners.size() - 1; i >= 0; i--) {
-                SessionsListenerRecord record = mSessionsListeners.get(i);
-                if (record.mUserId == UserHandle.USER_ALL || record.mUserId == userId) {
-                    try {
-                        record.mListener.onActiveSessionsChanged(tokens);
-                    } catch (RemoteException e) {
-                        Log.w(TAG, "Dead ActiveSessionsListener in pushSessionsChanged, removing",
-                                e);
-                        mSessionsListeners.remove(i);
-                    }
-                }
-            }
-        }
-    }
-
-    private void pushRemoteVolumeUpdateLocked(int userId) {
-        if (mRvc != null) {
-            try {
-                FullUserRecord user = getFullUserRecordLocked(userId);
-                if (user == null) {
-                    Log.w(TAG, "pushRemoteVolumeUpdateLocked failed. No user with id=" + userId);
-                    return;
-                }
-                MediaSessionRecord record = user.mPriorityStack.getDefaultRemoteSession(userId);
-                mRvc.updateRemoteController(record == null ? null : record.getControllerBinder());
-            } catch (RemoteException e) {
-                Log.wtf(TAG, "Error sending default remote volume to sys ui.", e);
-            }
-        }
-    }
-
-    /**
-     * Called when the media button receiver for the {@param record} is changed.
-     *
-     * @param record the media session whose media button receiver is updated.
+     * Called when media button receiver changed.
      */
     public void onMediaButtonReceiverChanged(MediaSessionRecord record) {
-        synchronized (mLock) {
-            FullUserRecord user = getFullUserRecordLocked(record.getUserId());
-            MediaSessionRecord mediaButtonSession =
-                    user.mPriorityStack.getMediaButtonSession();
-            if (record == mediaButtonSession) {
-                user.rememberMediaButtonReceiverLocked(mediaButtonSession);
-            }
-        }
+        mImpl.onMediaButtonReceiverChanged(record);
     }
 
-    private String getCallingPackageName(int uid) {
-        String[] packages = getContext().getPackageManager().getPackagesForUid(uid);
-        if (packages != null && packages.length > 0) {
-            return packages[0];
-        }
-        return "";
-    }
-
-    private void dispatchVolumeKeyLongPressLocked(KeyEvent keyEvent) {
-        if (mCurrentFullUserRecord.mOnVolumeKeyLongPressListener == null) {
-            return;
-        }
-        try {
-            mCurrentFullUserRecord.mOnVolumeKeyLongPressListener.onVolumeKeyLongPress(keyEvent);
-        } catch (RemoteException e) {
-            Log.w(TAG, "Failed to send " + keyEvent + " to volume key long-press listener");
-        }
-    }
-
-    private FullUserRecord getFullUserRecordLocked(int userId) {
-        int fullUserId = mFullUserIds.get(userId, -1);
-        if (fullUserId < 0) {
-            return null;
-        }
-        return mUserRecords.get(fullUserId);
-    }
-
-    /**
-     * Information about a full user and its corresponding managed profiles.
-     *
-     * <p>Since the full user runs together with its managed profiles, a user wouldn't differentiate
-     * them when he/she presses a media/volume button. So keeping media sessions for them in one
-     * place makes more sense and increases the readability.</p>
-     * <p>The contents of this object is guarded by {@link #mLock}.
-     */
-    final class FullUserRecord implements MediaSessionStack.OnMediaButtonSessionChangedListener {
-        public static final int COMPONENT_TYPE_INVALID = 0;
-        public static final int COMPONENT_TYPE_BROADCAST = 1;
-        public static final int COMPONENT_TYPE_ACTIVITY = 2;
-        public static final int COMPONENT_TYPE_SERVICE = 3;
-        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;
-        private int mOnVolumeKeyLongPressListenerUid;
-        private KeyEvent mInitialDownVolumeKeyEvent;
-        private int mInitialDownVolumeStream;
-        private boolean mInitialDownMusicOnly;
-
-        private IOnMediaKeyListener mOnMediaKeyListener;
-        private int mOnMediaKeyListenerUid;
-        private ICallback mCallback;
-
-        public FullUserRecord(int fullUserId) {
-            mFullUserId = fullUserId;
-            mPriorityStack = new MediaSessionStack(mAudioPlayerStateMonitor, this);
-            // Restore the remembered media button receiver before the boot.
-            String mediaButtonReceiverInfo = Settings.Secure.getStringForUser(mContentResolver,
-                    Settings.System.MEDIA_BUTTON_RECEIVER, mFullUserId);
-            if (mediaButtonReceiverInfo == null) {
-                return;
-            }
-            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) {
-            List<MediaSessionRecord> sessions = mPriorityStack.getPriorityList(false, userId);
-            for (MediaSessionRecord session : sessions) {
-                MediaSessionService.this.destroySessionLocked(session);
-            }
-        }
-
-        public void dumpLocked(PrintWriter pw, String prefix) {
-            pw.print(prefix + "Record for full_user=" + mFullUserId);
-            // Dump managed profile user ids associated with this user.
-            int size = mFullUserIds.size();
-            for (int i = 0; i < size; i++) {
-                if (mFullUserIds.keyAt(i) != mFullUserIds.valueAt(i)
-                        && mFullUserIds.valueAt(i) == mFullUserId) {
-                    pw.print(", profile_user=" + mFullUserIds.keyAt(i));
-                }
-            }
-            pw.println();
-            String indent = prefix + "  ";
-            pw.println(indent + "Volume key long-press listener: " + mOnVolumeKeyLongPressListener);
-            pw.println(indent + "Volume key long-press listener package: " +
-                    getCallingPackageName(mOnVolumeKeyLongPressListenerUid));
-            pw.println(indent + "Media key listener: " + mOnMediaKeyListener);
-            pw.println(indent + "Media key listener package: " +
-                    getCallingPackageName(mOnMediaKeyListenerUid));
-            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);
-            pw.println(indent + "Session2Tokens:");
-            for (int i = 0; i < mSession2TokensPerUser.size(); i++) {
-                List<Session2Token> list = mSession2TokensPerUser.valueAt(i);
-                if (list == null || list.size() == 0) {
-                    continue;
-                }
-                for (Session2Token token : list) {
-                    pw.println(indent + "  " + token);
-                }
-            }
-        }
-
-        @Override
-        public void onMediaButtonSessionChanged(MediaSessionRecord oldMediaButtonSession,
-                MediaSessionRecord newMediaButtonSession) {
-            if (DEBUG_KEY_EVENT) {
-                Log.d(TAG, "Media button session is changed to " + newMediaButtonSession);
-            }
-            synchronized (mLock) {
-                if (oldMediaButtonSession != null) {
-                    mHandler.postSessionsChanged(oldMediaButtonSession.getUserId());
-                }
-                if (newMediaButtonSession != null) {
-                    rememberMediaButtonReceiverLocked(newMediaButtonSession);
-                    mHandler.postSessionsChanged(newMediaButtonSession.getUserId());
-                }
-                pushAddressedPlayerChangedLocked();
-            }
-        }
-
-        // Remember media button receiver and keep it in the persistent storage.
-        public void rememberMediaButtonReceiverLocked(MediaSessionRecord record) {
-            PendingIntent receiver = record.getMediaButtonReceiver();
-            mLastMediaButtonReceiver = receiver;
-            mRestoredMediaButtonReceiver = null;
-            mRestoredMediaButtonReceiverComponentType = COMPONENT_TYPE_INVALID;
-
-            String mediaButtonReceiverInfo = "";
-            if (receiver != null) {
-                ComponentName component = receiver.getIntent().getComponent();
-                if (component != null
-                        && record.getPackageName().equals(component.getPackageName())) {
-                    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, mediaButtonReceiverInfo,
-                    mFullUserId);
-        }
-
-        private void pushAddressedPlayerChangedLocked() {
-            if (mCallback == null) {
-                return;
-            }
-            try {
-                MediaSessionRecord mediaButtonSession = getMediaButtonSessionLocked();
-                if (mediaButtonSession != null) {
-                    mCallback.onAddressedPlayerChangedToMediaSession(
-                            new MediaSession.Token(mediaButtonSession.getControllerBinder()));
-                } else if (mCurrentFullUserRecord.mLastMediaButtonReceiver != null) {
-                    mCallback.onAddressedPlayerChangedToMediaButtonReceiver(
-                            mCurrentFullUserRecord.mLastMediaButtonReceiver
-                                    .getIntent().getComponent());
-                } else if (mCurrentFullUserRecord.mRestoredMediaButtonReceiver != null) {
-                    mCallback.onAddressedPlayerChangedToMediaButtonReceiver(
-                            mCurrentFullUserRecord.mRestoredMediaButtonReceiver);
-                }
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed to pushAddressedPlayerChangedLocked", e);
-            }
-        }
-
-        private MediaSessionRecord getMediaButtonSessionLocked() {
-            return isGlobalPriorityActiveLocked()
-                    ? mGlobalPrioritySession : mPriorityStack.getMediaButtonSession();
-        }
-
-        private int getComponentType(@Nullable ComponentName componentName) {
-            if (componentName == null) {
-                return COMPONENT_TYPE_INVALID;
-            }
-            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 {
-        private final IActiveSessionsListener mListener;
-        private final ComponentName mComponentName;
-        private final int mUserId;
-        private final int mPid;
-        private final int mUid;
-
-        public SessionsListenerRecord(IActiveSessionsListener listener,
-                ComponentName componentName,
-                int userId, int pid, int uid) {
-            mListener = listener;
-            mComponentName = componentName;
-            mUserId = userId;
-            mPid = pid;
-            mUid = uid;
-        }
-
-        @Override
-        public void binderDied() {
-            synchronized (mLock) {
-                mSessionsListeners.remove(this);
-            }
-        }
-    }
-
-    final class SettingsObserver extends ContentObserver {
-        private final Uri mSecureSettingsUri = Settings.Secure.getUriFor(
-                Settings.Secure.ENABLED_NOTIFICATION_LISTENERS);
-
-        private SettingsObserver() {
-            super(null);
-        }
-
-        private void observe() {
-            mContentResolver.registerContentObserver(mSecureSettingsUri,
-                    false, this, UserHandle.USER_ALL);
-        }
-
-        @Override
-        public void onChange(boolean selfChange, Uri uri) {
-            updateActiveSessionListeners();
-        }
-    }
-
-    class SessionManagerImpl extends ISessionManager.Stub {
-        private static final String EXTRA_WAKELOCK_ACQUIRED =
-                "android.media.AudioService.WAKELOCK_ACQUIRED";
-        private static final int WAKELOCK_RELEASE_ON_FINISHED = 1980; // magic number
-
-        private boolean mVoiceButtonDown = false;
-        private boolean mVoiceButtonHandled = false;
-
-        @Override
-        public ISession createSession(String packageName, SessionCallbackLink cb, String tag,
-                int userId) throws RemoteException {
-            final int pid = Binder.getCallingPid();
-            final int uid = Binder.getCallingUid();
-            final long token = Binder.clearCallingIdentity();
-            try {
-                enforcePackageName(packageName, uid);
-                int resolvedUserId = ActivityManager.handleIncomingUser(pid, uid, userId,
-                        false /* allowAll */, true /* requireFull */, "createSession", packageName);
-                if (cb == null) {
-                    throw new IllegalArgumentException("Controller callback cannot be null");
-                }
-                return createSessionInternal(pid, uid, resolvedUserId, packageName, cb, tag)
-                        .getSessionBinder();
-            } finally {
-                Binder.restoreCallingIdentity(token);
-            }
-        }
-
-        @Override
-        public void notifySession2Created(Session2Token sessionToken) throws RemoteException {
-            final int pid = Binder.getCallingPid();
-            final int uid = Binder.getCallingUid();
-            final long token = Binder.clearCallingIdentity();
-            try {
-                if (DEBUG) {
-                    Log.d(TAG, "Session2 is created " + sessionToken);
-                }
-                if (uid != sessionToken.getUid()) {
-                    throw new SecurityException("Unexpected Session2Token's UID, expected=" + uid
-                            + " but actually=" + sessionToken.getUid());
-                }
-                Controller2Callback callback = new Controller2Callback(sessionToken);
-                // Note: It's safe not to keep controller here because it wouldn't be GC'ed until
-                //       it's closed.
-                // TODO: Keep controller as well for better readability
-                //       because the GC behavior isn't straightforward.
-                MediaController2 controller = new MediaController2(getContext(), sessionToken,
-                        new HandlerExecutor(mHandler), callback);
-            } finally {
-                Binder.restoreCallingIdentity(token);
-            }
-        }
-
-        @Override
-        public List<IBinder> getSessions(ComponentName componentName, int userId) {
-            final int pid = Binder.getCallingPid();
-            final int uid = Binder.getCallingUid();
-            final long token = Binder.clearCallingIdentity();
-
-            try {
-                int resolvedUserId = verifySessionsRequest(componentName, userId, pid, uid);
-                ArrayList<IBinder> binders = new ArrayList<IBinder>();
-                synchronized (mLock) {
-                    List<MediaSessionRecord> records = getActiveSessionsLocked(resolvedUserId);
-                    for (MediaSessionRecord record : records) {
-                        binders.add(record.getControllerBinder().asBinder());
-                    }
-                }
-                return binders;
-            } finally {
-                Binder.restoreCallingIdentity(token);
-            }
-        }
-
-        @Override
-        public List<Session2Token> getSession2Tokens(int userId) {
-            final int pid = Binder.getCallingPid();
-            final int uid = Binder.getCallingUid();
-            final long token = Binder.clearCallingIdentity();
-
-            try {
-                // Check that they can make calls on behalf of the user and
-                // get the final user id
-                int resolvedUserId = ActivityManager.handleIncomingUser(pid, uid, userId,
-                        true /* allowAll */, true /* requireFull */, "getSession2Tokens",
-                        null /* optional packageName */);
-                List<Session2Token> result = new ArrayList<>();
-                synchronized (mLock) {
-                    if (resolvedUserId == UserHandle.USER_ALL) {
-                        for (int i = 0; i < mSession2TokensPerUser.size(); i++) {
-                            result.addAll(mSession2TokensPerUser.valueAt(i));
-                        }
-                    } else {
-                        result.addAll(mSession2TokensPerUser.get(userId));
-                    }
-                }
-                return result;
-            } finally {
-                Binder.restoreCallingIdentity(token);
-            }
-        }
-
-        @Override
-        public void addSessionsListener(IActiveSessionsListener listener,
-                ComponentName componentName, int userId) throws RemoteException {
-            final int pid = Binder.getCallingPid();
-            final int uid = Binder.getCallingUid();
-            final long token = Binder.clearCallingIdentity();
-
-            try {
-                int resolvedUserId = verifySessionsRequest(componentName, userId, pid, uid);
-                synchronized (mLock) {
-                    int index = findIndexOfSessionsListenerLocked(listener);
-                    if (index != -1) {
-                        Log.w(TAG, "ActiveSessionsListener is already added, ignoring");
-                        return;
-                    }
-                    SessionsListenerRecord record = new SessionsListenerRecord(listener,
-                            componentName, resolvedUserId, pid, uid);
-                    try {
-                        listener.asBinder().linkToDeath(record, 0);
-                    } catch (RemoteException e) {
-                        Log.e(TAG, "ActiveSessionsListener is dead, ignoring it", e);
-                        return;
-                    }
-                    mSessionsListeners.add(record);
-                }
-            } finally {
-                Binder.restoreCallingIdentity(token);
-            }
-        }
-
-        @Override
-        public void removeSessionsListener(IActiveSessionsListener listener)
-                throws RemoteException {
-            synchronized (mLock) {
-                int index = findIndexOfSessionsListenerLocked(listener);
-                if (index != -1) {
-                    SessionsListenerRecord record = mSessionsListeners.remove(index);
-                    try {
-                        record.mListener.asBinder().unlinkToDeath(record, 0);
-                    } catch (Exception e) {
-                        // ignore exceptions, the record is being removed
-                    }
-                }
-            }
-        }
-
-        /**
-         * Handles the dispatching of the media button events to one of the
-         * registered listeners, or if there was none, broadcast an
-         * ACTION_MEDIA_BUTTON intent to the rest of the system.
-         *
-         * @param packageName The caller package
-         * @param asSystemService {@code true} if the event sent to the session as if it was come
-         *          from the system service instead of the app process. This helps sessions to
-         *          distinguish between the key injection by the app and key events from the
-         *          hardware devices. Should be used only when the volume key events aren't handled
-         *          by foreground activity. {@code false} otherwise to tell session about the real
-         *          caller.
-         * @param keyEvent a non-null KeyEvent whose key code is one of the
-         *            supported media buttons
-         * @param needWakeLock true if a PARTIAL_WAKE_LOCK needs to be held
-         *            while this key event is dispatched.
-         */
-        @Override
-        public void dispatchMediaKeyEvent(String packageName, boolean asSystemService,
-                KeyEvent keyEvent, boolean needWakeLock) {
-            if (keyEvent == null || !KeyEvent.isMediaSessionKey(keyEvent.getKeyCode())) {
-                Log.w(TAG, "Attempted to dispatch null or non-media key event.");
-                return;
-            }
-
-            final int pid = Binder.getCallingPid();
-            final int uid = Binder.getCallingUid();
-            final long token = Binder.clearCallingIdentity();
-            try {
-                if (DEBUG) {
-                    Log.d(TAG, "dispatchMediaKeyEvent, pkg=" + packageName + " pid=" + pid
-                            + ", uid=" + uid + ", asSystem=" + asSystemService + ", event="
-                            + keyEvent);
-                }
-                if (!isUserSetupComplete()) {
-                    // Global media key handling can have the side-effect of starting new
-                    // activities which is undesirable while setup is in progress.
-                    Slog.i(TAG, "Not dispatching media key event because user "
-                            + "setup is in progress.");
-                    return;
-                }
-
-                synchronized (mLock) {
-                    boolean isGlobalPriorityActive = isGlobalPriorityActiveLocked();
-                    if (isGlobalPriorityActive && uid != Process.SYSTEM_UID) {
-                        // Prevent dispatching key event through reflection while the global
-                        // priority session is active.
-                        Slog.i(TAG, "Only the system can dispatch media key event "
-                                + "to the global priority session.");
-                        return;
-                    }
-                    if (!isGlobalPriorityActive) {
-                        if (mCurrentFullUserRecord.mOnMediaKeyListener != null) {
-                            if (DEBUG_KEY_EVENT) {
-                                Log.d(TAG, "Send " + keyEvent + " to the media key listener");
-                            }
-                            try {
-                                mCurrentFullUserRecord.mOnMediaKeyListener.onMediaKey(keyEvent,
-                                        new MediaKeyListenerResultReceiver(packageName, pid, uid,
-                                                asSystemService, keyEvent, needWakeLock));
-                                return;
-                            } catch (RemoteException e) {
-                                Log.w(TAG, "Failed to send " + keyEvent
-                                        + " to the media key listener");
-                            }
-                        }
-                    }
-                    if (!isGlobalPriorityActive && isVoiceKey(keyEvent.getKeyCode())) {
-                        handleVoiceKeyEventLocked(packageName, pid, uid, asSystemService, keyEvent,
-                                needWakeLock);
-                    } else {
-                        dispatchMediaKeyEventLocked(packageName, pid, uid, asSystemService,
-                                keyEvent, needWakeLock);
-                    }
-                }
-            } finally {
-                Binder.restoreCallingIdentity(token);
-            }
-        }
-
-        @Override
-        public void setCallback(ICallback callback) {
-            final int pid = Binder.getCallingPid();
-            final int uid = Binder.getCallingUid();
-            final long token = Binder.clearCallingIdentity();
-            try {
-                if (!UserHandle.isSameApp(uid, Process.BLUETOOTH_UID)) {
-                    throw new SecurityException("Only Bluetooth service processes can set"
-                            + " Callback");
-                }
-                synchronized (mLock) {
-                    int userId = UserHandle.getUserId(uid);
-                    FullUserRecord user = getFullUserRecordLocked(userId);
-                    if (user == null || user.mFullUserId != userId) {
-                        Log.w(TAG, "Only the full user can set the callback"
-                                + ", userId=" + userId);
-                        return;
-                    }
-                    user.mCallback = callback;
-                    Log.d(TAG, "The callback " + user.mCallback
-                            + " is set by " + getCallingPackageName(uid));
-                    if (user.mCallback == null) {
-                        return;
-                    }
-                    try {
-                        user.mCallback.asBinder().linkToDeath(
-                                new IBinder.DeathRecipient() {
-                                    @Override
-                                    public void binderDied() {
-                                        synchronized (mLock) {
-                                            user.mCallback = null;
-                                        }
-                                    }
-                                }, 0);
-                        user.pushAddressedPlayerChangedLocked();
-                    } catch (RemoteException e) {
-                        Log.w(TAG, "Failed to set callback", e);
-                        user.mCallback = null;
-                    }
-                }
-            } finally {
-                Binder.restoreCallingIdentity(token);
-            }
-        }
-
-        @Override
-        public void setOnVolumeKeyLongPressListener(IOnVolumeKeyLongPressListener listener) {
-            final int pid = Binder.getCallingPid();
-            final int uid = Binder.getCallingUid();
-            final long token = Binder.clearCallingIdentity();
-            try {
-                // Enforce SET_VOLUME_KEY_LONG_PRESS_LISTENER permission.
-                if (getContext().checkPermission(
-                        android.Manifest.permission.SET_VOLUME_KEY_LONG_PRESS_LISTENER, pid, uid)
-                            != PackageManager.PERMISSION_GRANTED) {
-                    throw new SecurityException("Must hold the SET_VOLUME_KEY_LONG_PRESS_LISTENER" +
-                            " permission.");
-                }
-
-                synchronized (mLock) {
-                    int userId = UserHandle.getUserId(uid);
-                    FullUserRecord user = getFullUserRecordLocked(userId);
-                    if (user == null || user.mFullUserId != userId) {
-                        Log.w(TAG, "Only the full user can set the volume key long-press listener"
-                                + ", userId=" + userId);
-                        return;
-                    }
-                    if (user.mOnVolumeKeyLongPressListener != null &&
-                            user.mOnVolumeKeyLongPressListenerUid != uid) {
-                        Log.w(TAG, "The volume key long-press listener cannot be reset"
-                                + " by another app , mOnVolumeKeyLongPressListener="
-                                + user.mOnVolumeKeyLongPressListenerUid
-                                + ", uid=" + uid);
-                        return;
-                    }
-
-                    user.mOnVolumeKeyLongPressListener = listener;
-                    user.mOnVolumeKeyLongPressListenerUid = uid;
-
-                    Log.d(TAG, "The volume key long-press listener "
-                            + listener + " is set by " + getCallingPackageName(uid));
-
-                    if (user.mOnVolumeKeyLongPressListener != null) {
-                        try {
-                            user.mOnVolumeKeyLongPressListener.asBinder().linkToDeath(
-                                    new IBinder.DeathRecipient() {
-                                        @Override
-                                        public void binderDied() {
-                                            synchronized (mLock) {
-                                                user.mOnVolumeKeyLongPressListener = null;
-                                            }
-                                        }
-                                    }, 0);
-                        } catch (RemoteException e) {
-                            Log.w(TAG, "Failed to set death recipient "
-                                    + user.mOnVolumeKeyLongPressListener);
-                            user.mOnVolumeKeyLongPressListener = null;
-                        }
-                    }
-                }
-            } finally {
-                Binder.restoreCallingIdentity(token);
-            }
-        }
-
-        @Override
-        public void setOnMediaKeyListener(IOnMediaKeyListener listener) {
-            final int pid = Binder.getCallingPid();
-            final int uid = Binder.getCallingUid();
-            final long token = Binder.clearCallingIdentity();
-            try {
-                // Enforce SET_MEDIA_KEY_LISTENER permission.
-                if (getContext().checkPermission(
-                        android.Manifest.permission.SET_MEDIA_KEY_LISTENER, pid, uid)
-                            != PackageManager.PERMISSION_GRANTED) {
-                    throw new SecurityException("Must hold the SET_MEDIA_KEY_LISTENER" +
-                            " permission.");
-                }
-
-                synchronized (mLock) {
-                    int userId = UserHandle.getUserId(uid);
-                    FullUserRecord user = getFullUserRecordLocked(userId);
-                    if (user == null || user.mFullUserId != userId) {
-                        Log.w(TAG, "Only the full user can set the media key listener"
-                                + ", userId=" + userId);
-                        return;
-                    }
-                    if (user.mOnMediaKeyListener != null && user.mOnMediaKeyListenerUid != uid) {
-                        Log.w(TAG, "The media key listener cannot be reset by another app. "
-                                + ", mOnMediaKeyListenerUid=" + user.mOnMediaKeyListenerUid
-                                + ", uid=" + uid);
-                        return;
-                    }
-
-                    user.mOnMediaKeyListener = listener;
-                    user.mOnMediaKeyListenerUid = uid;
-
-                    Log.d(TAG, "The media key listener " + user.mOnMediaKeyListener
-                            + " is set by " + getCallingPackageName(uid));
-
-                    if (user.mOnMediaKeyListener != null) {
-                        try {
-                            user.mOnMediaKeyListener.asBinder().linkToDeath(
-                                    new IBinder.DeathRecipient() {
-                                        @Override
-                                        public void binderDied() {
-                                            synchronized (mLock) {
-                                                user.mOnMediaKeyListener = null;
-                                            }
-                                        }
-                                    }, 0);
-                        } catch (RemoteException e) {
-                            Log.w(TAG, "Failed to set death recipient " + user.mOnMediaKeyListener);
-                            user.mOnMediaKeyListener = null;
-                        }
-                    }
-                }
-            } finally {
-                Binder.restoreCallingIdentity(token);
-            }
-        }
-
-        /**
-         * Handles the dispatching of the volume button events to one of the
-         * registered listeners. If there's a volume key long-press listener and
-         * there's no active global priority session, long-pressess will be sent to the
-         * long-press listener instead of adjusting volume.
-         *
-         * @param packageName The caller's package name, obtained by Context#getPackageName()
-         * @param opPackageName The caller's op package name, obtained by Context#getOpPackageName()
-         * @param asSystemService {@code true} if the event sent to the session as if it was come
-         *          from the system service instead of the app process. This helps sessions to
-         *          distinguish between the key injection by the app and key events from the
-         *          hardware devices. Should be used only when the volume key events aren't handled
-         *          by foreground activity. {@code false} otherwise to tell session about the real
-         *          caller.
-         * @param keyEvent a non-null KeyEvent whose key code is one of the
-         *            {@link KeyEvent#KEYCODE_VOLUME_UP},
-         *            {@link KeyEvent#KEYCODE_VOLUME_DOWN},
-         *            or {@link KeyEvent#KEYCODE_VOLUME_MUTE}.
-         * @param stream stream type to adjust volume.
-         * @param musicOnly true if both UI nor haptic feedback aren't needed when adjust volume.
-         */
-        @Override
-        public void dispatchVolumeKeyEvent(String packageName, String opPackageName,
-                boolean asSystemService, KeyEvent keyEvent, int stream, boolean musicOnly) {
-            if (keyEvent == null ||
-                    (keyEvent.getKeyCode() != KeyEvent.KEYCODE_VOLUME_UP
-                             && keyEvent.getKeyCode() != KeyEvent.KEYCODE_VOLUME_DOWN
-                             && keyEvent.getKeyCode() != KeyEvent.KEYCODE_VOLUME_MUTE)) {
-                Log.w(TAG, "Attempted to dispatch null or non-volume key event.");
-                return;
-            }
-
-            final int pid = Binder.getCallingPid();
-            final int uid = Binder.getCallingUid();
-            final long token = Binder.clearCallingIdentity();
-
-            if (DEBUG_KEY_EVENT) {
-                Log.d(TAG, "dispatchVolumeKeyEvent, pkg=" + packageName + ", pid=" + pid + ", uid="
-                        + uid + ", asSystem=" + asSystemService + ", event=" + keyEvent);
-            }
-
-            try {
-                synchronized (mLock) {
-                    if (isGlobalPriorityActiveLocked()
-                            || mCurrentFullUserRecord.mOnVolumeKeyLongPressListener == null) {
-                        dispatchVolumeKeyEventLocked(packageName, opPackageName, pid, uid,
-                                asSystemService, keyEvent, stream, musicOnly);
-                    } else {
-                        // TODO: Consider the case when both volume up and down keys are pressed
-                        //       at the same time.
-                        if (keyEvent.getAction() == KeyEvent.ACTION_DOWN) {
-                            if (keyEvent.getRepeatCount() == 0) {
-                                // Keeps the copy of the KeyEvent because it can be reused.
-                                mCurrentFullUserRecord.mInitialDownVolumeKeyEvent =
-                                        KeyEvent.obtain(keyEvent);
-                                mCurrentFullUserRecord.mInitialDownVolumeStream = stream;
-                                mCurrentFullUserRecord.mInitialDownMusicOnly = musicOnly;
-                                mHandler.sendMessageDelayed(
-                                        mHandler.obtainMessage(
-                                                MessageHandler.MSG_VOLUME_INITIAL_DOWN,
-                                                mCurrentFullUserRecord.mFullUserId, 0),
-                                        mLongPressTimeout);
-                            }
-                            if (keyEvent.getRepeatCount() > 0 || keyEvent.isLongPress()) {
-                                mHandler.removeMessages(MessageHandler.MSG_VOLUME_INITIAL_DOWN);
-                                if (mCurrentFullUserRecord.mInitialDownVolumeKeyEvent != null) {
-                                    dispatchVolumeKeyLongPressLocked(
-                                            mCurrentFullUserRecord.mInitialDownVolumeKeyEvent);
-                                    // Mark that the key is already handled.
-                                    mCurrentFullUserRecord.mInitialDownVolumeKeyEvent = null;
-                                }
-                                dispatchVolumeKeyLongPressLocked(keyEvent);
-                            }
-                        } else { // if up
-                            mHandler.removeMessages(MessageHandler.MSG_VOLUME_INITIAL_DOWN);
-                            if (mCurrentFullUserRecord.mInitialDownVolumeKeyEvent != null
-                                    && mCurrentFullUserRecord.mInitialDownVolumeKeyEvent
-                                            .getDownTime() == keyEvent.getDownTime()) {
-                                // Short-press. Should change volume.
-                                dispatchVolumeKeyEventLocked(packageName, opPackageName, pid, uid,
-                                        asSystemService,
-                                        mCurrentFullUserRecord.mInitialDownVolumeKeyEvent,
-                                        mCurrentFullUserRecord.mInitialDownVolumeStream,
-                                        mCurrentFullUserRecord.mInitialDownMusicOnly);
-                                dispatchVolumeKeyEventLocked(packageName, opPackageName, pid, uid,
-                                        asSystemService, keyEvent, stream, musicOnly);
-                            } else {
-                                dispatchVolumeKeyLongPressLocked(keyEvent);
-                            }
-                        }
-                    }
-                }
-            } finally {
-                Binder.restoreCallingIdentity(token);
-            }
-        }
-
-        private void dispatchVolumeKeyEventLocked(String packageName, String opPackageName, int pid,
-                int uid, boolean asSystemService, KeyEvent keyEvent, int stream,
-                boolean musicOnly) {
-            boolean down = keyEvent.getAction() == KeyEvent.ACTION_DOWN;
-            boolean up = keyEvent.getAction() == KeyEvent.ACTION_UP;
-            int direction = 0;
-            boolean isMute = false;
-            switch (keyEvent.getKeyCode()) {
-                case KeyEvent.KEYCODE_VOLUME_UP:
-                    direction = AudioManager.ADJUST_RAISE;
-                    break;
-                case KeyEvent.KEYCODE_VOLUME_DOWN:
-                    direction = AudioManager.ADJUST_LOWER;
-                    break;
-                case KeyEvent.KEYCODE_VOLUME_MUTE:
-                    isMute = true;
-                    break;
-            }
-            if (down || up) {
-                int flags = AudioManager.FLAG_FROM_KEY;
-                if (musicOnly) {
-                    // This flag is used when the screen is off to only affect active media.
-                    flags |= AudioManager.FLAG_ACTIVE_MEDIA_ONLY;
-                } else {
-                    // These flags are consistent with the home screen
-                    if (up) {
-                        flags |= AudioManager.FLAG_PLAY_SOUND | AudioManager.FLAG_VIBRATE;
-                    } else {
-                        flags |= AudioManager.FLAG_SHOW_UI | AudioManager.FLAG_VIBRATE;
-                    }
-                }
-                if (direction != 0) {
-                    // If this is action up we want to send a beep for non-music events
-                    if (up) {
-                        direction = 0;
-                    }
-                    dispatchAdjustVolumeLocked(packageName, opPackageName, pid, uid,
-                            asSystemService, stream, direction, flags);
-                } else if (isMute) {
-                    if (down && keyEvent.getRepeatCount() == 0) {
-                        dispatchAdjustVolumeLocked(packageName, opPackageName, pid, uid,
-                                asSystemService, stream, AudioManager.ADJUST_TOGGLE_MUTE, flags);
-                    }
-                }
-            }
-        }
-
-        @Override
-        public void dispatchAdjustVolume(String packageName, String opPackageName,
-                int suggestedStream, int delta, int flags) {
-            final int pid = Binder.getCallingPid();
-            final int uid = Binder.getCallingUid();
-            final long token = Binder.clearCallingIdentity();
-            try {
-                synchronized (mLock) {
-                    dispatchAdjustVolumeLocked(packageName, opPackageName, pid, uid, false,
-                            suggestedStream, delta, flags);
-                }
-            } finally {
-                Binder.restoreCallingIdentity(token);
-            }
-        }
-
-        @Override
-        public void setRemoteVolumeController(IRemoteVolumeController rvc) {
-            final int pid = Binder.getCallingPid();
-            final int uid = Binder.getCallingUid();
-            final long token = Binder.clearCallingIdentity();
-            try {
-                enforceSystemUiPermission("listen for volume changes", pid, uid);
-                mRvc = rvc;
-            } finally {
-                Binder.restoreCallingIdentity(token);
-            }
-        }
-
-        @Override
-        public boolean isGlobalPriorityActive() {
-            synchronized (mLock) {
-                return isGlobalPriorityActiveLocked();
-            }
-        }
-
-        @Override
-        public void dump(FileDescriptor fd, final PrintWriter pw, String[] args) {
-            if (!DumpUtils.checkDumpPermission(getContext(), TAG, pw)) return;
-
-            pw.println("MEDIA SESSION SERVICE (dumpsys media_session)");
-            pw.println();
-
-            synchronized (mLock) {
-                pw.println(mSessionsListeners.size() + " sessions listeners.");
-                pw.println("Global priority session is " + mGlobalPrioritySession);
-                if (mGlobalPrioritySession != null) {
-                    mGlobalPrioritySession.dump(pw, "  ");
-                }
-                pw.println("User Records:");
-                int count = mUserRecords.size();
-                for (int i = 0; i < count; i++) {
-                    mUserRecords.valueAt(i).dumpLocked(pw, "");
-                }
-                mAudioPlayerStateMonitor.dump(getContext(), pw, "");
-            }
-        }
-
-        /**
-         * Returns if the controller's package is trusted (i.e. has either MEDIA_CONTENT_CONTROL
-         * permission or an enabled notification listener)
-         *
-         * @param controllerPackageName package name of the controller app
-         * @param controllerPid pid of the controller app
-         * @param controllerUid uid of the controller app
-         */
-        @Override
-        public boolean isTrusted(String controllerPackageName, int controllerPid, int controllerUid)
-                throws RemoteException {
-            final int uid = Binder.getCallingUid();
-            final long token = Binder.clearCallingIdentity();
-            try {
-                // Don't perform sanity check between controllerPackageName and controllerUid.
-                // When an (activity|service) runs on the another apps process by specifying
-                // android:process in the AndroidManifest.xml, then PID and UID would have the
-                // running process' information instead of the (activity|service) that has created
-                // MediaController.
-                // Note that we can use Context#getOpPackageName() instead of
-                // Context#getPackageName() for getting package name that matches with the PID/UID,
-                // but it doesn't tell which package has created the MediaController, so useless.
-                return hasMediaControlPermission(UserHandle.getUserId(uid), controllerPackageName,
-                        controllerPid, controllerUid);
-            } finally {
-                Binder.restoreCallingIdentity(token);
-            }
-        }
-
-        // For MediaSession
-        private int verifySessionsRequest(ComponentName componentName, int userId, final int pid,
-                final int uid) {
-            String packageName = null;
-            if (componentName != null) {
-                // If they gave us a component name verify they own the
-                // package
-                packageName = componentName.getPackageName();
-                enforcePackageName(packageName, uid);
-            }
-            // Check that they can make calls on behalf of the user and
-            // get the final user id
-            int resolvedUserId = ActivityManager.handleIncomingUser(pid, uid, userId,
-                    true /* allowAll */, true /* requireFull */, "getSessions", packageName);
-            // Check if they have the permissions or their component is
-            // enabled for the user they're calling from.
-            enforceMediaPermissions(componentName, pid, uid, resolvedUserId);
-            return resolvedUserId;
-        }
-
-        private boolean hasMediaControlPermission(int resolvedUserId, String packageName,
-                int pid, int uid) throws RemoteException {
-            // Allow API calls from the System UI
-            if (isCurrentVolumeController(pid, uid)) {
-                return true;
-            }
-
-            // Check if it's system server or has MEDIA_CONTENT_CONTROL.
-            // Note that system server doesn't have MEDIA_CONTENT_CONTROL, so we need extra
-            // check here.
-            if (uid == Process.SYSTEM_UID || getContext().checkPermission(
-                    android.Manifest.permission.MEDIA_CONTENT_CONTROL, pid, uid)
-                    == PackageManager.PERMISSION_GRANTED) {
-                return true;
-            } else if (DEBUG) {
-                Log.d(TAG, packageName + " (uid=" + uid + ") hasn't granted MEDIA_CONTENT_CONTROL");
-            }
-
-            // You may not access another user's content as an enabled listener.
-            final int userId = UserHandle.getUserId(uid);
-            if (resolvedUserId != userId) {
-                return false;
-            }
-
-            // TODO(jaewan): (Post-P) Propose NotificationManager#hasEnabledNotificationListener(
-            //               String pkgName) to notification team for optimization
-            final List<ComponentName> enabledNotificationListeners =
-                    mNotificationManager.getEnabledNotificationListeners(userId);
-            if (enabledNotificationListeners != null) {
-                for (int i = 0; i < enabledNotificationListeners.size(); i++) {
-                    if (TextUtils.equals(packageName,
-                            enabledNotificationListeners.get(i).getPackageName())) {
-                        return true;
-                    }
-                }
-            }
-            if (DEBUG) {
-                Log.d(TAG, packageName + " (uid=" + uid + ") doesn't have an enabled "
-                        + "notification listener");
-            }
-            return false;
-        }
-
-        private void dispatchAdjustVolumeLocked(String packageName, String opPackageName, int pid,
-                int uid, boolean asSystemService, int suggestedStream, int direction, int flags) {
-            MediaSessionRecord session = isGlobalPriorityActiveLocked() ? mGlobalPrioritySession
-                    : mCurrentFullUserRecord.mPriorityStack.getDefaultVolumeSession();
-
-            boolean preferSuggestedStream = false;
-            if (isValidLocalStreamType(suggestedStream)
-                    && AudioSystem.isStreamActive(suggestedStream, 0)) {
-                preferSuggestedStream = true;
-            }
-            if (DEBUG_KEY_EVENT) {
-                Log.d(TAG, "Adjusting " + session + " by " + direction + ". flags="
-                        + flags + ", suggestedStream=" + suggestedStream
-                        + ", preferSuggestedStream=" + preferSuggestedStream);
-            }
-            if (session == null || preferSuggestedStream) {
-                if ((flags & AudioManager.FLAG_ACTIVE_MEDIA_ONLY) != 0
-                        && !AudioSystem.isStreamActive(AudioManager.STREAM_MUSIC, 0)) {
-                    if (DEBUG) {
-                        Log.d(TAG, "No active session to adjust, skipping media only volume event");
-                    }
-                    return;
-                }
-
-                // Execute mAudioService.adjustSuggestedStreamVolume() on
-                // handler thread of MediaSessionService.
-                // This will release the MediaSessionService.mLock sooner and avoid
-                // a potential deadlock between MediaSessionService.mLock and
-                // ActivityManagerService lock.
-                mHandler.post(new Runnable() {
-                    @Override
-                    public void run() {
-                        final String callingOpPackageName;
-                        final int callingUid;
-                        if (asSystemService) {
-                            callingOpPackageName = getContext().getOpPackageName();
-                            callingUid = Process.myUid();
-                        } else {
-                            callingOpPackageName = opPackageName;
-                            callingUid = uid;
-                        }
-                        try {
-                            mAudioManagerInternal.adjustSuggestedStreamVolumeForUid(suggestedStream,
-                                    direction, flags, callingOpPackageName, callingUid);
-                        } catch (SecurityException | IllegalArgumentException e) {
-                            Log.e(TAG, "Cannot adjust volume: direction=" + direction
-                                    + ", suggestedStream=" + suggestedStream + ", flags=" + flags
-                                    + ", packageName=" + packageName + ", uid=" + uid
-                                    + ", asSystemService=" + asSystemService, e);
-                        }
-                    }
-                });
-            } else {
-                session.adjustVolume(packageName, opPackageName, pid, uid, null, asSystemService,
-                        direction, flags, true);
-            }
-        }
-
-        private void handleVoiceKeyEventLocked(String packageName, int pid, int uid,
-                boolean asSystemService, KeyEvent keyEvent, boolean needWakeLock) {
-            int action = keyEvent.getAction();
-            boolean isLongPress = (keyEvent.getFlags() & KeyEvent.FLAG_LONG_PRESS) != 0;
-            if (action == KeyEvent.ACTION_DOWN) {
-                if (keyEvent.getRepeatCount() == 0) {
-                    mVoiceButtonDown = true;
-                    mVoiceButtonHandled = false;
-                } else if (mVoiceButtonDown && !mVoiceButtonHandled && isLongPress) {
-                    mVoiceButtonHandled = true;
-                    startVoiceInput(needWakeLock);
-                }
-            } else if (action == KeyEvent.ACTION_UP) {
-                if (mVoiceButtonDown) {
-                    mVoiceButtonDown = false;
-                    if (!mVoiceButtonHandled && !keyEvent.isCanceled()) {
-                        // Resend the down then send this event through
-                        KeyEvent downEvent = KeyEvent.changeAction(keyEvent, KeyEvent.ACTION_DOWN);
-                        dispatchMediaKeyEventLocked(packageName, pid, uid, asSystemService,
-                                downEvent, needWakeLock);
-                        dispatchMediaKeyEventLocked(packageName, pid, uid, asSystemService,
-                                keyEvent, needWakeLock);
-                    }
-                }
-            }
-        }
-
-        private void dispatchMediaKeyEventLocked(String packageName, int pid, int uid,
-                boolean asSystemService, KeyEvent keyEvent, boolean needWakeLock) {
-            MediaSessionRecord session = mCurrentFullUserRecord.getMediaButtonSessionLocked();
-            if (session != null) {
-                if (DEBUG_KEY_EVENT) {
-                    Log.d(TAG, "Sending " + keyEvent + " to " + session);
-                }
-                if (needWakeLock) {
-                    mKeyEventReceiver.aquireWakeLockLocked();
-                }
-                // If we don't need a wakelock use -1 as the id so we won't release it later.
-                session.sendMediaButton(packageName, pid, uid, asSystemService, keyEvent,
-                        needWakeLock ? mKeyEventReceiver.mLastTimeoutId : -1,
-                        mKeyEventReceiver);
-                if (mCurrentFullUserRecord.mCallback != null) {
-                    try {
-                        mCurrentFullUserRecord.mCallback.onMediaKeyEventDispatchedToMediaSession(
-                                keyEvent, new MediaSession.Token(session.getControllerBinder()));
-                    } catch (RemoteException e) {
-                        Log.w(TAG, "Failed to send callback", e);
-                    }
-                }
-            } else if (mCurrentFullUserRecord.mLastMediaButtonReceiver != null
-                    || mCurrentFullUserRecord.mRestoredMediaButtonReceiver != null) {
-                if (needWakeLock) {
-                    mKeyEventReceiver.aquireWakeLockLocked();
-                }
-                Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
-                mediaButtonIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
-                mediaButtonIntent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);
-                // TODO: Find a way to also send PID/UID in secure way.
-                String callerPackageName =
-                        (asSystemService) ? getContext().getPackageName() : packageName;
-                mediaButtonIntent.putExtra(Intent.EXTRA_PACKAGE_NAME, callerPackageName);
-                try {
-                    if (mCurrentFullUserRecord.mLastMediaButtonReceiver != null) {
-                        PendingIntent receiver = mCurrentFullUserRecord.mLastMediaButtonReceiver;
-                        if (DEBUG_KEY_EVENT) {
-                            Log.d(TAG, "Sending " + keyEvent
-                                    + " to the last known PendingIntent " + receiver);
-                        }
-                        receiver.send(getContext(),
-                                needWakeLock ? mKeyEventReceiver.mLastTimeoutId : -1,
-                                mediaButtonIntent, mKeyEventReceiver, mHandler);
-                        if (mCurrentFullUserRecord.mCallback != null) {
-                            ComponentName componentName = mCurrentFullUserRecord
-                                    .mLastMediaButtonReceiver.getIntent().getComponent();
-                            if (componentName != null) {
-                                mCurrentFullUserRecord.mCallback
-                                        .onMediaKeyEventDispatchedToMediaButtonReceiver(
-                                                keyEvent, componentName);
-                            }
-                        }
-                    } 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 + ", type=" + componentType);
-                        }
-                        mediaButtonIntent.setComponent(receiver);
-                        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(
-                                            keyEvent, receiver);
-                        }
-                    }
-                } catch (CanceledException e) {
-                    Log.i(TAG, "Error sending key event to media button receiver "
-                            + mCurrentFullUserRecord.mLastMediaButtonReceiver, e);
-                } catch (RemoteException e) {
-                    Log.w(TAG, "Failed to send callback", e);
-                }
-            }
-        }
-
-        private void startVoiceInput(boolean needWakeLock) {
-            Intent voiceIntent = null;
-            // select which type of search to launch:
-            // - screen on and device unlocked: action is ACTION_WEB_SEARCH
-            // - device locked or screen off: action is
-            // ACTION_VOICE_SEARCH_HANDS_FREE
-            // with EXTRA_SECURE set to true if the device is securely locked
-            PowerManager pm = (PowerManager) getContext().getSystemService(Context.POWER_SERVICE);
-            boolean isLocked = mKeyguardManager != null && mKeyguardManager.isKeyguardLocked();
-            if (!isLocked && pm.isScreenOn()) {
-                voiceIntent = new Intent(android.speech.RecognizerIntent.ACTION_WEB_SEARCH);
-                Log.i(TAG, "voice-based interactions: about to use ACTION_WEB_SEARCH");
-            } else {
-                voiceIntent = new Intent(RecognizerIntent.ACTION_VOICE_SEARCH_HANDS_FREE);
-                voiceIntent.putExtra(RecognizerIntent.EXTRA_SECURE,
-                        isLocked && mKeyguardManager.isKeyguardSecure());
-                Log.i(TAG, "voice-based interactions: about to use ACTION_VOICE_SEARCH_HANDS_FREE");
-            }
-            // start the search activity
-            if (needWakeLock) {
-                mMediaEventWakeLock.acquire();
-            }
-            try {
-                if (voiceIntent != null) {
-                    voiceIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
-                            | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
-                    if (DEBUG) Log.d(TAG, "voiceIntent: " + voiceIntent);
-                    getContext().startActivityAsUser(voiceIntent, UserHandle.CURRENT);
-                }
-            } catch (ActivityNotFoundException e) {
-                Log.w(TAG, "No activity for search: " + e);
-            } finally {
-                if (needWakeLock) {
-                    mMediaEventWakeLock.release();
-                }
-            }
-        }
-
-        private boolean isVoiceKey(int keyCode) {
-            return keyCode == KeyEvent.KEYCODE_HEADSETHOOK
-                    || (!mHasFeatureLeanback && keyCode == KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE);
-        }
-
-        private boolean isUserSetupComplete() {
-            return Settings.Secure.getIntForUser(getContext().getContentResolver(),
-                    Settings.Secure.USER_SETUP_COMPLETE, 0, UserHandle.USER_CURRENT) != 0;
-        }
-
-        // we only handle public stream types, which are 0-5
-        private boolean isValidLocalStreamType(int streamType) {
-            return streamType >= AudioManager.STREAM_VOICE_CALL
-                    && streamType <= AudioManager.STREAM_NOTIFICATION;
-        }
-
-        private class MediaKeyListenerResultReceiver extends ResultReceiver implements Runnable {
-            private final String mPackageName;
-            private final int mPid;
-            private final int mUid;
-            private final boolean mAsSystemService;
-            private final KeyEvent mKeyEvent;
-            private final boolean mNeedWakeLock;
-            private boolean mHandled;
-
-            private MediaKeyListenerResultReceiver(String packageName, int pid, int uid,
-                    boolean asSystemService, KeyEvent keyEvent, boolean needWakeLock) {
-                super(mHandler);
-                mHandler.postDelayed(this, MEDIA_KEY_LISTENER_TIMEOUT);
-                mPackageName = packageName;
-                mPid = pid;
-                mUid = uid;
-                mAsSystemService = asSystemService;
-                mKeyEvent = keyEvent;
-                mNeedWakeLock = needWakeLock;
-            }
-
-            @Override
-            public void run() {
-                Log.d(TAG, "The media key listener is timed-out for " + mKeyEvent);
-                dispatchMediaKeyEvent();
-            }
-
-            @Override
-            protected void onReceiveResult(int resultCode, Bundle resultData) {
-                if (resultCode == MediaSessionManager.RESULT_MEDIA_KEY_HANDLED) {
-                    mHandled = true;
-                    mHandler.removeCallbacks(this);
-                    return;
-                }
-                dispatchMediaKeyEvent();
-            }
-
-            private void dispatchMediaKeyEvent() {
-                if (mHandled) {
-                    return;
-                }
-                mHandled = true;
-                mHandler.removeCallbacks(this);
-                synchronized (mLock) {
-                    if (!isGlobalPriorityActiveLocked()
-                            && isVoiceKey(mKeyEvent.getKeyCode())) {
-                        handleVoiceKeyEventLocked(mPackageName, mPid, mUid, mAsSystemService,
-                                mKeyEvent, mNeedWakeLock);
-                    } else {
-                        dispatchMediaKeyEventLocked(mPackageName, mPid, mUid, mAsSystemService,
-                                mKeyEvent, mNeedWakeLock);
-                    }
-                }
-            }
-        }
-
-        private KeyEventWakeLockReceiver mKeyEventReceiver = new KeyEventWakeLockReceiver(mHandler);
-
-        class KeyEventWakeLockReceiver extends ResultReceiver implements Runnable,
-                PendingIntent.OnFinished {
-            private final Handler mHandler;
-            private int mRefCount = 0;
-            private int mLastTimeoutId = 0;
-
-            public KeyEventWakeLockReceiver(Handler handler) {
-                super(handler);
-                mHandler = handler;
-            }
-
-            public void onTimeout() {
-                synchronized (mLock) {
-                    if (mRefCount == 0) {
-                        // We've already released it, so just return
-                        return;
-                    }
-                    mLastTimeoutId++;
-                    mRefCount = 0;
-                    releaseWakeLockLocked();
-                }
-            }
-
-            public void aquireWakeLockLocked() {
-                if (mRefCount == 0) {
-                    mMediaEventWakeLock.acquire();
-                }
-                mRefCount++;
-                mHandler.removeCallbacks(this);
-                mHandler.postDelayed(this, WAKELOCK_TIMEOUT);
-
-            }
-
-            @Override
-            public void run() {
-                onTimeout();
-            }
-
-            @Override
-            protected void onReceiveResult(int resultCode, Bundle resultData) {
-                if (resultCode < mLastTimeoutId) {
-                    // Ignore results from calls that were before the last
-                    // timeout, just in case.
-                    return;
-                } else {
-                    synchronized (mLock) {
-                        if (mRefCount > 0) {
-                            mRefCount--;
-                            if (mRefCount == 0) {
-                                releaseWakeLockLocked();
-                            }
-                        }
-                    }
-                }
-            }
-
-            private void releaseWakeLockLocked() {
-                mMediaEventWakeLock.release();
-                mHandler.removeCallbacks(this);
-            }
-
-            @Override
-            public void onSendFinished(PendingIntent pendingIntent, Intent intent, int resultCode,
-                    String resultData, Bundle resultExtras) {
-                onReceiveResult(resultCode, null);
-            }
-        };
-
-        BroadcastReceiver mKeyEventDone = new BroadcastReceiver() {
-            @Override
-            public void onReceive(Context context, Intent intent) {
-                if (intent == null) {
-                    return;
-                }
-                Bundle extras = intent.getExtras();
-                if (extras == null) {
-                    return;
-                }
-                synchronized (mLock) {
-                    if (extras.containsKey(EXTRA_WAKELOCK_ACQUIRED)
-                            && mMediaEventWakeLock.isHeld()) {
-                        mMediaEventWakeLock.release();
-                    }
-                }
-            }
-        };
-    }
-
-    final class MessageHandler extends Handler {
-        private static final int MSG_SESSIONS_CHANGED = 1;
-        private static final int MSG_VOLUME_INITIAL_DOWN = 2;
-        private final SparseArray<Integer> mIntegerCache = new SparseArray<>();
-
-        @Override
-        public void handleMessage(Message msg) {
-            switch (msg.what) {
-                case MSG_SESSIONS_CHANGED:
-                    pushSessionsChanged((int) msg.obj);
-                    break;
-                case MSG_VOLUME_INITIAL_DOWN:
-                    synchronized (mLock) {
-                        FullUserRecord user = mUserRecords.get((int) msg.arg1);
-                        if (user != null && user.mInitialDownVolumeKeyEvent != null) {
-                            dispatchVolumeKeyLongPressLocked(user.mInitialDownVolumeKeyEvent);
-                            // Mark that the key is already handled.
-                            user.mInitialDownVolumeKeyEvent = null;
-                        }
-                    }
-                    break;
-            }
-        }
-
-        public void postSessionsChanged(int userId) {
-            // Use object instead of the arguments when posting message to remove pending requests.
-            Integer userIdInteger = mIntegerCache.get(userId);
-            if (userIdInteger == null) {
-                userIdInteger = Integer.valueOf(userId);
-                mIntegerCache.put(userId, userIdInteger);
-            }
-            removeMessages(MSG_SESSIONS_CHANGED, userIdInteger);
-            obtainMessage(MSG_SESSIONS_CHANGED, userIdInteger).sendToTarget();
-        }
-    }
-
-    private class Controller2Callback extends MediaController2.ControllerCallback {
-        private final Session2Token mToken;
-
-        Controller2Callback(Session2Token token) {
-            mToken = token;
-        }
-
-        @Override
-        public void onConnected(MediaController2 controller, Session2CommandGroup allowedCommands) {
-            synchronized (mLock) {
-                int userId = UserHandle.getUserId(mToken.getUid());
-                mSession2TokensPerUser.get(userId).add(mToken);
-            }
-        }
-
-        @Override
-        public void onDisconnected(MediaController2 controller) {
-            synchronized (mLock) {
-                int userId = UserHandle.getUserId(mToken.getUid());
-                mSession2TokensPerUser.get(userId).remove(mToken);
-            }
-        }
+    abstract static class ServiceImpl {
+        public abstract void onStart();
+        public abstract void notifyRemoteVolumeChanged(int flags, MediaSessionRecord session);
+        public abstract void onSessionPlaystateChanged(
+                MediaSessionRecord record, int oldState, int newState);
+        public abstract void onSessionPlaybackTypeChanged(MediaSessionRecord record);
+        public abstract void onStartUser(int userId);
+        public abstract void onSwitchUser(int userId);
+        public abstract void monitor();
+        public abstract void onMediaButtonReceiverChanged(MediaSessionRecord record);
+        protected abstract void enforcePhoneStatePermission(int pid, int uid);
+        abstract void updateSession(MediaSessionRecord record);
+        abstract void setGlobalPrioritySession(MediaSessionRecord record);
+        abstract List<Session2Token> getSession2TokensLocked(int userId);
+        abstract void onStopUser(int userId);
+        abstract void sessionDied(MediaSessionRecord session);
+        abstract void destroySession(MediaSessionRecord session);
+        abstract void pushSession2TokensChangedLocked(int userId);
+        abstract Context getContext();
+        abstract IBinder getServiceBinder();
     }
 }
diff --git a/services/core/java/com/android/server/media/MediaSessionServiceImpl.java b/services/core/java/com/android/server/media/MediaSessionServiceImpl.java
new file mode 100644
index 0000000..f374c6d
--- /dev/null
+++ b/services/core/java/com/android/server/media/MediaSessionServiceImpl.java
@@ -0,0 +1,2142 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.media;
+
+import static android.os.UserHandle.USER_ALL;
+
+import android.annotation.Nullable;
+import android.app.ActivityManager;
+import android.app.INotificationManager;
+import android.app.KeyguardManager;
+import android.app.PendingIntent;
+import android.app.PendingIntent.CanceledException;
+import android.content.ActivityNotFoundException;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+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;
+import android.media.AudioManagerInternal;
+import android.media.AudioPlaybackConfiguration;
+import android.media.AudioSystem;
+import android.media.IAudioService;
+import android.media.IRemoteVolumeController;
+import android.media.MediaController2;
+import android.media.Session2CommandGroup;
+import android.media.Session2Token;
+import android.media.session.IActiveSessionsListener;
+import android.media.session.ICallback;
+import android.media.session.IOnMediaKeyListener;
+import android.media.session.IOnVolumeKeyLongPressListener;
+import android.media.session.ISession;
+import android.media.session.ISession2TokensListener;
+import android.media.session.ISessionManager;
+import android.media.session.MediaSession;
+import android.media.session.MediaSessionManager;
+import android.media.session.SessionCallbackLink;
+import android.net.Uri;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.HandlerExecutor;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.PowerManager;
+import android.os.Process;
+import android.os.RemoteException;
+import android.os.ResultReceiver;
+import android.os.ServiceManager;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.provider.Settings;
+import android.speech.RecognizerIntent;
+import android.text.TextUtils;
+import android.util.Log;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.util.SparseIntArray;
+import android.view.KeyEvent;
+import android.view.ViewConfiguration;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.DumpUtils;
+import com.android.server.LocalServices;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * System implementation of MediaSessionManager
+ */
+public class MediaSessionServiceImpl extends MediaSessionService.ServiceImpl {
+    private static final String TAG = "MediaSessionService";
+    static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+    // Leave log for key event always.
+    private static final boolean DEBUG_KEY_EVENT = true;
+
+    private static final int WAKELOCK_TIMEOUT = 5000;
+    private static final int MEDIA_KEY_LISTENER_TIMEOUT = 1000;
+
+    private final Context mContext;
+    private final SessionManagerImpl mSessionManagerImpl;
+    private final MessageHandler mHandler = new MessageHandler();
+    private final PowerManager.WakeLock mMediaEventWakeLock;
+    private final int mLongPressTimeout;
+    private final INotificationManager mNotificationManager;
+    private final Object mLock = new Object();
+    // Keeps the full user id for each user.
+    @GuardedBy("mLock")
+    private final SparseIntArray mFullUserIds = new SparseIntArray();
+    @GuardedBy("mLock")
+    private final SparseArray<FullUserRecord> mUserRecords = new SparseArray<FullUserRecord>();
+    @GuardedBy("mLock")
+    private final ArrayList<SessionsListenerRecord> mSessionsListeners =
+            new ArrayList<SessionsListenerRecord>();
+    // Map user id as index to list of Session2Tokens
+    // TODO: Keep session2 info in MediaSessionStack for prioritizing both session1 and session2 in
+    //       one place.
+    @GuardedBy("mLock")
+    private final SparseArray<List<Session2Token>> mSession2TokensPerUser = new SparseArray<>();
+    @GuardedBy("mLock")
+    private final List<Session2TokensListenerRecord> mSession2TokensListenerRecords =
+            new ArrayList<>();
+
+    private KeyguardManager mKeyguardManager;
+    private IAudioService mAudioService;
+    private AudioManagerInternal mAudioManagerInternal;
+    private ActivityManager mActivityManager;
+    private ContentResolver mContentResolver;
+    private SettingsObserver mSettingsObserver;
+    private boolean mHasFeatureLeanback;
+
+    // The FullUserRecord of the current users. (i.e. The foreground user that isn't a profile)
+    // It's always not null after the MediaSessionService is started.
+    private FullUserRecord mCurrentFullUserRecord;
+    private MediaSessionRecord mGlobalPrioritySession;
+    private AudioPlayerStateMonitor mAudioPlayerStateMonitor;
+
+    // Used to notify system UI when remote volume was changed. TODO find a
+    // better way to handle this.
+    private IRemoteVolumeController mRvc;
+
+    public MediaSessionServiceImpl(Context context) {
+        mContext = context;
+        mSessionManagerImpl = new SessionManagerImpl();
+        PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
+        mMediaEventWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "handleMediaEvent");
+        mLongPressTimeout = ViewConfiguration.getLongPressTimeout();
+        mNotificationManager = INotificationManager.Stub.asInterface(
+                ServiceManager.getService(Context.NOTIFICATION_SERVICE));
+    }
+
+    Context getContext() {
+        return mContext;
+    }
+
+    IBinder getServiceBinder() {
+        return mSessionManagerImpl;
+    }
+
+    @Override
+    public void onStart() {
+        mKeyguardManager = (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);
+        mAudioService = getAudioService();
+        mAudioManagerInternal = LocalServices.getService(AudioManagerInternal.class);
+        mActivityManager =
+                (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
+        mAudioPlayerStateMonitor = AudioPlayerStateMonitor.getInstance();
+        mAudioPlayerStateMonitor.registerListener(
+                (config, isRemoved) -> {
+                    if (isRemoved || !config.isActive() || config.getPlayerType()
+                            == AudioPlaybackConfiguration.PLAYER_TYPE_JAM_SOUNDPOOL) {
+                        return;
+                    }
+                    synchronized (mLock) {
+                        FullUserRecord user = getFullUserRecordLocked(
+                                UserHandle.getUserId(config.getClientUid()));
+                        if (user != null) {
+                            user.mPriorityStack.updateMediaButtonSessionIfNeeded();
+                        }
+                    }
+                }, null /* handler */);
+        mAudioPlayerStateMonitor.registerSelfIntoAudioServiceIfNeeded(mAudioService);
+        mContentResolver = mContext.getContentResolver();
+        mSettingsObserver = new SettingsObserver();
+        mSettingsObserver.observe();
+        mHasFeatureLeanback = mContext.getPackageManager().hasSystemFeature(
+                PackageManager.FEATURE_LEANBACK);
+
+        updateUser();
+    }
+
+    private IAudioService getAudioService() {
+        IBinder b = ServiceManager.getService(Context.AUDIO_SERVICE);
+        return IAudioService.Stub.asInterface(b);
+    }
+
+    private boolean isGlobalPriorityActiveLocked() {
+        return mGlobalPrioritySession != null && mGlobalPrioritySession.isActive();
+    }
+
+    @Override
+    public void updateSession(MediaSessionRecord record) {
+        synchronized (mLock) {
+            FullUserRecord user = getFullUserRecordLocked(record.getUserId());
+            if (user == null) {
+                Log.w(TAG, "Unknown session updated. Ignoring.");
+                return;
+            }
+            if ((record.getFlags() & MediaSession.FLAG_EXCLUSIVE_GLOBAL_PRIORITY) != 0) {
+                if (DEBUG_KEY_EVENT) {
+                    Log.d(TAG, "Global priority session is updated, active=" + record.isActive());
+                }
+                user.pushAddressedPlayerChangedLocked();
+            } else {
+                if (!user.mPriorityStack.contains(record)) {
+                    Log.w(TAG, "Unknown session updated. Ignoring.");
+                    return;
+                }
+                user.mPriorityStack.onSessionStateChange(record);
+            }
+            mHandler.postSessionsChanged(record.getUserId());
+        }
+    }
+
+    @Override
+    public void setGlobalPrioritySession(MediaSessionRecord record) {
+        synchronized (mLock) {
+            FullUserRecord user = getFullUserRecordLocked(record.getUserId());
+            if (mGlobalPrioritySession != record) {
+                Log.d(TAG, "Global priority session is changed from " + mGlobalPrioritySession
+                        + " to " + record);
+                mGlobalPrioritySession = record;
+                if (user != null && user.mPriorityStack.contains(record)) {
+                    // Handle the global priority session separately.
+                    // Otherwise, it can be the media button session regardless of the active state
+                    // because it or other system components might have been the lastly played media
+                    // app.
+                    user.mPriorityStack.removeSession(record);
+                }
+            }
+        }
+    }
+
+    private List<MediaSessionRecord> getActiveSessionsLocked(int userId) {
+        List<MediaSessionRecord> records = new ArrayList<>();
+        if (userId == USER_ALL) {
+            int size = mUserRecords.size();
+            for (int i = 0; i < size; i++) {
+                records.addAll(mUserRecords.valueAt(i).mPriorityStack.getActiveSessions(userId));
+            }
+        } else {
+            FullUserRecord user = getFullUserRecordLocked(userId);
+            if (user == null) {
+                Log.w(TAG, "getSessions failed. Unknown user " + userId);
+                return records;
+            }
+            records.addAll(user.mPriorityStack.getActiveSessions(userId));
+        }
+
+        // Return global priority session at the first whenever it's asked.
+        if (isGlobalPriorityActiveLocked()
+                && (userId == USER_ALL || userId == mGlobalPrioritySession.getUserId())) {
+            records.add(0, mGlobalPrioritySession);
+        }
+        return records;
+    }
+
+    List<Session2Token> getSession2TokensLocked(int userId) {
+        List<Session2Token> list = new ArrayList<>();
+        if (userId == USER_ALL) {
+            for (int i = 0; i < mSession2TokensPerUser.size(); i++) {
+                list.addAll(mSession2TokensPerUser.valueAt(i));
+            }
+        } else {
+            list.addAll(mSession2TokensPerUser.get(userId));
+        }
+        return list;
+    }
+
+    /**
+     * Tells the system UI that volume has changed on an active remote session.
+     */
+    public void notifyRemoteVolumeChanged(int flags, MediaSessionRecord session) {
+        if (mRvc == null || !session.isActive()) {
+            return;
+        }
+        try {
+            mRvc.remoteVolumeChanged(session.getControllerBinder(), flags);
+        } catch (Exception e) {
+            Log.wtf(TAG, "Error sending volume change to system UI.", e);
+        }
+    }
+
+    @Override
+    public void onSessionPlaystateChanged(MediaSessionRecord record, int oldState, int newState) {
+        synchronized (mLock) {
+            FullUserRecord user = getFullUserRecordLocked(record.getUserId());
+            if (user == null || !user.mPriorityStack.contains(record)) {
+                Log.d(TAG, "Unknown session changed playback state. Ignoring.");
+                return;
+            }
+            user.mPriorityStack.onPlaystateChanged(record, oldState, newState);
+        }
+    }
+
+    @Override
+    public void onSessionPlaybackTypeChanged(MediaSessionRecord record) {
+        synchronized (mLock) {
+            FullUserRecord user = getFullUserRecordLocked(record.getUserId());
+            if (user == null || !user.mPriorityStack.contains(record)) {
+                Log.d(TAG, "Unknown session changed playback type. Ignoring.");
+                return;
+            }
+            pushRemoteVolumeUpdateLocked(record.getUserId());
+        }
+    }
+
+    @Override
+    public void onStartUser(int userId) {
+        if (DEBUG) Log.d(TAG, "onStartUser: " + userId);
+        updateUser();
+    }
+
+    @Override
+    public void onSwitchUser(int userId) {
+        if (DEBUG) Log.d(TAG, "onSwitchUser: " + userId);
+        updateUser();
+    }
+
+    // Called when the user with the userId is removed.
+    @Override
+    public void onStopUser(int userId) {
+        if (DEBUG) Log.d(TAG, "onStopUser: " + userId);
+        synchronized (mLock) {
+            // TODO: Also handle removing user in updateUser() because adding/switching user is
+            //       handled in updateUser().
+            FullUserRecord user = getFullUserRecordLocked(userId);
+            if (user != null) {
+                if (user.mFullUserId == userId) {
+                    user.destroySessionsForUserLocked(USER_ALL);
+                    mUserRecords.remove(userId);
+                } else {
+                    user.destroySessionsForUserLocked(userId);
+                }
+            }
+            mSession2TokensPerUser.remove(userId);
+            updateUser();
+        }
+    }
+
+    @Override
+    public void monitor() {
+        synchronized (mLock) {
+            // Check for deadlock
+        }
+    }
+
+    protected void enforcePhoneStatePermission(int pid, int uid) {
+        if (mContext.checkPermission(android.Manifest.permission.MODIFY_PHONE_STATE, pid, uid)
+                != PackageManager.PERMISSION_GRANTED) {
+            throw new SecurityException("Must hold the MODIFY_PHONE_STATE permission.");
+        }
+    }
+
+    void sessionDied(MediaSessionRecord session) {
+        synchronized (mLock) {
+            destroySessionLocked(session);
+        }
+    }
+
+    void destroySession(MediaSessionRecord session) {
+        synchronized (mLock) {
+            destroySessionLocked(session);
+        }
+    }
+
+    private void updateUser() {
+        synchronized (mLock) {
+            UserManager manager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
+            mFullUserIds.clear();
+            List<UserInfo> allUsers = manager.getUsers();
+            if (allUsers != null) {
+                for (UserInfo userInfo : allUsers) {
+                    if (userInfo.isManagedProfile()) {
+                        mFullUserIds.put(userInfo.id, userInfo.profileGroupId);
+                    } else {
+                        mFullUserIds.put(userInfo.id, userInfo.id);
+                        if (mUserRecords.get(userInfo.id) == null) {
+                            mUserRecords.put(userInfo.id, new FullUserRecord(userInfo.id));
+                        }
+                    }
+                    if (mSession2TokensPerUser.get(userInfo.id) == null) {
+                        mSession2TokensPerUser.put(userInfo.id, new ArrayList<>());
+                    }
+                }
+            }
+            // Ensure that the current full user exists.
+            int currentFullUserId = ActivityManager.getCurrentUser();
+            mCurrentFullUserRecord = mUserRecords.get(currentFullUserId);
+            if (mCurrentFullUserRecord == null) {
+                Log.w(TAG, "Cannot find FullUserInfo for the current user " + currentFullUserId);
+                mCurrentFullUserRecord = new FullUserRecord(currentFullUserId);
+                mUserRecords.put(currentFullUserId, mCurrentFullUserRecord);
+                if (mSession2TokensPerUser.get(currentFullUserId) == null) {
+                    mSession2TokensPerUser.put(currentFullUserId, new ArrayList<>());
+                }
+            }
+            mFullUserIds.put(currentFullUserId, currentFullUserId);
+        }
+    }
+
+    private void updateActiveSessionListeners() {
+        synchronized (mLock) {
+            for (int i = mSessionsListeners.size() - 1; i >= 0; i--) {
+                SessionsListenerRecord listener = mSessionsListeners.get(i);
+                try {
+                    enforceMediaPermissions(listener.componentName, listener.pid, listener.uid,
+                            listener.userId);
+                } catch (SecurityException e) {
+                    Log.i(TAG, "ActiveSessionsListener " + listener.componentName
+                            + " is no longer authorized. Disconnecting.");
+                    mSessionsListeners.remove(i);
+                    try {
+                        listener.listener
+                                .onActiveSessionsChanged(new ArrayList<MediaSession.Token>());
+                    } catch (Exception e1) {
+                        // ignore
+                    }
+                }
+            }
+        }
+    }
+
+    /*
+     * When a session is removed several things need to happen.
+     * 1. We need to remove it from the relevant user.
+     * 2. We need to remove it from the priority stack.
+     * 3. We need to remove it from all sessions.
+     * 4. If this is the system priority session we need to clear it.
+     * 5. We need to unlink to death from the cb binder
+     * 6. We need to tell the session to do any final cleanup (onDestroy)
+     */
+    private void destroySessionLocked(MediaSessionRecord session) {
+        if (DEBUG) {
+            Log.d(TAG, "Destroying " + session);
+        }
+        FullUserRecord user = getFullUserRecordLocked(session.getUserId());
+        if (mGlobalPrioritySession == session) {
+            mGlobalPrioritySession = null;
+            if (session.isActive() && user != null) {
+                user.pushAddressedPlayerChangedLocked();
+            }
+        } else {
+            if (user != null) {
+                user.mPriorityStack.removeSession(session);
+            }
+        }
+
+        try {
+            session.getCallback().getBinder().unlinkToDeath(session, 0);
+        } catch (Exception e) {
+            // ignore exceptions while destroying a session.
+        }
+        session.onDestroy();
+        mHandler.postSessionsChanged(session.getUserId());
+    }
+
+    private void enforcePackageName(String packageName, int uid) {
+        if (TextUtils.isEmpty(packageName)) {
+            throw new IllegalArgumentException("packageName may not be empty");
+        }
+        String[] packages = mContext.getPackageManager().getPackagesForUid(uid);
+        final int packageCount = packages.length;
+        for (int i = 0; i < packageCount; i++) {
+            if (packageName.equals(packages[i])) {
+                return;
+            }
+        }
+        throw new IllegalArgumentException("packageName is not owned by the calling process");
+    }
+
+    /**
+     * Checks a caller's authorization to register an IRemoteControlDisplay.
+     * Authorization is granted if one of the following is true:
+     * <ul>
+     * <li>the caller has android.Manifest.permission.MEDIA_CONTENT_CONTROL
+     * permission</li>
+     * <li>the caller's listener is one of the enabled notification listeners
+     * for the caller's user</li>
+     * </ul>
+     */
+    private void enforceMediaPermissions(ComponentName compName, int pid, int uid,
+            int resolvedUserId) {
+        if (isCurrentVolumeController(pid, uid)) return;
+        if (mContext
+                .checkPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL, pid, uid)
+                != PackageManager.PERMISSION_GRANTED
+                && !isEnabledNotificationListener(compName, UserHandle.getUserId(uid),
+                resolvedUserId)) {
+            throw new SecurityException("Missing permission to control media.");
+        }
+    }
+
+    private boolean isCurrentVolumeController(int pid, int uid) {
+        return mContext.checkPermission(android.Manifest.permission.STATUS_BAR_SERVICE,
+                pid, uid) == PackageManager.PERMISSION_GRANTED;
+    }
+
+    private void enforceSystemUiPermission(String action, int pid, int uid) {
+        if (!isCurrentVolumeController(pid, uid)) {
+            throw new SecurityException("Only system ui may " + action);
+        }
+    }
+
+    /**
+     * This checks if the component is an enabled notification listener for the
+     * specified user. Enabled components may only operate on behalf of the user
+     * they're running as.
+     *
+     * @param compName The component that is enabled.
+     * @param userId The user id of the caller.
+     * @param forUserId The user id they're making the request on behalf of.
+     * @return True if the component is enabled, false otherwise
+     */
+    private boolean isEnabledNotificationListener(ComponentName compName, int userId,
+            int forUserId) {
+        if (userId != forUserId) {
+            // You may not access another user's content as an enabled listener.
+            return false;
+        }
+        if (DEBUG) {
+            Log.d(TAG, "Checking if enabled notification listener " + compName);
+        }
+        if (compName != null) {
+            try {
+                return mNotificationManager.isNotificationListenerAccessGrantedForUser(
+                        compName, userId);
+            } catch (RemoteException e) {
+                Log.w(TAG, "Dead NotificationManager in isEnabledNotificationListener", e);
+            }
+        }
+        return false;
+    }
+
+    private MediaSessionRecord createSessionInternal(int callerPid, int callerUid, int userId,
+            String callerPackageName, SessionCallbackLink cb, String tag) throws RemoteException {
+        synchronized (mLock) {
+            return createSessionLocked(callerPid, callerUid, userId, callerPackageName, cb, tag);
+        }
+    }
+
+    /*
+     * When a session is created the following things need to happen.
+     * 1. Its callback binder needs a link to death
+     * 2. It needs to be added to all sessions.
+     * 3. It needs to be added to the priority stack.
+     * 4. It needs to be added to the relevant user record.
+     */
+    private MediaSessionRecord createSessionLocked(int callerPid, int callerUid, int userId,
+            String callerPackageName, SessionCallbackLink cb, String tag) {
+        FullUserRecord user = getFullUserRecordLocked(userId);
+        if (user == null) {
+            Log.wtf(TAG, "Request from invalid user: " +  userId);
+            throw new RuntimeException("Session request from invalid user.");
+        }
+
+        final MediaSessionRecord session = new MediaSessionRecord(callerPid, callerUid, userId,
+                callerPackageName, cb, tag, this, mHandler.getLooper());
+        try {
+            cb.getBinder().linkToDeath(session, 0);
+        } catch (RemoteException e) {
+            throw new RuntimeException("Media Session owner died prematurely.", e);
+        }
+
+        user.mPriorityStack.addSession(session);
+        mHandler.postSessionsChanged(userId);
+
+        if (DEBUG) {
+            Log.d(TAG, "Created session for " + callerPackageName + " with tag " + tag);
+        }
+        return session;
+    }
+
+    private int findIndexOfSessionsListenerLocked(IActiveSessionsListener listener) {
+        for (int i = mSessionsListeners.size() - 1; i >= 0; i--) {
+            if (mSessionsListeners.get(i).listener.asBinder() == listener.asBinder()) {
+                return i;
+            }
+        }
+        return -1;
+    }
+
+    private int findIndexOfSession2TokensListenerLocked(ISession2TokensListener listener) {
+        for (int i = mSession2TokensListenerRecords.size() - 1; i >= 0; i--) {
+            if (mSession2TokensListenerRecords.get(i).listener.asBinder() == listener.asBinder()) {
+                return i;
+            }
+        }
+        return -1;
+    }
+
+
+    private void pushSessionsChanged(int userId) {
+        synchronized (mLock) {
+            FullUserRecord user = getFullUserRecordLocked(userId);
+            if (user == null) {
+                Log.w(TAG, "pushSessionsChanged failed. No user with id=" + userId);
+                return;
+            }
+            List<MediaSessionRecord> records = getActiveSessionsLocked(userId);
+            int size = records.size();
+            ArrayList<MediaSession.Token> tokens = new ArrayList<MediaSession.Token>();
+            for (int i = 0; i < size; i++) {
+                tokens.add(new MediaSession.Token(records.get(i).getControllerBinder()));
+            }
+            pushRemoteVolumeUpdateLocked(userId);
+            for (int i = mSessionsListeners.size() - 1; i >= 0; i--) {
+                SessionsListenerRecord record = mSessionsListeners.get(i);
+                if (record.userId == USER_ALL || record.userId == userId) {
+                    try {
+                        record.listener.onActiveSessionsChanged(tokens);
+                    } catch (RemoteException e) {
+                        Log.w(TAG, "Dead ActiveSessionsListener in pushSessionsChanged, removing",
+                                e);
+                        mSessionsListeners.remove(i);
+                    }
+                }
+            }
+        }
+    }
+
+    private void pushRemoteVolumeUpdateLocked(int userId) {
+        if (mRvc != null) {
+            try {
+                FullUserRecord user = getFullUserRecordLocked(userId);
+                if (user == null) {
+                    Log.w(TAG, "pushRemoteVolumeUpdateLocked failed. No user with id=" + userId);
+                    return;
+                }
+                MediaSessionRecord record = user.mPriorityStack.getDefaultRemoteSession(userId);
+                mRvc.updateRemoteController(record == null ? null : record.getControllerBinder());
+            } catch (RemoteException e) {
+                Log.wtf(TAG, "Error sending default remote volume to sys ui.", e);
+            }
+        }
+    }
+
+    void pushSession2TokensChangedLocked(int userId) {
+        List<Session2Token> allSession2Tokens = getSession2TokensLocked(USER_ALL);
+        List<Session2Token> session2Tokens = getSession2TokensLocked(userId);
+
+        for (int i = mSession2TokensListenerRecords.size() - 1; i >= 0; i--) {
+            Session2TokensListenerRecord listenerRecord = mSession2TokensListenerRecords.get(i);
+            try {
+                if (listenerRecord.userId == USER_ALL) {
+                    listenerRecord.listener.onSession2TokensChanged(allSession2Tokens);
+                } else if (listenerRecord.userId == userId) {
+                    listenerRecord.listener.onSession2TokensChanged(session2Tokens);
+                }
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failed to notify Session2Token change. Removing listener.", e);
+                mSession2TokensListenerRecords.remove(i);
+            }
+        }
+    }
+
+    /**
+     * Called when the media button receiver for the {@code record} is changed.
+     *
+     * @param record the media session whose media button receiver is updated.
+     */
+    public void onMediaButtonReceiverChanged(MediaSessionRecord record) {
+        synchronized (mLock) {
+            FullUserRecord user = getFullUserRecordLocked(record.getUserId());
+            MediaSessionRecord mediaButtonSession =
+                    user.mPriorityStack.getMediaButtonSession();
+            if (record == mediaButtonSession) {
+                user.rememberMediaButtonReceiverLocked(mediaButtonSession);
+            }
+        }
+    }
+
+    private String getCallingPackageName(int uid) {
+        String[] packages = mContext.getPackageManager().getPackagesForUid(uid);
+        if (packages != null && packages.length > 0) {
+            return packages[0];
+        }
+        return "";
+    }
+
+    private void dispatchVolumeKeyLongPressLocked(KeyEvent keyEvent) {
+        if (mCurrentFullUserRecord.mOnVolumeKeyLongPressListener == null) {
+            return;
+        }
+        try {
+            mCurrentFullUserRecord.mOnVolumeKeyLongPressListener.onVolumeKeyLongPress(keyEvent);
+        } catch (RemoteException e) {
+            Log.w(TAG, "Failed to send " + keyEvent + " to volume key long-press listener");
+        }
+    }
+
+    private FullUserRecord getFullUserRecordLocked(int userId) {
+        int fullUserId = mFullUserIds.get(userId, -1);
+        if (fullUserId < 0) {
+            return null;
+        }
+        return mUserRecords.get(fullUserId);
+    }
+
+    /**
+     * Information about a full user and its corresponding managed profiles.
+     *
+     * <p>Since the full user runs together with its managed profiles, a user wouldn't differentiate
+     * them when he/she presses a media/volume button. So keeping media sessions for them in one
+     * place makes more sense and increases the readability.</p>
+     * <p>The contents of this object is guarded by {@link #mLock}.
+     */
+    final class FullUserRecord implements MediaSessionStack.OnMediaButtonSessionChangedListener {
+        public static final int COMPONENT_TYPE_INVALID = 0;
+        public static final int COMPONENT_TYPE_BROADCAST = 1;
+        public static final int COMPONENT_TYPE_ACTIVITY = 2;
+        public static final int COMPONENT_TYPE_SERVICE = 3;
+        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;
+        private int mOnVolumeKeyLongPressListenerUid;
+        private KeyEvent mInitialDownVolumeKeyEvent;
+        private int mInitialDownVolumeStream;
+        private boolean mInitialDownMusicOnly;
+
+        private IOnMediaKeyListener mOnMediaKeyListener;
+        private int mOnMediaKeyListenerUid;
+        private ICallback mCallback;
+
+        FullUserRecord(int fullUserId) {
+            mFullUserId = fullUserId;
+            mPriorityStack = new MediaSessionStack(mAudioPlayerStateMonitor, this);
+            // Restore the remembered media button receiver before the boot.
+            String mediaButtonReceiverInfo = Settings.Secure.getStringForUser(mContentResolver,
+                    Settings.System.MEDIA_BUTTON_RECEIVER, mFullUserId);
+            if (mediaButtonReceiverInfo == null) {
+                return;
+            }
+            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) {
+            List<MediaSessionRecord> sessions = mPriorityStack.getPriorityList(false, userId);
+            for (MediaSessionRecord session : sessions) {
+                MediaSessionServiceImpl.this.destroySessionLocked(session);
+            }
+        }
+
+        public void dumpLocked(PrintWriter pw, String prefix) {
+            pw.print(prefix + "Record for full_user=" + mFullUserId);
+            // Dump managed profile user ids associated with this user.
+            int size = mFullUserIds.size();
+            for (int i = 0; i < size; i++) {
+                if (mFullUserIds.keyAt(i) != mFullUserIds.valueAt(i)
+                        && mFullUserIds.valueAt(i) == mFullUserId) {
+                    pw.print(", profile_user=" + mFullUserIds.keyAt(i));
+                }
+            }
+            pw.println();
+            String indent = prefix + "  ";
+            pw.println(indent + "Volume key long-press listener: " + mOnVolumeKeyLongPressListener);
+            pw.println(indent + "Volume key long-press listener package: "
+                    + getCallingPackageName(mOnVolumeKeyLongPressListenerUid));
+            pw.println(indent + "Media key listener: " + mOnMediaKeyListener);
+            pw.println(indent + "Media key listener package: "
+                    + getCallingPackageName(mOnMediaKeyListenerUid));
+            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);
+            pw.println(indent + "Session2Tokens:");
+            for (int i = 0; i < mSession2TokensPerUser.size(); i++) {
+                List<Session2Token> list = mSession2TokensPerUser.valueAt(i);
+                if (list == null || list.size() == 0) {
+                    continue;
+                }
+                for (Session2Token token : list) {
+                    pw.println(indent + "  " + token);
+                }
+            }
+        }
+
+        @Override
+        public void onMediaButtonSessionChanged(MediaSessionRecord oldMediaButtonSession,
+                MediaSessionRecord newMediaButtonSession) {
+            if (DEBUG_KEY_EVENT) {
+                Log.d(TAG, "Media button session is changed to " + newMediaButtonSession);
+            }
+            synchronized (mLock) {
+                if (oldMediaButtonSession != null) {
+                    mHandler.postSessionsChanged(oldMediaButtonSession.getUserId());
+                }
+                if (newMediaButtonSession != null) {
+                    rememberMediaButtonReceiverLocked(newMediaButtonSession);
+                    mHandler.postSessionsChanged(newMediaButtonSession.getUserId());
+                }
+                pushAddressedPlayerChangedLocked();
+            }
+        }
+
+        // Remember media button receiver and keep it in the persistent storage.
+        public void rememberMediaButtonReceiverLocked(MediaSessionRecord record) {
+            PendingIntent receiver = record.getMediaButtonReceiver();
+            mLastMediaButtonReceiver = receiver;
+            mRestoredMediaButtonReceiver = null;
+            mRestoredMediaButtonReceiverComponentType = COMPONENT_TYPE_INVALID;
+
+            String mediaButtonReceiverInfo = "";
+            if (receiver != null) {
+                ComponentName component = receiver.getIntent().getComponent();
+                if (component != null
+                        && record.getPackageName().equals(component.getPackageName())) {
+                    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, mediaButtonReceiverInfo,
+                    mFullUserId);
+        }
+
+        private void pushAddressedPlayerChangedLocked() {
+            if (mCallback == null) {
+                return;
+            }
+            try {
+                MediaSessionRecord mediaButtonSession = getMediaButtonSessionLocked();
+                if (mediaButtonSession != null) {
+                    mCallback.onAddressedPlayerChangedToMediaSession(
+                            new MediaSession.Token(mediaButtonSession.getControllerBinder()));
+                } else if (mCurrentFullUserRecord.mLastMediaButtonReceiver != null) {
+                    mCallback.onAddressedPlayerChangedToMediaButtonReceiver(
+                            mCurrentFullUserRecord.mLastMediaButtonReceiver
+                                    .getIntent().getComponent());
+                } else if (mCurrentFullUserRecord.mRestoredMediaButtonReceiver != null) {
+                    mCallback.onAddressedPlayerChangedToMediaButtonReceiver(
+                            mCurrentFullUserRecord.mRestoredMediaButtonReceiver);
+                }
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failed to pushAddressedPlayerChangedLocked", e);
+            }
+        }
+
+        private MediaSessionRecord getMediaButtonSessionLocked() {
+            return isGlobalPriorityActiveLocked()
+                    ? mGlobalPrioritySession : mPriorityStack.getMediaButtonSession();
+        }
+
+        private int getComponentType(@Nullable ComponentName componentName) {
+            if (componentName == null) {
+                return COMPONENT_TYPE_INVALID;
+            }
+            PackageManager pm = mContext.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 {
+        public final IActiveSessionsListener listener;
+        public final ComponentName componentName;
+        public final int userId;
+        public final int pid;
+        public final int uid;
+
+        SessionsListenerRecord(IActiveSessionsListener listener,
+                ComponentName componentName,
+                int userId, int pid, int uid) {
+            this.listener = listener;
+            this.componentName = componentName;
+            this.userId = userId;
+            this.pid = pid;
+            this.uid = uid;
+        }
+
+        @Override
+        public void binderDied() {
+            synchronized (mLock) {
+                mSessionsListeners.remove(this);
+            }
+        }
+    }
+
+    final class Session2TokensListenerRecord implements IBinder.DeathRecipient {
+        public final ISession2TokensListener listener;
+        public final int userId;
+
+        Session2TokensListenerRecord(ISession2TokensListener listener,
+                int userId) {
+            this.listener = listener;
+            this.userId = userId;
+        }
+
+        @Override
+        public void binderDied() {
+            synchronized (mLock) {
+                mSession2TokensListenerRecords.remove(this);
+            }
+        }
+    }
+
+    final class SettingsObserver extends ContentObserver {
+        private final Uri mSecureSettingsUri = Settings.Secure.getUriFor(
+                Settings.Secure.ENABLED_NOTIFICATION_LISTENERS);
+
+        private SettingsObserver() {
+            super(null);
+        }
+
+        private void observe() {
+            mContentResolver.registerContentObserver(mSecureSettingsUri,
+                    false, this, USER_ALL);
+        }
+
+        @Override
+        public void onChange(boolean selfChange, Uri uri) {
+            updateActiveSessionListeners();
+        }
+    }
+
+    class SessionManagerImpl extends ISessionManager.Stub {
+        private static final String EXTRA_WAKELOCK_ACQUIRED =
+                "android.media.AudioService.WAKELOCK_ACQUIRED";
+        private static final int WAKELOCK_RELEASE_ON_FINISHED = 1980; // magic number
+
+        private boolean mVoiceButtonDown = false;
+        private boolean mVoiceButtonHandled = false;
+
+        @Override
+        public ISession createSession(String packageName, SessionCallbackLink cb, String tag,
+                int userId) throws RemoteException {
+            final int pid = Binder.getCallingPid();
+            final int uid = Binder.getCallingUid();
+            final long token = Binder.clearCallingIdentity();
+            try {
+                enforcePackageName(packageName, uid);
+                int resolvedUserId = ActivityManager.handleIncomingUser(pid, uid, userId,
+                        false /* allowAll */, true /* requireFull */, "createSession", packageName);
+                if (cb == null) {
+                    throw new IllegalArgumentException("Controller callback cannot be null");
+                }
+                return createSessionInternal(pid, uid, resolvedUserId, packageName, cb, tag)
+                        .getSessionBinder();
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        @Override
+        public void notifySession2Created(Session2Token sessionToken) throws RemoteException {
+            final int pid = Binder.getCallingPid();
+            final int uid = Binder.getCallingUid();
+            final long token = Binder.clearCallingIdentity();
+            try {
+                if (DEBUG) {
+                    Log.d(TAG, "Session2 is created " + sessionToken);
+                }
+                if (uid != sessionToken.getUid()) {
+                    throw new SecurityException("Unexpected Session2Token's UID, expected=" + uid
+                            + " but actually=" + sessionToken.getUid());
+                }
+                Controller2Callback callback = new Controller2Callback(sessionToken);
+                // Note: It's safe not to keep controller here because it wouldn't be GC'ed until
+                //       it's closed.
+                // TODO: Keep controller as well for better readability
+                //       because the GC behavior isn't straightforward.
+                MediaController2 controller = new MediaController2(mContext, sessionToken,
+                        new HandlerExecutor(mHandler), callback);
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        @Override
+        public List<IBinder> getSessions(ComponentName componentName, int userId) {
+            final int pid = Binder.getCallingPid();
+            final int uid = Binder.getCallingUid();
+            final long token = Binder.clearCallingIdentity();
+
+            try {
+                int resolvedUserId = verifySessionsRequest(componentName, userId, pid, uid);
+                ArrayList<IBinder> binders = new ArrayList<IBinder>();
+                synchronized (mLock) {
+                    List<MediaSessionRecord> records = getActiveSessionsLocked(resolvedUserId);
+                    for (MediaSessionRecord record : records) {
+                        binders.add(record.getControllerBinder().asBinder());
+                    }
+                }
+                return binders;
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        @Override
+        public List<Session2Token> getSession2Tokens(int userId) {
+            final int pid = Binder.getCallingPid();
+            final int uid = Binder.getCallingUid();
+            final long token = Binder.clearCallingIdentity();
+
+            try {
+                // Check that they can make calls on behalf of the user and
+                // get the final user id
+                int resolvedUserId = ActivityManager.handleIncomingUser(pid, uid, userId,
+                        true /* allowAll */, true /* requireFull */, "getSession2Tokens",
+                        null /* optional packageName */);
+                List<Session2Token> result;
+                synchronized (mLock) {
+                    result = getSession2TokensLocked(resolvedUserId);
+                }
+                return result;
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        @Override
+        public void addSessionsListener(IActiveSessionsListener listener,
+                ComponentName componentName, int userId) throws RemoteException {
+            final int pid = Binder.getCallingPid();
+            final int uid = Binder.getCallingUid();
+            final long token = Binder.clearCallingIdentity();
+
+            try {
+                int resolvedUserId = verifySessionsRequest(componentName, userId, pid, uid);
+                synchronized (mLock) {
+                    int index = findIndexOfSessionsListenerLocked(listener);
+                    if (index != -1) {
+                        Log.w(TAG, "ActiveSessionsListener is already added, ignoring");
+                        return;
+                    }
+                    SessionsListenerRecord record = new SessionsListenerRecord(listener,
+                            componentName, resolvedUserId, pid, uid);
+                    try {
+                        listener.asBinder().linkToDeath(record, 0);
+                    } catch (RemoteException e) {
+                        Log.e(TAG, "ActiveSessionsListener is dead, ignoring it", e);
+                        return;
+                    }
+                    mSessionsListeners.add(record);
+                }
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        @Override
+        public void removeSessionsListener(IActiveSessionsListener listener)
+                throws RemoteException {
+            synchronized (mLock) {
+                int index = findIndexOfSessionsListenerLocked(listener);
+                if (index != -1) {
+                    SessionsListenerRecord record = mSessionsListeners.remove(index);
+                    try {
+                        record.listener.asBinder().unlinkToDeath(record, 0);
+                    } catch (Exception e) {
+                        // ignore exceptions, the record is being removed
+                    }
+                }
+            }
+        }
+
+        @Override
+        public void addSession2TokensListener(ISession2TokensListener listener,
+                int userId) {
+            final int pid = Binder.getCallingPid();
+            final int uid = Binder.getCallingUid();
+            final long token = Binder.clearCallingIdentity();
+
+            try {
+                // Check that they can make calls on behalf of the user and get the final user id.
+                int resolvedUserId = ActivityManager.handleIncomingUser(pid, uid, userId,
+                        true /* allowAll */, true /* requireFull */, "addSession2TokensListener",
+                        null /* optional packageName */);
+                synchronized (mLock) {
+                    int index = findIndexOfSession2TokensListenerLocked(listener);
+                    if (index >= 0) {
+                        Log.w(TAG, "addSession2TokensListener is already added, ignoring");
+                        return;
+                    }
+                    mSession2TokensListenerRecords.add(
+                            new Session2TokensListenerRecord(listener, resolvedUserId));
+                }
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        @Override
+        public void removeSession2TokensListener(ISession2TokensListener listener) {
+            final int pid = Binder.getCallingPid();
+            final int uid = Binder.getCallingUid();
+            final long token = Binder.clearCallingIdentity();
+
+            try {
+                synchronized (mLock) {
+                    int index = findIndexOfSession2TokensListenerLocked(listener);
+                    if (index >= 0) {
+                        Session2TokensListenerRecord listenerRecord =
+                                mSession2TokensListenerRecords.remove(index);
+                        try {
+                            listenerRecord.listener.asBinder().unlinkToDeath(listenerRecord, 0);
+                        } catch (Exception e) {
+                            // Ignore exception.
+                        }
+                    }
+                }
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        /**
+         * Handles the dispatching of the media button events to one of the
+         * registered listeners, or if there was none, broadcast an
+         * ACTION_MEDIA_BUTTON intent to the rest of the system.
+         *
+         * @param packageName The caller package
+         * @param asSystemService {@code true} if the event sent to the session as if it was come
+         *          from the system service instead of the app process. This helps sessions to
+         *          distinguish between the key injection by the app and key events from the
+         *          hardware devices. Should be used only when the volume key events aren't handled
+         *          by foreground activity. {@code false} otherwise to tell session about the real
+         *          caller.
+         * @param keyEvent a non-null KeyEvent whose key code is one of the
+         *            supported media buttons
+         * @param needWakeLock true if a PARTIAL_WAKE_LOCK needs to be held
+         *            while this key event is dispatched.
+         */
+        @Override
+        public void dispatchMediaKeyEvent(String packageName, boolean asSystemService,
+                KeyEvent keyEvent, boolean needWakeLock) {
+            if (keyEvent == null || !KeyEvent.isMediaSessionKey(keyEvent.getKeyCode())) {
+                Log.w(TAG, "Attempted to dispatch null or non-media key event.");
+                return;
+            }
+
+            final int pid = Binder.getCallingPid();
+            final int uid = Binder.getCallingUid();
+            final long token = Binder.clearCallingIdentity();
+            try {
+                if (DEBUG) {
+                    Log.d(TAG, "dispatchMediaKeyEvent, pkg=" + packageName + " pid=" + pid
+                            + ", uid=" + uid + ", asSystem=" + asSystemService + ", event="
+                            + keyEvent);
+                }
+                if (!isUserSetupComplete()) {
+                    // Global media key handling can have the side-effect of starting new
+                    // activities which is undesirable while setup is in progress.
+                    Slog.i(TAG, "Not dispatching media key event because user "
+                            + "setup is in progress.");
+                    return;
+                }
+
+                synchronized (mLock) {
+                    boolean isGlobalPriorityActive = isGlobalPriorityActiveLocked();
+                    if (isGlobalPriorityActive && uid != Process.SYSTEM_UID) {
+                        // Prevent dispatching key event through reflection while the global
+                        // priority session is active.
+                        Slog.i(TAG, "Only the system can dispatch media key event "
+                                + "to the global priority session.");
+                        return;
+                    }
+                    if (!isGlobalPriorityActive) {
+                        if (mCurrentFullUserRecord.mOnMediaKeyListener != null) {
+                            if (DEBUG_KEY_EVENT) {
+                                Log.d(TAG, "Send " + keyEvent + " to the media key listener");
+                            }
+                            try {
+                                mCurrentFullUserRecord.mOnMediaKeyListener.onMediaKey(keyEvent,
+                                        new MediaKeyListenerResultReceiver(packageName, pid, uid,
+                                                asSystemService, keyEvent, needWakeLock));
+                                return;
+                            } catch (RemoteException e) {
+                                Log.w(TAG, "Failed to send " + keyEvent
+                                        + " to the media key listener");
+                            }
+                        }
+                    }
+                    if (!isGlobalPriorityActive && isVoiceKey(keyEvent.getKeyCode())) {
+                        handleVoiceKeyEventLocked(packageName, pid, uid, asSystemService, keyEvent,
+                                needWakeLock);
+                    } else {
+                        dispatchMediaKeyEventLocked(packageName, pid, uid, asSystemService,
+                                keyEvent, needWakeLock);
+                    }
+                }
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        @Override
+        public void setCallback(ICallback callback) {
+            final int pid = Binder.getCallingPid();
+            final int uid = Binder.getCallingUid();
+            final long token = Binder.clearCallingIdentity();
+            try {
+                if (!UserHandle.isSameApp(uid, Process.BLUETOOTH_UID)) {
+                    throw new SecurityException("Only Bluetooth service processes can set"
+                            + " Callback");
+                }
+                synchronized (mLock) {
+                    int userId = UserHandle.getUserId(uid);
+                    FullUserRecord user = getFullUserRecordLocked(userId);
+                    if (user == null || user.mFullUserId != userId) {
+                        Log.w(TAG, "Only the full user can set the callback"
+                                + ", userId=" + userId);
+                        return;
+                    }
+                    user.mCallback = callback;
+                    Log.d(TAG, "The callback " + user.mCallback
+                            + " is set by " + getCallingPackageName(uid));
+                    if (user.mCallback == null) {
+                        return;
+                    }
+                    try {
+                        user.mCallback.asBinder().linkToDeath(
+                                new IBinder.DeathRecipient() {
+                                    @Override
+                                    public void binderDied() {
+                                        synchronized (mLock) {
+                                            user.mCallback = null;
+                                        }
+                                    }
+                                }, 0);
+                        user.pushAddressedPlayerChangedLocked();
+                    } catch (RemoteException e) {
+                        Log.w(TAG, "Failed to set callback", e);
+                        user.mCallback = null;
+                    }
+                }
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        @Override
+        public void setOnVolumeKeyLongPressListener(IOnVolumeKeyLongPressListener listener) {
+            final int pid = Binder.getCallingPid();
+            final int uid = Binder.getCallingUid();
+            final long token = Binder.clearCallingIdentity();
+            try {
+                // Enforce SET_VOLUME_KEY_LONG_PRESS_LISTENER permission.
+                if (mContext.checkPermission(
+                        android.Manifest.permission.SET_VOLUME_KEY_LONG_PRESS_LISTENER, pid, uid)
+                        != PackageManager.PERMISSION_GRANTED) {
+                    throw new SecurityException("Must hold the SET_VOLUME_KEY_LONG_PRESS_LISTENER"
+                            + " permission.");
+                }
+
+                synchronized (mLock) {
+                    int userId = UserHandle.getUserId(uid);
+                    FullUserRecord user = getFullUserRecordLocked(userId);
+                    if (user == null || user.mFullUserId != userId) {
+                        Log.w(TAG, "Only the full user can set the volume key long-press listener"
+                                + ", userId=" + userId);
+                        return;
+                    }
+                    if (user.mOnVolumeKeyLongPressListener != null
+                            && user.mOnVolumeKeyLongPressListenerUid != uid) {
+                        Log.w(TAG, "The volume key long-press listener cannot be reset"
+                                + " by another app , mOnVolumeKeyLongPressListener="
+                                + user.mOnVolumeKeyLongPressListenerUid
+                                + ", uid=" + uid);
+                        return;
+                    }
+
+                    user.mOnVolumeKeyLongPressListener = listener;
+                    user.mOnVolumeKeyLongPressListenerUid = uid;
+
+                    Log.d(TAG, "The volume key long-press listener "
+                            + listener + " is set by " + getCallingPackageName(uid));
+
+                    if (user.mOnVolumeKeyLongPressListener != null) {
+                        try {
+                            user.mOnVolumeKeyLongPressListener.asBinder().linkToDeath(
+                                    new IBinder.DeathRecipient() {
+                                        @Override
+                                        public void binderDied() {
+                                            synchronized (mLock) {
+                                                user.mOnVolumeKeyLongPressListener = null;
+                                            }
+                                        }
+                                    }, 0);
+                        } catch (RemoteException e) {
+                            Log.w(TAG, "Failed to set death recipient "
+                                    + user.mOnVolumeKeyLongPressListener);
+                            user.mOnVolumeKeyLongPressListener = null;
+                        }
+                    }
+                }
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        @Override
+        public void setOnMediaKeyListener(IOnMediaKeyListener listener) {
+            final int pid = Binder.getCallingPid();
+            final int uid = Binder.getCallingUid();
+            final long token = Binder.clearCallingIdentity();
+            try {
+                // Enforce SET_MEDIA_KEY_LISTENER permission.
+                if (mContext.checkPermission(
+                        android.Manifest.permission.SET_MEDIA_KEY_LISTENER, pid, uid)
+                        != PackageManager.PERMISSION_GRANTED) {
+                    throw new SecurityException("Must hold the SET_MEDIA_KEY_LISTENER permission.");
+                }
+
+                synchronized (mLock) {
+                    int userId = UserHandle.getUserId(uid);
+                    FullUserRecord user = getFullUserRecordLocked(userId);
+                    if (user == null || user.mFullUserId != userId) {
+                        Log.w(TAG, "Only the full user can set the media key listener"
+                                + ", userId=" + userId);
+                        return;
+                    }
+                    if (user.mOnMediaKeyListener != null && user.mOnMediaKeyListenerUid != uid) {
+                        Log.w(TAG, "The media key listener cannot be reset by another app. "
+                                + ", mOnMediaKeyListenerUid=" + user.mOnMediaKeyListenerUid
+                                + ", uid=" + uid);
+                        return;
+                    }
+
+                    user.mOnMediaKeyListener = listener;
+                    user.mOnMediaKeyListenerUid = uid;
+
+                    Log.d(TAG, "The media key listener " + user.mOnMediaKeyListener
+                            + " is set by " + getCallingPackageName(uid));
+
+                    if (user.mOnMediaKeyListener != null) {
+                        try {
+                            user.mOnMediaKeyListener.asBinder().linkToDeath(
+                                    new IBinder.DeathRecipient() {
+                                        @Override
+                                        public void binderDied() {
+                                            synchronized (mLock) {
+                                                user.mOnMediaKeyListener = null;
+                                            }
+                                        }
+                                    }, 0);
+                        } catch (RemoteException e) {
+                            Log.w(TAG, "Failed to set death recipient " + user.mOnMediaKeyListener);
+                            user.mOnMediaKeyListener = null;
+                        }
+                    }
+                }
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        /**
+         * Handles the dispatching of the volume button events to one of the
+         * registered listeners. If there's a volume key long-press listener and
+         * there's no active global priority session, long-pressess will be sent to the
+         * long-press listener instead of adjusting volume.
+         *
+         * @param packageName The caller's package name, obtained by Context#getPackageName()
+         * @param opPackageName The caller's op package name, obtained by Context#getOpPackageName()
+         * @param asSystemService {@code true} if the event sent to the session as if it was come
+         *          from the system service instead of the app process. This helps sessions to
+         *          distinguish between the key injection by the app and key events from the
+         *          hardware devices. Should be used only when the volume key events aren't handled
+         *          by foreground activity. {@code false} otherwise to tell session about the real
+         *          caller.
+         * @param keyEvent a non-null KeyEvent whose key code is one of the
+         *            {@link KeyEvent#KEYCODE_VOLUME_UP},
+         *            {@link KeyEvent#KEYCODE_VOLUME_DOWN},
+         *            or {@link KeyEvent#KEYCODE_VOLUME_MUTE}.
+         * @param stream stream type to adjust volume.
+         * @param musicOnly true if both UI nor haptic feedback aren't needed when adjust volume.
+         */
+        @Override
+        public void dispatchVolumeKeyEvent(String packageName, String opPackageName,
+                boolean asSystemService, KeyEvent keyEvent, int stream, boolean musicOnly) {
+            if (keyEvent == null
+                    || (keyEvent.getKeyCode() != KeyEvent.KEYCODE_VOLUME_UP
+                            && keyEvent.getKeyCode() != KeyEvent.KEYCODE_VOLUME_DOWN
+                            && keyEvent.getKeyCode() != KeyEvent.KEYCODE_VOLUME_MUTE)) {
+                Log.w(TAG, "Attempted to dispatch null or non-volume key event.");
+                return;
+            }
+
+            final int pid = Binder.getCallingPid();
+            final int uid = Binder.getCallingUid();
+            final long token = Binder.clearCallingIdentity();
+
+            if (DEBUG_KEY_EVENT) {
+                Log.d(TAG, "dispatchVolumeKeyEvent, pkg=" + packageName + ", pid=" + pid + ", uid="
+                        + uid + ", asSystem=" + asSystemService + ", event=" + keyEvent);
+            }
+
+            try {
+                synchronized (mLock) {
+                    if (isGlobalPriorityActiveLocked()
+                            || mCurrentFullUserRecord.mOnVolumeKeyLongPressListener == null) {
+                        dispatchVolumeKeyEventLocked(packageName, opPackageName, pid, uid,
+                                asSystemService, keyEvent, stream, musicOnly);
+                    } else {
+                        // TODO: Consider the case when both volume up and down keys are pressed
+                        //       at the same time.
+                        if (keyEvent.getAction() == KeyEvent.ACTION_DOWN) {
+                            if (keyEvent.getRepeatCount() == 0) {
+                                // Keeps the copy of the KeyEvent because it can be reused.
+                                mCurrentFullUserRecord.mInitialDownVolumeKeyEvent =
+                                        KeyEvent.obtain(keyEvent);
+                                mCurrentFullUserRecord.mInitialDownVolumeStream = stream;
+                                mCurrentFullUserRecord.mInitialDownMusicOnly = musicOnly;
+                                mHandler.sendMessageDelayed(
+                                        mHandler.obtainMessage(
+                                                MessageHandler.MSG_VOLUME_INITIAL_DOWN,
+                                                mCurrentFullUserRecord.mFullUserId, 0),
+                                        mLongPressTimeout);
+                            }
+                            if (keyEvent.getRepeatCount() > 0 || keyEvent.isLongPress()) {
+                                mHandler.removeMessages(MessageHandler.MSG_VOLUME_INITIAL_DOWN);
+                                if (mCurrentFullUserRecord.mInitialDownVolumeKeyEvent != null) {
+                                    dispatchVolumeKeyLongPressLocked(
+                                            mCurrentFullUserRecord.mInitialDownVolumeKeyEvent);
+                                    // Mark that the key is already handled.
+                                    mCurrentFullUserRecord.mInitialDownVolumeKeyEvent = null;
+                                }
+                                dispatchVolumeKeyLongPressLocked(keyEvent);
+                            }
+                        } else { // if up
+                            mHandler.removeMessages(MessageHandler.MSG_VOLUME_INITIAL_DOWN);
+                            if (mCurrentFullUserRecord.mInitialDownVolumeKeyEvent != null
+                                    && mCurrentFullUserRecord.mInitialDownVolumeKeyEvent
+                                    .getDownTime() == keyEvent.getDownTime()) {
+                                // Short-press. Should change volume.
+                                dispatchVolumeKeyEventLocked(packageName, opPackageName, pid, uid,
+                                        asSystemService,
+                                        mCurrentFullUserRecord.mInitialDownVolumeKeyEvent,
+                                        mCurrentFullUserRecord.mInitialDownVolumeStream,
+                                        mCurrentFullUserRecord.mInitialDownMusicOnly);
+                                dispatchVolumeKeyEventLocked(packageName, opPackageName, pid, uid,
+                                        asSystemService, keyEvent, stream, musicOnly);
+                            } else {
+                                dispatchVolumeKeyLongPressLocked(keyEvent);
+                            }
+                        }
+                    }
+                }
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        private void dispatchVolumeKeyEventLocked(String packageName, String opPackageName, int pid,
+                int uid, boolean asSystemService, KeyEvent keyEvent, int stream,
+                boolean musicOnly) {
+            boolean down = keyEvent.getAction() == KeyEvent.ACTION_DOWN;
+            boolean up = keyEvent.getAction() == KeyEvent.ACTION_UP;
+            int direction = 0;
+            boolean isMute = false;
+            switch (keyEvent.getKeyCode()) {
+                case KeyEvent.KEYCODE_VOLUME_UP:
+                    direction = AudioManager.ADJUST_RAISE;
+                    break;
+                case KeyEvent.KEYCODE_VOLUME_DOWN:
+                    direction = AudioManager.ADJUST_LOWER;
+                    break;
+                case KeyEvent.KEYCODE_VOLUME_MUTE:
+                    isMute = true;
+                    break;
+            }
+            if (down || up) {
+                int flags = AudioManager.FLAG_FROM_KEY;
+                if (musicOnly) {
+                    // This flag is used when the screen is off to only affect active media.
+                    flags |= AudioManager.FLAG_ACTIVE_MEDIA_ONLY;
+                } else {
+                    // These flags are consistent with the home screen
+                    if (up) {
+                        flags |= AudioManager.FLAG_PLAY_SOUND | AudioManager.FLAG_VIBRATE;
+                    } else {
+                        flags |= AudioManager.FLAG_SHOW_UI | AudioManager.FLAG_VIBRATE;
+                    }
+                }
+                if (direction != 0) {
+                    // If this is action up we want to send a beep for non-music events
+                    if (up) {
+                        direction = 0;
+                    }
+                    dispatchAdjustVolumeLocked(packageName, opPackageName, pid, uid,
+                            asSystemService, stream, direction, flags);
+                } else if (isMute) {
+                    if (down && keyEvent.getRepeatCount() == 0) {
+                        dispatchAdjustVolumeLocked(packageName, opPackageName, pid, uid,
+                                asSystemService, stream, AudioManager.ADJUST_TOGGLE_MUTE, flags);
+                    }
+                }
+            }
+        }
+
+        @Override
+        public void dispatchAdjustVolume(String packageName, String opPackageName,
+                int suggestedStream, int delta, int flags) {
+            final int pid = Binder.getCallingPid();
+            final int uid = Binder.getCallingUid();
+            final long token = Binder.clearCallingIdentity();
+            try {
+                synchronized (mLock) {
+                    dispatchAdjustVolumeLocked(packageName, opPackageName, pid, uid, false,
+                            suggestedStream, delta, flags);
+                }
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        @Override
+        public void setRemoteVolumeController(IRemoteVolumeController rvc) {
+            final int pid = Binder.getCallingPid();
+            final int uid = Binder.getCallingUid();
+            final long token = Binder.clearCallingIdentity();
+            try {
+                enforceSystemUiPermission("listen for volume changes", pid, uid);
+                mRvc = rvc;
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        @Override
+        public boolean isGlobalPriorityActive() {
+            synchronized (mLock) {
+                return isGlobalPriorityActiveLocked();
+            }
+        }
+
+        @Override
+        public void dump(FileDescriptor fd, final PrintWriter pw, String[] args) {
+            if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
+
+            pw.println("MEDIA SESSION SERVICE (dumpsys media_session)");
+            pw.println();
+
+            synchronized (mLock) {
+                pw.println(mSessionsListeners.size() + " sessions listeners.");
+                pw.println("Global priority session is " + mGlobalPrioritySession);
+                if (mGlobalPrioritySession != null) {
+                    mGlobalPrioritySession.dump(pw, "  ");
+                }
+                pw.println("User Records:");
+                int count = mUserRecords.size();
+                for (int i = 0; i < count; i++) {
+                    mUserRecords.valueAt(i).dumpLocked(pw, "");
+                }
+                mAudioPlayerStateMonitor.dump(mContext, pw, "");
+            }
+        }
+
+        /**
+         * Returns if the controller's package is trusted (i.e. has either MEDIA_CONTENT_CONTROL
+         * permission or an enabled notification listener)
+         *
+         * @param controllerPackageName package name of the controller app
+         * @param controllerPid pid of the controller app
+         * @param controllerUid uid of the controller app
+         */
+        @Override
+        public boolean isTrusted(String controllerPackageName, int controllerPid, int controllerUid)
+                throws RemoteException {
+            final int uid = Binder.getCallingUid();
+            final long token = Binder.clearCallingIdentity();
+            try {
+                // Don't perform sanity check between controllerPackageName and controllerUid.
+                // When an (activity|service) runs on the another apps process by specifying
+                // android:process in the AndroidManifest.xml, then PID and UID would have the
+                // running process' information instead of the (activity|service) that has created
+                // MediaController.
+                // Note that we can use Context#getOpPackageName() instead of
+                // Context#getPackageName() for getting package name that matches with the PID/UID,
+                // but it doesn't tell which package has created the MediaController, so useless.
+                return hasMediaControlPermission(UserHandle.getUserId(uid), controllerPackageName,
+                        controllerPid, controllerUid);
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        // For MediaSession
+        private int verifySessionsRequest(ComponentName componentName, int userId, final int pid,
+                final int uid) {
+            String packageName = null;
+            if (componentName != null) {
+                // If they gave us a component name verify they own the
+                // package
+                packageName = componentName.getPackageName();
+                enforcePackageName(packageName, uid);
+            }
+            // Check that they can make calls on behalf of the user and
+            // get the final user id
+            int resolvedUserId = ActivityManager.handleIncomingUser(pid, uid, userId,
+                    true /* allowAll */, true /* requireFull */, "getSessions", packageName);
+            // Check if they have the permissions or their component is
+            // enabled for the user they're calling from.
+            enforceMediaPermissions(componentName, pid, uid, resolvedUserId);
+            return resolvedUserId;
+        }
+
+        private boolean hasMediaControlPermission(int resolvedUserId, String packageName,
+                int pid, int uid) throws RemoteException {
+            // Allow API calls from the System UI
+            if (isCurrentVolumeController(pid, uid)) {
+                return true;
+            }
+
+            // Check if it's system server or has MEDIA_CONTENT_CONTROL.
+            // Note that system server doesn't have MEDIA_CONTENT_CONTROL, so we need extra
+            // check here.
+            if (uid == Process.SYSTEM_UID || mContext.checkPermission(
+                    android.Manifest.permission.MEDIA_CONTENT_CONTROL, pid, uid)
+                    == PackageManager.PERMISSION_GRANTED) {
+                return true;
+            } else if (DEBUG) {
+                Log.d(TAG, packageName + " (uid=" + uid + ") hasn't granted MEDIA_CONTENT_CONTROL");
+            }
+
+            // You may not access another user's content as an enabled listener.
+            final int userId = UserHandle.getUserId(uid);
+            if (resolvedUserId != userId) {
+                return false;
+            }
+
+            // TODO(jaewan): (Post-P) Propose NotificationManager#hasEnabledNotificationListener(
+            //               String pkgName) to notification team for optimization
+            final List<ComponentName> enabledNotificationListeners =
+                    mNotificationManager.getEnabledNotificationListeners(userId);
+            if (enabledNotificationListeners != null) {
+                for (int i = 0; i < enabledNotificationListeners.size(); i++) {
+                    if (TextUtils.equals(packageName,
+                            enabledNotificationListeners.get(i).getPackageName())) {
+                        return true;
+                    }
+                }
+            }
+            if (DEBUG) {
+                Log.d(TAG, packageName + " (uid=" + uid + ") doesn't have an enabled "
+                        + "notification listener");
+            }
+            return false;
+        }
+
+        private void dispatchAdjustVolumeLocked(String packageName, String opPackageName, int pid,
+                int uid, boolean asSystemService, int suggestedStream, int direction, int flags) {
+            MediaSessionRecord session = isGlobalPriorityActiveLocked() ? mGlobalPrioritySession
+                    : mCurrentFullUserRecord.mPriorityStack.getDefaultVolumeSession();
+
+            boolean preferSuggestedStream = false;
+            if (isValidLocalStreamType(suggestedStream)
+                    && AudioSystem.isStreamActive(suggestedStream, 0)) {
+                preferSuggestedStream = true;
+            }
+            if (DEBUG_KEY_EVENT) {
+                Log.d(TAG, "Adjusting " + session + " by " + direction + ". flags="
+                        + flags + ", suggestedStream=" + suggestedStream
+                        + ", preferSuggestedStream=" + preferSuggestedStream);
+            }
+            if (session == null || preferSuggestedStream) {
+                if ((flags & AudioManager.FLAG_ACTIVE_MEDIA_ONLY) != 0
+                        && !AudioSystem.isStreamActive(AudioManager.STREAM_MUSIC, 0)) {
+                    if (DEBUG) {
+                        Log.d(TAG, "No active session to adjust, skipping media only volume event");
+                    }
+                    return;
+                }
+
+                // Execute mAudioService.adjustSuggestedStreamVolume() on
+                // handler thread of MediaSessionService.
+                // This will release the MediaSessionService.mLock sooner and avoid
+                // a potential deadlock between MediaSessionService.mLock and
+                // ActivityManagerService lock.
+                mHandler.post(new Runnable() {
+                    @Override
+                    public void run() {
+                        final String callingOpPackageName;
+                        final int callingUid;
+                        if (asSystemService) {
+                            callingOpPackageName = mContext.getOpPackageName();
+                            callingUid = Process.myUid();
+                        } else {
+                            callingOpPackageName = opPackageName;
+                            callingUid = uid;
+                        }
+                        try {
+                            mAudioManagerInternal.adjustSuggestedStreamVolumeForUid(suggestedStream,
+                                    direction, flags, callingOpPackageName, callingUid);
+                        } catch (SecurityException | IllegalArgumentException e) {
+                            Log.e(TAG, "Cannot adjust volume: direction=" + direction
+                                    + ", suggestedStream=" + suggestedStream + ", flags=" + flags
+                                    + ", packageName=" + packageName + ", uid=" + uid
+                                    + ", asSystemService=" + asSystemService, e);
+                        }
+                    }
+                });
+            } else {
+                session.adjustVolume(packageName, opPackageName, pid, uid, null, asSystemService,
+                        direction, flags, true);
+            }
+        }
+
+        private void handleVoiceKeyEventLocked(String packageName, int pid, int uid,
+                boolean asSystemService, KeyEvent keyEvent, boolean needWakeLock) {
+            int action = keyEvent.getAction();
+            boolean isLongPress = (keyEvent.getFlags() & KeyEvent.FLAG_LONG_PRESS) != 0;
+            if (action == KeyEvent.ACTION_DOWN) {
+                if (keyEvent.getRepeatCount() == 0) {
+                    mVoiceButtonDown = true;
+                    mVoiceButtonHandled = false;
+                } else if (mVoiceButtonDown && !mVoiceButtonHandled && isLongPress) {
+                    mVoiceButtonHandled = true;
+                    startVoiceInput(needWakeLock);
+                }
+            } else if (action == KeyEvent.ACTION_UP) {
+                if (mVoiceButtonDown) {
+                    mVoiceButtonDown = false;
+                    if (!mVoiceButtonHandled && !keyEvent.isCanceled()) {
+                        // Resend the down then send this event through
+                        KeyEvent downEvent = KeyEvent.changeAction(keyEvent, KeyEvent.ACTION_DOWN);
+                        dispatchMediaKeyEventLocked(packageName, pid, uid, asSystemService,
+                                downEvent, needWakeLock);
+                        dispatchMediaKeyEventLocked(packageName, pid, uid, asSystemService,
+                                keyEvent, needWakeLock);
+                    }
+                }
+            }
+        }
+
+        private void dispatchMediaKeyEventLocked(String packageName, int pid, int uid,
+                boolean asSystemService, KeyEvent keyEvent, boolean needWakeLock) {
+            MediaSessionRecord session = mCurrentFullUserRecord.getMediaButtonSessionLocked();
+            if (session != null) {
+                if (DEBUG_KEY_EVENT) {
+                    Log.d(TAG, "Sending " + keyEvent + " to " + session);
+                }
+                if (needWakeLock) {
+                    mKeyEventReceiver.aquireWakeLockLocked();
+                }
+                // If we don't need a wakelock use -1 as the id so we won't release it later.
+                session.sendMediaButton(packageName, pid, uid, asSystemService, keyEvent,
+                        needWakeLock ? mKeyEventReceiver.mLastTimeoutId : -1,
+                        mKeyEventReceiver);
+                if (mCurrentFullUserRecord.mCallback != null) {
+                    try {
+                        mCurrentFullUserRecord.mCallback.onMediaKeyEventDispatchedToMediaSession(
+                                keyEvent, new MediaSession.Token(session.getControllerBinder()));
+                    } catch (RemoteException e) {
+                        Log.w(TAG, "Failed to send callback", e);
+                    }
+                }
+            } else if (mCurrentFullUserRecord.mLastMediaButtonReceiver != null
+                    || mCurrentFullUserRecord.mRestoredMediaButtonReceiver != null) {
+                if (needWakeLock) {
+                    mKeyEventReceiver.aquireWakeLockLocked();
+                }
+                Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
+                mediaButtonIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+                mediaButtonIntent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);
+                // TODO: Find a way to also send PID/UID in secure way.
+                String callerPackageName =
+                        (asSystemService) ? mContext.getPackageName() : packageName;
+                mediaButtonIntent.putExtra(Intent.EXTRA_PACKAGE_NAME, callerPackageName);
+                try {
+                    if (mCurrentFullUserRecord.mLastMediaButtonReceiver != null) {
+                        PendingIntent receiver = mCurrentFullUserRecord.mLastMediaButtonReceiver;
+                        if (DEBUG_KEY_EVENT) {
+                            Log.d(TAG, "Sending " + keyEvent
+                                    + " to the last known PendingIntent " + receiver);
+                        }
+                        receiver.send(mContext,
+                                needWakeLock ? mKeyEventReceiver.mLastTimeoutId : -1,
+                                mediaButtonIntent, mKeyEventReceiver, mHandler);
+                        if (mCurrentFullUserRecord.mCallback != null) {
+                            ComponentName componentName = mCurrentFullUserRecord
+                                    .mLastMediaButtonReceiver.getIntent().getComponent();
+                            if (componentName != null) {
+                                mCurrentFullUserRecord.mCallback
+                                        .onMediaKeyEventDispatchedToMediaButtonReceiver(
+                                                keyEvent, componentName);
+                            }
+                        }
+                    } 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 + ", type=" + componentType);
+                        }
+                        mediaButtonIntent.setComponent(receiver);
+                        try {
+                            switch (componentType) {
+                                case FullUserRecord.COMPONENT_TYPE_ACTIVITY:
+                                    mContext.startActivityAsUser(mediaButtonIntent, userHandle);
+                                    break;
+                                case FullUserRecord.COMPONENT_TYPE_SERVICE:
+                                    mContext.startForegroundServiceAsUser(mediaButtonIntent,
+                                            userHandle);
+                                    break;
+                                default:
+                                    // Legacy behavior for other cases.
+                                    mContext.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(
+                                            keyEvent, receiver);
+                        }
+                    }
+                } catch (CanceledException e) {
+                    Log.i(TAG, "Error sending key event to media button receiver "
+                            + mCurrentFullUserRecord.mLastMediaButtonReceiver, e);
+                } catch (RemoteException e) {
+                    Log.w(TAG, "Failed to send callback", e);
+                }
+            }
+        }
+
+        private void startVoiceInput(boolean needWakeLock) {
+            Intent voiceIntent = null;
+            // select which type of search to launch:
+            // - screen on and device unlocked: action is ACTION_WEB_SEARCH
+            // - device locked or screen off: action is
+            // ACTION_VOICE_SEARCH_HANDS_FREE
+            // with EXTRA_SECURE set to true if the device is securely locked
+            PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
+            boolean isLocked = mKeyguardManager != null && mKeyguardManager.isKeyguardLocked();
+            if (!isLocked && pm.isScreenOn()) {
+                voiceIntent = new Intent(android.speech.RecognizerIntent.ACTION_WEB_SEARCH);
+                Log.i(TAG, "voice-based interactions: about to use ACTION_WEB_SEARCH");
+            } else {
+                voiceIntent = new Intent(RecognizerIntent.ACTION_VOICE_SEARCH_HANDS_FREE);
+                voiceIntent.putExtra(RecognizerIntent.EXTRA_SECURE,
+                        isLocked && mKeyguardManager.isKeyguardSecure());
+                Log.i(TAG, "voice-based interactions: about to use ACTION_VOICE_SEARCH_HANDS_FREE");
+            }
+            // start the search activity
+            if (needWakeLock) {
+                mMediaEventWakeLock.acquire();
+            }
+            try {
+                if (voiceIntent != null) {
+                    voiceIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+                            | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+                    if (DEBUG) Log.d(TAG, "voiceIntent: " + voiceIntent);
+                    mContext.startActivityAsUser(voiceIntent, UserHandle.CURRENT);
+                }
+            } catch (ActivityNotFoundException e) {
+                Log.w(TAG, "No activity for search: " + e);
+            } finally {
+                if (needWakeLock) {
+                    mMediaEventWakeLock.release();
+                }
+            }
+        }
+
+        private boolean isVoiceKey(int keyCode) {
+            return keyCode == KeyEvent.KEYCODE_HEADSETHOOK
+                    || (!mHasFeatureLeanback && keyCode == KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE);
+        }
+
+        private boolean isUserSetupComplete() {
+            return Settings.Secure.getIntForUser(mContext.getContentResolver(),
+                    Settings.Secure.USER_SETUP_COMPLETE, 0, UserHandle.USER_CURRENT) != 0;
+        }
+
+        // we only handle public stream types, which are 0-5
+        private boolean isValidLocalStreamType(int streamType) {
+            return streamType >= AudioManager.STREAM_VOICE_CALL
+                    && streamType <= AudioManager.STREAM_NOTIFICATION;
+        }
+
+        private class MediaKeyListenerResultReceiver extends ResultReceiver implements Runnable {
+            private final String mPackageName;
+            private final int mPid;
+            private final int mUid;
+            private final boolean mAsSystemService;
+            private final KeyEvent mKeyEvent;
+            private final boolean mNeedWakeLock;
+            private boolean mHandled;
+
+            private MediaKeyListenerResultReceiver(String packageName, int pid, int uid,
+                    boolean asSystemService, KeyEvent keyEvent, boolean needWakeLock) {
+                super(mHandler);
+                mHandler.postDelayed(this, MEDIA_KEY_LISTENER_TIMEOUT);
+                mPackageName = packageName;
+                mPid = pid;
+                mUid = uid;
+                mAsSystemService = asSystemService;
+                mKeyEvent = keyEvent;
+                mNeedWakeLock = needWakeLock;
+            }
+
+            @Override
+            public void run() {
+                Log.d(TAG, "The media key listener is timed-out for " + mKeyEvent);
+                dispatchMediaKeyEvent();
+            }
+
+            @Override
+            protected void onReceiveResult(int resultCode, Bundle resultData) {
+                if (resultCode == MediaSessionManager.RESULT_MEDIA_KEY_HANDLED) {
+                    mHandled = true;
+                    mHandler.removeCallbacks(this);
+                    return;
+                }
+                dispatchMediaKeyEvent();
+            }
+
+            private void dispatchMediaKeyEvent() {
+                if (mHandled) {
+                    return;
+                }
+                mHandled = true;
+                mHandler.removeCallbacks(this);
+                synchronized (mLock) {
+                    if (!isGlobalPriorityActiveLocked()
+                            && isVoiceKey(mKeyEvent.getKeyCode())) {
+                        handleVoiceKeyEventLocked(mPackageName, mPid, mUid, mAsSystemService,
+                                mKeyEvent, mNeedWakeLock);
+                    } else {
+                        dispatchMediaKeyEventLocked(mPackageName, mPid, mUid, mAsSystemService,
+                                mKeyEvent, mNeedWakeLock);
+                    }
+                }
+            }
+        }
+
+        private KeyEventWakeLockReceiver mKeyEventReceiver = new KeyEventWakeLockReceiver(mHandler);
+
+        class KeyEventWakeLockReceiver extends ResultReceiver implements Runnable,
+                PendingIntent.OnFinished {
+            private final Handler mHandler;
+            private int mRefCount = 0;
+            private int mLastTimeoutId = 0;
+
+            KeyEventWakeLockReceiver(Handler handler) {
+                super(handler);
+                mHandler = handler;
+            }
+
+            public void onTimeout() {
+                synchronized (mLock) {
+                    if (mRefCount == 0) {
+                        // We've already released it, so just return
+                        return;
+                    }
+                    mLastTimeoutId++;
+                    mRefCount = 0;
+                    releaseWakeLockLocked();
+                }
+            }
+
+            public void aquireWakeLockLocked() {
+                if (mRefCount == 0) {
+                    mMediaEventWakeLock.acquire();
+                }
+                mRefCount++;
+                mHandler.removeCallbacks(this);
+                mHandler.postDelayed(this, WAKELOCK_TIMEOUT);
+
+            }
+
+            @Override
+            public void run() {
+                onTimeout();
+            }
+
+            @Override
+            protected void onReceiveResult(int resultCode, Bundle resultData) {
+                if (resultCode < mLastTimeoutId) {
+                    // Ignore results from calls that were before the last
+                    // timeout, just in case.
+                    return;
+                } else {
+                    synchronized (mLock) {
+                        if (mRefCount > 0) {
+                            mRefCount--;
+                            if (mRefCount == 0) {
+                                releaseWakeLockLocked();
+                            }
+                        }
+                    }
+                }
+            }
+
+            private void releaseWakeLockLocked() {
+                mMediaEventWakeLock.release();
+                mHandler.removeCallbacks(this);
+            }
+
+            @Override
+            public void onSendFinished(PendingIntent pendingIntent, Intent intent, int resultCode,
+                    String resultData, Bundle resultExtras) {
+                onReceiveResult(resultCode, null);
+            }
+        };
+
+        BroadcastReceiver mKeyEventDone = new BroadcastReceiver() {
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                if (intent == null) {
+                    return;
+                }
+                Bundle extras = intent.getExtras();
+                if (extras == null) {
+                    return;
+                }
+                synchronized (mLock) {
+                    if (extras.containsKey(EXTRA_WAKELOCK_ACQUIRED)
+                            && mMediaEventWakeLock.isHeld()) {
+                        mMediaEventWakeLock.release();
+                    }
+                }
+            }
+        };
+    }
+
+    final class MessageHandler extends Handler {
+        private static final int MSG_SESSIONS_CHANGED = 1;
+        private static final int MSG_VOLUME_INITIAL_DOWN = 2;
+        private final SparseArray<Integer> mIntegerCache = new SparseArray<>();
+
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case MSG_SESSIONS_CHANGED:
+                    pushSessionsChanged((int) msg.obj);
+                    break;
+                case MSG_VOLUME_INITIAL_DOWN:
+                    synchronized (mLock) {
+                        FullUserRecord user = mUserRecords.get((int) msg.arg1);
+                        if (user != null && user.mInitialDownVolumeKeyEvent != null) {
+                            dispatchVolumeKeyLongPressLocked(user.mInitialDownVolumeKeyEvent);
+                            // Mark that the key is already handled.
+                            user.mInitialDownVolumeKeyEvent = null;
+                        }
+                    }
+                    break;
+            }
+        }
+
+        public void postSessionsChanged(int userId) {
+            // Use object instead of the arguments when posting message to remove pending requests.
+            Integer userIdInteger = mIntegerCache.get(userId);
+            if (userIdInteger == null) {
+                userIdInteger = Integer.valueOf(userId);
+                mIntegerCache.put(userId, userIdInteger);
+            }
+            removeMessages(MSG_SESSIONS_CHANGED, userIdInteger);
+            obtainMessage(MSG_SESSIONS_CHANGED, userIdInteger).sendToTarget();
+        }
+    }
+
+    private class Controller2Callback extends MediaController2.ControllerCallback {
+        private final Session2Token mToken;
+
+        Controller2Callback(Session2Token token) {
+            mToken = token;
+        }
+
+        @Override
+        public void onConnected(MediaController2 controller, Session2CommandGroup allowedCommands) {
+            synchronized (mLock) {
+                int userId = UserHandle.getUserId(mToken.getUid());
+                mSession2TokensPerUser.get(userId).add(mToken);
+                pushSession2TokensChangedLocked(userId);
+            }
+        }
+
+        @Override
+        public void onDisconnected(MediaController2 controller) {
+            synchronized (mLock) {
+                int userId = UserHandle.getUserId(mToken.getUid());
+                mSession2TokensPerUser.get(userId).remove(mToken);
+                pushSession2TokensChangedLocked(userId);
+            }
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/media/RemoteDisplayProviderWatcher.java b/services/core/java/com/android/server/media/RemoteDisplayProviderWatcher.java
index 6a5f563..64c451d 100644
--- a/services/core/java/com/android/server/media/RemoteDisplayProviderWatcher.java
+++ b/services/core/java/com/android/server/media/RemoteDisplayProviderWatcher.java
@@ -159,12 +159,13 @@
                     + serviceInfo.packageName + "/" + serviceInfo.name);
             return false;
         }
-        if (!hasCaptureVideoPermission(serviceInfo.packageName)) {
-            // If the service does not have permission to capture video then it
-            // isn't going to be terribly useful as a remote display, is it?
-            // Kind of makes you wonder what it's doing there in the first place.
+        if (mPackageManager.checkPermission(Manifest.permission.REMOTE_DISPLAY_PROVIDER,
+                        serviceInfo.packageName) != PackageManager.PERMISSION_GRANTED) {
+            // If the service does not have this permission then the system will not bind to it.
+            // This is to prevent non privileged apps declaring themselves as remote display
+            // providers just to be bound to by the system and keep their process alive.
             Slog.w(TAG, "Ignoring remote display provider service because it does not "
-                    + "have the CAPTURE_VIDEO_OUTPUT or CAPTURE_SECURE_VIDEO_OUTPUT "
+                    + "have the REMOTE_DISPLAY_PROVIDER "
                     + "permission: " + serviceInfo.packageName + "/" + serviceInfo.name);
             return false;
         }
@@ -172,18 +173,6 @@
         return true;
     }
 
-    private boolean hasCaptureVideoPermission(String packageName) {
-        if (mPackageManager.checkPermission(Manifest.permission.CAPTURE_VIDEO_OUTPUT,
-                packageName) == PackageManager.PERMISSION_GRANTED) {
-            return true;
-        }
-        if (mPackageManager.checkPermission(Manifest.permission.CAPTURE_SECURE_VIDEO_OUTPUT,
-                packageName) == PackageManager.PERMISSION_GRANTED) {
-            return true;
-        }
-        return false;
-    }
-
     private int findProvider(String packageName, String className) {
         int count = mProviders.size();
         for (int i = 0; i < count; i++) {
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 7ecdad2..7323e93 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -1581,6 +1581,9 @@
 
         mIsAutomotive =
                 mPackageManagerClient.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE, 0);
+
+        mPreferencesHelper.lockChannelsForOEM(getContext().getResources().getStringArray(
+                com.android.internal.R.array.config_nonBlockableNotificationPackages));
     }
 
     @Override
@@ -2308,22 +2311,22 @@
         }
 
         @Override
-        public boolean areAppOverlaysAllowed(String pkg) {
-            return areAppOverlaysAllowedForPackage(pkg, Binder.getCallingUid());
+        public boolean areBubblesAllowed(String pkg) {
+            return areBubblesAllowedForPackage(pkg, Binder.getCallingUid());
         }
 
         @Override
-        public boolean areAppOverlaysAllowedForPackage(String pkg, int uid) {
-            checkCallerIsSystemOrSameApp(pkg);
-
-            return mPreferencesHelper.areAppOverlaysAllowed(pkg, uid);
+        public boolean areBubblesAllowedForPackage(String pkg, int uid) {
+            enforceSystemOrSystemUIOrSamePackage(pkg,
+                    "Caller not system or systemui or same package");
+            return mPreferencesHelper.areBubblessAllowed(pkg, uid);
         }
 
         @Override
-        public void setAppOverlaysAllowed(String pkg, int uid, boolean allowed) {
+        public void setBubblesAllowed(String pkg, int uid, boolean allowed) {
             checkCallerIsSystem();
 
-            mPreferencesHelper.setAppOverlaysAllowed(pkg, uid, allowed);
+            mPreferencesHelper.setBubblesAllowed(pkg, uid, allowed);
             handleSavePolicyFile();
         }
 
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index 9942f59..5598741 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -91,7 +91,6 @@
 public final class NotificationRecord {
     static final String TAG = "NotificationRecord";
     static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
-    private static final int MAX_LOGTAG_LENGTH = 35;
     // the period after which a notification is updated where it can make sound
     private static final int MAX_SOUND_DELAY_MS = 2000;
     final StatusBarNotification sbn;
@@ -162,10 +161,7 @@
     private ArrayList<String> mPeopleOverride;
     private ArrayList<SnoozeCriterion> mSnoozeCriteria;
     private boolean mShowBadge;
-    private LogMaker mLogMaker;
     private Light mLight;
-    private String mGroupLogTag;
-    private String mChannelIdLogTag;
     /**
      * This list contains system generated smart actions from NAS, app-generated smart actions are
      * stored in Notification.actions with isContextual() set to true.
@@ -789,7 +785,8 @@
             mImportanceExplanation = "user";
         }
         if (!getChannel().hasUserSetImportance()
-                && mAssistantImportance != IMPORTANCE_UNSPECIFIED) {
+                && mAssistantImportance != IMPORTANCE_UNSPECIFIED
+                && !getChannel().isImportanceLockedByOEM()) {
             mImportance = mAssistantImportance;
             mImportanceExplanation = "asst";
         }
@@ -969,33 +966,6 @@
 
     public void setOverrideGroupKey(String overrideGroupKey) {
         sbn.setOverrideGroupKey(overrideGroupKey);
-        mGroupLogTag = null;
-    }
-
-    private String getGroupLogTag() {
-        if (mGroupLogTag == null) {
-            mGroupLogTag = shortenTag(sbn.getGroup());
-        }
-        return mGroupLogTag;
-    }
-
-    private String getChannelIdLogTag() {
-        if (mChannelIdLogTag == null) {
-            mChannelIdLogTag = shortenTag(mChannel.getId());
-        }
-        return mChannelIdLogTag;
-    }
-
-    private String shortenTag(String longTag) {
-        if (longTag == null) {
-            return null;
-        }
-        if (longTag.length() < MAX_LOGTAG_LENGTH) {
-            return longTag;
-        } else {
-            return longTag.substring(0, MAX_LOGTAG_LENGTH - 8) + "-" +
-                    Integer.toHexString(longTag.hashCode());
-        }
     }
 
     public NotificationChannel getChannel() {
@@ -1272,24 +1242,9 @@
     }
 
     public LogMaker getLogMaker(long now) {
-        if (mLogMaker == null) {
-            // initialize fields that only change on update (so a new record)
-            mLogMaker = new LogMaker(MetricsEvent.VIEW_UNKNOWN)
-                    .setPackageName(sbn.getPackageName())
-                    .addTaggedData(MetricsEvent.NOTIFICATION_ID, sbn.getId())
-                    .addTaggedData(MetricsEvent.NOTIFICATION_TAG, sbn.getTag())
-                    .addTaggedData(MetricsEvent.FIELD_NOTIFICATION_CHANNEL_ID, getChannelIdLogTag());
-        }
-        // reset fields that can change between updates, or are used by multiple logs
-        return mLogMaker
-                .clearCategory()
-                .clearType()
-                .clearSubtype()
+        return sbn.getLogMaker()
                 .clearTaggedData(MetricsEvent.NOTIFICATION_SHADE_INDEX)
                 .addTaggedData(MetricsEvent.FIELD_NOTIFICATION_CHANNEL_IMPORTANCE, mImportance)
-                .addTaggedData(MetricsEvent.FIELD_NOTIFICATION_GROUP_ID, getGroupLogTag())
-                .addTaggedData(MetricsEvent.FIELD_NOTIFICATION_GROUP_SUMMARY,
-                        sbn.getNotification().isGroupSummary() ? 1 : 0)
                 .addTaggedData(MetricsEvent.NOTIFICATION_SINCE_CREATE_MILLIS, getLifespanMs(now))
                 .addTaggedData(MetricsEvent.NOTIFICATION_SINCE_UPDATE_MILLIS, getFreshnessMs(now))
                 .addTaggedData(MetricsEvent.NOTIFICATION_SINCE_VISIBLE_MILLIS, getExposureMs(now))
diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java
index 7c0e0b0..7a21aa2 100644
--- a/services/core/java/com/android/server/notification/PreferencesHelper.java
+++ b/services/core/java/com/android/server/notification/PreferencesHelper.java
@@ -68,6 +68,7 @@
     private static final String TAG = "NotificationPrefHelper";
     private static final int XML_VERSION = 1;
     private static final int UNKNOWN_UID = UserHandle.USER_NULL;
+    private static final String NON_BLOCKABLE_CHANNEL_DELIM = ":";
 
     @VisibleForTesting
     static final String TAG_RANKING = "ranking";
@@ -80,7 +81,7 @@
     private static final String ATT_NAME = "name";
     private static final String ATT_UID = "uid";
     private static final String ATT_ID = "id";
-    private static final String ATT_APP_OVERLAY = "overlay";
+    private static final String ATT_ALLOW_BUBBLE = "allow_bubble";
     private static final String ATT_PRIORITY = "priority";
     private static final String ATT_VISIBILITY = "visibility";
     private static final String ATT_IMPORTANCE = "importance";
@@ -93,7 +94,9 @@
     private static final int DEFAULT_VISIBILITY = NotificationManager.VISIBILITY_NO_OVERRIDE;
     private static final int DEFAULT_IMPORTANCE = NotificationManager.IMPORTANCE_UNSPECIFIED;
     private static final boolean DEFAULT_SHOW_BADGE = true;
-    private static final boolean DEFAULT_ALLOW_APP_OVERLAY = true;
+    private static final boolean DEFAULT_ALLOW_BUBBLE = true;
+    private static final boolean DEFAULT_OEM_LOCKED_IMPORTANCE  = false;
+
     /**
      * Default value for what fields are user locked. See {@link LockableAppFields} for all lockable
      * fields.
@@ -106,7 +109,7 @@
     @IntDef({LockableAppFields.USER_LOCKED_IMPORTANCE})
     public @interface LockableAppFields {
         int USER_LOCKED_IMPORTANCE = 0x00000001;
-        int USER_LOCKED_APP_OVERLAY = 0x00000002;
+        int USER_LOCKED_BUBBLE = 0x00000002;
     }
 
     // pkg|uid => PackagePreferences
@@ -174,7 +177,7 @@
                                     XmlUtils.readBooleanAttribute(
                                             parser, ATT_SHOW_BADGE, DEFAULT_SHOW_BADGE),
                                     XmlUtils.readBooleanAttribute(
-                                            parser, ATT_APP_OVERLAY, DEFAULT_ALLOW_APP_OVERLAY));
+                                            parser, ATT_ALLOW_BUBBLE, DEFAULT_ALLOW_BUBBLE));
                             r.importance = XmlUtils.readIntAttribute(
                                     parser, ATT_IMPORTANCE, DEFAULT_IMPORTANCE);
                             r.priority = XmlUtils.readIntAttribute(
@@ -270,11 +273,11 @@
     private PackagePreferences getOrCreatePackagePreferences(String pkg, int uid) {
         return getOrCreatePackagePreferences(pkg, uid,
                 DEFAULT_IMPORTANCE, DEFAULT_PRIORITY, DEFAULT_VISIBILITY, DEFAULT_SHOW_BADGE,
-                DEFAULT_ALLOW_APP_OVERLAY);
+                DEFAULT_ALLOW_BUBBLE);
     }
 
     private PackagePreferences getOrCreatePackagePreferences(String pkg, int uid, int importance,
-            int priority, int visibility, boolean showBadge, boolean allowAppOverlay) {
+            int priority, int visibility, boolean showBadge, boolean allowBubble) {
         final String key = packagePreferencesKey(pkg, uid);
         synchronized (mPackagePreferences) {
             PackagePreferences
@@ -288,7 +291,7 @@
                 r.priority = priority;
                 r.visibility = visibility;
                 r.showBadge = showBadge;
-                r.appOverlay = allowAppOverlay;
+                r.allowBubble = allowBubble;
 
                 try {
                     createDefaultChannelIfNeeded(r);
@@ -390,7 +393,7 @@
                                 || r.channels.size() > 0
                                 || r.groups.size() > 0
                                 || r.delegate != null
-                                || r.appOverlay != DEFAULT_ALLOW_APP_OVERLAY;
+                                || r.allowBubble != DEFAULT_ALLOW_BUBBLE;
                 if (hasNonDefaultSettings) {
                     out.startTag(null, TAG_PACKAGE);
                     out.attribute(null, ATT_NAME, r.pkg);
@@ -403,8 +406,8 @@
                     if (r.visibility != DEFAULT_VISIBILITY) {
                         out.attribute(null, ATT_VISIBILITY, Integer.toString(r.visibility));
                     }
-                    if (r.appOverlay != DEFAULT_ALLOW_APP_OVERLAY) {
-                        out.attribute(null, ATT_APP_OVERLAY, Boolean.toString(r.appOverlay));
+                    if (r.allowBubble != DEFAULT_ALLOW_BUBBLE) {
+                        out.attribute(null, ATT_ALLOW_BUBBLE, Boolean.toString(r.allowBubble));
                     }
                     out.attribute(null, ATT_SHOW_BADGE, Boolean.toString(r.showBadge));
                     out.attribute(null, ATT_APP_USER_LOCKED_FIELDS,
@@ -450,14 +453,28 @@
         out.endTag(null, TAG_RANKING);
     }
 
-    public void setAppOverlaysAllowed(String pkg, int uid, boolean allowed) {
+    /**
+     * Sets whether bubbles are allowed.
+     *
+     * @param pkg the package to allow or not allow bubbles for.
+     * @param uid the uid to allow or not allow bubbles for.
+     * @param allowed whether bubbles are allowed.
+     */
+    public void setBubblesAllowed(String pkg, int uid, boolean allowed) {
         PackagePreferences p = getOrCreatePackagePreferences(pkg, uid);
-        p.appOverlay = allowed;
-        p.lockedAppFields = p.lockedAppFields | LockableAppFields.USER_LOCKED_APP_OVERLAY;
+        p.allowBubble = allowed;
+        p.lockedAppFields = p.lockedAppFields | LockableAppFields.USER_LOCKED_BUBBLE;
     }
 
-    public boolean areAppOverlaysAllowed(String pkg, int uid) {
-        return getOrCreatePackagePreferences(pkg, uid).appOverlay;
+    /**
+     * Whether bubbles are allowed.
+     *
+     * @param pkg the package to check if bubbles are allowed for
+     * @param uid the uid to check if bubbles are allowed for.
+     * @return whether bubbles are allowed.
+     */
+    public boolean areBubblessAllowed(String pkg, int uid) {
+        return getOrCreatePackagePreferences(pkg, uid).allowBubble;
     }
 
     public int getAppLockedFields(String pkg, int uid) {
@@ -621,6 +638,12 @@
             channel.setLockscreenVisibility(r.visibility);
         }
         clearLockedFields(channel);
+        channel.setImportanceLockedByOEM(r.oemLockedImportance);
+        if (!channel.isImportanceLockedByOEM()) {
+            if (r.futureOemLockedChannels.remove(channel.getId())) {
+                channel.setImportanceLockedByOEM(true);
+            }
+        }
         if (channel.getLockscreenVisibility() == Notification.VISIBILITY_PUBLIC) {
             channel.setLockscreenVisibility(
                     NotificationListenerService.Ranking.VISIBILITY_NO_OVERRIDE);
@@ -664,6 +687,12 @@
         } else {
             updatedChannel.unlockFields(updatedChannel.getUserLockedFields());
         }
+        // no importance updates are allowed if OEM blocked it
+        updatedChannel.setImportanceLockedByOEM(channel.isImportanceLockedByOEM());
+        if (updatedChannel.isImportanceLockedByOEM()) {
+            updatedChannel.setImportance(channel.getImportance());
+        }
+
         r.channels.put(updatedChannel.getId(), updatedChannel);
 
         if (onlyHasDefaultChannel(pkg, uid)) {
@@ -753,6 +782,44 @@
         }
     }
 
+    public void lockChannelsForOEM(String[] appOrChannelList) {
+        if (appOrChannelList == null) {
+            return;
+        }
+        for (String appOrChannel : appOrChannelList) {
+            if (!TextUtils.isEmpty(appOrChannel)) {
+                String[] appSplit = appOrChannel.split(NON_BLOCKABLE_CHANNEL_DELIM);
+                if (appSplit != null && appSplit.length > 0) {
+                    String appName = appSplit[0];
+                    String channelId = appSplit.length == 2 ? appSplit[1] : null;
+
+                    synchronized (mPackagePreferences) {
+                        for (PackagePreferences r : mPackagePreferences.values()) {
+                            if (r.pkg.equals(appName)) {
+                                if (channelId == null) {
+                                    // lock all channels for the app
+                                    r.oemLockedImportance = true;
+                                    for (NotificationChannel channel : r.channels.values()) {
+                                        channel.setImportanceLockedByOEM(true);
+                                    }
+                                } else {
+                                    NotificationChannel channel = r.channels.get(channelId);
+                                    if (channel != null) {
+                                        channel.setImportanceLockedByOEM(true);
+                                    } else {
+                                        // if this channel shows up in the future, make sure it'll
+                                        // be locked immediately
+                                        r.futureOemLockedChannels.add(channelId);
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+
     public NotificationChannelGroup getNotificationChannelGroupWithChannels(String pkg,
             int uid, String groupId, boolean includeDeleted) {
         Preconditions.checkNotNull(pkg);
@@ -1180,8 +1247,8 @@
         if (original.canShowBadge() != update.canShowBadge()) {
             update.lockFields(NotificationChannel.USER_LOCKED_SHOW_BADGE);
         }
-        if (original.isAppOverlayAllowed() != update.isAppOverlayAllowed()) {
-            update.lockFields(NotificationChannel.USER_LOCKED_ALLOW_APP_OVERLAY);
+        if (original.isBubbleAllowed() != update.isBubbleAllowed()) {
+            update.lockFields(NotificationChannel.USER_LOCKED_ALLOW_BUBBLE);
         }
     }
 
@@ -1602,8 +1669,10 @@
         int priority = DEFAULT_PRIORITY;
         int visibility = DEFAULT_VISIBILITY;
         boolean showBadge = DEFAULT_SHOW_BADGE;
-        boolean appOverlay = DEFAULT_ALLOW_APP_OVERLAY;
+        boolean allowBubble = DEFAULT_ALLOW_BUBBLE;
         int lockedAppFields = DEFAULT_LOCKED_APP_FIELDS;
+        boolean oemLockedImportance = DEFAULT_OEM_LOCKED_IMPORTANCE;
+        List<String> futureOemLockedChannels = new ArrayList<>();
 
         Delegate delegate = null;
         ArrayMap<String, NotificationChannel> channels = new ArrayMap<>();
diff --git a/services/core/java/com/android/server/notification/RankingHelper.java b/services/core/java/com/android/server/notification/RankingHelper.java
index b1e9144..b145e1e 100644
--- a/services/core/java/com/android/server/notification/RankingHelper.java
+++ b/services/core/java/com/android/server/notification/RankingHelper.java
@@ -106,9 +106,7 @@
 
         synchronized (mProxyByGroupTmp) {
             // record individual ranking result and nominate proxies for each group
-            // Note: iteration is done backwards such that the index can be used as a sort key
-            // in a string compare below
-            for (int i = N - 1; i >= 0; i--) {
+            for (int i = 0; i < N; i++) {
                 final NotificationRecord record = notificationList.get(i);
                 record.setAuthoritativeRank(i);
                 final String groupKey = record.getGroupKey();
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index 7f1fb6c..b6dae19 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -24,6 +24,7 @@
 import android.app.AppGlobals;
 import android.app.IApplicationThread;
 import android.app.PendingIntent;
+import android.app.admin.DevicePolicyManager;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -115,6 +116,7 @@
         private final ShortcutServiceInternal mShortcutServiceInternal;
         private final PackageCallbackList<IOnAppsChangedListener> mListeners
                 = new PackageCallbackList<IOnAppsChangedListener>();
+        private final DevicePolicyManager mDpm;
 
         private final MyPackageMonitor mPackageMonitor = new MyPackageMonitor();
 
@@ -133,6 +135,7 @@
                     LocalServices.getService(ShortcutServiceInternal.class));
             mShortcutServiceInternal.addListener(mPackageMonitor);
             mCallbackHandler = BackgroundThread.getHandler();
+            mDpm = (DevicePolicyManager) mContext.getSystemService(Context.DEVICE_POLICY_SERVICE);
         }
 
         @VisibleForTesting
@@ -301,6 +304,17 @@
         }
 
         @Override
+        public boolean shouldHideFromSuggestions(String packageName, UserHandle user) {
+            if (!canAccessProfile(user.getIdentifier(), "cannot get shouldHideFromSuggestions")) {
+                return false;
+            }
+            final PackageManagerInternal pmi = LocalServices.getService(
+                    PackageManagerInternal.class);
+            int flags = pmi.getDistractingPackageRestrictions(packageName, user.getIdentifier());
+            return (flags & PackageManager.RESTRICTION_HIDE_FROM_SUGGESTIONS) != 0;
+        }
+
+        @Override
         public ParceledListSlice<ResolveInfo> getLauncherActivities(String callingPackage,
                 String packageName, UserHandle user) throws RemoteException {
             ParceledListSlice<ResolveInfo> launcherActivities = queryActivitiesForUser(
@@ -313,30 +327,42 @@
                     Settings.Global.SHOW_HIDDEN_LAUNCHER_ICON_APPS_ENABLED, 1) == 0) {
                 return launcherActivities;
             }
-
-            final int callingUid = injectBinderCallingUid();
-            final ArrayList<ResolveInfo> result = new ArrayList<>(launcherActivities.getList());
-            final PackageManagerInternal pmInt =
-                    LocalServices.getService(PackageManagerInternal.class);
-            if (packageName != null) {
-                // If this hidden app should not be shown, return the original list.
-                // Otherwise, inject hidden activity that forwards user to app details page.
-                if (result.size() > 0) {
-                    return launcherActivities;
-                }
-                ApplicationInfo appInfo = pmInt.getApplicationInfo(packageName, /*flags*/ 0,
-                        callingUid, user.getIdentifier());
-                if (shouldShowHiddenApp(appInfo)) {
-                    ResolveInfo info = getHiddenAppActivityInfo(packageName, callingUid, user);
-                    if (info != null) {
-                        result.add(info);
-                    }
-                }
-                return new ParceledListSlice<>(result);
+            if (launcherActivities == null) {
+                // Cannot access profile, so we don't even return any hidden apps.
+                return null;
             }
 
-            long ident = injectClearCallingIdentity();
+            final int callingUid = injectBinderCallingUid();
+            final long ident = injectClearCallingIdentity();
             try {
+                if (mUm.getUserInfo(user.getIdentifier()).isManagedProfile()) {
+                    // Managed profile should not show hidden apps
+                    return launcherActivities;
+                }
+                if (mDpm.getDeviceOwnerComponentOnAnyUser() != null) {
+                    // Device owner devices should not show hidden apps
+                    return launcherActivities;
+                }
+
+                final ArrayList<ResolveInfo> result = new ArrayList<>(launcherActivities.getList());
+                final PackageManagerInternal pmInt =
+                        LocalServices.getService(PackageManagerInternal.class);
+                if (packageName != null) {
+                    // If this hidden app should not be shown, return the original list.
+                    // Otherwise, inject hidden activity that forwards user to app details page.
+                    if (result.size() > 0) {
+                        return launcherActivities;
+                    }
+                    ApplicationInfo appInfo = pmInt.getApplicationInfo(packageName, /*flags*/ 0,
+                            callingUid, user.getIdentifier());
+                    if (shouldShowHiddenApp(appInfo)) {
+                        ResolveInfo info = getHiddenAppActivityInfo(packageName, callingUid, user);
+                        if (info != null) {
+                            result.add(info);
+                        }
+                    }
+                    return new ParceledListSlice<>(result);
+                }
                 final HashSet<String> visiblePackages = new HashSet<>();
                 for (ResolveInfo info : result) {
                     visiblePackages.add(info.activityInfo.packageName);
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index d0b20e8..bf4e272 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -225,6 +225,17 @@
                 Slog.w(TAG, "Deleting orphan icon " + icon);
                 icon.delete();
             }
+
+            // Invalid sessions might have been marked while parsing. Re-write the database with
+            // the updated information.
+            writeSessionsLocked();
+
+            for (int i = 0; i < mSessions.size(); i++) {
+                final PackageInstallerSession session = mSessions.valueAt(i);
+                if (session.isStaged()) {
+                    mStagingManager.restoreSession(session);
+                }
+            }
         }
     }
 
@@ -329,9 +340,6 @@
 
                         if (valid) {
                             mSessions.put(session.sessionId, session);
-                            if (session.isStaged()) {
-                                mStagingManager.restoreSession(session);
-                            }
                         } else {
                             // Since this is early during boot we don't send
                             // any observer events about the session, but we
@@ -1122,6 +1130,11 @@
             mCallbacks.notifySessionProgressChanged(session.sessionId, session.userId, progress);
         }
 
+        public void onStagedSessionChanged(PackageInstallerSession session) {
+            writeSessionsAsync();
+            mPm.sendSessionUpdatedBroadcast(session.generateInfo(false), session.userId);
+        }
+
         public void onSessionFinished(final PackageInstallerSession session, boolean success) {
             mCallbacks.notifySessionFinished(session.sessionId, session.userId, success);
 
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 516927f..12d335d 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -2009,6 +2009,7 @@
         mCallback.onSessionFinished(this, success);
     }
 
+    /** {@hide} */
     void setStagedSessionReady() {
         synchronized (mLock) {
             mStagedSessionReady = true;
@@ -2016,6 +2017,7 @@
             mStagedSessionFailed = false;
             mStagedSessionErrorCode = SessionInfo.NO_ERROR;
         }
+        mCallback.onStagedSessionChanged(this);
     }
 
     /** {@hide} */
@@ -2026,6 +2028,33 @@
             mStagedSessionFailed = true;
             mStagedSessionErrorCode = errorCode;
         }
+        mCallback.onStagedSessionChanged(this);
+    }
+
+    /** {@hide} */
+    void setStagedSessionApplied() {
+        synchronized (mLock) {
+            mStagedSessionReady = false;
+            mStagedSessionApplied = true;
+            mStagedSessionFailed = false;
+            mStagedSessionErrorCode = SessionInfo.NO_ERROR;
+        }
+        mCallback.onStagedSessionChanged(this);
+    }
+
+    /** {@hide} */
+    boolean isStagedSessionReady() {
+        return mStagedSessionReady;
+    }
+
+    /** {@hide} */
+    boolean isStagedSessionApplied() {
+        return mStagedSessionApplied;
+    }
+
+    /** {@hide} */
+    boolean isStagedSessionFailed() {
+        return mStagedSessionFailed;
     }
 
     private void destroyInternal() {
@@ -2221,6 +2250,16 @@
         return permissionsArray;
     }
 
+    // Sanity check to be performed when the session is restored from an external file. Only one
+    // of the session states should be true, or none of them.
+    private static boolean isStagedSessionStateValid(boolean isReady, boolean isApplied,
+                                                     boolean isFailed) {
+        return (!isReady && !isApplied && !isFailed)
+                || (isReady && !isApplied && !isFailed)
+                || (!isReady && isApplied && !isFailed)
+                || (!isReady && !isApplied && isFailed);
+    }
+
     /**
      * Read new session from a {@link XmlPullParser xml description} and create it.
      *
@@ -2287,6 +2326,10 @@
         final boolean isApplied = readBooleanAttribute(in, ATTR_IS_APPLIED);
         final int stagedSessionErrorCode = readIntAttribute(in, ATTR_STAGED_SESSION_ERROR_CODE);
 
+        if (!isStagedSessionStateValid(isReady, isApplied, isFailed)) {
+            throw new IllegalArgumentException("Can't restore staged session with invalid state.");
+        }
+
         return new PackageInstallerSession(callback, context, pm, sessionProvider,
                 installerThread, stagingManager, sessionId, userId, installerPackageName,
                 installerUid, params, createdMillis, stageDir, stageCid, prepared, sealed,
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 597f5b3..6baf12c 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -84,6 +84,7 @@
 import static android.content.pm.PackageManager.MOVE_FAILED_SYSTEM_PACKAGE;
 import static android.content.pm.PackageManager.PERMISSION_DENIED;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.content.pm.PackageManager.RESTRICTION_NONE;
 import static android.content.pm.PackageParser.isApkFile;
 import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
 import static android.os.storage.StorageManager.FLAG_STORAGE_CE;
@@ -238,6 +239,7 @@
 import android.os.storage.StorageManagerInternal;
 import android.os.storage.VolumeInfo;
 import android.os.storage.VolumeRecord;
+import android.permission.PermissionControllerManager;
 import android.provider.MediaStore;
 import android.provider.Settings.Global;
 import android.provider.Settings.Secure;
@@ -316,7 +318,6 @@
 import com.android.server.pm.permission.PermissionManagerInternal.PermissionCallback;
 import com.android.server.pm.permission.PermissionManagerService;
 import com.android.server.pm.permission.PermissionsState;
-import com.android.server.pm.permission.PermissionsState.PermissionState;
 import com.android.server.security.VerityUtils;
 import com.android.server.storage.DeviceStorageMonitorInternal;
 import com.android.server.wm.ActivityTaskManagerInternal;
@@ -369,6 +370,7 @@
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicReference;
 import java.util.function.BiConsumer;
 import java.util.function.Consumer;
 import java.util.function.Predicate;
@@ -442,6 +444,8 @@
     private static final boolean ENABLE_FREE_CACHE_V2 =
             SystemProperties.getBoolean("fw.free_cache_v2", true);
 
+    private static final long BACKUP_TIMEOUT_MILLIS = TimeUnit.SECONDS.toMillis(60);
+
     private static final int RADIO_UID = Process.PHONE_UID;
     private static final int LOG_UID = Process.LOG_UID;
     private static final int NFC_UID = Process.NFC_UID;
@@ -12657,22 +12661,30 @@
         info.sendPackageRemovedBroadcasts(true /*killApp*/);
     }
 
+    private void sendDistractingPackagesChanged(String[] pkgList, int[] uidList, int userId,
+            int distractionFlags) {
+        final Bundle extras = new Bundle(3);
+        extras.putStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST, pkgList);
+        extras.putIntArray(Intent.EXTRA_CHANGED_UID_LIST, uidList);
+        extras.putInt(Intent.EXTRA_DISTRACTION_RESTRICTIONS, distractionFlags);
+        sendPackageBroadcast(Intent.ACTION_DISTRACTING_PACKAGES_CHANGED, null, extras,
+                Intent.FLAG_RECEIVER_REGISTERED_ONLY, null, null, new int[]{userId}, null);
+    }
+
     private void sendPackagesSuspendedForUser(String[] pkgList, int[] uidList, int userId,
             boolean suspended, PersistableBundle launcherExtras) {
-        if (pkgList.length > 0) {
-            Bundle extras = new Bundle(1);
-            extras.putStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST, pkgList);
-            extras.putIntArray(Intent.EXTRA_CHANGED_UID_LIST, uidList);
-            if (launcherExtras != null) {
-                extras.putBundle(Intent.EXTRA_LAUNCHER_EXTRAS,
-                        new Bundle(launcherExtras.deepCopy()));
-            }
-            sendPackageBroadcast(
-                    suspended ? Intent.ACTION_PACKAGES_SUSPENDED
-                            : Intent.ACTION_PACKAGES_UNSUSPENDED,
-                    null, extras, Intent.FLAG_RECEIVER_REGISTERED_ONLY, null, null,
-                    new int[] {userId}, null);
+        final Bundle extras = new Bundle(3);
+        extras.putStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST, pkgList);
+        extras.putIntArray(Intent.EXTRA_CHANGED_UID_LIST, uidList);
+        if (launcherExtras != null) {
+            extras.putBundle(Intent.EXTRA_LAUNCHER_EXTRAS,
+                    new Bundle(launcherExtras.deepCopy()));
         }
+        sendPackageBroadcast(
+                suspended ? Intent.ACTION_PACKAGES_SUSPENDED
+                        : Intent.ACTION_PACKAGES_UNSUSPENDED,
+                null, extras, Intent.FLAG_RECEIVER_REGISTERED_ONLY, null, null,
+                new int[] {userId}, null);
     }
 
     /**
@@ -12822,6 +12834,62 @@
     }
 
     @Override
+    public String[] setDistractingPackageRestrictionsAsUser(String[] packageNames,
+            int restrictionFlags, int userId) {
+        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.SUSPEND_APPS,
+                "setPackagesSuspendedAsUser");
+
+        final int callingUid = Binder.getCallingUid();
+        if (callingUid != Process.ROOT_UID && callingUid != Process.SYSTEM_UID
+                && UserHandle.getUserId(callingUid) != userId) {
+            throw new SecurityException("Calling uid " + callingUid + " cannot call for user "
+                    + userId);
+        }
+        Preconditions.checkNotNull(packageNames, "packageNames cannot be null");
+
+        final List<String> changedPackagesList = new ArrayList<>(packageNames.length);
+        final IntArray changedUids = new IntArray(packageNames.length);
+        final List<String> unactionedPackages = new ArrayList<>(packageNames.length);
+
+        for (int i = 0; i < packageNames.length; i++) {
+            final String packageName = packageNames[i];
+            final PackageSetting pkgSetting;
+            synchronized (mPackages) {
+                pkgSetting = mSettings.mPackages.get(packageName);
+                if (pkgSetting == null || filterAppAccessLPr(pkgSetting, callingUid, userId)) {
+                    Slog.w(TAG, "Could not find package setting for package: " + packageName
+                            + ". Skipping...");
+                    unactionedPackages.add(packageName);
+                    continue;
+                }
+            }
+            if (restrictionFlags != 0 && !canSuspendPackageForUserInternal(packageName, userId)) {
+                unactionedPackages.add(packageName);
+                continue;
+            }
+            synchronized (mPackages) {
+                final int oldDistractionFlags = pkgSetting.getDistractionFlags(userId);
+                if (restrictionFlags != oldDistractionFlags) {
+                    pkgSetting.setDistractionFlags(restrictionFlags, userId);
+                    changedPackagesList.add(packageName);
+                    changedUids.add(UserHandle.getUid(userId, pkgSetting.appId));
+                }
+            }
+        }
+
+        if (!changedPackagesList.isEmpty()) {
+            final String[] changedPackages = changedPackagesList.toArray(
+                    new String[changedPackagesList.size()]);
+            sendDistractingPackagesChanged(changedPackages, changedUids.toArray(), userId,
+                    restrictionFlags);
+            synchronized (mPackages) {
+                scheduleWritePackageRestrictionsLocked(userId);
+            }
+        }
+        return unactionedPackages.toArray(new String[0]);
+    }
+
+    @Override
     public String[] setPackagesSuspendedAsUser(String[] packageNames, boolean suspended,
             PersistableBundle appExtras, PersistableBundle launcherExtras,
             SuspendDialogInfo dialogInfo, String callingPackage, int userId) {
@@ -12846,44 +12914,37 @@
         final List<String> changedPackagesList = new ArrayList<>(packageNames.length);
         final IntArray changedUids = new IntArray(packageNames.length);
         final List<String> unactionedPackages = new ArrayList<>(packageNames.length);
-        final long callingId = Binder.clearCallingIdentity();
-        try {
-            for (int i = 0; i < packageNames.length; i++) {
-                final String packageName = packageNames[i];
-                if (callingPackage.equals(packageName)) {
-                    Slog.w(TAG, "Calling package: " + callingPackage + " trying to "
-                            + (suspended ? "" : "un") + "suspend itself. Ignoring");
-                    unactionedPackages.add(packageName);
-                    continue;
-                }
-                PackageSetting pkgSetting;
-                synchronized (mPackages) {
-                    pkgSetting = mSettings.mPackages.get(packageName);
-                    if (pkgSetting == null
-                            || filterAppAccessLPr(pkgSetting, callingUid, userId)) {
-                        Slog.w(TAG, "Could not find package setting for package: " + packageName
-                                + ". Skipping suspending/un-suspending.");
-                        unactionedPackages.add(packageName);
-                        continue;
-                    }
-                }
-                if (suspended && !canSuspendPackageForUserInternal(packageName, userId)) {
-                    unactionedPackages.add(packageName);
-                    continue;
-                }
-                synchronized (mPackages) {
-                    pkgSetting = mSettings.mPackages.get(packageName);
-                    if (pkgSetting != null) {
-                        pkgSetting.setSuspended(suspended, callingPackage, dialogInfo, appExtras,
-                                launcherExtras, userId);
-                    }
-                }
-                changedPackagesList.add(packageName);
-                changedUids.add(UserHandle.getUid(userId, pkgSetting.appId));
+
+        for (int i = 0; i < packageNames.length; i++) {
+            final String packageName = packageNames[i];
+            if (callingPackage.equals(packageName)) {
+                Slog.w(TAG, "Calling package: " + callingPackage + " trying to "
+                        + (suspended ? "" : "un") + "suspend itself. Ignoring");
+                unactionedPackages.add(packageName);
+                continue;
             }
-        } finally {
-            Binder.restoreCallingIdentity(callingId);
+            final PackageSetting pkgSetting;
+            synchronized (mPackages) {
+                pkgSetting = mSettings.mPackages.get(packageName);
+                if (pkgSetting == null || filterAppAccessLPr(pkgSetting, callingUid, userId)) {
+                    Slog.w(TAG, "Could not find package setting for package: " + packageName
+                            + ". Skipping suspending/un-suspending.");
+                    unactionedPackages.add(packageName);
+                    continue;
+                }
+            }
+            if (suspended && !canSuspendPackageForUserInternal(packageName, userId)) {
+                unactionedPackages.add(packageName);
+                continue;
+            }
+            synchronized (mPackages) {
+                pkgSetting.setSuspended(suspended, callingPackage, dialogInfo, appExtras,
+                        launcherExtras, userId);
+            }
+            changedPackagesList.add(packageName);
+            changedUids.add(UserHandle.getUid(userId, pkgSetting.appId));
         }
+
         if (!changedPackagesList.isEmpty()) {
             final String[] changedPackages = changedPackagesList.toArray(
                     new String[changedPackagesList.size()]);
@@ -13035,88 +13096,87 @@
                     + " cannot query getUnsuspendablePackagesForUser for user " + userId);
         }
         final ArraySet<String> unactionablePackages = new ArraySet<>();
-        final long identity = Binder.clearCallingIdentity();
-        try {
-            for (String packageName : packageNames) {
-                if (!canSuspendPackageForUserInternal(packageName, userId)) {
-                    unactionablePackages.add(packageName);
-                }
+        for (String packageName : packageNames) {
+            if (!canSuspendPackageForUserInternal(packageName, userId)) {
+                unactionablePackages.add(packageName);
             }
-        } finally {
-            Binder.restoreCallingIdentity(identity);
         }
         return unactionablePackages.toArray(new String[unactionablePackages.size()]);
     }
 
     private boolean canSuspendPackageForUserInternal(String packageName, int userId) {
-        if (isPackageDeviceAdmin(packageName, userId)) {
-            Slog.w(TAG, "Cannot suspend package \"" + packageName
-                    + "\": has an active device admin");
-            return false;
-        }
-
-        String activeLauncherPackageName = getActiveLauncherPackageName(userId);
-        if (packageName.equals(activeLauncherPackageName)) {
-            Slog.w(TAG, "Cannot suspend package \"" + packageName
-                    + "\": contains the active launcher");
-            return false;
-        }
-
-        if (packageName.equals(mRequiredInstallerPackage)) {
-            Slog.w(TAG, "Cannot suspend package \"" + packageName
-                    + "\": required for package installation");
-            return false;
-        }
-
-        if (packageName.equals(mRequiredUninstallerPackage)) {
-            Slog.w(TAG, "Cannot suspend package \"" + packageName
-                    + "\": required for package uninstallation");
-            return false;
-        }
-
-        if (packageName.equals(mRequiredVerifierPackage)) {
-            Slog.w(TAG, "Cannot suspend package \"" + packageName
-                    + "\": required for package verification");
-            return false;
-        }
-
-        if (packageName.equals(getDefaultDialerPackageName(userId))) {
-            Slog.w(TAG, "Cannot suspend package \"" + packageName
-                    + "\": is the default dialer");
-            return false;
-        }
-
-        if (packageName.equals(mRequiredPermissionControllerPackage)) {
-            Slog.w(TAG, "Cannot suspend package \"" + packageName
-                    + "\": required for permissions management");
-            return false;
-        }
-
-        synchronized (mPackages) {
-            if (mProtectedPackages.isPackageStateProtected(userId, packageName)) {
+        final long callingId = Binder.clearCallingIdentity();
+        try {
+            if (isPackageDeviceAdmin(packageName, userId)) {
                 Slog.w(TAG, "Cannot suspend package \"" + packageName
-                        + "\": protected package");
+                        + "\": has an active device admin");
                 return false;
             }
 
-            // Cannot suspend static shared libs as they are considered
-            // a part of the using app (emulating static linking). Also
-            // static libs are installed always on internal storage.
-            PackageParser.Package pkg = mPackages.get(packageName);
-            if (pkg != null && pkg.applicationInfo.isStaticSharedLibrary()) {
-                Slog.w(TAG, "Cannot suspend package: " + packageName
-                        + " providing static shared library: "
-                        + pkg.staticSharedLibName);
+            String activeLauncherPackageName = getActiveLauncherPackageName(userId);
+            if (packageName.equals(activeLauncherPackageName)) {
+                Slog.w(TAG, "Cannot suspend package \"" + packageName
+                        + "\": contains the active launcher");
                 return false;
             }
-        }
 
-        if (PLATFORM_PACKAGE_NAME.equals(packageName)) {
-            Slog.w(TAG, "Cannot suspend the platform package: " + packageName);
-            return false;
-        }
+            if (packageName.equals(mRequiredInstallerPackage)) {
+                Slog.w(TAG, "Cannot suspend package \"" + packageName
+                        + "\": required for package installation");
+                return false;
+            }
 
-        return true;
+            if (packageName.equals(mRequiredUninstallerPackage)) {
+                Slog.w(TAG, "Cannot suspend package \"" + packageName
+                        + "\": required for package uninstallation");
+                return false;
+            }
+
+            if (packageName.equals(mRequiredVerifierPackage)) {
+                Slog.w(TAG, "Cannot suspend package \"" + packageName
+                        + "\": required for package verification");
+                return false;
+            }
+
+            if (packageName.equals(getDefaultDialerPackageName(userId))) {
+                Slog.w(TAG, "Cannot suspend package \"" + packageName
+                        + "\": is the default dialer");
+                return false;
+            }
+
+            if (packageName.equals(mRequiredPermissionControllerPackage)) {
+                Slog.w(TAG, "Cannot suspend package \"" + packageName
+                        + "\": required for permissions management");
+                return false;
+            }
+
+            synchronized (mPackages) {
+                if (mProtectedPackages.isPackageStateProtected(userId, packageName)) {
+                    Slog.w(TAG, "Cannot suspend package \"" + packageName
+                            + "\": protected package");
+                    return false;
+                }
+
+                // Cannot suspend static shared libs as they are considered
+                // a part of the using app (emulating static linking). Also
+                // static libs are installed always on internal storage.
+                PackageParser.Package pkg = mPackages.get(packageName);
+                if (pkg != null && pkg.applicationInfo.isStaticSharedLibrary()) {
+                    Slog.w(TAG, "Cannot suspend package: " + packageName
+                            + " providing static shared library: "
+                            + pkg.staticSharedLibName);
+                    return false;
+                }
+            }
+
+            if (PLATFORM_PACKAGE_NAME.equals(packageName)) {
+                Slog.w(TAG, "Cannot suspend the platform package: " + packageName);
+                return false;
+            }
+            return true;
+        } finally {
+            Binder.restoreCallingIdentity(callingId);
+        }
     }
 
     private String getActiveLauncherPackageName(int userId) {
@@ -13717,14 +13777,15 @@
             IBackupManager bm = IBackupManager.Stub.asInterface(
                     ServiceManager.getService(Context.BACKUP_SERVICE));
             if (bm != null) {
+                int userId = args.user.getIdentifier();
                 if (DEBUG_INSTALL) {
-                    Log.v(TAG, "token " + token + " to BM for possible restore");
+                    Log.v(TAG, "token " + token + " to BM for possible restore for user " + userId);
                 }
                 Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "restore", token);
                 try {
-                    // TODO: http://b/22388012
-                    if (bm.isBackupServiceActive(UserHandle.USER_SYSTEM)) {
-                        bm.restoreAtInstall(res.pkg.applicationInfo.packageName, token);
+                    if (bm.isBackupServiceActive(userId)) {
+                        bm.restoreAtInstallForUser(
+                                userId, res.pkg.applicationInfo.packageName, token);
                     } else {
                         doRestore = false;
                     }
@@ -18425,6 +18486,7 @@
                     true /*stopped*/,
                     true /*notLaunched*/,
                     false /*hidden*/,
+                    0 /*distractionFlags*/,
                     false /*suspended*/,
                     null /*suspendingPackage*/,
                     null /*dialogInfo*/,
@@ -19514,28 +19576,32 @@
             throw new SecurityException("Only the system may call getPermissionGrantBackup()");
         }
 
-        ByteArrayOutputStream dataStream = new ByteArrayOutputStream();
-        try {
-            final XmlSerializer serializer = new FastXmlSerializer();
-            serializer.setOutput(dataStream, StandardCharsets.UTF_8.name());
-            serializer.startDocument(null, true);
-            serializer.startTag(null, TAG_PERMISSION_BACKUP);
+        AtomicReference<byte[]> backup = new AtomicReference<>();
+        mContext.getSystemService(PermissionControllerManager.class).getRuntimePermissionBackup(
+                UserHandle.of(userId), mContext.getMainExecutor(), (b) -> {
+                    synchronized (backup) {
+                        backup.set(b);
+                        backup.notifyAll();
+                    }
+                });
 
-            synchronized (mPackages) {
-                serializeRuntimePermissionGrantsLPr(serializer, userId);
-            }
+        long start = System.currentTimeMillis();
+        synchronized (backup) {
+            while (backup.get() == null) {
+                long timeLeft = start + BACKUP_TIMEOUT_MILLIS - System.currentTimeMillis();
+                if (timeLeft <= 0) {
+                    return null;
+                }
 
-            serializer.endTag(null, TAG_PERMISSION_BACKUP);
-            serializer.endDocument();
-            serializer.flush();
-        } catch (Exception e) {
-            if (DEBUG_BACKUP) {
-                Slog.e(TAG, "Unable to write default apps for backup", e);
+                try {
+                    backup.wait(timeLeft);
+                } catch (InterruptedException ignored) {
+                    return null;
+                }
             }
-            return null;
         }
 
-        return dataStream.toByteArray();
+        return backup.get();
     }
 
     @Override
@@ -19561,66 +19627,6 @@
     }
 
     @GuardedBy("mPackages")
-    private void serializeRuntimePermissionGrantsLPr(XmlSerializer serializer, final int userId)
-            throws IOException {
-        serializer.startTag(null, TAG_ALL_GRANTS);
-
-        final int N = mSettings.mPackages.size();
-        for (int i = 0; i < N; i++) {
-            final PackageSetting ps = mSettings.mPackages.valueAt(i);
-            boolean pkgGrantsKnown = false;
-
-            PermissionsState packagePerms = ps.getPermissionsState();
-
-            for (PermissionState state : packagePerms.getRuntimePermissionStates(userId)) {
-                final int grantFlags = state.getFlags();
-                // only look at grants that are not system/policy fixed
-                if ((grantFlags & SYSTEM_RUNTIME_GRANT_MASK) == 0) {
-                    final boolean isGranted = state.isGranted();
-                    // And only back up the user-twiddled state bits
-                    if (isGranted || (grantFlags & USER_RUNTIME_GRANT_MASK) != 0) {
-                        final String packageName = mSettings.mPackages.keyAt(i);
-                        if (!pkgGrantsKnown) {
-                            serializer.startTag(null, TAG_GRANT);
-                            serializer.attribute(null, ATTR_PACKAGE_NAME, packageName);
-                            pkgGrantsKnown = true;
-                        }
-
-                        final boolean userSet =
-                                (grantFlags & FLAG_PERMISSION_USER_SET) != 0;
-                        final boolean userFixed =
-                                (grantFlags & FLAG_PERMISSION_USER_FIXED) != 0;
-                        final boolean revoke =
-                                (grantFlags & FLAG_PERMISSION_REVOKE_ON_UPGRADE) != 0;
-
-                        serializer.startTag(null, TAG_PERMISSION);
-                        serializer.attribute(null, ATTR_PERMISSION_NAME, state.getName());
-                        if (isGranted) {
-                            serializer.attribute(null, ATTR_IS_GRANTED, "true");
-                        }
-                        if (userSet) {
-                            serializer.attribute(null, ATTR_USER_SET, "true");
-                        }
-                        if (userFixed) {
-                            serializer.attribute(null, ATTR_USER_FIXED, "true");
-                        }
-                        if (revoke) {
-                            serializer.attribute(null, ATTR_REVOKE_ON_UPGRADE, "true");
-                        }
-                        serializer.endTag(null, TAG_PERMISSION);
-                    }
-                }
-            }
-
-            if (pkgGrantsKnown) {
-                serializer.endTag(null, TAG_GRANT);
-            }
-        }
-
-        serializer.endTag(null, TAG_ALL_GRANTS);
-    }
-
-    @GuardedBy("mPackages")
     private void processRestoredPermissionGrantsLPr(XmlPullParser parser, int userId)
             throws XmlPullParserException, IOException {
         String pkgName = null;
@@ -23132,6 +23138,11 @@
         }
 
         @Override
+        public void setLocationExtraPackagesProvider(PackagesProvider provider) {
+            mDefaultPermissionPolicy.setLocationExtraPackagesProvider(provider);
+        }
+
+        @Override
         public void setVoiceInteractionPackagesProvider(PackagesProvider provider) {
             mDefaultPermissionPolicy.setVoiceInteractionPackagesProvider(provider);
         }
@@ -23240,6 +23251,14 @@
         }
 
         @Override
+        public int getDistractingPackageRestrictions(String packageName, int userId) {
+            synchronized (mPackages) {
+                final PackageSetting ps = mSettings.mPackages.get(packageName);
+                return (ps != null) ? ps.getDistractionFlags(userId) : RESTRICTION_NONE;
+            }
+        }
+
+        @Override
         public int getPackageUid(String packageName, int flags, int userId) {
             return PackageManagerService.this
                     .getPackageUid(packageName, flags, userId);
diff --git a/services/core/java/com/android/server/pm/PackageSettingBase.java b/services/core/java/com/android/server/pm/PackageSettingBase.java
index 3c22f07..58f262c 100644
--- a/services/core/java/com/android/server/pm/PackageSettingBase.java
+++ b/services/core/java/com/android/server/pm/PackageSettingBase.java
@@ -392,6 +392,14 @@
         modifyUserState(userId).hidden = hidden;
     }
 
+    int getDistractionFlags(int userId) {
+        return readUserState(userId).distractionFlags;
+    }
+
+    void setDistractionFlags(int distractionFlags, int userId) {
+        modifyUserState(userId).distractionFlags = distractionFlags;
+    }
+
     boolean getSuspended(int userId) {
         return readUserState(userId).suspended;
     }
@@ -423,7 +431,8 @@
     }
 
     void setUserState(int userId, long ceDataInode, int enabled, boolean installed, boolean stopped,
-            boolean notLaunched, boolean hidden, boolean suspended, String suspendingPackage,
+            boolean notLaunched, boolean hidden, int distractionFlags, boolean suspended,
+            String suspendingPackage,
             SuspendDialogInfo dialogInfo, PersistableBundle suspendedAppExtras,
             PersistableBundle suspendedLauncherExtras, boolean instantApp,
             boolean virtualPreload, String lastDisableAppCaller,
@@ -437,6 +446,7 @@
         state.stopped = stopped;
         state.notLaunched = notLaunched;
         state.hidden = hidden;
+        state.distractionFlags = distractionFlags;
         state.suspended = suspended;
         state.suspendingPackage = suspendingPackage;
         state.dialogInfo = dialogInfo;
@@ -607,6 +617,7 @@
             }
             proto.write(PackageProto.UserInfoProto.INSTALL_TYPE, installType);
             proto.write(PackageProto.UserInfoProto.IS_HIDDEN, state.hidden);
+            proto.write(PackageProto.UserInfoProto.DISTRACTION_FLAGS, state.distractionFlags);
             proto.write(PackageProto.UserInfoProto.IS_SUSPENDED, state.suspended);
             if (state.suspended) {
                 proto.write(PackageProto.UserInfoProto.SUSPENDING_PACKAGE, state.suspendingPackage);
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index c524dba..95da209 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -223,6 +223,7 @@
     private static final String ATTR_BLOCKED = "blocked";
     // New name for the above attribute.
     private static final String ATTR_HIDDEN = "hidden";
+    private static final String ATTR_DISTRACTION_FLAGS = "distraction_flags";
     private static final String ATTR_SUSPENDED = "suspended";
     private static final String ATTR_SUSPENDING_PACKAGE = "suspending-package";
     /**
@@ -734,6 +735,7 @@
                                 true /*stopped*/,
                                 true /*notLaunched*/,
                                 false /*hidden*/,
+                                0 /*distractionFlags*/,
                                 false /*suspended*/,
                                 null /*suspendingPackage*/,
                                 null /*dialogInfo*/,
@@ -1628,6 +1630,7 @@
                                 false /*stopped*/,
                                 false /*notLaunched*/,
                                 false /*hidden*/,
+                                0 /*distractionFlags*/,
                                 false /*suspended*/,
                                 null /*suspendingPackage*/,
                                 null /*dialogInfo*/,
@@ -1703,6 +1706,8 @@
                     hidden = hiddenStr == null
                             ? hidden : Boolean.parseBoolean(hiddenStr);
 
+                    final int distractionFlags = XmlUtils.readIntAttribute(parser,
+                            ATTR_DISTRACTION_FLAGS, 0);
                     final boolean suspended = XmlUtils.readBooleanAttribute(parser, ATTR_SUSPENDED,
                             false);
                     String suspendingPackage = parser.getAttributeValue(null,
@@ -1781,7 +1786,8 @@
                         setBlockUninstallLPw(userId, name, true);
                     }
                     ps.setUserState(userId, ceDataInode, enabled, installed, stopped, notLaunched,
-                            hidden, suspended, suspendingPackage, suspendDialogInfo,
+                            hidden, distractionFlags, suspended, suspendingPackage,
+                            suspendDialogInfo,
                             suspendedAppExtras, suspendedLauncherExtras, instantApp, virtualPreload,
                             enabledCaller, enabledComponents, disabledComponents, verifState,
                             linkGeneration, installReason, harmfulAppWarning);
@@ -2089,6 +2095,10 @@
                 if (ustate.hidden) {
                     serializer.attribute(null, ATTR_HIDDEN, "true");
                 }
+                if (ustate.distractionFlags != 0) {
+                    serializer.attribute(null, ATTR_DISTRACTION_FLAGS,
+                            Integer.toString(ustate.distractionFlags));
+                }
                 if (ustate.suspended) {
                     serializer.attribute(null, ATTR_SUSPENDED, "true");
                     if (ustate.suspendingPackage != null) {
diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java
index c297c62..bd14223 100644
--- a/services/core/java/com/android/server/pm/StagingManager.java
+++ b/services/core/java/com/android/server/pm/StagingManager.java
@@ -130,7 +130,7 @@
                 ServiceManager.getService("apexservice"));
         boolean success;
         try {
-            success = apex.submitStagedSession(sessionId, apexInfoList);
+            success = apex.submitStagedSession(sessionId, new int[0], apexInfoList);
         } catch (RemoteException re) {
             Slog.e(TAG, "Unable to contact apexservice", re);
             return false;
@@ -168,7 +168,6 @@
         } else {
             session.setStagedSessionFailed(SessionInfo.VERIFICATION_FAILED);
         }
-        mPm.sendSessionUpdatedBroadcast(session.generateInfo(false), session.userId);
     }
 
     void commitSession(@NonNull PackageInstallerSession session) {
diff --git a/services/core/java/com/android/server/pm/dex/DexLogger.java b/services/core/java/com/android/server/pm/dex/DexLogger.java
index 68a755b..78fa82c 100644
--- a/services/core/java/com/android/server/pm/dex/DexLogger.java
+++ b/services/core/java/com/android/server/pm/dex/DexLogger.java
@@ -28,7 +28,6 @@
 import android.util.Slog;
 import android.util.SparseArray;
 
-import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.pm.Installer;
 import com.android.server.pm.Installer.InstallerException;
@@ -53,21 +52,18 @@
 
     private final IPackageManager mPackageManager;
     private final PackageDynamicCodeLoading mPackageDynamicCodeLoading;
-    private final Object mInstallLock;
-    @GuardedBy("mInstallLock")
     private final Installer mInstaller;
 
-    public DexLogger(IPackageManager pms, Installer installer, Object installLock) {
-        this(pms, installer, installLock, new PackageDynamicCodeLoading());
+    public DexLogger(IPackageManager pms, Installer installer) {
+        this(pms, installer, new PackageDynamicCodeLoading());
     }
 
     @VisibleForTesting
-    DexLogger(IPackageManager pms, Installer installer, Object installLock,
+    DexLogger(IPackageManager pms, Installer installer,
             PackageDynamicCodeLoading packageDynamicCodeLoading) {
         mPackageManager = pms;
         mPackageDynamicCodeLoading = packageDynamicCodeLoading;
         mInstaller = installer;
-        mInstallLock = installLock;
     }
 
     public Set<String> getAllPackagesWithDynamicCodeLoading() {
@@ -131,14 +127,16 @@
             }
 
             byte[] hash = null;
-            synchronized (mInstallLock) {
-                try {
-                    hash = mInstaller.hashSecondaryDexFile(filePath, packageName, appInfo.uid,
-                            appInfo.volumeUuid, storageFlags);
-                } catch (InstallerException e) {
-                    Slog.e(TAG, "Got InstallerException when hashing file " + filePath
-                            + ": " + e.getMessage());
-                }
+            try {
+                // Note that we do not take the install lock here. Hashing should never interfere
+                // with app update/compilation/removal. We may get anomalous results if a file
+                // changes while we hash it, but that can happen anyway and is harmless for our
+                // purposes.
+                hash = mInstaller.hashSecondaryDexFile(filePath, packageName, appInfo.uid,
+                        appInfo.volumeUuid, storageFlags);
+            } catch (InstallerException e) {
+                Slog.e(TAG, "Got InstallerException when hashing file " + filePath
+                        + ": " + e.getMessage());
             }
 
             String fileName = new File(filePath).getName();
diff --git a/services/core/java/com/android/server/pm/dex/DexManager.java b/services/core/java/com/android/server/pm/dex/DexManager.java
index e57d9d7..b546836 100644
--- a/services/core/java/com/android/server/pm/dex/DexManager.java
+++ b/services/core/java/com/android/server/pm/dex/DexManager.java
@@ -129,7 +129,7 @@
         mPackageDexOptimizer = pdo;
         mInstaller = installer;
         mInstallLock = installLock;
-        mDexLogger = new DexLogger(pms, installer, installLock);
+        mDexLogger = new DexLogger(pms, installer);
     }
 
     public DexLogger getDexLogger() {
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 789664d..ceaf69d 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -225,6 +225,7 @@
     private final Handler mHandler;
 
     private PackagesProvider mLocationPackagesProvider;
+    private PackagesProvider mLocationExtraPackagesProvider;
     private PackagesProvider mVoiceInteractionPackagesProvider;
     private PackagesProvider mSmsAppPackagesProvider;
     private PackagesProvider mDialerAppPackagesProvider;
@@ -270,6 +271,13 @@
         }
     }
 
+    /** Sets the provider for loction extra packages. */
+    public void setLocationExtraPackagesProvider(PackagesProvider provider) {
+        synchronized (mLock) {
+            mLocationExtraPackagesProvider = provider;
+        }
+    }
+
     public void setVoiceInteractionPackagesProvider(PackagesProvider provider) {
         synchronized (mLock) {
             mVoiceInteractionPackagesProvider = provider;
@@ -403,6 +411,7 @@
         Log.i(TAG, "Granting permissions to default platform handlers for user " + userId);
 
         final PackagesProvider locationPackagesProvider;
+        final PackagesProvider locationExtraPackagesProvider;
         final PackagesProvider voiceInteractionPackagesProvider;
         final PackagesProvider smsAppPackagesProvider;
         final PackagesProvider dialerAppPackagesProvider;
@@ -412,6 +421,7 @@
 
         synchronized (mLock) {
             locationPackagesProvider = mLocationPackagesProvider;
+            locationExtraPackagesProvider = mLocationExtraPackagesProvider;
             voiceInteractionPackagesProvider = mVoiceInteractionPackagesProvider;
             smsAppPackagesProvider = mSmsAppPackagesProvider;
             dialerAppPackagesProvider = mDialerAppPackagesProvider;
@@ -424,6 +434,8 @@
                 ? voiceInteractionPackagesProvider.getPackages(userId) : null;
         String[] locationPackageNames = (locationPackagesProvider != null)
                 ? locationPackagesProvider.getPackages(userId) : null;
+        String[] locationExtraPackageNames = (locationExtraPackagesProvider != null)
+                ? locationExtraPackagesProvider.getPackages(userId) : null;
         String[] smsAppPackageNames = (smsAppPackagesProvider != null)
                 ? smsAppPackagesProvider.getPackages(userId) : null;
         String[] dialerAppPackageNames = (dialerAppPackagesProvider != null)
@@ -638,6 +650,12 @@
                         LOCATION_PERMISSIONS, ACTIVITY_RECOGNITION_PERMISSIONS);
             }
         }
+        if (locationExtraPackageNames != null) {
+            // Also grant location permission to location extra packages.
+            for (String packageName : locationExtraPackageNames) {
+                grantPermissionsToSystemPackage(packageName, userId, LOCATION_PERMISSIONS);
+            }
+        }
 
         // Music
         Intent musicIntent = new Intent(Intent.ACTION_VIEW)
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index c5434ea..ff93345 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -84,8 +84,10 @@
 import static android.view.WindowManagerGlobal.ADD_PERMISSION_DENIED;
 
 import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_COVERED;
-import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_COVER_ABSENT;
-import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_UNCOVERED;
+import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs
+        .CAMERA_LENS_COVER_ABSENT;
+import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs
+        .CAMERA_LENS_UNCOVERED;
 import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.LID_CLOSED;
 import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.LID_OPEN;
 import static com.android.server.wm.WindowManagerPolicyProto.KEYGUARD_DELEGATE;
@@ -817,6 +819,13 @@
         }
     };
 
+    private Runnable mPossibleVeryLongPressReboot = new Runnable() {
+        @Override
+        public void run() {
+            mActivityManagerInternal.prepareForPossibleShutdown();
+        }
+    };
+
     private void handleRingerChordGesture() {
         if (mRingerToggleChord == VOLUME_HUSH_OFF) {
             return;
@@ -962,6 +971,8 @@
         // Inform the StatusBar; but do not allow it to consume the event.
         sendSystemKeyToStatusBarAsync(event.getKeyCode());
 
+        schedulePossibleVeryLongPressReboot();
+
         // If the power key has still not yet been handled, then detect short
         // press, long press, or multi press and decide what to do.
         mPowerKeyHandled = hungUp || mScreenshotChordVolumeDownKeyTriggered
@@ -1065,6 +1076,7 @@
         if (hasVeryLongPressOnPowerBehavior()) {
             mHandler.removeMessages(MSG_POWER_VERY_LONG_PRESS);
         }
+        cancelPossibleVeryLongPressReboot();
     }
 
     private void cancelPendingBackKeyAction() {
@@ -4923,6 +4935,15 @@
         }
     }
 
+    private void schedulePossibleVeryLongPressReboot() {
+        mHandler.removeCallbacks(mPossibleVeryLongPressReboot);
+        mHandler.postDelayed(mPossibleVeryLongPressReboot, mVeryLongPressTimeout);
+    }
+
+    private void cancelPossibleVeryLongPressReboot() {
+        mHandler.removeCallbacks(mPossibleVeryLongPressReboot);
+    }
+
     // TODO (multidisplay): Support multiple displays in WindowManagerPolicy.
     private void updateScreenOffSleepToken(boolean acquire) {
         if (acquire) {
diff --git a/services/core/java/com/android/server/role/RoleManagerService.java b/services/core/java/com/android/server/role/RoleManagerService.java
index b488337..c0ec367 100644
--- a/services/core/java/com/android/server/role/RoleManagerService.java
+++ b/services/core/java/com/android/server/role/RoleManagerService.java
@@ -406,7 +406,6 @@
         @Nullable
         private ArraySet<String> getRoleHoldersInternal(@NonNull String roleName,
                 @UserIdInt int userId) {
-            migrateRoleIfNecessary(roleName, userId);
             RoleUserState userState = getOrCreateUserState(userId);
             return userState.getRoleHolders(roleName);
         }
diff --git a/services/core/java/com/android/server/role/RoleUserState.java b/services/core/java/com/android/server/role/RoleUserState.java
index 69e1449..02dcc49 100644
--- a/services/core/java/com/android/server/role/RoleUserState.java
+++ b/services/core/java/com/android/server/role/RoleUserState.java
@@ -193,14 +193,18 @@
      *
      * @param roleName the name of the role to query for
      *
-     * @return the set of role holders. {@code null} should not be returned and indicates an issue.
+     * @return the set of role holders, or {@code null} if and only if the role is not found
      */
     @Nullable
     public ArraySet<String> getRoleHolders(@NonNull String roleName) {
         synchronized (mLock) {
             throwIfDestroyedLocked();
 
-            return new ArraySet<>(mRoles.get(roleName));
+            ArraySet<String> packageNames = mRoles.get(roleName);
+            if (packageNames == null) {
+                return null;
+            }
+            return new ArraySet<>(packageNames);
         }
     }
 
@@ -268,8 +272,7 @@
      * @param roleName the name of the role to add the holder to
      * @param packageName the package name of the new holder
      *
-     * @return {@code false} only if the set of role holders is null, which should not happen and
-     *         indicates an issue.
+     * @return {@code false} if and only if the role is not found
      */
     @CheckResult
     public boolean addRoleHolder(@NonNull String roleName, @NonNull String packageName) {
@@ -302,8 +305,7 @@
      * @param roleName the name of the role to remove the holder from
      * @param packageName the package name of the holder to remove
      *
-     * @return {@code false} only if the set of role holders is null, which should not happen and
-     *         indicates an issue.
+     * @return {@code false} if and only if the role is not found
      */
     @CheckResult
     public boolean removeRoleHolder(@NonNull String roleName, @NonNull String packageName) {
diff --git a/services/core/java/com/android/server/rollback/PackageRollbackData.java b/services/core/java/com/android/server/rollback/RollbackData.java
similarity index 65%
rename from services/core/java/com/android/server/rollback/PackageRollbackData.java
rename to services/core/java/com/android/server/rollback/RollbackData.java
index 15d1242..f0589aa 100644
--- a/services/core/java/com/android/server/rollback/PackageRollbackData.java
+++ b/services/core/java/com/android/server/rollback/RollbackData.java
@@ -20,17 +20,21 @@
 
 import java.io.File;
 import java.time.Instant;
+import java.util.ArrayList;
+import java.util.List;
 
 /**
- * Information about a rollback available for a particular package.
- * This is similar to {@link PackageRollbackInfo}, but extended with
- * additional information for internal bookkeeping.
+ * Information about a rollback available for a set of atomically installed
+ * packages.
  */
-class PackageRollbackData {
-    public final PackageRollbackInfo info;
+class RollbackData {
+    /**
+     * The per-package rollback information.
+     */
+    public final List<PackageRollbackInfo> packages = new ArrayList<>();
 
     /**
-     * The directory where the apk backup is stored.
+     * The directory where the rollback data is stored.
      */
     public final File backupDir;
 
@@ -38,12 +42,9 @@
      * The time when the upgrade occurred, for purposes of expiring
      * rollback data.
      */
-    public final Instant timestamp;
+    public Instant timestamp;
 
-    PackageRollbackData(PackageRollbackInfo info,
-            File backupDir, Instant timestamp) {
-        this.info = info;
+    RollbackData(File backupDir) {
         this.backupDir = backupDir;
-        this.timestamp = timestamp;
     }
 }
diff --git a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
index 0c21312..d12f7ed 100644
--- a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
+++ b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
@@ -63,9 +63,11 @@
 import java.time.format.DateTimeParseException;
 import java.time.temporal.ChronoUnit;
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.LinkedBlockingQueue;
 import java.util.concurrent.TimeUnit;
@@ -86,10 +88,20 @@
     // mLock is held when they are called.
     private final Object mLock = new Object();
 
+    // Package rollback data for rollback-enabled installs that have not yet
+    // been committed. Maps from sessionId to rollback data.
+    @GuardedBy("mLock")
+    private final Map<Integer, RollbackData> mPendingRollbacks = new HashMap<>();
+
+    // Map from child session id's for enabled rollbacks to their
+    // corresponding parent session ids.
+    @GuardedBy("mLock")
+    private final Map<Integer, Integer> mChildSessions = new HashMap<>();
+
     // Package rollback data available to be used for rolling back a package.
     // This list is null until the rollback data has been loaded.
     @GuardedBy("mLock")
-    private List<PackageRollbackData> mAvailableRollbacks;
+    private List<RollbackData> mAvailableRollbacks;
 
     // The list of recently executed rollbacks.
     // This list is null until the rollback data has been loaded.
@@ -102,14 +114,25 @@
     // to store this data:
     //   /data/rollback/
     //      available/
-    //          com.package.A-XXX/
-    //              base.apk
-    //              rollback.json
-    //          com.package.B-YYY/
-    //              base.apk
-    //              rollback.json
+    //          XXX/
+    //              com.package.A/
+    //                  base.apk
+    //                  info.json
+    //              enabled.txt
+    //          YYY/
+    //              com.package.B/
+    //                  base.apk
+    //                  info.json
+    //              enabled.txt
     //      recently_executed.json
-    // TODO: Use AtomicFile for rollback.json and recently_executed.json.
+    //
+    // * XXX, YYY are random strings from Files.createTempDirectory
+    // * info.json contains the package version to roll back from/to.
+    // * enabled.txt contains a timestamp for when the rollback was first
+    //   made available. This file is not written until the rollback is made
+    //   available.
+    //
+    // TODO: Use AtomicFile for all the .json files?
     private final File mRollbackDataDir;
     private final File mAvailableRollbacksDir;
     private final File mRecentlyExecutedRollbacksFile;
@@ -135,6 +158,9 @@
         // expiration.
         getHandler().post(() -> ensureRollbackDataLoaded());
 
+        PackageInstaller installer = mContext.getPackageManager().getPackageInstaller();
+        installer.registerSessionCallback(new SessionCallback(), getHandler());
+
         IntentFilter filter = new IntentFilter();
         filter.addAction(Intent.ACTION_PACKAGE_REPLACED);
         filter.addAction(Intent.ACTION_PACKAGE_FULLY_REMOVED);
@@ -199,27 +225,23 @@
                 android.Manifest.permission.MANAGE_ROLLBACKS,
                 "getAvailableRollback");
 
-        PackageRollbackInfo.PackageVersion installedVersion =
-                getInstalledPackageVersion(packageName);
-        if (installedVersion == null) {
+        RollbackData data = getRollbackForPackage(packageName);
+        if (data == null) {
             return null;
         }
 
-        synchronized (mLock) {
-            // TODO: Have ensureRollbackDataLoadedLocked return the list of
-            // available rollbacks, to hopefully avoid forgetting to call it?
-            ensureRollbackDataLoadedLocked();
-            for (int i = 0; i < mAvailableRollbacks.size(); ++i) {
-                PackageRollbackData data = mAvailableRollbacks.get(i);
-                if (data.info.packageName.equals(packageName)
-                        && data.info.higherVersion.equals(installedVersion)) {
-                    // TODO: For atomic installs, check all dependent packages
-                    // for available rollbacks and include that info here.
-                    return new RollbackInfo(data.info);
-                }
+        // Note: The rollback for the package ought to be for the currently
+        // installed version, otherwise the rollback data is out of date. In
+        // that rare case, we'll check when we execute the rollback whether
+        // it's out of date or not, so no need to check package versions here.
+
+        for (PackageRollbackInfo info : data.packages) {
+            if (info.packageName.equals(packageName)) {
+                // TODO: Once the RollbackInfo API supports info about
+                // dependant packages, add that info here.
+                return new RollbackInfo(info);
             }
         }
-
         return null;
     }
 
@@ -229,16 +251,14 @@
                 android.Manifest.permission.MANAGE_ROLLBACKS,
                 "getPackagesWithAvailableRollbacks");
 
-        // TODO: This may return packages whose rollback is out of date or
-        // expired.  Presumably that's okay because the package rollback could
-        // be expired anyway between when the caller calls this method and
-        // when the caller calls getAvailableRollback for more details.
         final Set<String> packageNames = new HashSet<>();
         synchronized (mLock) {
             ensureRollbackDataLoadedLocked();
             for (int i = 0; i < mAvailableRollbacks.size(); ++i) {
-                PackageRollbackData data = mAvailableRollbacks.get(i);
-                packageNames.add(data.info.packageName);
+                RollbackData data = mAvailableRollbacks.get(i);
+                for (PackageRollbackInfo info : data.packages) {
+                    packageNames.add(info.packageName);
+                }
             }
         }
         return new StringParceledListSlice(new ArrayList<>(packageNames));
@@ -279,47 +299,49 @@
      */
     private void executeRollbackInternal(RollbackInfo rollback,
             String callerPackageName, IntentSender statusReceiver) {
-        String packageName = rollback.targetPackage.packageName;
-        Log.i(TAG, "Initiating rollback of " + packageName);
+        String targetPackageName = rollback.targetPackage.packageName;
+        Log.i(TAG, "Initiating rollback of " + targetPackageName);
 
-        PackageRollbackInfo.PackageVersion installedVersion =
-                getInstalledPackageVersion(packageName);
-        if (installedVersion == null) {
-            // TODO: Test this case
-            sendFailure(statusReceiver, "Target package to roll back is not installed");
+        // Get the latest RollbackData for the target package.
+        RollbackData data = getRollbackForPackage(targetPackageName);
+        if (data == null) {
+            sendFailure(statusReceiver, "No rollback available for package.");
             return;
         }
 
-        if (!rollback.targetPackage.higherVersion.equals(installedVersion)) {
-            // TODO: Test this case
-            sendFailure(statusReceiver, "Target package version to roll back not installed.");
-            return;
+        // Verify the latest rollback matches the version requested.
+        // TODO: Check dependant packages too once RollbackInfo includes that
+        // information.
+        for (PackageRollbackInfo info : data.packages) {
+            if (info.packageName.equals(targetPackageName)
+                    && !rollback.targetPackage.higherVersion.equals(info.higherVersion)) {
+                sendFailure(statusReceiver, "Rollback is out of date.");
+                return;
+            }
         }
 
+        // Verify the RollbackData is up to date with what's installed on
+        // device.
         // TODO: We assume that between now and the time we commit the
         // downgrade install, the currently installed package version does not
         // change. This is not safe to assume, particularly in the case of a
         // rollback racing with a roll-forward fix of a buggy package.
         // Figure out how to ensure we don't commit the rollback if
         // roll forward happens at the same time.
-        PackageRollbackData data = null;
-        synchronized (mLock) {
-            ensureRollbackDataLoadedLocked();
-            for (int i = 0; i < mAvailableRollbacks.size(); ++i) {
-                PackageRollbackData available = mAvailableRollbacks.get(i);
-                // TODO: Check if available.info.lowerVersion matches
-                // rollback.targetPackage.lowerVersion?
-                if (available.info.packageName.equals(packageName)
-                        && available.info.higherVersion.equals(installedVersion)) {
-                    data = available;
-                    break;
-                }
+        for (PackageRollbackInfo info : data.packages) {
+            PackageRollbackInfo.PackageVersion installedVersion =
+                    getInstalledPackageVersion(info.packageName);
+            if (installedVersion == null) {
+                // TODO: Test this case
+                sendFailure(statusReceiver, "Package to roll back is not installed");
+                return;
             }
-        }
 
-        if (data == null) {
-            sendFailure(statusReceiver, "Rollback not available");
-            return;
+            if (!info.higherVersion.equals(installedVersion)) {
+                // TODO: Test this case
+                sendFailure(statusReceiver, "Package version to roll back not installed.");
+                return;
+            }
         }
 
         // Get a context for the caller to use to install the downgraded
@@ -334,29 +356,39 @@
 
         PackageManager pm = context.getPackageManager();
         try {
-            PackageInstaller.Session session = null;
-
             PackageInstaller packageInstaller = pm.getPackageInstaller();
-            PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(
+            PackageInstaller.SessionParams parentParams = new PackageInstaller.SessionParams(
                     PackageInstaller.SessionParams.MODE_FULL_INSTALL);
-            params.setAllowDowngrade(true);
-            int sessionId = packageInstaller.createSession(params);
-            session = packageInstaller.openSession(sessionId);
+            parentParams.setAllowDowngrade(true);
+            parentParams.setMultiPackage();
+            int parentSessionId = packageInstaller.createSession(parentParams);
+            PackageInstaller.Session parentSession = packageInstaller.openSession(parentSessionId);
 
-            // TODO: Will it always be called "base.apk"? What about splits?
-            File baseApk = new File(data.backupDir, "base.apk");
-            try (ParcelFileDescriptor fd = ParcelFileDescriptor.open(baseApk,
-                    ParcelFileDescriptor.MODE_READ_ONLY)) {
-                final long token = Binder.clearCallingIdentity();
-                try {
-                    session.write("base.apk", 0, baseApk.length(), fd);
-                } finally {
-                    Binder.restoreCallingIdentity(token);
+            for (PackageRollbackInfo info : data.packages) {
+                PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(
+                        PackageInstaller.SessionParams.MODE_FULL_INSTALL);
+                params.setAllowDowngrade(true);
+                int sessionId = packageInstaller.createSession(params);
+                PackageInstaller.Session session = packageInstaller.openSession(sessionId);
+
+                // TODO: Will it always be called "base.apk"? What about splits?
+                // What about apex?
+                File packageDir = new File(data.backupDir, info.packageName);
+                File baseApk = new File(packageDir, "base.apk");
+                try (ParcelFileDescriptor fd = ParcelFileDescriptor.open(baseApk,
+                        ParcelFileDescriptor.MODE_READ_ONLY)) {
+                    final long token = Binder.clearCallingIdentity();
+                    try {
+                        session.write("base.apk", 0, baseApk.length(), fd);
+                    } finally {
+                        Binder.restoreCallingIdentity(token);
+                    }
                 }
+                parentSession.addChildSessionId(sessionId);
             }
 
             final LocalIntentReceiver receiver = new LocalIntentReceiver();
-            session.commit(receiver.getIntentSender());
+            parentSession.commit(receiver.getIntentSender());
 
             Intent result = receiver.getResult();
             int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
@@ -371,13 +403,14 @@
             sendSuccess(statusReceiver);
 
             Intent broadcast = new Intent(Intent.ACTION_PACKAGE_ROLLBACK_EXECUTED,
-                    Uri.fromParts("package", packageName, Manifest.permission.MANAGE_ROLLBACKS));
+                    Uri.fromParts("package", targetPackageName,
+                        Manifest.permission.MANAGE_ROLLBACKS));
 
             // TODO: This call emits the warning "Calling a method in the
             // system process without a qualified user". Fix that.
             mContext.sendBroadcast(broadcast);
         } catch (IOException e) {
-            Log.e(TAG, "Unable to roll back " + packageName, e);
+            Log.e(TAG, "Unable to roll back " + targetPackageName, e);
             sendFailure(statusReceiver, "IOException: " + e.toString());
             return;
         }
@@ -408,12 +441,15 @@
         // testing anyway.
         synchronized (mLock) {
             ensureRollbackDataLoadedLocked();
-            Iterator<PackageRollbackData> iter = mAvailableRollbacks.iterator();
+            Iterator<RollbackData> iter = mAvailableRollbacks.iterator();
             while (iter.hasNext()) {
-                PackageRollbackData data = iter.next();
-                if (data.info.packageName.equals(packageName)) {
-                    iter.remove();
-                    removeFile(data.backupDir);
+                RollbackData data = iter.next();
+                for (PackageRollbackInfo info : data.packages) {
+                    if (info.packageName.equals(packageName)) {
+                        iter.remove();
+                        removeFile(data.backupDir);
+                        break;
+                    }
                 }
             }
         }
@@ -453,27 +489,41 @@
         mAvailableRollbacksDir.mkdirs();
         mAvailableRollbacks = new ArrayList<>();
         for (File rollbackDir : mAvailableRollbacksDir.listFiles()) {
-            if (rollbackDir.isDirectory()) {
-                // TODO: How to detect and clean up an invalid rollback
-                // directory? We don't know if it's invalid because something
-                // went wrong, or if it's only temporarily invalid because
-                // it's in the process of being created.
+            File enabledFile = new File(rollbackDir, "enabled.txt");
+            // TODO: Delete any directories without an enabled.txt? That could
+            // potentially delete pending rollback data if reloadPersistedData
+            // is called, though there's no reason besides testing for that to
+            // be called.
+            if (rollbackDir.isDirectory() && enabledFile.isFile()) {
+                RollbackData data = new RollbackData(rollbackDir);
                 try {
-                    File jsonFile = new File(rollbackDir, "rollback.json");
-                    String jsonString = IoUtils.readFileAsString(jsonFile.getAbsolutePath());
-                    JSONObject jsonObject = new JSONObject(jsonString);
-                    String packageName = jsonObject.getString("packageName");
-                    long higherVersionCode = jsonObject.getLong("higherVersionCode");
-                    long lowerVersionCode = jsonObject.getLong("lowerVersionCode");
-                    Instant timestamp = Instant.parse(jsonObject.getString("timestamp"));
-                    PackageRollbackData data = new PackageRollbackData(
-                            new PackageRollbackInfo(packageName,
-                                new PackageRollbackInfo.PackageVersion(higherVersionCode),
-                                new PackageRollbackInfo.PackageVersion(lowerVersionCode)),
-                            rollbackDir, timestamp);
+                    PackageRollbackInfo info = null;
+                    for (File packageDir : rollbackDir.listFiles()) {
+                        if (packageDir.isDirectory()) {
+                            File jsonFile = new File(packageDir, "info.json");
+                            String jsonString = IoUtils.readFileAsString(
+                                    jsonFile.getAbsolutePath());
+                            JSONObject jsonObject = new JSONObject(jsonString);
+                            String packageName = jsonObject.getString("packageName");
+                            long higherVersionCode = jsonObject.getLong("higherVersionCode");
+                            long lowerVersionCode = jsonObject.getLong("lowerVersionCode");
+
+                            data.packages.add(new PackageRollbackInfo(packageName,
+                                        new PackageRollbackInfo.PackageVersion(higherVersionCode),
+                                        new PackageRollbackInfo.PackageVersion(lowerVersionCode)));
+                        }
+                    }
+
+                    if (data.packages.isEmpty()) {
+                        throw new IOException("No package rollback info found");
+                    }
+
+                    String enabledString = IoUtils.readFileAsString(enabledFile.getAbsolutePath());
+                    data.timestamp = Instant.parse(enabledString.trim());
                     mAvailableRollbacks.add(data);
                 } catch (IOException | JSONException | DateTimeParseException e) {
                     Log.e(TAG, "Unable to read rollback data at " + rollbackDir, e);
+                    removeFile(rollbackDir);
                 }
             }
         }
@@ -521,13 +571,16 @@
 
         synchronized (mLock) {
             ensureRollbackDataLoadedLocked();
-            Iterator<PackageRollbackData> iter = mAvailableRollbacks.iterator();
+            Iterator<RollbackData> iter = mAvailableRollbacks.iterator();
             while (iter.hasNext()) {
-                PackageRollbackData data = iter.next();
-                if (data.info.packageName.equals(packageName)
-                        && !data.info.higherVersion.equals(installedVersion)) {
-                    iter.remove();
-                    removeFile(data.backupDir);
+                RollbackData data = iter.next();
+                for (PackageRollbackInfo info : data.packages) {
+                    if (info.packageName.equals(packageName)
+                            && !info.higherVersion.equals(installedVersion)) {
+                        iter.remove();
+                        removeFile(data.backupDir);
+                        break;
+                    }
                 }
             }
         }
@@ -643,9 +696,9 @@
         synchronized (mLock) {
             ensureRollbackDataLoadedLocked();
 
-            Iterator<PackageRollbackData> iter = mAvailableRollbacks.iterator();
+            Iterator<RollbackData> iter = mAvailableRollbacks.iterator();
             while (iter.hasNext()) {
-                PackageRollbackData data = iter.next();
+                RollbackData data = iter.next();
                 if (!now.isBefore(data.timestamp.plusMillis(ROLLBACK_LIFETIME_DURATION_MILLIS))) {
                     iter.remove();
                     removeFile(data.backupDir);
@@ -673,6 +726,23 @@
         return mHandlerThread.getThreadHandler();
     }
 
+    // Returns true if <code>session</code> has installFlags and code path
+    // matching the installFlags and new package code path given to
+    // enableRollback.
+    private boolean sessionMatchesForEnableRollback(PackageInstaller.SessionInfo session,
+            int installFlags, File newPackageCodePath) {
+        if (session == null || session.resolvedBaseCodePath == null) {
+            return false;
+        }
+
+        File packageCodePath = new File(session.resolvedBaseCodePath).getParentFile();
+        if (newPackageCodePath.equals(packageCodePath) && installFlags == session.installFlags) {
+            return true;
+        }
+
+        return false;
+    }
+
     /**
      * Called via broadcast by the package manager when a package is being
      * staged for install with rollback enabled. Called before the package has
@@ -705,6 +775,34 @@
         String packageName = newPackage.packageName;
         Log.i(TAG, "Enabling rollback for install of " + packageName);
 
+        // Figure out the session id associated with this install.
+        int parentSessionId = PackageInstaller.SessionInfo.INVALID_ID;
+        int childSessionId = PackageInstaller.SessionInfo.INVALID_ID;
+        PackageInstaller installer = mContext.getPackageManager().getPackageInstaller();
+        for (PackageInstaller.SessionInfo info : installer.getAllSessions()) {
+            if (info.isMultiPackage()) {
+                for (int childId : info.getChildSessionIds()) {
+                    PackageInstaller.SessionInfo child = installer.getSessionInfo(childId);
+                    if (sessionMatchesForEnableRollback(child, installFlags, newPackageCodePath)) {
+                        // TODO: Check we only have one matching session?
+                        parentSessionId = info.getSessionId();
+                        childSessionId = childId;
+                    }
+                }
+            } else {
+                if (sessionMatchesForEnableRollback(info, installFlags, newPackageCodePath)) {
+                    // TODO: Check we only have one matching session?
+                    parentSessionId = info.getSessionId();
+                    childSessionId = parentSessionId;
+                }
+            }
+        }
+
+        if (parentSessionId == PackageInstaller.SessionInfo.INVALID_ID) {
+            Log.e(TAG, "Unable to find session id for enabled rollback.");
+            return false;
+        }
+
         PackageRollbackInfo.PackageVersion newVersion =
                 new PackageRollbackInfo.PackageVersion(newPackage.versionCode);
 
@@ -720,52 +818,53 @@
         PackageRollbackInfo.PackageVersion installedVersion =
                 new PackageRollbackInfo.PackageVersion(installedPackage.getLongVersionCode());
 
-        File backupDir;
+        PackageRollbackInfo info = new PackageRollbackInfo(
+                packageName, newVersion, installedVersion);
+
+        RollbackData data;
         try {
-            backupDir = Files.createTempDirectory(
-                mAvailableRollbacksDir.toPath(), packageName + "-").toFile();
+            synchronized (mLock) {
+                mChildSessions.put(childSessionId, parentSessionId);
+                data = mPendingRollbacks.get(parentSessionId);
+                if (data == null) {
+                    File backupDir = Files.createTempDirectory(
+                            mAvailableRollbacksDir.toPath(), null).toFile();
+                    data = new RollbackData(backupDir);
+                    mPendingRollbacks.put(parentSessionId, data);
+                }
+                data.packages.add(info);
+            }
         } catch (IOException e) {
             Log.e(TAG, "Unable to create rollback for " + packageName, e);
             return false;
         }
 
-        // TODO: Should the timestamp be for when we commit the install, not
-        // when we create the pending one?
-        Instant timestamp = Instant.now();
+        File packageDir = new File(data.backupDir, packageName);
+        packageDir.mkdirs();
         try {
             JSONObject json = new JSONObject();
             json.put("packageName", packageName);
             json.put("higherVersionCode", newVersion.versionCode);
             json.put("lowerVersionCode", installedVersion.versionCode);
-            json.put("timestamp", timestamp.toString());
 
-            File jsonFile = new File(backupDir, "rollback.json");
+            File jsonFile = new File(packageDir, "info.json");
             PrintWriter pw = new PrintWriter(jsonFile);
             pw.println(json.toString());
             pw.close();
         } catch (IOException | JSONException e) {
             Log.e(TAG, "Unable to create rollback for " + packageName, e);
-            removeFile(backupDir);
+            removeFile(packageDir);
             return false;
         }
 
         // TODO: Copy by hard link instead to save on cpu and storage space?
-        int status = PackageManagerServiceUtils.copyPackage(installedPackage.codePath, backupDir);
+        int status = PackageManagerServiceUtils.copyPackage(installedPackage.codePath, packageDir);
         if (status != PackageManager.INSTALL_SUCCEEDED) {
             Log.e(TAG, "Unable to copy package for rollback for " + packageName);
-            removeFile(backupDir);
+            removeFile(packageDir);
             return false;
         }
 
-        PackageRollbackData data = new PackageRollbackData(
-                new PackageRollbackInfo(packageName, newVersion, installedVersion),
-                backupDir, timestamp);
-
-        synchronized (mLock) {
-            ensureRollbackDataLoadedLocked();
-            mAvailableRollbacks.add(data);
-        }
-
         return true;
     }
 
@@ -829,4 +928,89 @@
 
         return new PackageRollbackInfo.PackageVersion(pkgInfo.getLongVersionCode());
     }
+
+    private class SessionCallback extends PackageInstaller.SessionCallback {
+
+        @Override
+        public void onCreated(int sessionId) { }
+
+        @Override
+        public void onBadgingChanged(int sessionId) { }
+
+        @Override
+        public void onActiveChanged(int sessionId, boolean active) { }
+
+        @Override
+        public void onProgressChanged(int sessionId, float progress) { }
+
+        @Override
+        public void onFinished(int sessionId, boolean success) {
+            RollbackData data = null;
+            synchronized (mLock) {
+                Integer parentSessionId = mChildSessions.remove(sessionId);
+                if (parentSessionId != null) {
+                    sessionId = parentSessionId;
+                }
+                data = mPendingRollbacks.remove(sessionId);
+            }
+
+            if (data != null) {
+                if (success) {
+                    try {
+                        data.timestamp = Instant.now();
+                        File enabledFile = new File(data.backupDir, "enabled.txt");
+                        PrintWriter pw = new PrintWriter(enabledFile);
+                        pw.println(data.timestamp.toString());
+                        pw.close();
+
+                        synchronized (mLock) {
+                            // Note: There is a small window of time between when
+                            // the session has been committed by the package
+                            // manager and when we make the rollback available
+                            // here. Presumably the window is small enough that
+                            // nobody will want to roll back the newly installed
+                            // package before we make the rollback available.
+                            // TODO: We'll lose the rollback data if the
+                            // device reboots between when the session is
+                            // committed and this point. Revisit this after
+                            // adding support for rollback of staged installs.
+                            ensureRollbackDataLoadedLocked();
+                            mAvailableRollbacks.add(data);
+                        }
+
+                        scheduleExpiration(ROLLBACK_LIFETIME_DURATION_MILLIS);
+                    } catch (IOException e) {
+                        Log.e(TAG, "Unable to enable rollback", e);
+                        removeFile(data.backupDir);
+                    }
+                } else {
+                    // The install session was aborted, clean up the pending
+                    // install.
+                    removeFile(data.backupDir);
+                }
+            }
+        }
+    }
+
+    /*
+     * Returns the RollbackData, if any, for an available rollback that would
+     * roll back the given package. Note: This assumes we have at most one
+     * available rollback for a given package at any one time.
+     */
+    private RollbackData getRollbackForPackage(String packageName) {
+        synchronized (mLock) {
+            // TODO: Have ensureRollbackDataLoadedLocked return the list of
+            // available rollbacks, to hopefully avoid forgetting to call it?
+            ensureRollbackDataLoadedLocked();
+            for (int i = 0; i < mAvailableRollbacks.size(); ++i) {
+                RollbackData data = mAvailableRollbacks.get(i);
+                for (PackageRollbackInfo info : data.packages) {
+                    if (info.packageName.equals(packageName)) {
+                        return data;
+                    }
+                }
+            }
+        }
+        return null;
+    }
 }
diff --git a/services/core/java/com/android/server/signedconfig/GlobalSettingsConfigApplicator.java b/services/core/java/com/android/server/signedconfig/GlobalSettingsConfigApplicator.java
index 438c303..d77cf90 100644
--- a/services/core/java/com/android/server/signedconfig/GlobalSettingsConfigApplicator.java
+++ b/services/core/java/com/android/server/signedconfig/GlobalSettingsConfigApplicator.java
@@ -23,6 +23,7 @@
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.Slog;
+import android.util.StatsLog;
 
 import java.security.GeneralSecurityException;
 import java.util.Arrays;
@@ -66,12 +67,14 @@
 
     private final Context mContext;
     private final String mSourcePackage;
+    private final SignedConfigEvent mEvent;
     private final SignatureVerifier mVerifier;
 
-    GlobalSettingsConfigApplicator(Context context, String sourcePackage) {
+    GlobalSettingsConfigApplicator(Context context, String sourcePackage, SignedConfigEvent event) {
         mContext = context;
         mSourcePackage = sourcePackage;
-        mVerifier = new SignatureVerifier();
+        mEvent = event;
+        mVerifier = new SignatureVerifier(mEvent);
     }
 
     private boolean checkSignature(String data, String signature) {
@@ -79,6 +82,7 @@
             return mVerifier.verifySignature(data, signature);
         } catch (GeneralSecurityException e) {
             Slog.e(TAG, "Failed to verify signature", e);
+            mEvent.status = StatsLog.SIGNED_CONFIG_REPORTED__STATUS__SECURITY_EXCEPTION;
             return false;
         }
     }
@@ -109,14 +113,17 @@
         SignedConfig config;
         try {
             config = SignedConfig.parse(configStr, ALLOWED_KEYS, KEY_VALUE_MAPPERS);
+            mEvent.version = config.version;
         } catch (InvalidConfigException e) {
             Slog.e(TAG, "Failed to parse global settings from package " + mSourcePackage, e);
+            mEvent.status = StatsLog.SIGNED_CONFIG_REPORTED__STATUS__INVALID_CONFIG;
             return;
         }
         int currentVersion = getCurrentConfigVersion();
         if (currentVersion >= config.version) {
             Slog.i(TAG, "Global settings from package " + mSourcePackage
                     + " is older than existing: " + config.version + "<=" + currentVersion);
+            mEvent.status = StatsLog.SIGNED_CONFIG_REPORTED__STATUS__OLD_CONFIG;
             return;
         }
         // We have new config!
@@ -126,10 +133,12 @@
                 config.getMatchingConfig(Build.VERSION.SDK_INT);
         if (matchedConfig == null) {
             Slog.i(TAG, "Settings is not applicable to current SDK version; ignoring");
+            mEvent.status = StatsLog.SIGNED_CONFIG_REPORTED__STATUS__NOT_APPLICABLE;
             return;
         }
 
         Slog.i(TAG, "Updating global settings to version " + config.version);
         updateCurrentConfig(config.version, matchedConfig.values);
+        mEvent.status = StatsLog.SIGNED_CONFIG_REPORTED__STATUS__APPLIED;
     }
 }
diff --git a/services/core/java/com/android/server/signedconfig/SignatureVerifier.java b/services/core/java/com/android/server/signedconfig/SignatureVerifier.java
index 944db84..56db32a 100644
--- a/services/core/java/com/android/server/signedconfig/SignatureVerifier.java
+++ b/services/core/java/com/android/server/signedconfig/SignatureVerifier.java
@@ -18,6 +18,7 @@
 
 import android.os.Build;
 import android.util.Slog;
+import android.util.StatsLog;
 
 import java.nio.charset.StandardCharsets;
 import java.security.InvalidKeyException;
@@ -42,11 +43,18 @@
     private static final String DEBUG_KEY =
             "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEaAn2XVifsLTHg616nTsOMVmlhBoECGbTEBTKKvdd2hO60"
             + "pj1pnU8SMkhYfaNxZuKgw9LNvOwlFwStboIYeZ3lQ==";
+    private static final String PROD_KEY =
+            "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE+lky6wKyGL6lE1VrD0YTMHwb0Xwc+tzC8MvnrzVxodvTp"
+            + "VY/jV7V+Zktcx+pry43XPABFRXtbhTo+qykhyBA1g==";
 
+    private final SignedConfigEvent mEvent;
     private final PublicKey mDebugKey;
+    private final PublicKey mProdKey;
 
-    public SignatureVerifier() {
-        mDebugKey = createKey(DEBUG_KEY);
+    public SignatureVerifier(SignedConfigEvent event) {
+        mEvent = event;
+        mDebugKey = Build.IS_DEBUGGABLE ? createKey(DEBUG_KEY) : null;
+        mProdKey = createKey(PROD_KEY);
     }
 
     private static PublicKey createKey(String base64) {
@@ -67,6 +75,14 @@
         }
     }
 
+    private boolean verifyWithPublicKey(PublicKey key, byte[] data, byte[] signature)
+            throws NoSuchAlgorithmException, InvalidKeyException, SignatureException {
+        Signature verifier = Signature.getInstance("SHA256withECDSA");
+        verifier.initVerify(key);
+        verifier.update(data);
+        return verifier.verify(signature);
+    }
+
     /**
      * Verify a signature for signed config.
      *
@@ -80,6 +96,7 @@
         try {
             signature = Base64.getDecoder().decode(base64Signature);
         } catch (IllegalArgumentException e) {
+            mEvent.status = StatsLog.SIGNED_CONFIG_REPORTED__STATUS__BASE64_FAILURE_SIGNATURE;
             Slog.e(TAG, "Failed to base64 decode signature");
             return false;
         }
@@ -89,11 +106,9 @@
         if (Build.IS_DEBUGGABLE) {
             if (mDebugKey != null) {
                 if (DBG) Slog.w(TAG, "Trying to verify signature using debug key");
-                Signature verifier = Signature.getInstance("SHA256withECDSA");
-                verifier.initVerify(mDebugKey);
-                verifier.update(data);
-                if (verifier.verify(signature)) {
+                if (verifyWithPublicKey(mDebugKey, data, signature)) {
                     Slog.i(TAG, "Verified config using debug key");
+                    mEvent.verifiedWith = StatsLog.SIGNED_CONFIG_REPORTED__VERIFIED_WITH__DEBUG;
                     return true;
                 } else {
                     if (DBG) Slog.i(TAG, "Config verification failed using debug key");
@@ -102,8 +117,20 @@
                 Slog.w(TAG, "Debuggable build, but have no debug key");
             }
         }
-        // TODO verify production key.
-        Slog.w(TAG, "NO PRODUCTION KEY YET, FAILING VERIFICATION");
-        return false;
+        if (mProdKey ==  null) {
+            Slog.e(TAG, "No prod key; construction failed?");
+            mEvent.status =
+                    StatsLog.SIGNED_CONFIG_REPORTED__STATUS__SIGNATURE_CHECK_FAILED_PROD_KEY_ABSENT;
+            return false;
+        }
+        if (verifyWithPublicKey(mProdKey, data, signature)) {
+            Slog.i(TAG, "Verified config using production key");
+            mEvent.verifiedWith = StatsLog.SIGNED_CONFIG_REPORTED__VERIFIED_WITH__PRODUCTION;
+            return true;
+        } else {
+            if (DBG) Slog.i(TAG, "Verification failed using production key");
+            mEvent.status = StatsLog.SIGNED_CONFIG_REPORTED__STATUS__SIGNATURE_CHECK_FAILED;
+            return false;
+        }
     }
 }
diff --git a/services/core/java/com/android/server/signedconfig/SignedConfigEvent.java b/services/core/java/com/android/server/signedconfig/SignedConfigEvent.java
new file mode 100644
index 0000000..2f2062c
--- /dev/null
+++ b/services/core/java/com/android/server/signedconfig/SignedConfigEvent.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.signedconfig;
+
+import android.util.StatsLog;
+
+/**
+ * Helper class to allow a SignedConfigReported event to be built up in stages.
+ */
+public class SignedConfigEvent {
+
+    public int type = StatsLog.SIGNED_CONFIG_REPORTED__TYPE__UNKNOWN_TYPE;
+    public int status = StatsLog.SIGNED_CONFIG_REPORTED__STATUS__UNKNOWN_STATUS;
+    public int version = 0;
+    public String fromPackage = null;
+    public int verifiedWith = StatsLog.SIGNED_CONFIG_REPORTED__VERIFIED_WITH__NO_KEY;
+
+    /**
+     * Write this event to statslog.
+     */
+    public void send() {
+        StatsLog.write(StatsLog.SIGNED_CONFIG_REPORTED,
+                type, status, version, fromPackage, verifiedWith);
+    }
+
+}
diff --git a/services/core/java/com/android/server/signedconfig/SignedConfigService.java b/services/core/java/com/android/server/signedconfig/SignedConfigService.java
index 6bcee14..dc39542 100644
--- a/services/core/java/com/android/server/signedconfig/SignedConfigService.java
+++ b/services/core/java/com/android/server/signedconfig/SignedConfigService.java
@@ -26,6 +26,7 @@
 import android.net.Uri;
 import android.os.Bundle;
 import android.util.Slog;
+import android.util.StatsLog;
 
 import com.android.server.LocalServices;
 
@@ -82,21 +83,30 @@
         }
         if (metaData.containsKey(KEY_GLOBAL_SETTINGS)
                 && metaData.containsKey(KEY_GLOBAL_SETTINGS_SIGNATURE)) {
-            String config = metaData.getString(KEY_GLOBAL_SETTINGS);
-            String signature = metaData.getString(KEY_GLOBAL_SETTINGS_SIGNATURE);
+            SignedConfigEvent event = new SignedConfigEvent();
             try {
-                // Base64 encoding is standard (not URL safe) encoding: RFC4648
-                config = new String(Base64.getDecoder().decode(config), StandardCharsets.UTF_8);
-            } catch (IllegalArgumentException iae) {
-                Slog.e(TAG, "Failed to base64 decode global settings config from " + packageName);
-                return;
+                event.type = StatsLog.SIGNED_CONFIG_REPORTED__TYPE__GLOBAL_SETTINGS;
+                event.fromPackage = packageName;
+                String config = metaData.getString(KEY_GLOBAL_SETTINGS);
+                String signature = metaData.getString(KEY_GLOBAL_SETTINGS_SIGNATURE);
+                try {
+                    // Base64 encoding is standard (not URL safe) encoding: RFC4648
+                    config = new String(Base64.getDecoder().decode(config), StandardCharsets.UTF_8);
+                } catch (IllegalArgumentException iae) {
+                    Slog.e(TAG, "Failed to base64 decode global settings config from "
+                            + packageName);
+                    event.status = StatsLog.SIGNED_CONFIG_REPORTED__STATUS__BASE64_FAILURE_CONFIG;
+                    return;
+                }
+                if (DBG) {
+                    Slog.d(TAG, "Got global settings config: " + config);
+                    Slog.d(TAG, "Got global settings signature: " + signature);
+                }
+                new GlobalSettingsConfigApplicator(mContext, packageName, event).applyConfig(
+                        config, signature);
+            } finally {
+                event.send();
             }
-            if (DBG) {
-                Slog.d(TAG, "Got global settings config: " + config);
-                Slog.d(TAG, "Got global settings signature: " + signature);
-            }
-            new GlobalSettingsConfigApplicator(mContext, packageName).applyConfig(
-                    config, signature);
         } else {
             if (DBG) Slog.d(TAG, "Package has no global settings config/signature.");
         }
diff --git a/services/core/java/com/android/server/slice/SlicePermissionManager.java b/services/core/java/com/android/server/slice/SlicePermissionManager.java
index 315d5e3..1d1c28f 100644
--- a/services/core/java/com/android/server/slice/SlicePermissionManager.java
+++ b/services/core/java/com/android/server/slice/SlicePermissionManager.java
@@ -175,18 +175,24 @@
                 handlePersist();
             }
             for (String file : new File(mSliceDir.getAbsolutePath()).list()) {
-                if (file.isEmpty()) continue;
                 try (ParserHolder parser = getParser(file)) {
-                    Persistable p;
-                    while (parser.parser.getEventType() != XmlPullParser.START_TAG) {
+                    Persistable p = null;
+                    while (parser.parser.getEventType() != XmlPullParser.END_DOCUMENT) {
+                        if (parser.parser.getEventType() == XmlPullParser.START_TAG) {
+                            if (SliceClientPermissions.TAG_CLIENT.equals(parser.parser.getName())) {
+                                p = SliceClientPermissions.createFrom(parser.parser, tracker);
+                            } else {
+                                p = SliceProviderPermissions.createFrom(parser.parser, tracker);
+                            }
+                            break;
+                        }
                         parser.parser.next();
                     }
-                    if (SliceClientPermissions.TAG_CLIENT.equals(parser.parser.getName())) {
-                        p = SliceClientPermissions.createFrom(parser.parser, tracker);
+                    if (p != null) {
+                        p.writeTo(out);
                     } else {
-                        p = SliceProviderPermissions.createFrom(parser.parser, tracker);
+                        Slog.w(TAG, "Invalid or empty slice permissions file: " + file);
                     }
-                    p.writeTo(out);
                 }
             }
 
diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java
index 30aa528..4e71a05 100644
--- a/services/core/java/com/android/server/stats/StatsCompanionService.java
+++ b/services/core/java/com/android/server/stats/StatsCompanionService.java
@@ -43,6 +43,7 @@
 import android.content.pm.UserInfo;
 import android.hardware.fingerprint.FingerprintManager;
 import android.net.ConnectivityManager;
+import android.net.INetworkStatsService;
 import android.net.Network;
 import android.net.NetworkRequest;
 import android.net.NetworkStats;
@@ -90,7 +91,6 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.app.procstats.IProcessStats;
 import com.android.internal.app.procstats.ProcessStats;
-import com.android.internal.net.NetworkStatsFactory;
 import com.android.internal.os.BatterySipper;
 import com.android.internal.os.BatteryStatsHelper;
 import com.android.internal.os.BinderCallsStats.ExportedCallStat;
@@ -210,6 +210,7 @@
 
     private final Context mContext;
     private final AlarmManager mAlarmManager;
+    private final INetworkStatsService mNetworkStatsService;
     @GuardedBy("sStatsdLock")
     private static IStatsManager sStatsd;
     private static final Object sStatsdLock = new Object();
@@ -257,6 +258,8 @@
         super();
         mContext = context;
         mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
+        mNetworkStatsService = INetworkStatsService.Stub.asInterface(
+              ServiceManager.getService(Context.NETWORK_STATS_SERVICE));
         mBaseDir.mkdirs();
         mAppUpdateReceiver = new AppUpdateReceiver();
         mUserUpdateReceiver = new BroadcastReceiver() {
@@ -788,14 +791,14 @@
             if (ifaces.length == 0) {
                 return;
             }
-            NetworkStatsFactory nsf = new NetworkStatsFactory();
+            if (mNetworkStatsService == null) {
+                Slog.e(TAG, "NetworkStats Service is not available!");
+                return;
+            }
             // Combine all the metrics per Uid into one record.
-            NetworkStats stats =
-                    nsf.readNetworkStatsDetail(NetworkStats.UID_ALL, ifaces, NetworkStats.TAG_NONE,
-                            null)
-                            .groupedByUid();
+            NetworkStats stats = mNetworkStatsService.getDetailedUidStats(ifaces).groupedByUid();
             addNetworkStats(tagId, pulledData, stats, false);
-        } catch (java.io.IOException e) {
+        } catch (RemoteException e) {
             Slog.e(TAG, "Pulling netstats for wifi bytes has error", e);
         } finally {
             Binder.restoreCallingIdentity(token);
@@ -812,12 +815,14 @@
             if (ifaces.length == 0) {
                 return;
             }
-            NetworkStatsFactory nsf = new NetworkStatsFactory();
+            if (mNetworkStatsService == null) {
+                Slog.e(TAG, "NetworkStats Service is not available!");
+                return;
+            }
             NetworkStats stats = rollupNetworkStatsByFGBG(
-                    nsf.readNetworkStatsDetail(NetworkStats.UID_ALL, ifaces, NetworkStats.TAG_NONE,
-                            null));
+                    mNetworkStatsService.getDetailedUidStats(ifaces));
             addNetworkStats(tagId, pulledData, stats, true);
-        } catch (java.io.IOException e) {
+        } catch (RemoteException e) {
             Slog.e(TAG, "Pulling netstats for wifi bytes w/ fg/bg has error", e);
         } finally {
             Binder.restoreCallingIdentity(token);
@@ -834,14 +839,14 @@
             if (ifaces.length == 0) {
                 return;
             }
-            NetworkStatsFactory nsf = new NetworkStatsFactory();
+            if (mNetworkStatsService == null) {
+                Slog.e(TAG, "NetworkStats Service is not available!");
+                return;
+            }
             // Combine all the metrics per Uid into one record.
-            NetworkStats stats =
-                    nsf.readNetworkStatsDetail(NetworkStats.UID_ALL, ifaces, NetworkStats.TAG_NONE,
-                            null)
-                            .groupedByUid();
+            NetworkStats stats = mNetworkStatsService.getDetailedUidStats(ifaces).groupedByUid();
             addNetworkStats(tagId, pulledData, stats, false);
-        } catch (java.io.IOException e) {
+        } catch (RemoteException e) {
             Slog.e(TAG, "Pulling netstats for mobile bytes has error", e);
         } finally {
             Binder.restoreCallingIdentity(token);
@@ -874,12 +879,14 @@
             if (ifaces.length == 0) {
                 return;
             }
-            NetworkStatsFactory nsf = new NetworkStatsFactory();
+            if (mNetworkStatsService == null) {
+                Slog.e(TAG, "NetworkStats Service is not available!");
+                return;
+            }
             NetworkStats stats = rollupNetworkStatsByFGBG(
-                    nsf.readNetworkStatsDetail(NetworkStats.UID_ALL, ifaces, NetworkStats.TAG_NONE,
-                            null));
+                    mNetworkStatsService.getDetailedUidStats(ifaces));
             addNetworkStats(tagId, pulledData, stats, true);
-        } catch (java.io.IOException e) {
+        } catch (RemoteException e) {
             Slog.e(TAG, "Pulling netstats for mobile bytes w/ fg/bg has error", e);
         } finally {
             Binder.restoreCallingIdentity(token);
@@ -2058,6 +2065,17 @@
         mContext.unregisterReceiver(mShutdownEventReceiver);
         cancelAnomalyAlarm();
         cancelPullingAlarm();
+
+        BinderCallsStatsService.Internal binderStats =
+                LocalServices.getService(BinderCallsStatsService.Internal.class);
+        if (binderStats != null) {
+            binderStats.reset();
+        }
+
+        LooperStats looperStats = LocalServices.getService(LooperStats.class);
+        if (looperStats != null) {
+            looperStats.reset();
+        }
     }
 
     @Override
diff --git a/services/core/java/com/android/server/wm/ActivityDisplay.java b/services/core/java/com/android/server/wm/ActivityDisplay.java
index 1f638c7..0616846 100644
--- a/services/core/java/com/android/server/wm/ActivityDisplay.java
+++ b/services/core/java/com/android/server/wm/ActivityDisplay.java
@@ -278,12 +278,12 @@
         }
 
         // Since positionChildAt() is called during the creation process of pinned stacks,
-        // ActivityStack#getWindowContainerController() can be null. In this special case,
+        // ActivityStack#getStack() can be null. In this special case,
         // since DisplayContest#positionStackAt() is called in TaskStack#onConfigurationChanged(),
         // we don't have to call WindowContainerController#positionChildAt() here.
-        if (stack.getWindowContainerController() != null && mDisplayContent != null) {
+        if (stack.getTaskStack() != null && mDisplayContent != null) {
             mDisplayContent.positionStackAt(insertPosition,
-                    stack.getWindowContainerController().mContainer, includingParents);
+                    stack.getTaskStack(), includingParents);
         }
         if (!wasContained) {
             stack.setParent(this);
@@ -450,13 +450,12 @@
     @VisibleForTesting
     <T extends ActivityStack> T createStackUnchecked(int windowingMode, int activityType,
             int stackId, boolean onTop) {
-        if (windowingMode == WINDOWING_MODE_PINNED) {
-            return (T) new PinnedActivityStack(this, stackId,
-                    mRootActivityContainer.mStackSupervisor, onTop);
+        if (windowingMode == WINDOWING_MODE_PINNED && activityType != ACTIVITY_TYPE_STANDARD) {
+            throw new IllegalArgumentException("Stack with windowing mode cannot with non standard "
+                    + "activity type.");
         }
         return (T) new ActivityStack(this, stackId,
-                mRootActivityContainer.mStackSupervisor, windowingMode, activityType,
-                onTop);
+                mRootActivityContainer.mStackSupervisor, windowingMode, activityType, onTop);
     }
 
     /**
@@ -1019,8 +1018,8 @@
         return mSplitScreenPrimaryStack != null;
     }
 
-    PinnedActivityStack getPinnedStack() {
-        return (PinnedActivityStack) mPinnedStack;
+    ActivityStack getPinnedStack() {
+        return mPinnedStack;
     }
 
     boolean hasPinnedStack() {
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index f8f0d1c..9f8af50 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -54,6 +54,7 @@
 import static android.content.pm.ActivityInfo.FLAG_ALWAYS_FOCUSABLE;
 import static android.content.pm.ActivityInfo.FLAG_EXCLUDE_FROM_RECENTS;
 import static android.content.pm.ActivityInfo.FLAG_IMMERSIVE;
+import static android.content.pm.ActivityInfo.FLAG_INHERIT_SHOW_WHEN_LOCKED;
 import static android.content.pm.ActivityInfo.FLAG_MULTIPROCESS;
 import static android.content.pm.ActivityInfo.FLAG_NO_HISTORY;
 import static android.content.pm.ActivityInfo.FLAG_SHOW_FOR_ALL_USERS;
@@ -142,6 +143,7 @@
 import static org.xmlpull.v1.XmlPullParser.START_TAG;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.app.ActivityManager.TaskDescription;
 import android.app.ActivityOptions;
 import android.app.PendingIntent;
@@ -387,6 +389,7 @@
     int mRotationAnimationHint = -1;
 
     private boolean mShowWhenLocked;
+    private boolean mInheritShownWhenLocked;
     private boolean mTurnScreenOn;
 
     /**
@@ -1002,6 +1005,7 @@
                 null : ComponentName.unflattenFromString(aInfo.requestedVrComponent);
 
         mShowWhenLocked = (aInfo.flags & FLAG_SHOW_WHEN_LOCKED) != 0;
+        mInheritShownWhenLocked = (aInfo.privateFlags & FLAG_INHERIT_SHOW_WHEN_LOCKED) != 0;
         mTurnScreenOn = (aInfo.flags & FLAG_TURN_SCREEN_ON) != 0;
 
         mRotationAnimationHint = aInfo.rotationAnimation;
@@ -1487,14 +1491,6 @@
         return true;
     }
 
-    /**
-     * @return true if the activity contains windows that have
-     *         {@link LayoutParams#FLAG_DISMISS_KEYGUARD} set
-     */
-    boolean hasDismissKeyguardWindows() {
-        return mAtmService.mWindowManager.containsDismissKeyguardWindow(appToken);
-    }
-
     void makeFinishingLocked() {
         if (finishing) {
             return;
@@ -3224,16 +3220,45 @@
                 false /* preserveWindows */);
     }
 
+    void setInheritShowWhenLocked(boolean inheritShowWhenLocked) {
+        mInheritShownWhenLocked = inheritShowWhenLocked;
+        mRootActivityContainer.ensureActivitiesVisible(null, 0, false);
+    }
+
     /**
      * @return true if the activity windowing mode is not
-     *         {@link android.app.WindowConfiguration#WINDOWING_MODE_PINNED} and activity contains
-     *         windows that have {@link LayoutParams#FLAG_SHOW_WHEN_LOCKED} set or if the activity
-     *         has set {@link #mShowWhenLocked}.
+     *         {@link android.app.WindowConfiguration#WINDOWING_MODE_PINNED} and a) activity
+     *         contains windows that have {@link LayoutParams#FLAG_SHOW_WHEN_LOCKED} set or if the
+     *         activity has set {@link #mShowWhenLocked}, or b) if the activity has set
+     *         {@link #mInheritShownWhenLocked} and the activity behind this satisfies the
+     *         conditions a) above.
      *         Multi-windowing mode will be exited if true is returned.
      */
     boolean canShowWhenLocked() {
-        return !inPinnedWindowingMode() && (mShowWhenLocked
-                || mAtmService.mWindowManager.containsShowWhenLockedWindow(appToken));
+        if (!inPinnedWindowingMode() && (mShowWhenLocked
+                || (mAppWindowToken != null && mAppWindowToken.containsShowWhenLockedWindow()))) {
+            return true;
+        } else if (mInheritShownWhenLocked) {
+            ActivityRecord r = getActivityBelow();
+            return r != null && !r.inPinnedWindowingMode() && (r.mShowWhenLocked
+                    || (r.mAppWindowToken != null
+                        && r.mAppWindowToken.containsShowWhenLockedWindow()));
+        } else {
+            return false;
+        }
+    }
+
+    /**
+     * @return an {@link ActivityRecord} of the activity below this activity, or {@code null} if no
+     * such activity exists.
+     */
+    @Nullable
+    private ActivityRecord getActivityBelow() {
+        final int pos = task.mActivities.indexOf(this);
+        if (pos == -1) {
+            throw new IllegalStateException("Activity not found in its task");
+        }
+        return pos == 0 ? null : task.getChildAt(pos - 1);
     }
 
     void setTurnScreenOn(boolean turnScreenOn) {
diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java
index 4581a0f..891c3da 100644
--- a/services/core/java/com/android/server/wm/ActivityStack.java
+++ b/services/core/java/com/android/server/wm/ActivityStack.java
@@ -114,6 +114,7 @@
 import android.app.ActivityOptions;
 import android.app.AppGlobals;
 import android.app.IActivityController;
+import android.app.RemoteAction;
 import android.app.ResultInfo;
 import android.app.WindowConfiguration.ActivityType;
 import android.app.WindowConfiguration.WindowingMode;
@@ -173,8 +174,7 @@
 /**
  * State and management of a single stack of activities.
  */
-class ActivityStack<T extends StackWindowController> extends ConfigurationContainer
-        implements StackWindowListener {
+class ActivityStack extends ConfigurationContainer {
     private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityStack" : TAG_ATM;
     private static final String TAG_ADD_REMOVE = TAG + POSTFIX_ADD_REMOVE;
     private static final String TAG_APP = TAG + POSTFIX_APP;
@@ -297,8 +297,7 @@
     static final int REMOVE_TASK_MODE_MOVING_TO_TOP = 2;
 
     final ActivityTaskManagerService mService;
-    private final WindowManagerService mWindowManager;
-    T mWindowContainerController;
+    final WindowManagerService mWindowManager;
 
     /**
      * The back history of all previous (and possibly still
@@ -397,6 +396,9 @@
     static final int DESTROY_ACTIVITIES_MSG = FIRST_ACTIVITY_STACK_MSG + 5;
     static final int TRANSLUCENT_TIMEOUT_MSG = FIRST_ACTIVITY_STACK_MSG + 6;
 
+    // TODO: remove after unification.
+    TaskStack mTaskStack;
+
     private static class ScheduleDestroyArgs {
         final WindowProcessController mOwner;
         final String mReason;
@@ -495,21 +497,30 @@
         // stacks on a wrong display.
         mDisplayId = display.mDisplayId;
         setActivityType(activityType);
-        mWindowContainerController = createStackWindowController(display.mDisplayId, onTop,
-                mTmpRect2);
+        createTaskStack(display.mDisplayId, onTop, mTmpRect2);
         setWindowingMode(windowingMode, false /* animate */, false /* showRecents */,
                 false /* enteringSplitScreenMode */, false /* deferEnsuringVisibility */,
                 true /* creating */);
         display.addChild(this, onTop ? POSITION_TOP : POSITION_BOTTOM);
     }
 
-    T createStackWindowController(int displayId, boolean onTop, Rect outBounds) {
-        return (T) new StackWindowController(mStackId, this, displayId, onTop, outBounds,
-                mRootActivityContainer.mWindowManager);
+    void createTaskStack(int displayId, boolean onTop, Rect outBounds) {
+        final DisplayContent dc = mWindowManager.mRoot.getDisplayContent(displayId);
+        if (dc == null) {
+            throw new IllegalArgumentException("Trying to add stackId=" + mStackId
+                    + " to unknown displayId=" + displayId);
+        }
+        mTaskStack = new TaskStack(mWindowManager, mStackId, this);
+        dc.setStackOnDisplay(mStackId, onTop, mTaskStack);
+        if (mTaskStack.matchParentBounds()) {
+            outBounds.setEmpty();
+        } else {
+            mTaskStack.getRawBounds(outBounds);
+        }
     }
 
-    T getWindowContainerController() {
-        return mWindowContainerController;
+    TaskStack getTaskStack() {
+        return mTaskStack;
     }
 
     /**
@@ -553,6 +564,9 @@
         if (display == null) {
             return;
         }
+        if (getTaskStack() == null) {
+            return;
+        }
 
         // Update bounds if applicable
         boolean hasNewOverrideBounds = false;
@@ -560,8 +574,7 @@
         if (getRequestedOverrideWindowingMode() == WINDOWING_MODE_PINNED) {
             // Pinned calculation already includes rotation
             mTmpRect2.set(mTmpRect);
-            hasNewOverrideBounds = getWindowContainerController().mContainer
-                            .calculatePinnedBoundsForConfigChange(mTmpRect2);
+            hasNewOverrideBounds = getTaskStack().calculatePinnedBoundsForConfigChange(mTmpRect2);
         } else {
             final int newRotation = getWindowConfiguration().getRotation();
             if (!matchParentBounds()) {
@@ -588,7 +601,7 @@
                             || getRequestedOverrideWindowingMode()
                             == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY) {
                         mTmpRect2.set(mTmpRect);
-                        getWindowContainerController().mContainer
+                        getTaskStack()
                                 .calculateDockedBoundsForConfigChange(newParentConfig, mTmpRect2);
                         hasNewOverrideBounds = true;
                     }
@@ -786,7 +799,11 @@
 
             mTmpRect2.setEmpty();
             if (windowingMode != WINDOWING_MODE_FULLSCREEN) {
-                mWindowContainerController.getRawBounds(mTmpRect2);
+                if (mTaskStack.matchParentBounds()) {
+                    mTmpRect2.setEmpty();
+                } else {
+                    mTaskStack.getRawBounds(mTmpRect2);
+                }
             }
 
             if (!Objects.equals(getRequestedOverrideBounds(), mTmpRect2)) {
@@ -843,7 +860,12 @@
         // Reparent the window container before we try to update the position when adding it to
         // the new display below
         mTmpRect2.setEmpty();
-        mWindowContainerController.reparent(activityDisplay.mDisplayId, mTmpRect2, onTop);
+        if (mTaskStack == null) {
+            // TODO: Remove after unification.
+            Log.w(TAG, "Task stack is not valid when reparenting.");
+        } else {
+            mTaskStack.reparent(activityDisplay.mDisplayId, mTmpRect2, onTop);
+        }
         setBounds(mTmpRect2.isEmpty() ? null : mTmpRect2);
         activityDisplay.addChild(this, onTop ? POSITION_TOP : POSITION_BOTTOM);
         if (!displayRemoved) {
@@ -876,8 +898,10 @@
     /** Removes the stack completely. Also calls WindowManager to do the same on its side. */
     void remove() {
         removeFromDisplay();
-        mWindowContainerController.removeContainer();
-        mWindowContainerController = null;
+        if (mTaskStack != null) {
+            mTaskStack.removeIfPossible();
+            mTaskStack = null;
+        }
         onParentChanged();
     }
 
@@ -890,26 +914,35 @@
      */
     void getStackDockedModeBounds(Rect dockedBounds, Rect currentTempTaskBounds,
             Rect outStackBounds, Rect outTempTaskBounds) {
-        mWindowContainerController.getStackDockedModeBounds(getParent().getConfiguration(),
-                dockedBounds, currentTempTaskBounds,
-                outStackBounds, outTempTaskBounds);
+        if (mTaskStack != null) {
+            mTaskStack.getStackDockedModeBoundsLocked(getParent().getConfiguration(), dockedBounds,
+                    currentTempTaskBounds, outStackBounds, outTempTaskBounds);
+        } else {
+            outStackBounds.setEmpty();
+            outTempTaskBounds.setEmpty();
+        }
     }
 
     void prepareFreezingTaskBounds() {
-        mWindowContainerController.prepareFreezingTaskBounds();
+        if (mTaskStack != null) {
+            // TODO: This cannot be false after unification.
+            mTaskStack.prepareFreezingTaskBounds();
+        }
     }
 
     void getWindowContainerBounds(Rect outBounds) {
-        if (mWindowContainerController != null) {
-            mWindowContainerController.getBounds(outBounds);
+        if (mTaskStack != null) {
+            mTaskStack.getBounds(outBounds);
             return;
         }
         outBounds.setEmpty();
     }
 
     void positionChildWindowContainerAtTop(TaskRecord child) {
-        mWindowContainerController.positionChildAtTop(child.getTask(),
-                true /* includingParents */);
+        if (mTaskStack != null) {
+            // TODO: Remove after unification. This cannot be false after that.
+            mTaskStack.positionChildAtTop(child.getTask(), true /* includingParents */);
+        }
     }
 
     void positionChildWindowContainerAtBottom(TaskRecord child) {
@@ -918,14 +951,27 @@
         // task to bottom, the next focusable stack on the same display should be focused.
         final ActivityStack nextFocusableStack = getDisplay().getNextFocusableStack(
                 child.getStack(), true /* ignoreCurrent */);
-        mWindowContainerController.positionChildAtBottom(child.getTask(),
-                nextFocusableStack == null /* includingParents */);
+        if (mTaskStack != null) {
+            // TODO: Remove after unification. This cannot be false after that.
+            mTaskStack.positionChildAtBottom(child.getTask(),
+                    nextFocusableStack == null /* includingParents */);
+        }
     }
 
     /**
      * Returns whether to defer the scheduling of the multi-window mode.
      */
     boolean deferScheduleMultiWindowModeChanged() {
+        if (inPinnedWindowingMode()) {
+            // For the pinned stack, the deferring of the multi-window mode changed is tied to the
+            // transition animation into picture-in-picture, and is called once the animation
+            // completes, or is interrupted in a way that would leave the stack in a non-fullscreen
+            // state.
+            // @see BoundsAnimationController
+            // @see BoundsAnimationControllerTests
+            if (getTaskStack() == null) return false;
+            return getTaskStack().deferScheduleMultiWindowModeChanged();
+        }
         return false;
     }
 
@@ -2204,7 +2250,8 @@
                 .isKeyguardOrAodShowing(displayId);
         final boolean keyguardLocked = mStackSupervisor.getKeyguardController().isKeyguardLocked();
         final boolean showWhenLocked = r.canShowWhenLocked();
-        final boolean dismissKeyguard = r.hasDismissKeyguardWindows();
+        final boolean dismissKeyguard = r.mAppWindowToken != null
+                && r.mAppWindowToken.containsDismissKeyguardWindow();
         if (shouldBeVisible) {
             if (dismissKeyguard && mTopDismissingKeyguardActivity == null) {
                 mTopDismissingKeyguardActivity = r;
@@ -2561,7 +2608,9 @@
                 final boolean canShowWhenLocked = !mTopActivityOccludesKeyguard
                         && next.canShowWhenLocked();
                 final boolean mayDismissKeyguard = mTopDismissingKeyguardActivity != next
-                        && next.hasDismissKeyguardWindows();
+                        && next.mAppWindowToken != null
+                        && next.mAppWindowToken.containsDismissKeyguardWindow();
+
                 if (canShowWhenLocked || mayDismissKeyguard) {
                     ensureActivitiesVisibleLocked(null /* starting */, 0 /* configChanges */,
                             !PRESERVE_WINDOWS);
@@ -2991,7 +3040,10 @@
         position = getAdjustedPositionForTask(task, position, null /* starting */);
         mTaskHistory.remove(task);
         mTaskHistory.add(position, task);
-        mWindowContainerController.positionChildAt(task.getTask(), position);
+        if (mTaskStack != null) {
+            // TODO: this could not be false after unification.
+            mTaskStack.positionChildAt(task.getTask(), position);
+        }
         updateTaskMovement(task, true);
     }
 
@@ -4906,8 +4958,7 @@
     }
 
     // TODO: Figure-out a way to consolidate with resize() method below.
-    @Override
-    public void requestResize(Rect bounds) {
+    void requestResize(Rect bounds) {
         mService.resizeStack(mStackId, bounds,
                 true /* allowResizeInDockedMode */, false /* preserveWindows */,
                 false /* animate */, -1 /* animationDuration */);
@@ -4945,7 +4996,8 @@
     }
 
     void onPipAnimationEndResize() {
-        mWindowContainerController.onPipAnimationEndResize();
+        if (mTaskStack == null) return;
+        mTaskStack.onPipAnimationEndResize();
     }
 
 
@@ -5491,6 +5543,65 @@
         }
     }
 
+
+    Rect getDefaultPictureInPictureBounds(float aspectRatio) {
+        if (getTaskStack() == null) return null;
+        return getTaskStack().getPictureInPictureBounds(aspectRatio, null /* currentStackBounds */);
+    }
+
+    void animateResizePinnedStack(Rect sourceHintBounds, Rect toBounds, int animationDuration,
+            boolean fromFullscreen) {
+        if (!inPinnedWindowingMode()) return;
+        if (skipResizeAnimation(toBounds == null /* toFullscreen */)) {
+            mService.moveTasksToFullscreenStack(mStackId, true /* onTop */);
+        } else {
+            if (getTaskStack() == null) return;
+            getTaskStack().animateResizePinnedStack(toBounds, sourceHintBounds,
+                    animationDuration, fromFullscreen);
+        }
+    }
+
+    private boolean skipResizeAnimation(boolean toFullscreen) {
+        if (!toFullscreen) {
+            return false;
+        }
+        final Configuration parentConfig = getParent().getConfiguration();
+        final ActivityRecord top = topRunningNonOverlayTaskActivity();
+        return top != null && !top.isConfigurationCompatible(parentConfig);
+    }
+
+    void setPictureInPictureAspectRatio(float aspectRatio) {
+        if (getTaskStack() == null) return;
+        getTaskStack().setPictureInPictureAspectRatio(aspectRatio);
+    }
+
+    void setPictureInPictureActions(List<RemoteAction> actions) {
+        if (getTaskStack() == null) return;
+        getTaskStack().setPictureInPictureActions(actions);
+    }
+
+    boolean isAnimatingBoundsToFullscreen() {
+        if (getTaskStack() == null) return false;
+        return getTaskStack().isAnimatingBoundsToFullscreen();
+    }
+
+    public void updatePictureInPictureModeForPinnedStackAnimation(Rect targetStackBounds,
+            boolean forceUpdate) {
+        // It is guaranteed that the activities requiring the update will be in the pinned stack at
+        // this point (either reparented before the animation into PiP, or before reparenting after
+        // the animation out of PiP)
+        synchronized (mService.mGlobalLock) {
+            if (!isAttached()) {
+                return;
+            }
+            ArrayList<TaskRecord> tasks = getAllTasks();
+            for (int i = 0; i < tasks.size(); i++) {
+                mStackSupervisor.updatePictureInPictureMode(tasks.get(i), targetStackBounds,
+                        forceUpdate);
+            }
+        }
+    }
+
     public int getStackId() {
         return mStackId;
     }
diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
index a50ae84..a4cda5a 100644
--- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
@@ -1674,8 +1674,8 @@
     }
 
     void resizePinnedStackLocked(Rect pinnedBounds, Rect tempPinnedTaskBounds) {
-        // TODO(multi-display): Pinned stack display should be passed in.
-        final PinnedActivityStack stack =
+        // TODO(multi-display): The display containing the stack should be passed in.
+        final ActivityStack stack =
                 mRootActivityContainer.getDefaultDisplay().getPinnedStack();
         if (stack == null) {
             Slog.w(TAG, "resizePinnedStackLocked: pinned stack not found");
@@ -1686,7 +1686,7 @@
         // another AM call that is holding the AMS lock. In such a case, the pinnedBounds may be
         // incorrect if AMS.resizeStackWithBoundsFromWindowManager() is already called while waiting
         // for the AMS lock to be freed. So check and make sure these bounds are still good.
-        final PinnedStackWindowController stackController = stack.getWindowContainerController();
+        final TaskStack stackController = stack.getTaskStack();
         if (stackController.pinnedStackResizeDisallowed()) {
             return;
         }
@@ -1730,15 +1730,14 @@
              * invisible as well and added to the stopping list.  After which we process the
              * stopping list by handling the idle.
              */
-            final PinnedActivityStack pinnedStack = (PinnedActivityStack) stack;
-            pinnedStack.mForceHidden = true;
-            pinnedStack.ensureActivitiesVisibleLocked(null, 0, PRESERVE_WINDOWS);
-            pinnedStack.mForceHidden = false;
+            stack.mForceHidden = true;
+            stack.ensureActivitiesVisibleLocked(null, 0, PRESERVE_WINDOWS);
+            stack.mForceHidden = false;
             activityIdleInternalLocked(null, false /* fromTimeout */,
                     true /* processPausingActivites */, null /* configuration */);
 
             // Move all the tasks to the bottom of the fullscreen stack
-            moveTasksToFullscreenStackLocked(pinnedStack, !ON_TOP);
+            moveTasksToFullscreenStackLocked(stack, !ON_TOP);
         } else {
             for (int i = tasks.size() - 1; i >= 0; i--) {
                 removeTaskByIdLocked(tasks.get(i).taskId, true /* killProcess */,
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index b100ecd..916baa0 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -16,6 +16,7 @@
 
 package com.android.server.wm;
 
+import static android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND;
 import static android.app.Activity.RESULT_CANCELED;
 import static android.app.ActivityManager.START_ABORTED;
 import static android.app.ActivityManager.START_CANCELED;
@@ -50,6 +51,7 @@
 import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_INSTANCE;
 import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_TASK;
 import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_TOP;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.Display.INVALID_DISPLAY;
 
@@ -745,8 +747,9 @@
         // not sure if we need to create START_ABORTED_BACKGROUND so for now piggybacking
         // on START_ABORTED
         if (!abort) {
-            abort |= shouldAbortBackgroundActivityStart(callingUid, callingPackage, realCallingUid,
-                    callerApp, originatingPendingIntent, allowBackgroundActivityStart);
+            abort |= shouldAbortBackgroundActivityStart(callingUid, callingPid, callingPackage,
+                    realCallingUid, callerApp, originatingPendingIntent,
+                    allowBackgroundActivityStart, intent);
         }
 
         // Merge the two options bundles, while realCallerOptions takes precedence.
@@ -893,9 +896,10 @@
                 true /* doResume */, checkedOptions, inTask, outActivity);
     }
 
-    private boolean shouldAbortBackgroundActivityStart(int callingUid, final String callingPackage,
-            int realCallingUid, WindowProcessController callerApp,
-            PendingIntentRecord originatingPendingIntent, boolean allowBackgroundActivityStart) {
+    private boolean shouldAbortBackgroundActivityStart(int callingUid, int callingPid,
+            final String callingPackage, int realCallingUid, WindowProcessController callerApp,
+            PendingIntentRecord originatingPendingIntent, boolean allowBackgroundActivityStart,
+            Intent intent) {
         if (mService.isBackgroundActivityStartsEnabled()) {
             return false;
         }
@@ -908,19 +912,24 @@
             return false;
         }
         // don't abort if the callingUid is in the foreground or is a persistent system process
-        if (isUidForeground(callingUid) || isUidPersistentSystemProcess(callingUid)) {
+        final boolean isCallingUidForeground = isUidForeground(callingUid);
+        final boolean isCallingUidPersistentSystemProcess = isUidPersistentSystemProcess(
+                callingUid);
+        if (isCallingUidForeground || isCallingUidPersistentSystemProcess) {
             return false;
         }
         // take realCallingUid into consideration
+        final boolean isRealCallingUidForeground = isUidForeground(realCallingUid);
+        final boolean isRealCallingUidPersistentSystemProcess = isUidPersistentSystemProcess(
+                realCallingUid);
         if (realCallingUid != callingUid) {
             // don't abort if the realCallingUid is in the foreground and callingUid isn't
-            if (isUidForeground(realCallingUid)) {
+            if (isRealCallingUidForeground) {
                 return false;
             }
             // if the realCallingUid is a persistent system process, abort if the IntentSender
             // wasn't whitelisted to start an activity
-            if (isUidPersistentSystemProcess(realCallingUid) && (originatingPendingIntent != null)
-                    && allowBackgroundActivityStart) {
+            if (isRealCallingUidPersistentSystemProcess && allowBackgroundActivityStart) {
                 return false;
             }
         }
@@ -928,11 +937,28 @@
         if (callerApp != null && callerApp.areBackgroundActivityStartsAllowed()) {
             return false;
         }
+        // don't abort if the callingUid has START_ACTIVITIES_FROM_BACKGROUND permission
+        if (mService.checkPermission(START_ACTIVITIES_FROM_BACKGROUND, callingPid, callingUid)
+                == PERMISSION_GRANTED) {
+            return false;
+        }
         // don't abort if the caller has the same uid as the recents component
         if (mSupervisor.mRecentTasks.isCallerRecents(callingUid)) {
             return false;
         }
         // anything that has fallen through will currently be aborted
+        Slog.w(TAG, "Blocking background activity start [callingPackage: " + callingPackage
+                + "; callingUid: " + callingUid
+                + "; isCallingUidForeground: " + isCallingUidForeground
+                + "; isCallingUidPersistentSystemProcess: " + isCallingUidPersistentSystemProcess
+                + "; realCallingUid: " + realCallingUid
+                + "; isRealCallingUidForeground: " + isRealCallingUidForeground
+                + "; isRealCallingUidPersistentSystemProcess: "
+                        + isRealCallingUidPersistentSystemProcess
+                + "; originatingPendingIntent: " + originatingPendingIntent
+                + "; isBgStartWhitelisted: " + allowBackgroundActivityStart
+                + "; intent: " + intent
+                + "]");
         // TODO: remove this toast after feature development is done
         mService.mUiHandler.post(() -> {
             Toast.makeText(mService.mContext,
@@ -945,7 +971,7 @@
     /** Returns true if uid has a visible window or its process is in a top state. */
     private boolean isUidForeground(int uid) {
         return (mService.getUidStateLocked(uid) == ActivityManager.PROCESS_STATE_TOP)
-            || mService.mWindowManager.isAnyWindowVisibleForUid(uid);
+            || mService.mWindowManager.mRoot.isAnyNonToastWindowVisibleForUid(uid);
     }
 
     /** Returns true if uid is in a persistent state. */
@@ -968,18 +994,19 @@
             Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "logActivityStart");
             final int callingUidProcState = mService.getUidStateLocked(callingUid);
             final boolean callingUidHasAnyVisibleWindow =
-                    mService.mWindowManager.isAnyWindowVisibleForUid(callingUid);
+                    mService.mWindowManager.mRoot.isAnyNonToastWindowVisibleForUid(callingUid);
             final int realCallingUidProcState = (callingUid == realCallingUid)
                     ? callingUidProcState
                     : mService.getUidStateLocked(realCallingUid);
             final boolean realCallingUidHasAnyVisibleWindow = (callingUid == realCallingUid)
                     ? callingUidHasAnyVisibleWindow
-                    : mService.mWindowManager.isAnyWindowVisibleForUid(realCallingUid);
+                    : mService.mWindowManager.mRoot.isAnyNonToastWindowVisibleForUid(
+                            realCallingUid);
             final String targetPackage = (r != null) ? r.packageName : null;
             final int targetUid = (r!= null) ? ((r.appInfo != null) ? r.appInfo.uid : -1) : -1;
             final int targetUidProcState = mService.getUidStateLocked(targetUid);
             final boolean targetUidHasAnyVisibleWindow = (targetUid != -1)
-                    ? mService.mWindowManager.isAnyWindowVisibleForUid(targetUid)
+                    ? mService.mWindowManager.mRoot.isAnyNonToastWindowVisibleForUid(targetUid)
                     : false;
             final String targetWhitelistTag = (targetUid != -1)
                     ? mService.getPendingTempWhitelistTagForUidLocked(targetUid)
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index e5bf21a..6a495d4 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -82,13 +82,10 @@
 import static com.android.server.am.ActivityManagerServiceDumpProcessesProto.HOME_PROC;
 import static com.android.server.am.ActivityManagerServiceDumpProcessesProto.LAUNCHING_ACTIVITY;
 import static com.android.server.am.ActivityManagerServiceDumpProcessesProto.PREVIOUS_PROC;
-import static com.android.server.am.ActivityManagerServiceDumpProcessesProto
-        .PREVIOUS_PROC_VISIBLE_TIME_MS;
+import static com.android.server.am.ActivityManagerServiceDumpProcessesProto.PREVIOUS_PROC_VISIBLE_TIME_MS;
 import static com.android.server.am.ActivityManagerServiceDumpProcessesProto.SCREEN_COMPAT_PACKAGES;
-import static com.android.server.am.ActivityManagerServiceDumpProcessesProto.ScreenCompatPackage
-        .MODE;
-import static com.android.server.am.ActivityManagerServiceDumpProcessesProto.ScreenCompatPackage
-        .PACKAGE;
+import static com.android.server.am.ActivityManagerServiceDumpProcessesProto.ScreenCompatPackage.MODE;
+import static com.android.server.am.ActivityManagerServiceDumpProcessesProto.ScreenCompatPackage.PACKAGE;
 import static com.android.server.wm.ActivityStack.REMOVE_TASK_MODE_DESTROYING;
 import static com.android.server.wm.ActivityStackSupervisor.DEFER_RESUME;
 import static com.android.server.wm.ActivityStackSupervisor.ON_TOP;
@@ -210,6 +207,7 @@
 import android.provider.Settings;
 import android.service.voice.IVoiceInteractionSession;
 import android.service.voice.VoiceInteractionManagerInternal;
+import android.sysprop.DisplayProperties;
 import android.telecom.TelecomManager;
 import android.text.TextUtils;
 import android.text.format.Time;
@@ -245,7 +243,6 @@
 import com.android.internal.util.FastPrintWriter;
 import com.android.internal.util.Preconditions;
 import com.android.internal.util.function.pooled.PooledLambda;
-import com.android.server.appop.AppOpsService;
 import com.android.server.AttributeCache;
 import com.android.server.LocalServices;
 import com.android.server.SystemService;
@@ -261,6 +258,7 @@
 import com.android.server.am.PendingIntentController;
 import com.android.server.am.PendingIntentRecord;
 import com.android.server.am.UserState;
+import com.android.server.appop.AppOpsService;
 import com.android.server.firewall.IntentFirewall;
 import com.android.server.pm.UserManagerService;
 import com.android.server.uri.UriGrantsManagerInternal;
@@ -694,7 +692,7 @@
         final boolean isPc = mContext.getPackageManager().hasSystemFeature(FEATURE_PC);
 
         // Transfer any global setting for forcing RTL layout, into a System Property
-        SystemProperties.set(DEVELOPMENT_FORCE_RTL, forceRtl ? "1":"0");
+        DisplayProperties.debug_force_rtl(forceRtl);
 
         final Configuration configuration = new Configuration();
         Settings.System.getConfiguration(resolver, configuration);
@@ -2407,7 +2405,7 @@
         try {
             synchronized (mGlobalLock) {
                 if (animate) {
-                    final PinnedActivityStack stack = mRootActivityContainer.getStack(stackId);
+                    final ActivityStack stack = mRootActivityContainer.getStack(stackId);
                     if (stack == null) {
                         Slog.w(TAG, "resizeStack: stackId " + stackId + " not found.");
                         return;
@@ -3712,7 +3710,7 @@
         final long ident = Binder.clearCallingIdentity();
         try {
             synchronized (mGlobalLock) {
-                final PinnedActivityStack stack =
+                final ActivityStack stack =
                         mRootActivityContainer.getDefaultDisplay().getPinnedStack();
                 if (stack == null) {
                     Slog.w(TAG, "dismissPip: pinned stack not found.");
@@ -3834,9 +3832,8 @@
 
         // If we are animating to fullscreen then we have already dispatched the PIP mode
         // changed, so we should reflect that check here as well.
-        final PinnedActivityStack stack = r.getActivityStack();
-        final PinnedStackWindowController windowController = stack.getWindowContainerController();
-        return !windowController.mContainer.isAnimatingBoundsToFullscreen();
+        final TaskStack taskStack = r.getActivityStack().getTaskStack();
+        return !taskStack.isAnimatingBoundsToFullscreen();
     }
 
     @Override
@@ -3870,7 +3867,7 @@
                                 r.pictureInPictureArgs.getSourceRectHint());
                         mRootActivityContainer.moveActivityToPinnedStack(
                                 r, sourceBounds, aspectRatio, "enterPictureInPictureMode");
-                        final PinnedActivityStack stack = r.getActivityStack();
+                        final ActivityStack stack = r.getActivityStack();
                         stack.setPictureInPictureAspectRatio(aspectRatio);
                         stack.setPictureInPictureActions(actions);
                         MetricsLoggerWrapper.logPictureInPictureEnter(mContext, r.appInfo.uid,
@@ -3914,7 +3911,7 @@
                     // If the activity is already in picture-in-picture, update the pinned stack now
                     // if it is not already expanding to fullscreen. Otherwise, the arguments will
                     // be used the next time the activity enters PiP
-                    final PinnedActivityStack stack = r.getActivityStack();
+                    final ActivityStack stack = r.getActivityStack();
                     if (!stack.isAnimatingBoundsToFullscreen()) {
                         stack.setPictureInPictureAspectRatio(
                                 r.pictureInPictureArgs.getAspectRatio());
@@ -4334,6 +4331,22 @@
     }
 
     @Override
+    public void setInheritShowWhenLocked(IBinder token, boolean inheritShowWhenLocked) {
+        synchronized (mGlobalLock) {
+            final ActivityRecord r = ActivityRecord.isInStackLocked(token);
+            if (r == null) {
+                return;
+            }
+            final long origId = Binder.clearCallingIdentity();
+            try {
+                r.setInheritShowWhenLocked(inheritShowWhenLocked);
+            } finally {
+                Binder.restoreCallingIdentity(origId);
+            }
+        }
+    }
+
+    @Override
     public void setTurnScreenOn(IBinder token, boolean turnScreenOn) {
         synchronized (mGlobalLock) {
             final ActivityRecord r = ActivityRecord.isInStackLocked(token);
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 740d472..b735115 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -1160,12 +1160,14 @@
     @Override
     boolean onDescendantOrientationChanged(IBinder freezeDisplayToken,
             ConfigurationContainer requestingContainer) {
+        final int previousRotation = mRotation;
         final Configuration config = updateOrientationFromAppTokens(
                 getRequestedOverrideConfiguration(), freezeDisplayToken, false);
-        // If display rotation class tells us that it doesn't consider app requested orientation,
-        // this display won't rotate just because of an app changes its requested orientation. Thus
-        // it indicates that this display chooses not to handle this request.
-        final boolean handled = getDisplayRotation().respectAppRequestedOrientation();
+        // This event is considered handled iff a configuration propagation is triggered, because
+        // that's the only place lower level containers check if they need to do something to this
+        // request. The only guaranteed signal is that the display is rotated to a different
+        // orientation (i.e. rotating 180 degrees doesn't count).
+        final boolean handled = (mRotation - previousRotation) % 2 != 0;
         if (config == null) {
             return handled;
         }
@@ -2307,13 +2309,12 @@
         out.set(mDisplayFrames.mStable);
     }
 
-    TaskStack createStack(int stackId, boolean onTop, StackWindowController controller) {
-        if (DEBUG_STACK) Slog.d(TAG_WM, "Create new stackId=" + stackId + " on displayId="
-                + mDisplayId);
+    void setStackOnDisplay(int stackId, boolean onTop, TaskStack stack) {
+        if (DEBUG_STACK) {
+            Slog.d(TAG_WM, "Create new stackId=" + stackId + " on displayId=" + mDisplayId);
+        }
 
-        final TaskStack stack = new TaskStack(mWmService, stackId, controller);
         mTaskStackContainers.addStackToDisplay(stack, onTop);
-        return stack;
     }
 
     void moveStackToDisplay(TaskStack stack, boolean onTop) {
@@ -4015,7 +4016,6 @@
 
         /**
          * Adds the stack to this container.
-         * @see DisplayContent#createStack(int, boolean, StackWindowController)
          */
         void addStackToDisplay(TaskStack stack, boolean onTop) {
             addStackReferenceIfNeeded(stack);
diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java
index bcc7be4..7aabc15 100644
--- a/services/core/java/com/android/server/wm/DisplayRotation.java
+++ b/services/core/java/com/android/server/wm/DisplayRotation.java
@@ -329,15 +329,6 @@
         return mFixedToUserRotation;
     }
 
-    /**
-     * Returns {@code true} if this display rotation takes app requested orientation into
-     * consideration; {@code false} otherwise. For the time being the only case where this is {@code
-     * false} is when {@link #isFixedToUserRotation()} is {@code true}.
-     */
-    boolean respectAppRequestedOrientation() {
-        return !mFixedToUserRotation;
-    }
-
     public int getLandscapeRotation() {
         return mLandscapeRotation;
     }
diff --git a/services/core/java/com/android/server/wm/KeyguardController.java b/services/core/java/com/android/server/wm/KeyguardController.java
index 5f56fe5..177f244 100644
--- a/services/core/java/com/android/server/wm/KeyguardController.java
+++ b/services/core/java/com/android/server/wm/KeyguardController.java
@@ -462,22 +462,19 @@
             mOccluded = false;
             mDismissingKeyguardActivity = null;
 
-            // Only the top activity of the focused stack on each display may control it's
-            // occluded state.
-            final ActivityStack focusedStack = display.getFocusedStack();
-            if (focusedStack != null) {
-                final ActivityRecord topDismissing =
-                        focusedStack.getTopDismissingKeyguardActivity();
-                mOccluded = focusedStack.topActivityOccludesKeyguard() || (topDismissing != null
-                                && focusedStack.topRunningActivityLocked() == topDismissing
-                                && controller.canShowWhileOccluded(
+            final ActivityStack stack = getStackForControllingOccluding(display);
+            if (stack != null) {
+                final ActivityRecord topDismissing = stack.getTopDismissingKeyguardActivity();
+                mOccluded = stack.topActivityOccludesKeyguard() || (topDismissing != null
+                        && stack.topRunningActivityLocked() == topDismissing
+                        && controller.canShowWhileOccluded(
                                 true /* dismissKeyguard */,
                                 false /* showWhenLocked */));
-                if (focusedStack.getTopDismissingKeyguardActivity() != null) {
-                    mDismissingKeyguardActivity = focusedStack.getTopDismissingKeyguardActivity();
+                if (stack.getTopDismissingKeyguardActivity() != null) {
+                    mDismissingKeyguardActivity = stack.getTopDismissingKeyguardActivity();
                 }
-                mOccluded |= controller.mWindowManager.isShowingDream();
             }
+            mOccluded |= controller.mWindowManager.isShowingDream();
 
             // TODO(b/113840485): Handle app transition for individual display, and apply occluded
             // state change to secondary displays.
@@ -492,6 +489,23 @@
             }
         }
 
+        /**
+         * Gets the stack used to check the occluded state.
+         * <p>
+         * Only the top non-pinned activity of the focusable stack on each display can control its
+         * occlusion state.
+         */
+        private ActivityStack getStackForControllingOccluding(ActivityDisplay display) {
+            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
+                final ActivityStack stack = display.getChildAt(stackNdx);
+                if (stack != null && stack.isFocusableAndVisible()
+                        && !stack.inPinnedWindowingMode()) {
+                    return stack;
+                }
+            }
+            return null;
+        }
+
         void dumpStatus(PrintWriter pw, String prefix) {
             final StringBuilder sb = new StringBuilder();
             sb.append(prefix);
diff --git a/services/core/java/com/android/server/wm/LaunchParamsPersister.java b/services/core/java/com/android/server/wm/LaunchParamsPersister.java
index 3062d34..86dc66d 100644
--- a/services/core/java/com/android/server/wm/LaunchParamsPersister.java
+++ b/services/core/java/com/android/server/wm/LaunchParamsPersister.java
@@ -221,7 +221,7 @@
     }
 
     private boolean saveTaskToLaunchParam(TaskRecord task, PersistableLaunchParams params) {
-        final ActivityStack<?> stack = task.getStack();
+        final ActivityStack stack = task.getStack();
         final int displayId = stack.mDisplayId;
         final ActivityDisplay display =
                 mSupervisor.mRootActivityContainer.getActivityDisplay(displayId);
diff --git a/services/core/java/com/android/server/wm/PinnedActivityStack.java b/services/core/java/com/android/server/wm/PinnedActivityStack.java
deleted file mode 100644
index 2a05af4..0000000
--- a/services/core/java/com/android/server/wm/PinnedActivityStack.java
+++ /dev/null
@@ -1,111 +0,0 @@
-/*
- * Copyright (C) 2017 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.wm;
-
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
-import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
-
-import android.app.RemoteAction;
-import android.content.res.Configuration;
-import android.graphics.Rect;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * State and management of the pinned stack of activities.
- */
-class PinnedActivityStack extends ActivityStack<PinnedStackWindowController>
-        implements PinnedStackWindowListener {
-
-    PinnedActivityStack(ActivityDisplay display, int stackId, ActivityStackSupervisor supervisor,
-            boolean onTop) {
-        super(display, stackId, supervisor, WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, onTop);
-    }
-
-    @Override
-    PinnedStackWindowController createStackWindowController(int displayId, boolean onTop,
-            Rect outBounds) {
-        return new PinnedStackWindowController(mStackId, this, displayId, onTop, outBounds,
-                mRootActivityContainer.mWindowManager);
-    }
-
-    Rect getDefaultPictureInPictureBounds(float aspectRatio) {
-        return getWindowContainerController().getPictureInPictureBounds(aspectRatio,
-                null /* currentStackBounds */);
-    }
-
-    void animateResizePinnedStack(Rect sourceHintBounds, Rect toBounds, int animationDuration,
-            boolean fromFullscreen) {
-        if (skipResizeAnimation(toBounds == null /* toFullscreen */)) {
-            mService.moveTasksToFullscreenStack(mStackId, true /* onTop */);
-        } else {
-            getWindowContainerController().animateResizePinnedStack(toBounds, sourceHintBounds,
-                    animationDuration, fromFullscreen);
-        }
-    }
-
-    private boolean skipResizeAnimation(boolean toFullscreen) {
-        if (!toFullscreen) {
-            return false;
-        }
-        final Configuration parentConfig = getParent().getConfiguration();
-        final ActivityRecord top = topRunningNonOverlayTaskActivity();
-        return top != null && !top.isConfigurationCompatible(parentConfig);
-    }
-
-    void setPictureInPictureAspectRatio(float aspectRatio) {
-        getWindowContainerController().setPictureInPictureAspectRatio(aspectRatio);
-    }
-
-    void setPictureInPictureActions(List<RemoteAction> actions) {
-        getWindowContainerController().setPictureInPictureActions(actions);
-    }
-
-    boolean isAnimatingBoundsToFullscreen() {
-        return getWindowContainerController().mContainer.isAnimatingBoundsToFullscreen();
-    }
-
-    /**
-     * Returns whether to defer the scheduling of the multi-window mode.
-     */
-    boolean deferScheduleMultiWindowModeChanged() {
-        // For the pinned stack, the deferring of the multi-window mode changed is tied to the
-        // transition animation into picture-in-picture, and is called once the animation completes,
-        // or is interrupted in a way that would leave the stack in a non-fullscreen state.
-        // @see BoundsAnimationController
-        // @see BoundsAnimationControllerTests
-        return mWindowContainerController.deferScheduleMultiWindowModeChanged();
-    }
-
-    public void updatePictureInPictureModeForPinnedStackAnimation(Rect targetStackBounds,
-            boolean forceUpdate) {
-        // It is guaranteed that the activities requiring the update will be in the pinned stack at
-        // this point (either reparented before the animation into PiP, or before reparenting after
-        // the animation out of PiP)
-        synchronized (mService.mGlobalLock) {
-            if (!isAttached()) {
-                return;
-            }
-            ArrayList<TaskRecord> tasks = getAllTasks();
-            for (int i = 0; i < tasks.size(); i++ ) {
-                mStackSupervisor.updatePictureInPictureMode(tasks.get(i), targetStackBounds,
-                        forceUpdate);
-            }
-        }
-    }
-}
diff --git a/services/core/java/com/android/server/wm/PinnedStackWindowController.java b/services/core/java/com/android/server/wm/PinnedStackWindowController.java
deleted file mode 100644
index 518e39b..0000000
--- a/services/core/java/com/android/server/wm/PinnedStackWindowController.java
+++ /dev/null
@@ -1,205 +0,0 @@
-/*
- * Copyright (C) 2017 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.wm;
-
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
-
-import static com.android.server.wm.BoundsAnimationController.NO_PIP_MODE_CHANGED_CALLBACKS;
-import static com.android.server.wm.BoundsAnimationController.SCHEDULE_PIP_MODE_CHANGED_ON_END;
-import static com.android.server.wm.BoundsAnimationController.SCHEDULE_PIP_MODE_CHANGED_ON_START;
-import static com.android.server.wm.BoundsAnimationController.SchedulePipModeChangedState;
-
-import android.app.RemoteAction;
-import android.graphics.Rect;
-
-import java.util.List;
-
-/**
- * Controller for the pinned stack container. See {@link StackWindowController}.
- */
-public class PinnedStackWindowController extends StackWindowController {
-
-    private Rect mTmpFromBounds = new Rect();
-    private Rect mTmpToBounds = new Rect();
-
-    public PinnedStackWindowController(int stackId, PinnedStackWindowListener listener,
-            int displayId, boolean onTop, Rect outBounds, WindowManagerService service) {
-        super(stackId, listener, displayId, onTop, outBounds, service);
-    }
-
-    /**
-     * @return the {@param currentStackBounds} transformed to the give {@param aspectRatio}.  If
-     *         {@param currentStackBounds} is null, then the {@param aspectRatio} is applied to the
-     *         default bounds.
-     */
-    public Rect getPictureInPictureBounds(float aspectRatio, Rect stackBounds) {
-        synchronized (mGlobalLock) {
-            if (!mService.mSupportsPictureInPicture || mContainer == null) {
-                return null;
-            }
-
-            final DisplayContent displayContent = mContainer.getDisplayContent();
-            if (displayContent == null) {
-                return null;
-            }
-
-            final PinnedStackController pinnedStackController =
-                    displayContent.getPinnedStackController();
-            if (stackBounds == null) {
-                // Calculate the aspect ratio bounds from the default bounds
-                stackBounds = pinnedStackController.getDefaultOrLastSavedBounds();
-            }
-
-            if (pinnedStackController.isValidPictureInPictureAspectRatio(aspectRatio)) {
-                return pinnedStackController.transformBoundsToAspectRatio(stackBounds, aspectRatio,
-                        true /* useCurrentMinEdgeSize */);
-            } else {
-                return stackBounds;
-            }
-        }
-    }
-
-    /**
-     * Animates the pinned stack.
-     */
-    public void animateResizePinnedStack(Rect toBounds, Rect sourceHintBounds,
-            int animationDuration, boolean fromFullscreen) {
-        synchronized (mGlobalLock) {
-            if (mContainer == null) {
-                throw new IllegalArgumentException("Pinned stack container not found :(");
-            }
-
-            // Get the from-bounds
-            final Rect fromBounds = new Rect();
-            mContainer.getBounds(fromBounds);
-
-            // Get non-null fullscreen to-bounds for animating if the bounds are null
-            @SchedulePipModeChangedState int schedulePipModeChangedState =
-                NO_PIP_MODE_CHANGED_CALLBACKS;
-            final boolean toFullscreen = toBounds == null;
-            if (toFullscreen) {
-                if (fromFullscreen) {
-                    throw new IllegalArgumentException("Should not defer scheduling PiP mode"
-                            + " change on animation to fullscreen.");
-                }
-                schedulePipModeChangedState = SCHEDULE_PIP_MODE_CHANGED_ON_START;
-
-                mService.getStackBounds(
-                        WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, mTmpToBounds);
-                if (!mTmpToBounds.isEmpty()) {
-                    // If there is a fullscreen bounds, use that
-                    toBounds = new Rect(mTmpToBounds);
-                } else {
-                    // Otherwise, use the display bounds
-                    toBounds = new Rect();
-                    mContainer.getDisplayContent().getBounds(toBounds);
-                }
-            } else if (fromFullscreen) {
-                schedulePipModeChangedState = SCHEDULE_PIP_MODE_CHANGED_ON_END;
-            }
-
-            mContainer.setAnimationFinalBounds(sourceHintBounds, toBounds, toFullscreen);
-
-            final Rect finalToBounds = toBounds;
-            final @SchedulePipModeChangedState int finalSchedulePipModeChangedState =
-                schedulePipModeChangedState;
-            final DisplayContent displayContent = mContainer.getDisplayContent();
-            displayContent.mBoundsAnimationController.getHandler().post(() -> {
-                if (mContainer == null) {
-                    return;
-                }
-                displayContent.mBoundsAnimationController.animateBounds(mContainer, fromBounds,
-                        finalToBounds, animationDuration, finalSchedulePipModeChangedState,
-                        fromFullscreen, toFullscreen);
-            });
-        }
-    }
-
-    /**
-     * Sets the current picture-in-picture aspect ratio.
-     */
-    public void setPictureInPictureAspectRatio(float aspectRatio) {
-        synchronized (mGlobalLock) {
-            if (!mService.mSupportsPictureInPicture || mContainer == null) {
-                return;
-            }
-
-            final PinnedStackController pinnedStackController =
-                    mContainer.getDisplayContent().getPinnedStackController();
-
-            if (Float.compare(aspectRatio, pinnedStackController.getAspectRatio()) != 0) {
-                mContainer.getAnimationOrCurrentBounds(mTmpFromBounds);
-                mTmpToBounds.set(mTmpFromBounds);
-                getPictureInPictureBounds(aspectRatio, mTmpToBounds);
-                if (!mTmpToBounds.equals(mTmpFromBounds)) {
-                    animateResizePinnedStack(mTmpToBounds, null /* sourceHintBounds */,
-                            -1 /* duration */, false /* fromFullscreen */);
-                }
-                pinnedStackController.setAspectRatio(
-                        pinnedStackController.isValidPictureInPictureAspectRatio(aspectRatio)
-                                ? aspectRatio : -1f);
-            }
-        }
-    }
-
-    /**
-     * Sets the current picture-in-picture actions.
-     */
-    public void setPictureInPictureActions(List<RemoteAction> actions) {
-        synchronized (mGlobalLock) {
-            if (!mService.mSupportsPictureInPicture || mContainer == null) {
-                return;
-            }
-
-            mContainer.getDisplayContent().getPinnedStackController().setActions(actions);
-        }
-    }
-
-    /**
-     * @return whether the multi-window mode change should be deferred as a part of a transition
-     * from fullscreen to non-fullscreen bounds.
-     */
-    public boolean deferScheduleMultiWindowModeChanged() {
-        synchronized (mGlobalLock) {
-            return mContainer.deferScheduleMultiWindowModeChanged();
-        }
-    }
-
-    /**
-     * @return whether the stack can be resized from the bounds animation.
-     */
-    public boolean pinnedStackResizeDisallowed() {
-        synchronized (mGlobalLock) {
-            return mContainer.pinnedStackResizeDisallowed();
-        }
-    }
-
-    /**
-     * The following calls are made from WM to AM.
-     */
-
-    /** Calls directly into activity manager so window manager lock shouldn't held. */
-    public void updatePictureInPictureModeForPinnedStackAnimation(Rect targetStackBounds,
-            boolean forceUpdate) {
-        if (mListener != null) {
-            PinnedStackWindowListener listener = (PinnedStackWindowListener) mListener;
-            listener.updatePictureInPictureModeForPinnedStackAnimation(targetStackBounds,
-                    forceUpdate);
-        }
-    }
-}
diff --git a/services/core/java/com/android/server/wm/PinnedStackWindowListener.java b/services/core/java/com/android/server/wm/PinnedStackWindowListener.java
deleted file mode 100644
index 33e8a60..0000000
--- a/services/core/java/com/android/server/wm/PinnedStackWindowListener.java
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright (C) 2017 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.wm;
-
-import android.graphics.Rect;
-
-/**
- * Interface used by the creator of {@link PinnedStackWindowController} to listen to changes with
- * the stack container.
- */
-public interface PinnedStackWindowListener extends StackWindowListener {
-
-    /**
-     * Called when the stack container pinned stack animation will change the picture-in-picture
-     * mode. This is a direct call into ActivityManager.
-     */
-    default void updatePictureInPictureModeForPinnedStackAnimation(Rect targetStackBounds,
-            boolean forceUpdate) {}
-}
diff --git a/services/core/java/com/android/server/wm/RootActivityContainer.java b/services/core/java/com/android/server/wm/RootActivityContainer.java
index f55c7c9..55554a7 100644
--- a/services/core/java/com/android/server/wm/RootActivityContainer.java
+++ b/services/core/java/com/android/server/wm/RootActivityContainer.java
@@ -955,7 +955,7 @@
         mWindowManager.deferSurfaceLayout();
 
         final ActivityDisplay display = r.getActivityStack().getDisplay();
-        PinnedActivityStack stack = display.getPinnedStack();
+        ActivityStack stack = display.getPinnedStack();
 
         // This will clear the pinned stack by moving an existing task to the full screen stack,
         // ensuring only one task is present.
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 4e70bbc..8fb7947 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -23,6 +23,7 @@
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_SUSTAINED_PERFORMANCE_MODE;
 import static android.view.WindowManager.LayoutParams.TYPE_DREAM;
 import static android.view.WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG;
+import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
 
 import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT;
 import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
@@ -280,6 +281,15 @@
     }
 
     /**
+     * Returns true if the callingUid has any non-toast window currently visible to the user.
+     */
+    boolean isAnyNonToastWindowVisibleForUid(int callingUid) {
+        return forAllWindows(w -> {
+            return w.getOwningUid() == callingUid && w.isVisible() && w.mAttrs.type != TYPE_TOAST;
+        }, true /* traverseTopToBottom */);
+    }
+
+    /**
      * Returns the app window token for the input binder if it exist in the system.
      * NOTE: Only one AppWindowToken is allowed to exist in the system for a binder token, since
      * AppWindowToken represents an activity which can only exist on one display.
diff --git a/services/core/java/com/android/server/wm/StackWindowController.java b/services/core/java/com/android/server/wm/StackWindowController.java
deleted file mode 100644
index ada807b..0000000
--- a/services/core/java/com/android/server/wm/StackWindowController.java
+++ /dev/null
@@ -1,245 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.server.wm;
-
-import static com.android.server.wm.WindowContainer.POSITION_BOTTOM;
-import static com.android.server.wm.WindowContainer.POSITION_TOP;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STACK;
-import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
-
-import android.content.res.Configuration;
-import android.graphics.Rect;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
-import android.util.Slog;
-import android.util.SparseArray;
-
-import com.android.internal.annotations.VisibleForTesting;
-
-import java.lang.ref.WeakReference;
-
-/**
- * Controller for the stack container. This is created by activity manager to link activity stacks
- * to the stack container they use in window manager.
- *
- * Test class: {@link StackWindowControllerTests}
- */
-public class StackWindowController
-        extends WindowContainerController<TaskStack, StackWindowListener> {
-
-    private final int mStackId;
-
-    private final H mHandler;
-
-    final Rect mTmpBounds = new Rect();
-
-    public StackWindowController(int stackId, StackWindowListener listener, int displayId,
-            boolean onTop, Rect outBounds) {
-        this(stackId, listener, displayId, onTop, outBounds, WindowManagerService.getInstance());
-    }
-
-    @VisibleForTesting
-    public StackWindowController(int stackId, StackWindowListener listener,
-            int displayId, boolean onTop, Rect outBounds, WindowManagerService service) {
-        super(listener, service);
-        mStackId = stackId;
-        mHandler = new H(new WeakReference<>(this), service.mH.getLooper());
-
-        final DisplayContent dc = mRoot.getDisplayContent(displayId);
-        if (dc == null) {
-            throw new IllegalArgumentException("Trying to add stackId=" + stackId
-                    + " to unknown displayId=" + displayId);
-        }
-
-        dc.createStack(stackId, onTop, this);
-        getRawBounds(outBounds);
-    }
-
-    @Override
-    public void removeContainer() {
-        if (mContainer != null) {
-            mContainer.removeIfPossible();
-            super.removeContainer();
-        }
-    }
-
-    void reparent(int displayId, Rect outStackBounds, boolean onTop) {
-        if (mContainer == null) {
-            throw new IllegalArgumentException("Trying to move unknown stackId=" + mStackId
-                    + " to displayId=" + displayId);
-        }
-
-        final DisplayContent targetDc = mRoot.getDisplayContent(displayId);
-        if (targetDc == null) {
-            throw new IllegalArgumentException("Trying to move stackId=" + mStackId
-                    + " to unknown displayId=" + displayId);
-        }
-
-        targetDc.moveStackToDisplay(mContainer, onTop);
-        getRawBounds(outStackBounds);
-    }
-
-    void positionChildAt(Task child, int position) {
-        if (DEBUG_STACK) {
-            Slog.i(TAG_WM, "positionChildAt: positioning task=" + child + " at " + position);
-        }
-        if (child == null) {
-            if (DEBUG_STACK) {
-                Slog.i(TAG_WM, "positionChildAt: could not find task=" + this);
-            }
-            return;
-        }
-        if (mContainer == null) {
-            if (DEBUG_STACK) {
-                Slog.i(TAG_WM, "positionChildAt: could not find stack for task=" + mContainer);
-            }
-            return;
-        }
-        child.positionAt(position);
-        mContainer.getDisplayContent().layoutAndAssignWindowLayersIfNeeded();
-    }
-
-    void positionChildAtTop(Task child, boolean includingParents) {
-        if (child == null) {
-            // TODO: Fix the call-points that cause this to happen.
-            return;
-        }
-
-        mContainer.positionChildAt(POSITION_TOP, child, includingParents);
-
-        final DisplayContent displayContent = mContainer.getDisplayContent();
-        if (displayContent.mAppTransition.isTransitionSet()) {
-            child.setSendingToBottom(false);
-        }
-        displayContent.layoutAndAssignWindowLayersIfNeeded();
-    }
-
-    void positionChildAtBottom(Task child, boolean includingParents) {
-        if (child == null) {
-            // TODO: Fix the call-points that cause this to happen.
-            return;
-        }
-
-        mContainer.positionChildAt(POSITION_BOTTOM, child, includingParents);
-
-        if (mContainer.getDisplayContent().mAppTransition.isTransitionSet()) {
-            child.setSendingToBottom(true);
-        }
-        mContainer.getDisplayContent().layoutAndAssignWindowLayersIfNeeded();
-    }
-
-    /**
-     * Re-sizes a stack and its containing tasks.
-     *
-     * @param bounds New stack bounds. Passing in null sets the bounds to fullscreen.
-     * @param taskBounds Bounds for tasks in the resized stack, keyed by task id.
-     * @param taskTempInsetBounds Inset bounds for individual tasks, keyed by task id.
-     */
-    public void resize(Rect bounds, SparseArray<Rect> taskBounds,
-            SparseArray<Rect> taskTempInsetBounds) {
-        if (mContainer == null) {
-            throw new IllegalArgumentException("resizeStack: stack " + this + " not found.");
-        }
-        // We might trigger a configuration change. Save the current task bounds for freezing.
-        mContainer.prepareFreezingTaskBounds();
-        if (mContainer.setBounds(bounds, taskBounds, taskTempInsetBounds)
-                && mContainer.isVisible()) {
-            mContainer.getDisplayContent().setLayoutNeeded();
-            mService.mWindowPlacerLocked.performSurfacePlacement();
-        }
-    }
-
-    public void onPipAnimationEndResize() {
-        mContainer.onPipAnimationEndResize();
-    }
-
-    /**
-     * @see TaskStack.getStackDockedModeBoundsLocked(ConfigurationContainer, Rect, Rect, Rect)
-     */
-    public void getStackDockedModeBounds(Configuration parentConfig, Rect dockedBounds,
-            Rect currentTempTaskBounds,
-            Rect outStackBounds, Rect outTempTaskBounds) {
-        if (mContainer != null) {
-            mContainer.getStackDockedModeBoundsLocked(parentConfig, dockedBounds,
-                    currentTempTaskBounds, outStackBounds, outTempTaskBounds);
-            return;
-        }
-        outStackBounds.setEmpty();
-        outTempTaskBounds.setEmpty();
-    }
-
-    public void prepareFreezingTaskBounds() {
-        if (mContainer == null) {
-            throw new IllegalArgumentException("prepareFreezingTaskBounds: stack " + this
-                    + " not found.");
-        }
-        mContainer.prepareFreezingTaskBounds();
-    }
-
-    public void getRawBounds(Rect outBounds) {
-        if (mContainer.matchParentBounds()) {
-            outBounds.setEmpty();
-        } else {
-            mContainer.getRawBounds(outBounds);
-        }
-    }
-
-    public void getBounds(Rect outBounds) {
-        if (mContainer != null) {
-            mContainer.getBounds(outBounds);
-            return;
-        }
-        outBounds.setEmpty();
-    }
-
-    void requestResize(Rect bounds) {
-        mHandler.obtainMessage(H.REQUEST_RESIZE, bounds).sendToTarget();
-    }
-
-    @Override
-    public String toString() {
-        return "{StackWindowController stackId=" + mStackId + "}";
-    }
-
-    private static final class H extends Handler {
-
-        static final int REQUEST_RESIZE = 0;
-
-        private final WeakReference<StackWindowController> mController;
-
-        H(WeakReference<StackWindowController> controller, Looper looper) {
-            super(looper);
-            mController = controller;
-        }
-
-        @Override
-        public void handleMessage(Message msg) {
-            final StackWindowController controller = mController.get();
-            final StackWindowListener listener = (controller != null)
-                    ? controller.mListener : null;
-            if (listener == null) {
-                return;
-            }
-            switch (msg.what) {
-                case REQUEST_RESIZE:
-                    listener.requestResize((Rect) msg.obj);
-                    break;
-            }
-        }
-    }
-}
diff --git a/services/core/java/com/android/server/wm/StackWindowListener.java b/services/core/java/com/android/server/wm/StackWindowListener.java
deleted file mode 100644
index c763c17..0000000
--- a/services/core/java/com/android/server/wm/StackWindowListener.java
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Copyright (C) 2017 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.wm;
-
-import android.graphics.Rect;
-
-/**
- * Interface used by the creator of {@link StackWindowController} to listen to changes with
- * the stack container.
- */
-public interface StackWindowListener extends WindowContainerListener {
-
-    /** Called when the stack container would like its controller to resize. */
-    void requestResize(Rect bounds);
-}
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index d334bd2..a7dd55b 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -209,26 +209,14 @@
         super.removeImmediately();
     }
 
-    void reparent(StackWindowController stackController, int position, boolean moveParents) {
-        if (DEBUG_STACK) {
-            Slog.i(TAG_WM, "reparent: moving taskId=" + mTaskId
-                    + " to stack=" + stackController + " at " + position);
-        }
-        final TaskStack stack = stackController.mContainer;
-        if (stack == null) {
-            throw new IllegalArgumentException("reparent: could not find stack="
-                    + stackController);
-        }
-        reparent(stack, position, moveParents);
-        getDisplayContent().layoutAndAssignWindowLayersIfNeeded();
-    }
-
-
     void reparent(TaskStack stack, int position, boolean moveParents) {
         if (stack == mStack) {
             throw new IllegalArgumentException(
                     "task=" + this + " already child of stack=" + mStack);
         }
+        if (stack == null) {
+            throw new IllegalArgumentException("reparent: could not find stack.");
+        }
         if (DEBUG_STACK) Slog.i(TAG, "reParentTask: removing taskId=" + mTaskId
                 + " from stack=" + mStack);
         EventLog.writeEvent(WM_TASK_REMOVED, mTaskId, "reParentTask");
@@ -254,6 +242,7 @@
             onDisplayChanged(displayContent);
             prevDisplayContent.setLayoutNeeded();
         }
+        getDisplayContent().layoutAndAssignWindowLayersIfNeeded();
     }
 
     /** @see ActivityTaskManagerService#positionTaskInStack(int, int, int). */
diff --git a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
index 6acd864..f3050a9 100644
--- a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
+++ b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
@@ -709,7 +709,7 @@
 
         final List<TaskRecord> tasksToCheck = new ArrayList<>();
         for (int i = 0; i < display.getChildCount(); ++i) {
-            ActivityStack<?> stack = display.getChildAt(i);
+            final ActivityStack stack = display.getChildAt(i);
             if (!stack.inFreeformWindowingMode()) {
                 continue;
             }
diff --git a/services/core/java/com/android/server/wm/TaskRecord.java b/services/core/java/com/android/server/wm/TaskRecord.java
index 4a553cf..69dcaf4 100644
--- a/services/core/java/com/android/server/wm/TaskRecord.java
+++ b/services/core/java/com/android/server/wm/TaskRecord.java
@@ -455,17 +455,10 @@
         }
 
         final Rect bounds = updateOverrideConfigurationFromLaunchBounds();
-        final StackWindowController stackController = getStack().getWindowContainerController();
+        final TaskStack stack = getStack().getTaskStack();
 
-        if (DEBUG_STACK) {
-            Slog.i(TAG_WM, "TaskRecord: taskId=" + taskId
-                    + " stack=" + stackController + " bounds=" + bounds);
-        }
-
-        final TaskStack stack = stackController.mContainer;
         if (stack == null) {
-            throw new IllegalArgumentException("TaskRecord: invalid stack="
-                    + stackController);
+            throw new IllegalArgumentException("TaskRecord: invalid stack=" + mStack);
         }
         EventLog.writeEvent(WM_TASK_CREATED, taskId, stack.mStackId);
         mTask = new Task(taskId, stack, userId, mService.mWindowManager, mResizeMode,
@@ -742,7 +735,7 @@
 
             // Must reparent first in window manager to avoid a situation where AM can delete the
             // we are coming from in WM before we reparent because it became empty.
-            mTask.reparent(toStack.getWindowContainerController(), position,
+            mTask.reparent(toStack.getTaskStack(), position,
                     moveStackMode == REPARENT_MOVE_STACK_TO_FRONT);
 
             final boolean moveStackToFront = moveStackMode == REPARENT_MOVE_STACK_TO_FRONT
@@ -1278,28 +1271,28 @@
     }
 
     /**
-     * Checks if the root activity requires a particular orientation (either by override or
+     * Checks if the top activity requires a particular orientation (either by override or
      * activityInfo) and returns that. Otherwise, this returns ORIENTATION_UNDEFINED.
      */
-    private int getRootActivityRequestedOrientation() {
-        ActivityRecord root = getRootActivity();
+    private int getTopActivityRequestedOrientation() {
+        ActivityRecord top = getTopActivity();
         if (getRequestedOverrideConfiguration().orientation != ORIENTATION_UNDEFINED
-                || root == null) {
+                || top == null) {
             return getRequestedOverrideConfiguration().orientation;
         }
-        int rootScreenOrientation = root.getOrientation();
-        if (rootScreenOrientation == ActivityInfo.SCREEN_ORIENTATION_NOSENSOR) {
+        int screenOrientation = top.getOrientation();
+        if (screenOrientation == ActivityInfo.SCREEN_ORIENTATION_NOSENSOR) {
             // NOSENSOR means the display's "natural" orientation, so return that.
             ActivityDisplay display = mStack != null ? mStack.getDisplay() : null;
             if (display != null && display.mDisplayContent != null) {
                 return mStack.getDisplay().mDisplayContent.getNaturalOrientation();
             }
-        } else if (rootScreenOrientation == ActivityInfo.SCREEN_ORIENTATION_LOCKED) {
+        } else if (screenOrientation == ActivityInfo.SCREEN_ORIENTATION_LOCKED) {
             // LOCKED means the activity's orientation remains unchanged, so return existing value.
-            return root.getConfiguration().orientation;
-        } else if (ActivityInfo.isFixedOrientationLandscape(rootScreenOrientation)) {
+            return top.getConfiguration().orientation;
+        } else if (ActivityInfo.isFixedOrientationLandscape(screenOrientation)) {
             return ORIENTATION_LANDSCAPE;
-        } else if (ActivityInfo.isFixedOrientationPortrait(rootScreenOrientation)) {
+        } else if (ActivityInfo.isFixedOrientationPortrait(screenOrientation)) {
             return ORIENTATION_PORTRAIT;
         }
         return ORIENTATION_UNDEFINED;
@@ -2196,9 +2189,9 @@
             // In FULLSCREEN mode, always start with empty bounds to indicate "fill parent"
             outOverrideBounds.setEmpty();
 
-            // If the task or its root activity require a different orientation, make it fit the
+            // If the task or its top activity requires a different orientation, make it fit the
             // available bounds by scaling down its bounds.
-            int forcedOrientation = getRootActivityRequestedOrientation();
+            int forcedOrientation = getTopActivityRequestedOrientation();
             if (forcedOrientation != ORIENTATION_UNDEFINED
                     && forcedOrientation != newParentConfig.orientation) {
                 final Rect parentBounds = newParentConfig.windowConfiguration.getBounds();
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index ee74bdf..8ed7d04 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -21,6 +21,7 @@
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
 import static android.app.WindowConfiguration.PINNED_WINDOWING_MODE_ELEVATION_IN_DIP;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
@@ -33,6 +34,10 @@
 import static android.view.WindowManager.DOCKED_RIGHT;
 import static android.view.WindowManager.DOCKED_TOP;
 
+import static com.android.server.wm.BoundsAnimationController.NO_PIP_MODE_CHANGED_CALLBACKS;
+import static com.android.server.wm.BoundsAnimationController.SCHEDULE_PIP_MODE_CHANGED_ON_END;
+import static com.android.server.wm.BoundsAnimationController.SCHEDULE_PIP_MODE_CHANGED_ON_START;
+import static com.android.server.wm.BoundsAnimationController.SchedulePipModeChangedState;
 import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_DOCKED_DIVIDER;
 import static com.android.server.wm.StackProto.ADJUSTED_BOUNDS;
 import static com.android.server.wm.StackProto.ADJUSTED_FOR_IME;
@@ -47,10 +52,12 @@
 import static com.android.server.wm.StackProto.MINIMIZE_AMOUNT;
 import static com.android.server.wm.StackProto.TASKS;
 import static com.android.server.wm.StackProto.WINDOW_CONTAINER;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STACK;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TASK_MOVEMENT;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 
 import android.annotation.CallSuper;
+import android.app.RemoteAction;
 import android.content.res.Configuration;
 import android.graphics.Point;
 import android.graphics.Rect;
@@ -71,9 +78,10 @@
 import com.android.server.EventLogTags;
 
 import java.io.PrintWriter;
+import java.util.List;
 
 public class TaskStack extends WindowContainer<Task> implements
-        BoundsAnimationTarget {
+        BoundsAnimationTarget, ConfigurationContainerListener {
     /** Minimum size of an adjusted stack bounds relative to original stack bounds. Used to
      * restrict IME adjustment so that a min portion of top stack remains visible.*/
     private static final float ADJUSTED_STACK_FRACTION_MIN = 0.3f;
@@ -93,6 +101,10 @@
     private Rect mTmpRect2 = new Rect();
     private Rect mTmpRect3 = new Rect();
 
+    /** For Pinned stack controlling. */
+    private Rect mTmpFromBounds = new Rect();
+    private Rect mTmpToBounds = new Rect();
+
     /** Stack bounds adjusted to screen content area (taking into account IM windows, etc.) */
     private final Rect mAdjustedBounds = new Rect();
 
@@ -141,6 +153,9 @@
 
     private Dimmer mDimmer = new Dimmer(this);
 
+    // TODO: remove after unification.
+    ActivityStack mActivityStack;
+
     /**
      * For {@link #prepareSurfaces}.
      */
@@ -150,10 +165,11 @@
     private final AnimatingAppWindowTokenRegistry mAnimatingAppWindowTokenRegistry =
             new AnimatingAppWindowTokenRegistry();
 
-    TaskStack(WindowManagerService service, int stackId, StackWindowController controller) {
+    TaskStack(WindowManagerService service, int stackId, ActivityStack activityStack) {
         super(service);
         mStackId = stackId;
-        setController(controller);
+        mActivityStack = activityStack;
+        activityStack.registerConfigurationChangeListener(this);
         mDockedStackMinimizeThickness = service.mContext.getResources().getDimensionPixelSize(
                 com.android.internal.R.dimen.docked_stack_minimize_thickness);
         EventLog.writeEvent(EventLogTags.WM_STACK_CREATED, stackId);
@@ -572,6 +588,49 @@
         positionChildAt(position, task, moveParents /* includingParents */, showForAllUsers);
     }
 
+    void positionChildAt(Task child, int position) {
+        if (DEBUG_STACK) {
+            Slog.i(TAG_WM, "positionChildAt: positioning task=" + child + " at " + position);
+        }
+        if (child == null) {
+            if (DEBUG_STACK) {
+                Slog.i(TAG_WM, "positionChildAt: could not find task=" + this);
+            }
+            return;
+        }
+        child.positionAt(position);
+        getDisplayContent().layoutAndAssignWindowLayersIfNeeded();
+    }
+
+    void positionChildAtTop(Task child, boolean includingParents) {
+        if (child == null) {
+            // TODO: Fix the call-points that cause this to happen.
+            return;
+        }
+
+        positionChildAt(POSITION_TOP, child, includingParents);
+
+        final DisplayContent displayContent = getDisplayContent();
+        if (displayContent.mAppTransition.isTransitionSet()) {
+            child.setSendingToBottom(false);
+        }
+        displayContent.layoutAndAssignWindowLayersIfNeeded();
+    }
+
+    void positionChildAtBottom(Task child, boolean includingParents) {
+        if (child == null) {
+            // TODO: Fix the call-points that cause this to happen.
+            return;
+        }
+
+        positionChildAt(POSITION_BOTTOM, child, includingParents);
+
+        if (getDisplayContent().mAppTransition.isTransitionSet()) {
+            child.setSendingToBottom(true);
+        }
+        getDisplayContent().layoutAndAssignWindowLayersIfNeeded();
+    }
+
     @Override
     void positionChildAt(int position, Task child, boolean includingParents) {
         positionChildAt(position, child, includingParents, child.showForAllUsers());
@@ -596,6 +655,21 @@
         EventLog.writeEvent(EventLogTags.WM_TASK_MOVED, child.mTaskId, toTop, targetPosition);
     }
 
+    void reparent(int displayId, Rect outStackBounds, boolean onTop) {
+        final DisplayContent targetDc = mWmService.mRoot.getDisplayContent(displayId);
+        if (targetDc == null) {
+            throw new IllegalArgumentException("Trying to move stackId=" + mStackId
+                    + " to unknown displayId=" + displayId);
+        }
+
+        targetDc.moveStackToDisplay(this, onTop);
+        if (matchParentBounds()) {
+            outStackBounds.setEmpty();
+        } else {
+            getRawBounds(outStackBounds);
+        }
+    }
+
     // TODO: We should really have users as a window container in the hierarchy so that we don't
     // have to do complicated things like we are doing in this method.
     private int findPositionForTask(Task task, int targetPosition, boolean showForAllUsers,
@@ -725,6 +799,23 @@
     }
 
     /**
+     * Re-sizes a stack and its containing tasks.
+     *
+     * @param bounds New stack bounds. Passing in null sets the bounds to fullscreen.
+     * @param taskBounds Bounds for tasks in the resized stack, keyed by task id.
+     * @param taskTempInsetBounds Inset bounds for individual tasks, keyed by task id.
+     */
+    void resize(Rect bounds, SparseArray<Rect> taskBounds,
+            SparseArray<Rect> taskTempInsetBounds) {
+        // We might trigger a configuration change. Save the current task bounds for freezing.
+        prepareFreezingTaskBounds();
+        if (setBounds(bounds, taskBounds, taskTempInsetBounds) && isVisible()) {
+            getDisplayContent().setLayoutNeeded();
+            mWmService.mWindowPlacerLocked.performSurfacePlacement();
+        }
+    }
+
+    /**
      * Calculate an amount by which to expand the stack bounds in each direction.
      * Used to make room for shadows in the pinned windowing mode.
      */
@@ -929,12 +1020,7 @@
                 (dockedStack == null || dockedStack == this) ? null : dockedStack.getRawBounds();
         getStackDockedModeBoundsLocked(mDisplayContent.getConfiguration(), dockedBounds,
                 null /* currentTempTaskBounds */, bounds, tempBounds);
-        getController().requestResize(bounds);
-    }
-
-    @Override
-    StackWindowController getController() {
-        return (StackWindowController) super.getController();
+        mActivityStack.requestResize(bounds);
     }
 
     @Override
@@ -947,6 +1033,14 @@
     }
 
     @Override
+    void removeImmediately() {
+        if (mActivityStack != null) {
+            mActivityStack.unregisterConfigurationChangeListener(this);
+        }
+        super.removeImmediately();
+    }
+
+    @Override
     void onParentSet() {
         super.onParentSet();
 
@@ -1572,14 +1666,13 @@
                 // I don't believe you...
             }
 
-            final PinnedStackWindowController controller =
-                    (PinnedStackWindowController) getController();
-            if (schedulePipModeChangedCallback && controller != null) {
+            if (schedulePipModeChangedCallback && mActivityStack != null) {
                 // We need to schedule the PiP mode change before the animation up. It is possible
                 // in this case for the animation down to not have been completed, so always
                 // force-schedule and update to the client to ensure that it is notified that it
                 // is no longer in picture-in-picture mode
-                controller.updatePictureInPictureModeForPinnedStackAnimation(null, forceUpdate);
+                mActivityStack.updatePictureInPictureModeForPinnedStackAnimation(null,
+                        forceUpdate);
             }
         }
         return true;
@@ -1592,12 +1685,10 @@
             // Update to the final bounds if requested. This is done here instead of in the bounds
             // animator to allow us to coordinate this after we notify the PiP mode changed
 
-            final PinnedStackWindowController controller =
-                    (PinnedStackWindowController) getController();
-            if (schedulePipModeChangedCallback && controller != null) {
+            if (schedulePipModeChangedCallback) {
                 // We need to schedule the PiP mode change after the animation down, so use the
                 // final bounds
-                controller.updatePictureInPictureModeForPinnedStackAnimation(
+                mActivityStack.updatePictureInPictureModeForPinnedStackAnimation(
                         mBoundsAnimationTarget, false /* forceUpdate */);
             }
 
@@ -1624,6 +1715,135 @@
         }
     }
 
+    /**
+     * @return the current stack bounds transformed to the given {@param aspectRatio}. If
+     *         the default bounds is {@code null}, then the {@param aspectRatio} is applied to the
+     *         default bounds.
+     */
+    Rect getPictureInPictureBounds(float aspectRatio, Rect stackBounds) {
+        if (!mWmService.mSupportsPictureInPicture) {
+            return null;
+        }
+
+        final DisplayContent displayContent = getDisplayContent();
+        if (displayContent == null) {
+            return null;
+        }
+
+        if (!inPinnedWindowingMode()) {
+            return null;
+        }
+
+        final PinnedStackController pinnedStackController =
+                displayContent.getPinnedStackController();
+        if (stackBounds == null) {
+            // Calculate the aspect ratio bounds from the default bounds
+            stackBounds = pinnedStackController.getDefaultOrLastSavedBounds();
+        }
+
+        if (pinnedStackController.isValidPictureInPictureAspectRatio(aspectRatio)) {
+            return pinnedStackController.transformBoundsToAspectRatio(stackBounds, aspectRatio,
+                    true /* useCurrentMinEdgeSize */);
+        } else {
+            return stackBounds;
+        }
+    }
+
+    /**
+     * Animates the pinned stack.
+     */
+    void animateResizePinnedStack(Rect toBounds, Rect sourceHintBounds,
+            int animationDuration, boolean fromFullscreen) {
+        if (!inPinnedWindowingMode()) {
+            return;
+        }
+        // Get the from-bounds
+        final Rect fromBounds = new Rect();
+        getBounds(fromBounds);
+
+        // Get non-null fullscreen to-bounds for animating if the bounds are null
+        @SchedulePipModeChangedState int schedulePipModeChangedState =
+                NO_PIP_MODE_CHANGED_CALLBACKS;
+        final boolean toFullscreen = toBounds == null;
+        if (toFullscreen) {
+            if (fromFullscreen) {
+                throw new IllegalArgumentException("Should not defer scheduling PiP mode"
+                        + " change on animation to fullscreen.");
+            }
+            schedulePipModeChangedState = SCHEDULE_PIP_MODE_CHANGED_ON_START;
+
+            mWmService.getStackBounds(
+                    WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, mTmpToBounds);
+            if (!mTmpToBounds.isEmpty()) {
+                // If there is a fullscreen bounds, use that
+                toBounds = new Rect(mTmpToBounds);
+            } else {
+                // Otherwise, use the display bounds
+                toBounds = new Rect();
+                getDisplayContent().getBounds(toBounds);
+            }
+        } else if (fromFullscreen) {
+            schedulePipModeChangedState = SCHEDULE_PIP_MODE_CHANGED_ON_END;
+        }
+
+        setAnimationFinalBounds(sourceHintBounds, toBounds, toFullscreen);
+
+        final Rect finalToBounds = toBounds;
+        final @SchedulePipModeChangedState int finalSchedulePipModeChangedState =
+                schedulePipModeChangedState;
+        final DisplayContent displayContent = getDisplayContent();
+        displayContent.mBoundsAnimationController.getHandler().post(() -> {
+            displayContent.mBoundsAnimationController.animateBounds(this, fromBounds,
+                    finalToBounds, animationDuration, finalSchedulePipModeChangedState,
+                    fromFullscreen, toFullscreen);
+        });
+    }
+
+    /**
+     * Sets the current picture-in-picture aspect ratio.
+     */
+    void setPictureInPictureAspectRatio(float aspectRatio) {
+        if (!mWmService.mSupportsPictureInPicture) {
+            return;
+        }
+
+        if (!inPinnedWindowingMode()) {
+            return;
+        }
+
+        final PinnedStackController pinnedStackController =
+                getDisplayContent().getPinnedStackController();
+
+        if (Float.compare(aspectRatio, pinnedStackController.getAspectRatio()) == 0) {
+            return;
+        }
+        getAnimationOrCurrentBounds(mTmpFromBounds);
+        mTmpToBounds.set(mTmpFromBounds);
+        getPictureInPictureBounds(aspectRatio, mTmpToBounds);
+        if (!mTmpToBounds.equals(mTmpFromBounds)) {
+            animateResizePinnedStack(mTmpToBounds, null /* sourceHintBounds */,
+                    -1 /* duration */, false /* fromFullscreen */);
+        }
+        pinnedStackController.setAspectRatio(
+                pinnedStackController.isValidPictureInPictureAspectRatio(aspectRatio)
+                        ? aspectRatio : -1f);
+    }
+
+    /**
+     * Sets the current picture-in-picture actions.
+     */
+    void setPictureInPictureActions(List<RemoteAction> actions) {
+        if (!mWmService.mSupportsPictureInPicture) {
+            return;
+        }
+
+        if (!inPinnedWindowingMode()) {
+            return;
+        }
+
+        getDisplayContent().getPinnedStackController().setActions(actions);
+    }
+
     @Override
     public boolean isAttached() {
         synchronized (mWmService.mGlobalLock) {
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index ea9a3e6..0c4f496 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -2662,28 +2662,6 @@
     }
 
     /**
-     * @return true if the activity contains windows that have
-     *         {@link LayoutParams#FLAG_SHOW_WHEN_LOCKED} set
-     */
-    public boolean containsShowWhenLockedWindow(IBinder token) {
-        synchronized (mGlobalLock) {
-            final AppWindowToken wtoken = mRoot.getAppWindowToken(token);
-            return wtoken != null && wtoken.containsShowWhenLockedWindow();
-        }
-    }
-
-    /**
-     * @return true if the activity contains windows that have
-     *         {@link LayoutParams#FLAG_DISMISS_KEYGUARD} set
-     */
-    public boolean containsDismissKeyguardWindow(IBinder token) {
-        synchronized (mGlobalLock) {
-            final AppWindowToken wtoken = mRoot.getAppWindowToken(token);
-            return wtoken != null && wtoken.containsDismissKeyguardWindow();
-        }
-    }
-
-    /**
      * Notifies activity manager that some Keyguard flags have changed and that it needs to
      * reevaluate the visibilities of the activities.
      * @param callback Runnable to be called when activity manager is done reevaluating visibilities
@@ -5749,17 +5727,6 @@
     }
 
     /**
-     * Returns true if the callingUid has any window currently visible to the user.
-     */
-    public boolean isAnyWindowVisibleForUid(int callingUid) {
-        synchronized (mGlobalLock) {
-            return mRoot.forAllWindows(w -> {
-                return w.getOwningUid() == callingUid && w.isVisible();
-            }, true /* traverseTopToBottom */);
-        }
-    }
-
-    /**
      * Called when a task has been removed from the recent tasks list.
      * <p>
      * Note: This doesn't go through {@link TaskWindowContainerController} yet as the window
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index c8c5e8f..fb00aeb 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -107,6 +107,7 @@
         "android.hardware.gnss@1.0",
         "android.hardware.gnss@1.1",
         "android.hardware.gnss@2.0",
+        "android.hardware.gnss.visibility_control@1.0",
         "android.hardware.input.classifier@1.0",
         "android.hardware.ir@1.0",
         "android.hardware.light@2.0",
diff --git a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
index b290bc5..dcb2ff5 100644
--- a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
+++ b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
@@ -26,6 +26,7 @@
 #include <android/hardware/gnss/1.1/IGnssMeasurement.h>
 #include <android/hardware/gnss/2.0/IGnssMeasurement.h>
 #include <android/hardware/gnss/measurement_corrections/1.0/IMeasurementCorrections.h>
+#include <android/hardware/gnss/visibility_control/1.0/IGnssVisibilityControl.h>
 #include <nativehelper/JNIHelp.h>
 #include "jni.h"
 #include "hardware_legacy/power.h"
@@ -88,6 +89,8 @@
 static jmethodID method_correctionPlaneLngDeg;
 static jmethodID method_correctionPlaneAltDeg;
 static jmethodID method_correctionPlaneAzimDeg;
+static jmethodID method_reportNfwNotification;
+static jmethodID method_isInEmergencySession;
 
 /*
  * Save a pointer to JavaVm to attach/detach threads executing
@@ -152,6 +155,9 @@
 using IMeasurementCorrections =
     android::hardware::gnss::measurement_corrections::V1_0::IMeasurementCorrections;
 
+using android::hardware::gnss::visibility_control::V1_0::IGnssVisibilityControl;
+using android::hardware::gnss::visibility_control::V1_0::IGnssVisibilityControlCallback;
+
 struct GnssDeathRecipient : virtual public hidl_death_recipient
 {
     // hidl_death_recipient interface
@@ -190,7 +196,7 @@
 // This boolean is needed to ensure that Gnsss Measurement Corrections related method are only
 // initalized when needed which will be few devices initially
 bool firstGnssMeasurementCorrectionInjected = false;
-
+sp<IGnssVisibilityControl> gnssVisibilityControlIface = nullptr;
 
 #define WAKE_LOCK_NAME  "GPS"
 
@@ -856,7 +862,7 @@
 
 Return<void> GnssMeasurementCallback::gnssMeasurementCb_2_0(
         const IGnssMeasurementCallback_V2_0::GnssData& data) {
-    // TODO(b/119571122): implement gnssMeasurementCb_2_0
+    translateAndSetGnssData(data);
     return Void();
 }
 
@@ -894,9 +900,8 @@
     return data.measurementCount;
 }
 
-template<>
-size_t GnssMeasurementCallback::getMeasurementCount<IGnssMeasurementCallback_V1_1::GnssData>
-        (const IGnssMeasurementCallback_V1_1::GnssData& data) {
+template<class T>
+size_t GnssMeasurementCallback::getMeasurementCount(const T& data) {
     return data.measurements.size();
 }
 
@@ -958,6 +963,17 @@
             ADR_STATE_HALF_CYCLE_REPORTED));
 }
 
+// Preallocate object as: JavaObject object(env, "android/location/GnssMeasurement");
+template<>
+void GnssMeasurementCallback::translateSingleGnssMeasurement
+        <IGnssMeasurementCallback_V2_0::GnssMeasurement>(
+        const IGnssMeasurementCallback_V2_0::GnssMeasurement* measurement_V2_0,
+        JavaObject& object) {
+    translateSingleGnssMeasurement(&(measurement_V2_0->v1_1), object);
+
+    SET(CodeType, (static_cast<int32_t>(measurement_V2_0->codeType)));
+}
+
 jobject GnssMeasurementCallback::translateGnssClock(
        JNIEnv* env, const IGnssMeasurementCallback_V1_0::GnssClock* clock) {
     JavaObject object(env, "android/location/GnssClock");
@@ -1080,6 +1096,54 @@
 }
 
 /*
+ * GnssVisibilityControlCallback implements callback methods of IGnssVisibilityControlCallback.hal.
+ */
+struct GnssVisibilityControlCallback : public IGnssVisibilityControlCallback {
+    Return<void> nfwNotifyCb(const IGnssVisibilityControlCallback::NfwNotification& notification)
+            override;
+    Return<bool> isInEmergencySession() override;
+};
+
+Return<void> GnssVisibilityControlCallback::nfwNotifyCb(
+        const IGnssVisibilityControlCallback::NfwNotification& notification) {
+    JNIEnv* env = getJniEnv();
+    jstring proxyAppPackageName = env->NewStringUTF(notification.proxyAppPackageName.c_str());
+    jstring otherProtocolStackName = env->NewStringUTF(notification.otherProtocolStackName.c_str());
+    jstring requestorId = env->NewStringUTF(notification.requestorId.c_str());
+
+    if (proxyAppPackageName && otherProtocolStackName && requestorId) {
+        env->CallVoidMethod(mCallbacksObj, method_reportNfwNotification, proxyAppPackageName,
+                            notification.protocolStack, otherProtocolStackName,
+                            notification.requestor, requestorId,
+                            notification.inEmergencyMode, notification.isCachedLocation);
+    } else {
+        ALOGE("%s: OOM Error\n", __func__);
+    }
+
+    if (requestorId) {
+        env->DeleteLocalRef(requestorId);
+    }
+
+    if (otherProtocolStackName) {
+        env->DeleteLocalRef(otherProtocolStackName);
+    }
+
+    if (proxyAppPackageName) {
+        env->DeleteLocalRef(proxyAppPackageName);
+    }
+
+    checkAndClearExceptionFromCallback(env, __FUNCTION__);
+    return Void();
+}
+
+Return<bool> GnssVisibilityControlCallback::isInEmergencySession() {
+    JNIEnv* env = getJniEnv();
+    auto result = env->CallBooleanMethod(mCallbacksObj, method_isInEmergencySession);
+    checkAndClearExceptionFromCallback(env, __FUNCTION__);
+    return result;
+}
+
+/*
  * AGnssCallback_V1_0 implements callback methods required by the IAGnssCallback 1.0 interface.
  */
 struct AGnssCallback_V1_0 : public IAGnssCallback_V1_0 {
@@ -1313,6 +1377,9 @@
             "reportLocationBatch",
             "([Landroid/location/Location;)V");
     method_reportGnssServiceDied = env->GetMethodID(clazz, "reportGnssServiceDied", "()V");
+    method_reportNfwNotification = env->GetMethodID(clazz, "reportNfwNotification",
+            "(Ljava/lang/String;BLjava/lang/String;BLjava/lang/String;BZZ)V");
+    method_isInEmergencySession = env->GetMethodID(clazz, "isInEmergencySession", "()Z");
 
     /*
      * Save a pointer to JVM.
@@ -1388,12 +1455,6 @@
     if (gnssHal_V2_0 != nullptr) {
         // TODO(b/119638366): getExtensionGnssMeasurement_1_1 from gnssHal_V2_0
         auto gnssMeasurement = gnssHal_V2_0->getExtensionGnssMeasurement_2_0();
-        auto gnssCorrections = gnssHal_V2_0->getExtensionMeasurementCorrections();
-        if (!gnssCorrections.isOk()) {
-            ALOGD("Unable to get a handle to GnssMeasurementCorrections interface");
-        } else {
-            gnssCorrectionsIface = gnssCorrections;
-        }
         if (!gnssMeasurement.isOk()) {
             ALOGD("Unable to get a handle to GnssMeasurement_V2_0");
         } else {
@@ -1401,6 +1462,12 @@
             gnssMeasurementIface_V1_1 = gnssMeasurementIface_V2_0;
             gnssMeasurementIface = gnssMeasurementIface_V2_0;
         }
+        auto gnssCorrections = gnssHal_V2_0->getExtensionMeasurementCorrections();
+        if (!gnssCorrections.isOk()) {
+            ALOGD("Unable to get a handle to GnssMeasurementCorrections interface");
+        } else {
+            gnssCorrectionsIface = gnssCorrections;
+        }
     } else if (gnssHal_V1_1 != nullptr) {
          auto gnssMeasurement = gnssHal_V1_1->getExtensionGnssMeasurement_1_1();
          if (!gnssMeasurement.isOk()) {
@@ -1471,6 +1538,15 @@
     } else {
         gnssBatchingIface = gnssBatching;
     }
+
+    if (gnssHal_V2_0 != nullptr) {
+        auto gnssVisibilityControl = gnssHal_V2_0->getExtensionVisibilityControl();
+        if (!gnssVisibilityControl.isOk()) {
+            ALOGD("Unable to get a handle to GnssVisibilityControl interface");
+        } else {
+            gnssVisibilityControlIface = gnssVisibilityControl;
+        }
+    }
 }
 
 static jboolean android_location_GnssLocationProvider_is_supported(
@@ -1572,7 +1648,13 @@
     if (agnssRilIface != nullptr) {
         agnssRilIface->setCallback(aGnssRilCbIface);
     } else {
-        ALOGI("Unable to Initialize AGnss Ril interface\n");
+        ALOGI("Unable to initialize AGnss Ril interface\n");
+    }
+
+    if (gnssVisibilityControlIface != nullptr) {
+        sp<IGnssVisibilityControlCallback> gnssVisibilityControlCbIface =
+                new GnssVisibilityControlCallback();
+        gnssVisibilityControlIface->setCallback(gnssVisibilityControlCbIface);
     }
 
     return JNI_TRUE;
@@ -1974,6 +2056,11 @@
     return result;
 }
 
+static jboolean android_location_GnssLocationProvider_is_gnss_visibility_control_supported(
+        JNIEnv* /* env */, jclass /* clazz */) {
+    return (gnssVisibilityControlIface != nullptr) ?  JNI_TRUE : JNI_FALSE;
+}
+
 static void android_location_GnssNetworkConnectivityHandler_update_network_state(JNIEnv* env,
                                                                        jobject /* obj */,
                                                                        jboolean connected,
@@ -2557,6 +2644,29 @@
     return gnssBatchingIface->stop();
 }
 
+static jboolean android_location_GnssVisibilityControl_enable_nfw_location_access(
+        JNIEnv* env, jobject, jobjectArray proxyApps) {
+    if (gnssVisibilityControlIface == nullptr) {
+        ALOGI("No GNSS Visibility Control interface available");
+        return JNI_FALSE;
+    }
+
+    const jsize length = env->GetArrayLength(proxyApps);
+    hidl_vec<hidl_string> hidlProxyApps(length);
+    for (int i = 0; i < length; ++i) {
+        jstring proxyApp = (jstring) (env->GetObjectArrayElement(proxyApps, i));
+        ScopedJniString jniProxyApp(env, proxyApp);
+        hidlProxyApps[i] = jniProxyApp;
+    }
+
+    auto result = gnssVisibilityControlIface->enableNfwLocationAccess(hidlProxyApps);
+    if (result.isOk()) {
+        return result;
+    } else {
+        return JNI_FALSE;
+    }
+}
+
 static const JNINativeMethod sMethods[] = {
      /* name, signature, funcPtr */
     {"class_init_native", "()V", reinterpret_cast<void *>(
@@ -2607,6 +2717,8 @@
     {"native_get_internal_state",
             "()Ljava/lang/String;",
             reinterpret_cast<void *>(android_location_GnssLocationProvider_get_internal_state)},
+    {"native_is_gnss_visibility_control_supported", "()Z", reinterpret_cast<void *>(
+            android_location_GnssLocationProvider_is_gnss_visibility_control_supported)},
 };
 
 static const JNINativeMethod sMethodsBatching[] = {
@@ -2736,6 +2848,14 @@
             reinterpret_cast<void *>(android_location_GnssConfiguration_set_es_extension_sec)},
 };
 
+static const JNINativeMethod sVisibilityControlMethods[] = {
+     /* name, signature, funcPtr */
+    {"native_enable_nfw_location_access",
+            "([Ljava/lang/String;)Z",
+            reinterpret_cast<void *>(
+                    android_location_GnssVisibilityControl_enable_nfw_location_access)},
+};
+
 int register_android_server_location_GnssLocationProvider(JNIEnv* env) {
     jniRegisterNativeMethods(
             env,
@@ -2767,6 +2887,11 @@
             "com/android/server/location/GnssConfiguration",
             sConfigurationMethods,
             NELEM(sConfigurationMethods));
+    jniRegisterNativeMethods(
+            env,
+            "com/android/server/location/GnssVisibilityControl",
+            sVisibilityControlMethods,
+            NELEM(sVisibilityControlMethods));
     return jniRegisterNativeMethods(
             env,
             "com/android/server/location/GnssLocationProvider",
diff --git a/services/ipmemorystore/java/com/android/server/net/ipmemorystore/IpMemoryStoreDatabase.java b/services/ipmemorystore/java/com/android/server/net/ipmemorystore/IpMemoryStoreDatabase.java
new file mode 100644
index 0000000..eaab650
--- /dev/null
+++ b/services/ipmemorystore/java/com/android/server/net/ipmemorystore/IpMemoryStoreDatabase.java
@@ -0,0 +1,143 @@
+/*
+ * 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.net.ipmemorystore;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteOpenHelper;
+
+/**
+ * Encapsulating class for using the SQLite database backing the memory store.
+ *
+ * This class groups together the contracts and the SQLite helper used to
+ * use the database.
+ *
+ * @hide
+ */
+public class IpMemoryStoreDatabase {
+    /**
+     * Contract class for the Network Attributes table.
+     */
+    public static class NetworkAttributesContract {
+        public static final String TABLENAME = "NetworkAttributes";
+
+        public static final String COLNAME_L2KEY = "l2Key";
+        public static final String COLTYPE_L2KEY = "TEXT NOT NULL";
+
+        public static final String COLNAME_EXPIRYDATE = "expiryDate";
+        // Milliseconds since the Epoch, in true Java style
+        public static final String COLTYPE_EXPIRYDATE = "BIGINT";
+
+        public static final String COLNAME_ASSIGNEDV4ADDRESS = "assignedV4Address";
+        public static final String COLTYPE_ASSIGNEDV4ADDRESS = "INTEGER";
+
+        // Please note that the group hint is only a *hint*, hence its name. The client can offer
+        // this information to nudge the grouping in the decision it thinks is right, but it can't
+        // decide for the memory store what is the same L3 network.
+        public static final String COLNAME_GROUPHINT = "groupHint";
+        public static final String COLTYPE_GROUPHINT = "TEXT";
+
+        public static final String COLNAME_DNSADDRESSES = "dnsAddresses";
+        // Stored in marshalled form as is
+        public static final String COLTYPE_DNSADDRESSES = "BLOB";
+
+        public static final String COLNAME_MTU = "mtu";
+        public static final String COLTYPE_MTU = "INTEGER";
+
+        public static final String CREATE_TABLE = "CREATE TABLE IF NOT EXISTS "
+                + TABLENAME                 + " ("
+                + COLNAME_L2KEY             + " " + COLTYPE_L2KEY + " PRIMARY KEY NOT NULL, "
+                + COLNAME_EXPIRYDATE        + " " + COLTYPE_EXPIRYDATE        + ", "
+                + COLNAME_ASSIGNEDV4ADDRESS + " " + COLTYPE_ASSIGNEDV4ADDRESS + ", "
+                + COLNAME_GROUPHINT         + " " + COLTYPE_GROUPHINT         + ", "
+                + COLNAME_DNSADDRESSES      + " " + COLTYPE_DNSADDRESSES      + ", "
+                + COLNAME_MTU               + " " + COLTYPE_MTU               + ")";
+        public static final String DROP_TABLE = "DROP TABLE IF EXISTS " + TABLENAME;
+    }
+
+    /**
+     * Contract class for the Private Data table.
+     */
+    public static class PrivateDataContract {
+        public static final String TABLENAME = "PrivateData";
+
+        public static final String COLNAME_L2KEY = "l2Key";
+        public static final String COLTYPE_L2KEY = "TEXT NOT NULL";
+
+        public static final String COLNAME_CLIENT = "client";
+        public static final String COLTYPE_CLIENT = "TEXT NOT NULL";
+
+        public static final String COLNAME_DATANAME = "dataName";
+        public static final String COLTYPE_DATANAME = "TEXT NOT NULL";
+
+        public static final String COLNAME_DATA = "data";
+        public static final String COLTYPE_DATA = "BLOB NOT NULL";
+
+        public static final String CREATE_TABLE = "CREATE TABLE IF NOT EXISTS "
+                + TABLENAME        + " ("
+                + COLNAME_L2KEY    + " " + COLTYPE_L2KEY    + ", "
+                + COLNAME_CLIENT   + " " + COLTYPE_CLIENT   + ", "
+                + COLNAME_DATANAME + " " + COLTYPE_DATANAME + ", "
+                + COLNAME_DATA     + " " + COLTYPE_DATA     + ", "
+                + "PRIMARY KEY ("
+                + COLNAME_L2KEY    + ", "
+                + COLNAME_CLIENT   + ", "
+                + COLNAME_DATANAME + "))";
+        public static final String DROP_TABLE = "DROP TABLE IF EXISTS " + TABLENAME;
+    }
+
+    // To save memory when the DB is not used, close it after 30s of inactivity. This is
+    // determined manually based on what feels right.
+    private static final long IDLE_CONNECTION_TIMEOUT_MS = 30_000;
+
+    /** The SQLite DB helper */
+    public static class DbHelper extends SQLiteOpenHelper {
+        // Update this whenever changing the schema.
+        private static final int SCHEMA_VERSION = 1;
+        private static final String DATABASE_FILENAME = "IpMemoryStore.db";
+
+        public DbHelper(@NonNull final Context context) {
+            super(context, DATABASE_FILENAME, null, SCHEMA_VERSION);
+            setIdleConnectionTimeout(IDLE_CONNECTION_TIMEOUT_MS);
+        }
+
+        /** Called when the database is created */
+        public void onCreate(@NonNull final SQLiteDatabase db) {
+            db.execSQL(NetworkAttributesContract.CREATE_TABLE);
+            db.execSQL(PrivateDataContract.CREATE_TABLE);
+        }
+
+        /** Called when the database is upgraded */
+        public void onUpgrade(@NonNull final SQLiteDatabase db, final int oldVersion,
+                final int newVersion) {
+            // No upgrade supported yet.
+            db.execSQL(NetworkAttributesContract.DROP_TABLE);
+            db.execSQL(PrivateDataContract.DROP_TABLE);
+            onCreate(db);
+        }
+
+        /** Called when the database is downgraded */
+        public void onDowngrade(@NonNull final SQLiteDatabase db, final int oldVersion,
+                final int newVersion) {
+            // Downgrades always nuke all data and recreate an empty table.
+            db.execSQL(NetworkAttributesContract.DROP_TABLE);
+            db.execSQL(PrivateDataContract.DROP_TABLE);
+            onCreate(db);
+        }
+    }
+}
diff --git a/services/ipmemorystore/java/com/android/server/net/ipmemorystore/IpMemoryStoreService.java b/services/ipmemorystore/java/com/android/server/net/ipmemorystore/IpMemoryStoreService.java
index c9759bf..55a72190 100644
--- a/services/ipmemorystore/java/com/android/server/net/ipmemorystore/IpMemoryStoreService.java
+++ b/services/ipmemorystore/java/com/android/server/net/ipmemorystore/IpMemoryStoreService.java
@@ -19,6 +19,8 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.Context;
+import android.database.SQLException;
+import android.database.sqlite.SQLiteDatabase;
 import android.net.IIpMemoryStore;
 import android.net.ipmemorystore.Blob;
 import android.net.ipmemorystore.IOnBlobRetrievedListener;
@@ -27,6 +29,10 @@
 import android.net.ipmemorystore.IOnSameNetworkResponseListener;
 import android.net.ipmemorystore.IOnStatusListener;
 import android.net.ipmemorystore.NetworkAttributesParcelable;
+import android.util.Log;
+
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
 
 /**
  * Implementation for the IP memory store.
@@ -37,10 +43,75 @@
  * @hide
  */
 public class IpMemoryStoreService extends IIpMemoryStore.Stub {
-    final Context mContext;
+    private static final String TAG = IpMemoryStoreService.class.getSimpleName();
+    private static final int MAX_CONCURRENT_THREADS = 4;
 
+    @NonNull
+    final Context mContext;
+    @Nullable
+    final SQLiteDatabase mDb;
+    @NonNull
+    final ExecutorService mExecutor;
+
+    /**
+     * Construct an IpMemoryStoreService object.
+     * This constructor will block on disk access to open the database.
+     * @param context the context to access storage with.
+     */
     public IpMemoryStoreService(@NonNull final Context context) {
+        // Note that constructing the service will access the disk and block
+        // for some time, but it should make no difference to the clients. Because
+        // the interface is one-way, clients fire and forget requests, and the callback
+        // will get called eventually in any case, and the framework will wait for the
+        // service to be created to deliver subsequent requests.
+        // Avoiding this would mean the mDb member can't be final, which means the service would
+        // have to test for nullity, care for failure, and allow for a wait at every single access,
+        // which would make the code a lot more complex and require all methods to possibly block.
         mContext = context;
+        SQLiteDatabase db;
+        final IpMemoryStoreDatabase.DbHelper helper = new IpMemoryStoreDatabase.DbHelper(context);
+        try {
+            db = helper.getWritableDatabase();
+            if (null == db) Log.e(TAG, "Unexpected null return of getWriteableDatabase");
+        } catch (final SQLException e) {
+            Log.e(TAG, "Can't open the Ip Memory Store database", e);
+            db = null;
+        } catch (final Exception e) {
+            Log.wtf(TAG, "Impossible exception Ip Memory Store database", e);
+            db = null;
+        }
+        mDb = db;
+        // The work-stealing thread pool executor will spawn threads as needed up to
+        // the max only when there is no free thread available. This generally behaves
+        // exactly like one would expect it intuitively :
+        // - When work arrives, it will spawn a new thread iff there are no available threads
+        // - When there is no work to do it will shutdown threads after a while (the while
+        //   being equal to 2 seconds (not configurable) when max threads are spun up and
+        //   twice as much for every one less thread)
+        // - When all threads are busy the work is enqueued and waits for any worker
+        //   to become available.
+        // Because the stealing pool is made for very heavily parallel execution of
+        // small tasks that spawn others, it creates a queue per thread that in this
+        // case is overhead. However, the three behaviors above make it a superior
+        // choice to cached or fixedThreadPoolExecutor, neither of which can actually
+        // enqueue a task waiting for a thread to be free. This can probably be solved
+        // with judicious subclassing of ThreadPoolExecutor, but that's a lot of dangerous
+        // complexity for little benefit in this case.
+        mExecutor = Executors.newWorkStealingPool(MAX_CONCURRENT_THREADS);
+    }
+
+    /**
+     * Shutdown the memory store service, cancelling running tasks and dropping queued tasks.
+     *
+     * This is provided to give a way to clean up, and is meant to be available in case of an
+     * emergency shutdown.
+     */
+    public void shutdown() {
+        // By contrast with ExecutorService#shutdown, ExecutorService#shutdownNow tries
+        // to cancel the existing tasks, and does not wait for completion. It does not
+        // guarantee the threads can be terminated in any given amount of time.
+        mExecutor.shutdownNow();
+        if (mDb != null) mDb.close();
     }
 
     /**
@@ -61,7 +132,7 @@
     public void storeNetworkAttributes(@NonNull final String l2Key,
             @NonNull final NetworkAttributesParcelable attributes,
             @Nullable final IOnStatusListener listener) {
-        // TODO : implement this
+        // TODO : implement this.
     }
 
     /**
@@ -79,7 +150,7 @@
     public void storeBlob(@NonNull final String l2Key, @NonNull final String clientId,
             @NonNull final String name, @NonNull final Blob data,
             @Nullable final IOnStatusListener listener) {
-        // TODO : implement this
+        // TODO : implement this.
     }
 
     /**
@@ -99,7 +170,7 @@
     @Override
     public void findL2Key(@NonNull final NetworkAttributesParcelable attributes,
             @NonNull final IOnL2KeyResponseListener listener) {
-        // TODO : implement this
+        // TODO : implement this.
     }
 
     /**
@@ -114,7 +185,7 @@
     @Override
     public void isSameNetwork(@NonNull final String l2Key1, @NonNull final String l2Key2,
             @NonNull final IOnSameNetworkResponseListener listener) {
-        // TODO : implement this
+        // TODO : implement this.
     }
 
     /**
diff --git a/services/ipmemorystore/java/com/android/server/net/ipmemorystore/RelevanceUtils.java b/services/ipmemorystore/java/com/android/server/net/ipmemorystore/RelevanceUtils.java
new file mode 100644
index 0000000..aa45400
--- /dev/null
+++ b/services/ipmemorystore/java/com/android/server/net/ipmemorystore/RelevanceUtils.java
@@ -0,0 +1,307 @@
+/*
+ * 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.net.ipmemorystore;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+/**
+ * A class containing the logic around the relevance value for
+ * IP Memory Store.
+ *
+ * @hide
+ */
+public class RelevanceUtils {
+    /**
+     * The relevance is a decaying value that gets lower and lower until it
+     * reaches 0 after some time passes. It follows an exponential decay law,
+     * dropping slowly at first then faster and faster, because a network is
+     * likely to be visited again if it was visited not long ago, and the longer
+     * it hasn't been visited the more likely it is that it won't be visited
+     * again. For example, a network visited on holiday should stay fresh for
+     * the duration of the holiday and persist for a while, but after the venue
+     * hasn't been visited for a while it should quickly be discarded. What
+     * should accelerate forgetting the network is extended periods without
+     * visits, so that occasional venues get discarded but regular visits keep
+     * the network relevant, even if the visits are infrequent.
+     *
+     * This function must be stable by iteration, meaning that adjusting the same value
+     * for different dates iteratively multiple times should give the same result.
+     * Formally, if f is the decay function that associates a relevance x at a date d1
+     * to the value at ulterior date d3, then for any date d2 between d1 and d3 :
+     * f(x, d3 - d1) = f(f(x, d3 - d2), d2 - d1). Intuitively, this property simply
+     * means it should be the same to compute and store back the value after two months,
+     * or to do it once after one month, store it back, and do it again after another
+     * months has passed.
+     * The pair of the relevance and date define the entire curve, so any pair
+     * of values on the curve will define the same curve. Setting one of them to a
+     * constant, so as not to have to store it, means the other one will always suffice
+     * to describe the curve. For example, only storing the date for a known, constant
+     * value of the relevance is an efficient way of remembering this information (and
+     * to compare relevances together, as f is monotonically decreasing).
+     *
+     *** Choosing the function :
+     * Functions of the kind described above are standard exponential decay functions
+     * like the ones that govern atomic decay where the value at any given date can be
+     * computed uniformly from the value at a previous date and the time elapsed since
+     * that date. It is simple to picture this kind of function as one where after a
+     * given period of time called the half-life, the relevance value will have been
+     * halved. Decay of this kind is expressed in function of the previous value by
+     * functions like
+     * f(x, t) = x * F ^ (t / L)
+     * ...where x is the value, t is the elapsed time, L is the half-life (or more
+     * generally the F-th-life) and F the decay factor (typically 0.5, hence why L is
+     * usually called the half-life). The ^ symbol here is used for exponentiation.
+     * Or, starting at a given M for t = 0 :
+     * f(t) = M * F ^ (t / L)
+     *
+     * Because a line in the store needs to become irrelevant at some point but
+     * this class of functions never go to 0, a minimum cutoff has to be chosen to
+     * represent irrelevance. The simpler way of doing this is to simply add this
+     * minimum cutoff to the computation before and removing it after.
+     * Thus the function becomes :
+     * f(x, t) = ((x + K) * F ^ (t / L)) - K
+     * ...where K is the minimum cutoff, L the half-life, and F the factor between
+     * the original x and x after its half-life. Strictly speaking using the word
+     * "half-life" implies that F = 0.5, but the relation works for any value of F.
+     *
+     * It is easy enough to check that this function satisfies the stability
+     * relation that was given above for any value of F, L and K, which become
+     * parameters that can be defined at will.
+     *
+     * relevance
+     *  1.0 |
+     *      |\
+     *      | \
+     *      |  \            (this graph rendered with L = 75 days and K = 1/40)
+     *  0.75|   ',
+     *      |     \
+     *      |      '.
+     *      |        \.
+     *      |          \
+     *  0.5 |           '\
+     *      |             ''.
+     *      |                ''.
+     *      |                   ''.
+     *  0.25|                      '''..
+     *      |                           '''..
+     *      |                                ''''....
+     *      |                                        '''''..........
+     *    0 +-------------------------------------------------------''''''''''----
+     *      0       50       100      150     200      250     300      350     400 days
+     *
+     *** Choosing the parameters
+     * The maximum M is an arbitrary parameter that simply scales the curve.
+     * The tradeoff for M is pretty simple : if the relevance is going to be an
+     * integer, the bigger M is the more precision there is in the relevance.
+     * However, values of M that are easy for humans to read are preferable to
+     * help debugging, and a suitably low value may be enough to ensure there
+     * won't be integer overflows in intermediate computations.
+     * A value of 1_000_000 probably is plenty for precision, while still in the
+     * low range of what ints can represent.
+     *
+     * F and L are parameters to be chosen arbitrarily and have an impact on how
+     * fast the relevance will be decaying at first, keeping in mind that
+     * the 400 days value and the cap stay the same. In simpler words, F and L
+     * define the steepness of the curve.
+     * To keep things simple (and familiar) F is arbitrarily chosen to be 0.5, and
+     * L is set to 200 days visually to achieve the desired effect. Refer to the
+     * illustration above to get a feel of how that feels.
+     *
+     * Moreover, the memory store works on an assumption that the relevance should
+     * be capped, and that an entry with capped relevance should decay in 400 days.
+     * This is on premises that the networks a device will need to remember the
+     * longest should be networks visited about once a year.
+     * For this reason, the relevance is at the maximum M 400 days before expiry :
+     * f(M, 400 days) = 0
+     * From replacing this with the value of the function, K can then be derived
+     * from the values of M, F and L :
+     * (M + K) * F ^ (t / L) - K = 0
+     * K = M * F ^ (400 days / L) / (1 - F ^ (400 days / L))
+     * Replacing with actual values this gives :
+     * K = 1_000_000 * 0.5 ^ (400 / 200) / (1 - 0.5 ^ (400 / 200))
+     *   = 1_000_000 / 3 ≈ 333_333.3
+     * This ensures the function has the desired profile, the desired value at
+     * cap, and the desired value at expiry.
+     *
+     *** Useful relations
+     * Let's define the expiry time for any given relevance x as the interval of
+     * time such as :
+     * f(x, expiry) = 0
+     * which can be rewritten
+     * ((x + K) * F ^ (expiry / L)) = K
+     * ...giving an expression of the expiry in function of the relevance x as
+     * expiry = L * logF(K / (x + K))
+     * Conversely the relevance x can be expressed in function of the expiry as
+     * x = K / F ^ (expiry / L) - K
+     * These relations are useful in utility functions.
+     *
+     *** Bumping things up
+     * The last issue therefore is to decide how to bump up the relevance. The
+     * simple approach is to simply lift up the curve a little bit by a constant
+     * normalized amount, delaying the time of expiry. For example increasing
+     * the relevance by an amount I gives :
+     * x2 = x1 + I
+     * x2 and x1 correspond to two different expiry times expiry2 and expiry1,
+     * and replacing x1 and x2 in the relation above with their expression in
+     * function of the expiry comes :
+     * K / F ^ (expiry2 / L) - K = K / F ^ (expiry1 / L) - K + I
+     * which resolves to :
+     * expiry2 = L * logF(K / (I + K / F ^ (expiry1 / L)))
+     *
+     * In this implementation, the bump is defined as 1/25th of the cap for
+     * the relevance. This means a network will be remembered for the maximum
+     * period of 400 days if connected 25 times in succession not accounting
+     * for decay. Of course decay actually happens so it will take more than 25
+     * connections for any given network to actually reach the cap, but because
+     * decay is slow at first, it is a good estimate of how fast cap happens.
+     *
+     * Specifically, it gives the following four results :
+     * - A network that a device connects to once hits irrelevance about 32.7 days after
+     *   it was first registered if never connected again.
+     * - A network that a device connects to once a day at a fixed hour will hit the cap
+     *   on the 27th connection.
+     * - A network that a device connects to once a week at a fixed hour will hit the cap
+     *   on the 57th connection.
+     * - A network that a device connects to every day for 7 straight days then never again
+     *   expires 144 days after the last connection.
+     * These metrics tend to match pretty well the requirements.
+     */
+
+    // TODO : make these constants configurable at runtime. Don't forget to build it so that
+    // changes will wipe the database, migrate the values, or otherwise make sure the relevance
+    // values are still meaningful.
+
+    // How long, in milliseconds, is a capped relevance valid for, or in other
+    // words how many milliseconds after its relevance was set to RELEVANCE_CAP does
+    // any given line expire. 400 days.
+    @VisibleForTesting
+    public static final long CAPPED_RELEVANCE_LIFETIME_MS = 400L * 24 * 60 * 60 * 1000;
+
+    // The constant that represents a normalized 1.0 value for the relevance. In other words,
+    // the cap for the relevance. This is referred to as M in the explanation above.
+    @VisibleForTesting
+    public static final int CAPPED_RELEVANCE = 1_000_000;
+
+    // The decay factor. After a half-life, the relevance will have decayed by this value.
+    // This is referred to as F in the explanation above.
+    private static final double DECAY_FACTOR = 0.5;
+
+    // The half-life. After this time, the relevance will have decayed by a factor DECAY_FACTOR.
+    // This is referred to as L in the explanation above.
+    private static final long HALF_LIFE_MS = 200L * 24 * 60 * 60 * 1000;
+
+    // The value of the frame change. This is referred to as K in the explanation above.
+    private static final double IRRELEVANCE_FLOOR =
+            CAPPED_RELEVANCE * powF((double) CAPPED_RELEVANCE_LIFETIME_MS / HALF_LIFE_MS)
+            / (1 - powF((double) CAPPED_RELEVANCE_LIFETIME_MS / HALF_LIFE_MS));
+
+    // How much to bump the relevance by every time a line is written to.
+    @VisibleForTesting
+    public static final int RELEVANCE_BUMP = CAPPED_RELEVANCE / 25;
+
+    // Java doesn't include a function for the logarithm in an arbitrary base, so implement it
+    private static final double LOG_DECAY_FACTOR = Math.log(DECAY_FACTOR);
+    private static double logF(final double value) {
+        return Math.log(value) / LOG_DECAY_FACTOR;
+    }
+
+    // Utility function to get a power of the decay factor, to simplify the code.
+    private static double powF(final double value) {
+        return Math.pow(DECAY_FACTOR, value);
+    }
+
+    /**
+     * Compute the value of the relevance now given an expiry date.
+     *
+     * @param expiry the date at which the column in the database expires.
+     * @return the adjusted value of the relevance for this moment in time.
+     */
+    public static int computeRelevanceForNow(final long expiry) {
+        return computeRelevanceForTargetDate(expiry, System.currentTimeMillis());
+    }
+
+    /**
+     * Compute the value of the relevance at a given date from an expiry date.
+     *
+     * Because relevance decays with time, a relevance in the past corresponds to
+     * a different relevance later.
+     *
+     * Relevance is always a positive value. 0 means not relevant at all.
+     *
+     * See the explanation at the top of this file to get the justification for this
+     * computation.
+     *
+     * @param expiry the date at which the column in the database expires.
+     * @param target the target date to adjust the relevance to.
+     * @return the adjusted value of the relevance for the target moment.
+     */
+    public static int computeRelevanceForTargetDate(final long expiry, final long target) {
+        final long delay = expiry - target;
+        if (delay >= CAPPED_RELEVANCE_LIFETIME_MS) return CAPPED_RELEVANCE;
+        if (delay <= 0) return 0;
+        return (int) (IRRELEVANCE_FLOOR / powF((float) delay / HALF_LIFE_MS) - IRRELEVANCE_FLOOR);
+    }
+
+    /**
+     * Compute the expiry duration adjusted up for a new fresh write.
+     *
+     * Every time data is written to the memory store for a given line, the
+     * relevance is bumped up by a certain amount, which will boost the priority
+     * of this line for computation of group attributes, and delay (possibly
+     * indefinitely, if the line is accessed regularly) forgetting the data stored
+     * in that line.
+     * As opposed to bumpExpiryDate, this function uses a duration from now to expiry.
+     *
+     * See the explanation at the top of this file for a justification of this computation.
+     *
+     * @param oldExpiryDuration the old expiry duration in milliseconds from now.
+     * @return the expiry duration representing a bumped up relevance value.
+     */
+    public static long bumpExpiryDuration(final long oldExpiryDuration) {
+        // L * logF(K / (I + K / F ^ (expiry1 / L))), as documented above
+        final double divisionFactor = powF(((double) oldExpiryDuration) / HALF_LIFE_MS);
+        final double oldRelevance = IRRELEVANCE_FLOOR / divisionFactor;
+        final long newDuration =
+                (long) (HALF_LIFE_MS * logF(IRRELEVANCE_FLOOR / (RELEVANCE_BUMP + oldRelevance)));
+        return Math.min(newDuration, CAPPED_RELEVANCE_LIFETIME_MS);
+    }
+
+    /**
+     * Compute the new expiry date adjusted up for a new fresh write.
+     *
+     * Every time data is written to the memory store for a given line, the
+     * relevance is bumped up by a certain amount, which will boost the priority
+     * of this line for computation of group attributes, and delay (possibly
+     * indefinitely, if the line is accessed regularly) forgetting the data stored
+     * in that line.
+     * As opposed to bumpExpiryDuration, this function takes the old timestamp and returns the
+     * new timestamp.
+     *
+     * {@see bumpExpiryDuration}, and keep in mind that the bump depends on when this is called,
+     * because the relevance decays exponentially, therefore bumping up a high relevance (for a
+     * date far in the future) is less potent than bumping up a low relevance (for a date in
+     * a close future).
+     *
+     * @param oldExpiryDate the old date of expiration.
+     * @return the new expiration date after the relevance bump.
+     */
+    public static long bumpExpiryDate(final long oldExpiryDate) {
+        final long now = System.currentTimeMillis();
+        final long newDuration = bumpExpiryDuration(oldExpiryDate - now);
+        return now + newDuration;
+    }
+}
diff --git a/services/robotests/Android.mk b/services/robotests/Android.mk
index 9159f0d..0cf0d34 100644
--- a/services/robotests/Android.mk
+++ b/services/robotests/Android.mk
@@ -26,9 +26,6 @@
 LOCAL_PRIVILEGED_MODULE := true
 
 LOCAL_STATIC_JAVA_LIBRARIES := \
-    bmgrlib \
-    bu \
-    services.backup \
     services.core \
     services.net
 
@@ -41,8 +38,7 @@
 
 LOCAL_MODULE := FrameworksServicesRoboTests
 
-LOCAL_SRC_FILES := $(call all-java-files-under, src) \
-    $(call all-java-files-under, backup/src)
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
 LOCAL_RESOURCE_DIR := \
     $(LOCAL_PATH)/res
@@ -82,8 +78,7 @@
 
 LOCAL_TEST_PACKAGE := FrameworksServicesLib
 
-LOCAL_ROBOTEST_FILES := $(call find-files-in-subdirs,$(LOCAL_PATH)/src,*Test.java,.) \
-    $(call find-files-in-subdirs,$(LOCAL_PATH)/backup/src,*Test.java,.)
+LOCAL_ROBOTEST_FILES := $(call find-files-in-subdirs,$(LOCAL_PATH)/src,*Test.java,.)
 
 include external/robolectric-shadows/run_robotests.mk
 
diff --git a/services/robotests/backup/src/com/android/server/backup/BackupManagerServiceTest.java b/services/robotests/backup/src/com/android/server/backup/BackupManagerServiceTest.java
index 769a9d4..b8db3f3 100644
--- a/services/robotests/backup/src/com/android/server/backup/BackupManagerServiceTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/BackupManagerServiceTest.java
@@ -46,6 +46,7 @@
 import android.util.SparseArray;
 
 import com.android.server.backup.testing.TransportData;
+import com.android.server.testing.shadows.ShadowApplicationPackageManager;
 import com.android.server.testing.shadows.ShadowBinder;
 
 import org.junit.After;
@@ -65,7 +66,7 @@
 
 /** Tests for the user-aware backup/restore system service {@link BackupManagerService}. */
 @RunWith(RobolectricTestRunner.class)
-@Config(shadows = {ShadowBinder.class})
+@Config(shadows = {ShadowApplicationPackageManager.class, ShadowBinder.class})
 @Presubmit
 public class BackupManagerServiceTest {
     private static final String TEST_PACKAGE = "package";
@@ -1075,7 +1076,7 @@
                 createServiceAndRegisterUser(UserHandle.USER_SYSTEM, mUserOneService);
         FullBackupJob job = new FullBackupJob();
 
-        backupManagerService.beginFullBackup(job);
+        backupManagerService.beginFullBackup(UserHandle.USER_SYSTEM, job);
 
         verify(mUserOneService).beginFullBackup(job);
     }
@@ -1086,7 +1087,7 @@
         BackupManagerService backupManagerService = createService();
         FullBackupJob job = new FullBackupJob();
 
-        backupManagerService.beginFullBackup(job);
+        backupManagerService.beginFullBackup(UserHandle.USER_SYSTEM, job);
 
         verify(mUserOneService, never()).beginFullBackup(job);
     }
@@ -1097,7 +1098,7 @@
         BackupManagerService backupManagerService =
                 createServiceAndRegisterUser(UserHandle.USER_SYSTEM, mUserOneService);
 
-        backupManagerService.endFullBackup();
+        backupManagerService.endFullBackup(UserHandle.USER_SYSTEM);
 
         verify(mUserOneService).endFullBackup();
     }
@@ -1107,7 +1108,7 @@
     public void testEndFullBackup_onUnknownUser_doesNotPropagateCall() throws Exception {
         BackupManagerService backupManagerService = createService();
 
-        backupManagerService.endFullBackup();
+        backupManagerService.endFullBackup(UserHandle.USER_SYSTEM);
 
         verify(mUserOneService, never()).endFullBackup();
     }
diff --git a/services/robotests/backup/src/com/android/server/backup/FullBackupJobTest.java b/services/robotests/backup/src/com/android/server/backup/FullBackupJobTest.java
new file mode 100644
index 0000000..9a78d0b3
--- /dev/null
+++ b/services/robotests/backup/src/com/android/server/backup/FullBackupJobTest.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.backup;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.annotation.UserIdInt;
+import android.app.job.JobScheduler;
+import android.content.Context;
+import android.os.Handler;
+import android.os.UserHandle;
+import android.platform.test.annotations.Presubmit;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.Shadows;
+import org.robolectric.annotation.Config;
+import org.robolectric.shadows.ShadowJobScheduler;
+
+@RunWith(RobolectricTestRunner.class)
+@Config(shadows = {ShadowJobScheduler.class})
+@Presubmit
+public class FullBackupJobTest {
+    private Context mContext;
+    private BackupManagerConstants mConstants;
+    private ShadowJobScheduler mShadowJobScheduler;
+
+    @UserIdInt private int mUserOneId;
+    @UserIdInt private int mUserTwoId;
+
+    @Before
+    public void setUp() throws Exception {
+        mContext = RuntimeEnvironment.application;
+        mConstants = new BackupManagerConstants(Handler.getMain(), mContext.getContentResolver());
+        mConstants.start();
+
+        mShadowJobScheduler = Shadows.shadowOf(mContext.getSystemService(JobScheduler.class));
+
+        mUserOneId = UserHandle.USER_SYSTEM;
+        mUserTwoId = mUserOneId + 1;
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        mConstants.stop();
+        FullBackupJob.cancel(mUserOneId, mContext);
+        FullBackupJob.cancel(mUserTwoId, mContext);
+    }
+
+    @Test
+    public void testSchedule_afterScheduling_jobExists() {
+        FullBackupJob.schedule(mUserOneId, mContext, 0, mConstants);
+        FullBackupJob.schedule(mUserTwoId, mContext, 0, mConstants);
+
+        assertThat(mShadowJobScheduler.getPendingJob(getJobIdForUserId(mUserOneId))).isNotNull();
+        assertThat(mShadowJobScheduler.getPendingJob(getJobIdForUserId(mUserTwoId))).isNotNull();
+    }
+
+    @Test
+    public void testCancel_afterCancelling_jobDoesntExist() {
+        FullBackupJob.schedule(mUserOneId, mContext, 0, mConstants);
+        FullBackupJob.schedule(mUserTwoId, mContext, 0, mConstants);
+        FullBackupJob.cancel(mUserOneId, mContext);
+        FullBackupJob.cancel(mUserTwoId, mContext);
+
+        assertThat(mShadowJobScheduler.getPendingJob(getJobIdForUserId(mUserOneId))).isNull();
+        assertThat(mShadowJobScheduler.getPendingJob(getJobIdForUserId(mUserTwoId))).isNull();
+    }
+//
+    @Test
+    public void testSchedule_onlySchedulesForRequestedUser() {
+        FullBackupJob.schedule(mUserOneId, mContext, 0, mConstants);
+
+        assertThat(mShadowJobScheduler.getPendingJob(getJobIdForUserId(mUserOneId))).isNotNull();
+        assertThat(mShadowJobScheduler.getPendingJob(getJobIdForUserId(mUserTwoId))).isNull();
+    }
+//
+    @Test
+    public void testCancel_onlyCancelsForRequestedUser() {
+        FullBackupJob.schedule(mUserOneId, mContext, 0, mConstants);
+        FullBackupJob.schedule(mUserTwoId, mContext, 0, mConstants);
+        FullBackupJob.cancel(mUserOneId, mContext);
+
+        assertThat(mShadowJobScheduler.getPendingJob(getJobIdForUserId(mUserOneId))).isNull();
+        assertThat(mShadowJobScheduler.getPendingJob(getJobIdForUserId(mUserTwoId))).isNotNull();
+    }
+
+    private static int getJobIdForUserId(int userId) {
+        return JobIdManager.getJobIdForUserId(FullBackupJob.MIN_JOB_ID, FullBackupJob.MAX_JOB_ID,
+                userId);
+    }
+}
diff --git a/services/robotests/backup/src/com/android/server/backup/JobIdManagerTest.java b/services/robotests/backup/src/com/android/server/backup/JobIdManagerTest.java
new file mode 100644
index 0000000..f8bb1ee
--- /dev/null
+++ b/services/robotests/backup/src/com/android/server/backup/JobIdManagerTest.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.backup;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.annotation.UserIdInt;
+import android.os.UserHandle;
+import android.platform.test.annotations.Presubmit;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import static org.testng.Assert.expectThrows;
+
+@RunWith(RobolectricTestRunner.class)
+@Presubmit
+public class JobIdManagerTest {
+    private static final int MIN_JOB_ID = 10;
+    private static final int MAX_JOB_ID = 20;
+
+    @UserIdInt private int mUserOneId;
+    @UserIdInt private int mUserTwoId;
+
+    @Before
+    public void setUp() {
+        mUserOneId = UserHandle.USER_SYSTEM;
+        mUserTwoId = mUserOneId + 1;
+    }
+
+    @Test
+    public void testGetJobIdForUserId_returnsDifferentJobIdsForDifferentUsers() {
+        int jobIdOne = JobIdManager.getJobIdForUserId(MIN_JOB_ID, MAX_JOB_ID, mUserOneId);
+        int jobIdTwo = JobIdManager.getJobIdForUserId(MIN_JOB_ID, MAX_JOB_ID, mUserTwoId);
+
+        assertThat(jobIdOne).isNotEqualTo(jobIdTwo);
+    }
+
+    @Test
+    public void testGetJobIdForUserId_returnsSameJobIdForSameUser() {
+        int jobIdOne = JobIdManager.getJobIdForUserId(MIN_JOB_ID, MAX_JOB_ID, mUserOneId);
+        int jobIdTwo = JobIdManager.getJobIdForUserId(MIN_JOB_ID, MAX_JOB_ID, mUserOneId);
+
+        assertThat(jobIdOne).isEqualTo(jobIdTwo);
+    }
+
+    @Test
+    public void testGetJobIdForUserId_throwsExceptionIfRangeIsExceeded() {
+        expectThrows(
+                RuntimeException.class,
+                () -> JobIdManager.getJobIdForUserId(MIN_JOB_ID, MAX_JOB_ID,
+                        MAX_JOB_ID + 1));
+    }
+}
diff --git a/services/robotests/backup/src/com/android/server/backup/KeyValueBackupJobTest.java b/services/robotests/backup/src/com/android/server/backup/KeyValueBackupJobTest.java
index 8e17209..8d9e44f 100644
--- a/services/robotests/backup/src/com/android/server/backup/KeyValueBackupJobTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/KeyValueBackupJobTest.java
@@ -18,8 +18,10 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import android.annotation.UserIdInt;
 import android.content.Context;
 import android.os.Handler;
+import android.os.UserHandle;
 import android.platform.test.annotations.Presubmit;
 
 import org.junit.After;
@@ -35,32 +37,67 @@
     private Context mContext;
     private BackupManagerConstants mConstants;
 
+    @UserIdInt private int mUserOneId;
+    @UserIdInt private int mUserTwoId;
+
     @Before
     public void setUp() throws Exception {
         mContext = RuntimeEnvironment.application;
         mConstants = new BackupManagerConstants(Handler.getMain(), mContext.getContentResolver());
         mConstants.start();
+
+        mUserOneId = UserHandle.USER_SYSTEM;
+        mUserTwoId = mUserOneId + 1;
     }
 
     @After
     public void tearDown() throws Exception {
         mConstants.stop();
-        KeyValueBackupJob.cancel(mContext);
+        KeyValueBackupJob.cancel(mUserOneId, mContext);
+        KeyValueBackupJob.cancel(mUserTwoId, mContext);
     }
 
     @Test
     public void testIsScheduled_beforeScheduling_returnsFalse() {
-        boolean isScheduled = KeyValueBackupJob.isScheduled();
-
-        assertThat(isScheduled).isFalse();
+        assertThat(KeyValueBackupJob.isScheduled(mUserOneId)).isFalse();
+        assertThat(KeyValueBackupJob.isScheduled(mUserTwoId)).isFalse();
     }
 
     @Test
     public void testIsScheduled_afterScheduling_returnsTrue() {
-        KeyValueBackupJob.schedule(mContext, mConstants);
+        KeyValueBackupJob.schedule(mUserOneId, mContext, mConstants);
+        KeyValueBackupJob.schedule(mUserTwoId, mContext, mConstants);
 
-        boolean isScheduled = KeyValueBackupJob.isScheduled();
+        assertThat(KeyValueBackupJob.isScheduled(mUserOneId)).isTrue();
+        assertThat(KeyValueBackupJob.isScheduled(mUserTwoId)).isTrue();
+    }
 
-        assertThat(isScheduled).isTrue();
+    @Test
+    public void testIsScheduled_afterCancelling_returnsFalse() {
+        KeyValueBackupJob.schedule(mUserOneId, mContext, mConstants);
+        KeyValueBackupJob.schedule(mUserTwoId, mContext, mConstants);
+        KeyValueBackupJob.cancel(mUserOneId, mContext);
+        KeyValueBackupJob.cancel(mUserTwoId, mContext);
+
+        assertThat(KeyValueBackupJob.isScheduled(mUserOneId)).isFalse();
+        assertThat(KeyValueBackupJob.isScheduled(mUserTwoId)).isFalse();
+    }
+
+    @Test
+    public void testIsScheduled_afterScheduling_returnsTrueOnlyForScheduledUser() {
+        KeyValueBackupJob.schedule(mUserOneId, mContext, mConstants);
+
+        assertThat(KeyValueBackupJob.isScheduled(mUserOneId)).isTrue();
+        assertThat(KeyValueBackupJob.isScheduled(mUserTwoId)).isFalse();
+    }
+
+    @Test
+    public void testIsScheduled_afterCancelling_returnsFalseOnlyForCancelledUser() {
+        KeyValueBackupJob.schedule(mUserOneId, mContext, mConstants);
+        KeyValueBackupJob.schedule(mUserTwoId, mContext, mConstants);
+        KeyValueBackupJob.cancel(mUserOneId, mContext);
+
+        assertThat(KeyValueBackupJob.isScheduled(mUserOneId)).isFalse();
+        assertThat(KeyValueBackupJob.isScheduled(mUserTwoId)).isTrue();
     }
 }
diff --git a/services/robotests/backup/src/com/android/server/backup/TransportManagerTest.java b/services/robotests/backup/src/com/android/server/backup/TransportManagerTest.java
index 693092d..9a6e003 100644
--- a/services/robotests/backup/src/com/android/server/backup/TransportManagerTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/TransportManagerTest.java
@@ -41,6 +41,7 @@
 import static java.util.stream.Stream.concat;
 
 import android.annotation.Nullable;
+import android.annotation.UserIdInt;
 import android.app.backup.BackupManager;
 import android.app.backup.BackupTransport;
 import android.content.ComponentName;
@@ -48,6 +49,7 @@
 import android.content.Intent;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageInfo;
+import android.os.UserHandle;
 import android.platform.test.annotations.Presubmit;
 
 import com.android.server.backup.testing.TransportData;
@@ -56,7 +58,9 @@
 import com.android.server.backup.transport.TransportClient;
 import com.android.server.backup.transport.TransportClientManager;
 import com.android.server.backup.transport.TransportNotRegisteredException;
+import com.android.server.testing.shadows.ShadowApplicationPackageManager;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -64,6 +68,7 @@
 import org.mockito.MockitoAnnotations;
 import org.robolectric.RobolectricTestRunner;
 import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
 import org.robolectric.shadows.ShadowPackageManager;
 
 import java.util.ArrayList;
@@ -73,6 +78,7 @@
 import java.util.stream.Stream;
 
 @RunWith(RobolectricTestRunner.class)
+@Config(shadows = {ShadowApplicationPackageManager.class})
 @Presubmit
 public class TransportManagerTest {
     private static final String PACKAGE_A = "some.package.a";
@@ -84,12 +90,14 @@
     private TransportData mTransportA2;
     private TransportData mTransportB1;
     private ShadowPackageManager mShadowPackageManager;
+    private @UserIdInt int mUserId;
     private Context mContext;
 
     @Before
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
 
+        mUserId = UserHandle.USER_SYSTEM;
         mContext = RuntimeEnvironment.application;
         mShadowPackageManager = shadowOf(mContext.getPackageManager());
 
@@ -98,6 +106,12 @@
         mTransportB1 = genericTransport(PACKAGE_B, "TransportBaz");
     }
 
+    /** Reset shadow state. */
+    @After
+    public void tearDown() throws Exception {
+        ShadowApplicationPackageManager.reset();
+    }
+
     @Test
     public void testRegisterTransports() throws Exception {
         setUpPackage(PACKAGE_A, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
@@ -662,7 +676,8 @@
         packageInfo.packageName = packageName;
         packageInfo.applicationInfo = new ApplicationInfo();
         packageInfo.applicationInfo.privateFlags = flags;
-        mShadowPackageManager.addPackage(packageInfo);
+        mShadowPackageManager.installPackage(packageInfo);
+        ShadowApplicationPackageManager.addInstalledPackage(packageName, packageInfo);
     }
 
     private TransportManager createTransportManagerWithRegisteredTransports(
@@ -684,6 +699,7 @@
             @Nullable TransportData selectedTransport, TransportData... transports) {
         TransportManager transportManager =
                 new TransportManager(
+                        mUserId,
                         mContext,
                         merge(selectedTransport, transports)
                                 .stream()
diff --git a/services/robotests/backup/src/com/android/server/backup/UserBackupManagerServiceTest.java b/services/robotests/backup/src/com/android/server/backup/UserBackupManagerServiceTest.java
index 8d5c301..427aed7 100644
--- a/services/robotests/backup/src/com/android/server/backup/UserBackupManagerServiceTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/UserBackupManagerServiceTest.java
@@ -42,6 +42,8 @@
 import android.content.Context;
 import android.content.ContextWrapper;
 import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
 import android.os.Binder;
 import android.os.HandlerThread;
 import android.os.PowerManager;
@@ -54,6 +56,7 @@
 import com.android.server.backup.testing.TransportTestUtils.TransportMock;
 import com.android.server.backup.transport.TransportNotRegisteredException;
 import com.android.server.testing.shadows.ShadowAppBackupUtils;
+import com.android.server.testing.shadows.ShadowApplicationPackageManager;
 import com.android.server.testing.shadows.ShadowBinder;
 import com.android.server.testing.shadows.ShadowKeyValueBackupJob;
 import com.android.server.testing.shadows.ShadowKeyValueBackupTask;
@@ -80,7 +83,7 @@
  * UserBackupManagerService} that performs operations for its target user.
  */
 @RunWith(RobolectricTestRunner.class)
-@Config(shadows = {ShadowAppBackupUtils.class})
+@Config(shadows = {ShadowAppBackupUtils.class, ShadowApplicationPackageManager.class})
 @Presubmit
 public class UserBackupManagerServiceTest {
     private static final String TAG = "BMSTest";
@@ -137,6 +140,7 @@
     public void tearDown() throws Exception {
         mBackupThread.quit();
         ShadowAppBackupUtils.reset();
+        ShadowApplicationPackageManager.reset();
     }
 
     /**
@@ -195,6 +199,7 @@
     public void testIsAppEligibleForBackup_whenAppNotEligible() throws Exception {
         mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
         setUpCurrentTransport(mTransportManager, mTransport);
+        registerPackages(PACKAGE_1);
         UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks();
 
         boolean result = backupManagerService.isAppEligibleForBackup(PACKAGE_1);
@@ -210,6 +215,7 @@
     public void testIsAppEligibleForBackup_whenAppEligible() throws Exception {
         mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
         TransportMock transportMock = setUpCurrentTransport(mTransportManager, backupTransport());
+        registerPackages(PACKAGE_1);
         ShadowAppBackupUtils.setAppRunningAndEligibleForBackupWithTransport(PACKAGE_1);
         UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks();
 
@@ -228,6 +234,7 @@
     public void testIsAppEligibleForBackup_withoutPermission() throws Exception {
         mShadowContext.denyPermissions(android.Manifest.permission.BACKUP);
         setUpCurrentTransport(mTransportManager, mTransport);
+        registerPackages(PACKAGE_1);
         ShadowAppBackupUtils.setAppRunningAndEligibleForBackupWithTransport(PACKAGE_1);
         UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks();
 
@@ -245,6 +252,7 @@
     public void testFilterAppsEligibleForBackup() throws Exception {
         mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
         TransportMock transportMock = setUpCurrentTransport(mTransportManager, mTransport);
+        registerPackages(PACKAGE_1, PACKAGE_2);
         ShadowAppBackupUtils.setAppRunningAndEligibleForBackupWithTransport(PACKAGE_1);
         UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks();
 
@@ -264,6 +272,7 @@
     @Test
     public void testFilterAppsEligibleForBackup_whenNoneIsEligible() throws Exception {
         mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+        registerPackages(PACKAGE_1, PACKAGE_2);
         UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks();
 
         String[] filtered =
@@ -281,6 +290,7 @@
     public void testFilterAppsEligibleForBackup_withoutPermission() throws Exception {
         mShadowContext.denyPermissions(android.Manifest.permission.BACKUP);
         setUpCurrentTransport(mTransportManager, mTransport);
+        registerPackages(PACKAGE_1, PACKAGE_2);
         UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks();
 
         expectThrows(
@@ -504,10 +514,14 @@
     private void setUpForUpdateTransportAttributes() throws Exception {
         mTransportComponent = mTransport.getTransportComponent();
         String transportPackage = mTransportComponent.getPackageName();
+        PackageInfo packageInfo = getPackageInfo(transportPackage);
 
         ShadowPackageManager shadowPackageManager = shadowOf(mContext.getPackageManager());
-        shadowPackageManager.addPackage(transportPackage);
+        shadowPackageManager.installPackage(packageInfo);
         shadowPackageManager.setPackagesForUid(PACKAGE_UID, transportPackage);
+        // Set up for user invocations on ApplicationPackageManager.
+        ShadowApplicationPackageManager.addInstalledPackage(transportPackage, packageInfo);
+        ShadowApplicationPackageManager.setPackageUid(transportPackage, PACKAGE_UID);
 
         mTransportUid = mContext.getPackageManager().getPackageUid(transportPackage, 0);
     }
@@ -749,7 +763,7 @@
     private void setUpForRequestBackup(String... packages) throws Exception {
         mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
         for (String packageName : packages) {
-            mShadowPackageManager.addPackage(packageName);
+            registerPackages(packageName);
             ShadowAppBackupUtils.setAppRunningAndEligibleForBackupWithTransport(packageName);
         }
         setUpCurrentTransport(mTransportManager, mTransport);
@@ -864,7 +878,7 @@
     @Test
     public void testRequestBackup_whenAppNotEligibleForBackup() throws Exception {
         mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
-        mShadowPackageManager.addPackage(PACKAGE_1);
+        registerPackages(PACKAGE_1);
         setUpCurrentTransport(mTransportManager, mTransport);
         UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks();
         backupManagerService.setEnabled(true);
@@ -1127,6 +1141,22 @@
         backupManagerService.setPowerManager(powerManagerMock);
     }
 
+    private PackageInfo getPackageInfo(String packageName) {
+        PackageInfo packageInfo = new PackageInfo();
+        packageInfo.packageName = packageName;
+        packageInfo.applicationInfo = new ApplicationInfo();
+        packageInfo.applicationInfo.packageName = packageName;
+        return packageInfo;
+    }
+
+    private void registerPackages(String... packages) {
+        for (String packageName : packages) {
+            PackageInfo packageInfo = getPackageInfo(packageName);
+            mShadowPackageManager.installPackage(packageInfo);
+            ShadowApplicationPackageManager.addInstalledPackage(packageName, packageInfo);
+        }
+    }
+
     /**
      * We can't mock the void method {@link #schedule(Context, long, BackupManagerConstants)} so we
      * extend {@link ShadowKeyValueBackupJob} and throw an exception at the end of the method.
@@ -1137,8 +1167,9 @@
          * Implementation of {@link ShadowKeyValueBackupJob#schedule(Context, long,
          * BackupManagerConstants)} that throws an {@link IllegalArgumentException}.
          */
-        public static void schedule(Context ctx, long delay, BackupManagerConstants constants) {
-            ShadowKeyValueBackupJob.schedule(ctx, delay, constants);
+        public static void schedule(int userId, Context ctx, long delay,
+                BackupManagerConstants constants) {
+            ShadowKeyValueBackupJob.schedule(userId, ctx, delay, constants);
             throw new IllegalArgumentException();
         }
     }
diff --git a/services/robotests/backup/src/com/android/server/backup/internal/SetupObserverTest.java b/services/robotests/backup/src/com/android/server/backup/internal/SetupObserverTest.java
index b754356..bfb2b14 100644
--- a/services/robotests/backup/src/com/android/server/backup/internal/SetupObserverTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/internal/SetupObserverTest.java
@@ -29,6 +29,7 @@
 import com.android.server.backup.UserBackupManagerService;
 import com.android.server.backup.testing.BackupManagerServiceTestUtils;
 import com.android.server.backup.testing.TestUtils;
+import com.android.server.testing.shadows.ShadowApplicationPackageManager;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -37,6 +38,7 @@
 import org.mockito.MockitoAnnotations;
 import org.robolectric.RobolectricTestRunner;
 import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
 
 import java.io.File;
 
@@ -45,6 +47,7 @@
  * UserBackupManagerService}.
  */
 @RunWith(RobolectricTestRunner.class)
+@Config(shadows = {ShadowApplicationPackageManager.class})
 @Presubmit
 public class SetupObserverTest {
     private static final String TAG = "SetupObserverTest";
@@ -125,7 +128,7 @@
 
         setupObserver.onChange(true);
 
-        assertThat(KeyValueBackupJob.isScheduled()).isTrue();
+        assertThat(KeyValueBackupJob.isScheduled(mUserBackupManagerService.getUserId())).isTrue();
         // Verifies that the full backup job is scheduled. The job is scheduled via a posted message
         // on the backup handler so we verify that a message exists.
         assertThat(mUserBackupManagerService.getBackupHandler().hasMessagesOrCallbacks()).isTrue();
diff --git a/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java b/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java
index 7dac795..4811523 100644
--- a/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java
@@ -71,7 +71,6 @@
 
 import android.annotation.Nullable;
 import android.app.Application;
-import android.app.ApplicationPackageManager;
 import android.app.IBackupAgent;
 import android.app.backup.BackupAgent;
 import android.app.backup.BackupDataInput;
@@ -116,6 +115,7 @@
 import com.android.server.backup.testing.TransportTestUtils;
 import com.android.server.backup.testing.TransportTestUtils.TransportMock;
 import com.android.server.testing.shadows.FrameworkShadowLooper;
+import com.android.server.testing.shadows.ShadowApplicationPackageManager;
 import com.android.server.testing.shadows.ShadowBackupDataInput;
 import com.android.server.testing.shadows.ShadowBackupDataOutput;
 import com.android.server.testing.shadows.ShadowEventLog;
@@ -135,8 +135,6 @@
 import org.robolectric.RobolectricTestRunner;
 import org.robolectric.RuntimeEnvironment;
 import org.robolectric.annotation.Config;
-import org.robolectric.annotation.Implements;
-import org.robolectric.annotation.Resetter;
 import org.robolectric.shadows.ShadowLooper;
 import org.robolectric.shadows.ShadowPackageManager;
 import org.robolectric.shadows.ShadowQueuedWork;
@@ -160,7 +158,7 @@
 @Config(
         shadows = {
             FrameworkShadowLooper.class,
-            KeyValueBackupTaskTest.ShadowApplicationPackageManager.class,
+            ShadowApplicationPackageManager.class,
             ShadowBackupDataInput.class,
             ShadowBackupDataOutput.class,
             ShadowEventLog.class,
@@ -2437,11 +2435,12 @@
 
     private AgentMock setUpAgent(PackageData packageData) {
         try {
+            String packageName = packageData.packageName;
             mPackageManager.setApplicationEnabledSetting(
-                    packageData.packageName, PackageManager.COMPONENT_ENABLED_STATE_ENABLED, 0);
+                    packageName, PackageManager.COMPONENT_ENABLED_STATE_ENABLED, 0);
             PackageInfo packageInfo = getPackageInfo(packageData);
             mShadowPackageManager.installPackage(packageInfo);
-            ShadowApplicationPackageManager.setPackageInfo(packageInfo);
+            ShadowApplicationPackageManager.addInstalledPackage(packageName, packageInfo);
             mContext.sendBroadcast(getPackageAddedIntent(packageData));
             // Run the backup looper because on the receiver we post MSG_SCHEDULE_BACKUP_PACKAGE
             mShadowBackupLooper.runToEndOfTasks();
@@ -2537,7 +2536,7 @@
 
     private PackageManagerBackupAgent createPmAgent() {
         PackageManagerBackupAgent pmAgent =
-                new PackageManagerBackupAgent(mApplication.getPackageManager());
+                new PackageManagerBackupAgent(mApplication.getPackageManager(), USER_ID);
         pmAgent.attach(mApplication);
         pmAgent.onCreate();
         return pmAgent;
@@ -2727,7 +2726,7 @@
             throws RemoteException, IOException {
         verify(transportMock.transport).requestBackupTime();
         assertBackupPendingFor(packages);
-        assertThat(KeyValueBackupJob.isScheduled()).isTrue();
+        assertThat(KeyValueBackupJob.isScheduled(mBackupManagerService.getUserId())).isTrue();
     }
 
     private void assertBackupPendingFor(PackageData... packages) throws IOException {
@@ -2842,7 +2841,7 @@
 
         ThrowingPackageManagerBackupAgent(
                 PackageManager packageManager, RuntimeException exception) {
-            super(packageManager);
+            super(packageManager, USER_ID);
             mException = exception;
         }
 
@@ -2854,29 +2853,4 @@
             throw mException;
         }
     }
-
-    /**
-     * Extends {@link org.robolectric.shadows.ShadowApplicationPackageManager} to return the correct
-     * package in user-specific invocations.
-     */
-    @Implements(value = ApplicationPackageManager.class)
-    public static class ShadowApplicationPackageManager
-            extends org.robolectric.shadows.ShadowApplicationPackageManager {
-        private static PackageInfo sPackageInfo;
-
-        static void setPackageInfo(PackageInfo packageInfo) {
-            sPackageInfo = packageInfo;
-        }
-
-        @Override
-        protected PackageInfo getPackageInfoAsUser(String packageName, int flags, int userId) {
-            return sPackageInfo;
-        }
-
-        /** Clear {@link #sPackageInfo}. */
-        @Resetter
-        public static void reset() {
-            sPackageInfo = null;
-        }
-    }
 }
diff --git a/services/robotests/backup/src/com/android/server/backup/restore/ActiveRestoreSessionTest.java b/services/robotests/backup/src/com/android/server/backup/restore/ActiveRestoreSessionTest.java
index 4009876..f17a9fe 100644
--- a/services/robotests/backup/src/com/android/server/backup/restore/ActiveRestoreSessionTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/restore/ActiveRestoreSessionTest.java
@@ -57,6 +57,7 @@
 import com.android.server.backup.testing.TransportData;
 import com.android.server.backup.testing.TransportTestUtils;
 import com.android.server.backup.testing.TransportTestUtils.TransportMock;
+import com.android.server.testing.shadows.ShadowApplicationPackageManager;
 import com.android.server.testing.shadows.ShadowEventLog;
 import com.android.server.testing.shadows.ShadowPerformUnifiedRestoreTask;
 
@@ -77,7 +78,13 @@
 import java.util.ArrayDeque;
 
 @RunWith(RobolectricTestRunner.class)
-@Config(shadows = {ShadowEventLog.class, ShadowPerformUnifiedRestoreTask.class, ShadowBinder.class})
+@Config(
+        shadows = {
+            ShadowApplicationPackageManager.class,
+            ShadowBinder.class,
+            ShadowEventLog.class,
+            ShadowPerformUnifiedRestoreTask.class
+        })
 @Presubmit
 public class ActiveRestoreSessionTest {
     private static final String PACKAGE_1 = "com.example.package1";
@@ -140,6 +147,7 @@
 
     @After
     public void tearDown() throws Exception {
+        ShadowApplicationPackageManager.reset();
         ShadowPerformUnifiedRestoreTask.reset();
     }
 
@@ -561,7 +569,8 @@
         packageInfo.packageName = packageName;
         packageInfo.applicationInfo = new ApplicationInfo();
         packageInfo.applicationInfo.uid = uid;
-        mShadowPackageManager.addPackage(packageInfo);
+        mShadowPackageManager.installPackage(packageInfo);
+        ShadowApplicationPackageManager.addInstalledPackage(packageName, packageInfo);
     }
 
     private IRestoreSession createActiveRestoreSession(
diff --git a/services/robotests/backup/src/com/android/server/backup/transport/TransportClientManagerTest.java b/services/robotests/backup/src/com/android/server/backup/transport/TransportClientManagerTest.java
index 7dd0d92..f033af8 100644
--- a/services/robotests/backup/src/com/android/server/backup/transport/TransportClientManagerTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/transport/TransportClientManagerTest.java
@@ -25,6 +25,7 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import android.annotation.UserIdInt;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -49,6 +50,7 @@
 
     @Mock private Context mContext;
     @Mock private TransportConnectionListener mTransportConnectionListener;
+    private @UserIdInt int mUserId;
     private TransportClientManager mTransportClientManager;
     private ComponentName mTransportComponent;
     private Intent mBindIntent;
@@ -57,7 +59,9 @@
     public void setUp() {
         MockitoAnnotations.initMocks(this);
 
-        mTransportClientManager = new TransportClientManager(mContext, new TransportStats());
+        mUserId = UserHandle.USER_SYSTEM;
+        mTransportClientManager =
+                new TransportClientManager(mUserId, mContext, new TransportStats());
         mTransportComponent = new ComponentName(PACKAGE_NAME, CLASS_NAME);
         mBindIntent = new Intent(SERVICE_ACTION_TRANSPORT_HOST).setComponent(mTransportComponent);
 
diff --git a/services/robotests/backup/src/com/android/server/backup/transport/TransportClientTest.java b/services/robotests/backup/src/com/android/server/backup/transport/TransportClientTest.java
index 7281a3c..392f2ca 100644
--- a/services/robotests/backup/src/com/android/server/backup/transport/TransportClientTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/transport/TransportClientTest.java
@@ -36,6 +36,7 @@
 import static org.testng.Assert.expectThrows;
 
 import android.annotation.Nullable;
+import android.annotation.UserIdInt;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -83,6 +84,7 @@
     @Mock private TransportConnectionListener mTransportConnectionListener;
     @Mock private TransportConnectionListener mTransportConnectionListener2;
     @Mock private IBackupTransport.Stub mTransportBinder;
+    @UserIdInt private int mUserId;
     private TransportStats mTransportStats;
     private TransportClient mTransportClient;
     private ComponentName mTransportComponent;
@@ -96,6 +98,7 @@
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
 
+        mUserId = UserHandle.USER_SYSTEM;
         Looper mainLooper = Looper.getMainLooper();
         mShadowMainLooper = extract(mainLooper);
         mTransportComponent =
@@ -105,6 +108,7 @@
         mBindIntent = new Intent(SERVICE_ACTION_TRANSPORT_HOST).setComponent(mTransportComponent);
         mTransportClient =
                 new TransportClient(
+                        mUserId,
                         mContext,
                         mTransportStats,
                         mBindIntent,
diff --git a/services/robotests/src/com/android/server/testing/shadows/ShadowAppBackupUtils.java b/services/robotests/src/com/android/server/testing/shadows/ShadowAppBackupUtils.java
index 5fffb14..aefc871 100644
--- a/services/robotests/src/com/android/server/testing/shadows/ShadowAppBackupUtils.java
+++ b/services/robotests/src/com/android/server/testing/shadows/ShadowAppBackupUtils.java
@@ -54,7 +54,10 @@
 
     @Implementation
     protected static boolean appIsRunningAndEligibleForBackupWithTransport(
-            @Nullable TransportClient transportClient, String packageName, PackageManager pm) {
+            @Nullable TransportClient transportClient,
+            String packageName,
+            PackageManager pm,
+            int userId) {
         return sAppsRunningAndEligibleForBackupWithTransport.contains(packageName);
     }
 
diff --git a/services/robotests/src/com/android/server/testing/shadows/ShadowApplicationPackageManager.java b/services/robotests/src/com/android/server/testing/shadows/ShadowApplicationPackageManager.java
new file mode 100644
index 0000000..ab121ed
--- /dev/null
+++ b/services/robotests/src/com/android/server/testing/shadows/ShadowApplicationPackageManager.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.testing.shadows;
+
+import static android.content.pm.PackageManager.NameNotFoundException;
+
+import android.app.ApplicationPackageManager;
+import android.content.pm.PackageInfo;
+import android.util.ArrayMap;
+
+import org.robolectric.annotation.Implements;
+import org.robolectric.annotation.Resetter;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Extends {@link org.robolectric.shadows.ShadowApplicationPackageManager} to return the correct
+ * package in user-specific invocations.
+ */
+@Implements(value = ApplicationPackageManager.class)
+public class ShadowApplicationPackageManager
+        extends org.robolectric.shadows.ShadowApplicationPackageManager {
+    private static final Map<String, PackageInfo> sPackageInfos = new ArrayMap<>();
+    private static final List<PackageInfo> sInstalledPackages = new ArrayList<>();
+    private static final Map<String, Integer> sPackageUids = new ArrayMap<>();
+
+    /**
+     * Registers the package {@code packageName} to be returned when invoking {@link
+     * ApplicationPackageManager#getPackageInfoAsUser(String, int, int)} and {@link
+     * ApplicationPackageManager#getInstalledPackagesAsUser(int, int)}.
+     */
+    public static void addInstalledPackage(String packageName, PackageInfo packageInfo) {
+        sPackageInfos.put(packageName, packageInfo);
+        sInstalledPackages.add(packageInfo);
+    }
+
+    /**
+     * Sets the package uid {@code packageUid} for the package {@code packageName} to be returned
+     * when invoking {@link ApplicationPackageManager#getPackageUidAsUser(String, int, int)}.
+     */
+    public static void setPackageUid(String packageName, int packageUid) {
+        sPackageUids.put(packageName, packageUid);
+    }
+
+    @Override
+    protected PackageInfo getPackageInfoAsUser(String packageName, int flags, int userId)
+            throws NameNotFoundException {
+        if (!sPackageInfos.containsKey(packageName)) {
+            throw new NameNotFoundException(packageName);
+        }
+        return sPackageInfos.get(packageName);
+    }
+
+    @Override
+    protected List<PackageInfo> getInstalledPackagesAsUser(int flags, int userId) {
+        return sInstalledPackages;
+    }
+
+    @Override
+    protected int getPackageUidAsUser(String packageName, int flags, int userId)
+            throws NameNotFoundException {
+        if (!sPackageUids.containsKey(packageName)) {
+            throw new NameNotFoundException(packageName);
+        }
+        return sPackageUids.get(packageName);
+    }
+
+    /** Clear package state. */
+    @Resetter
+    public static void reset() {
+        sPackageInfos.clear();
+        sInstalledPackages.clear();
+        org.robolectric.shadows.ShadowApplicationPackageManager.reset();
+    }
+}
diff --git a/services/robotests/src/com/android/server/testing/shadows/ShadowKeyValueBackupJob.java b/services/robotests/src/com/android/server/testing/shadows/ShadowKeyValueBackupJob.java
index 23c44b0..f90ea6a 100644
--- a/services/robotests/src/com/android/server/testing/shadows/ShadowKeyValueBackupJob.java
+++ b/services/robotests/src/com/android/server/testing/shadows/ShadowKeyValueBackupJob.java
@@ -34,7 +34,8 @@
     }
 
     @Implementation
-    protected static void schedule(Context ctx, long delay, BackupManagerConstants constants) {
+    protected static void schedule(int userId, Context ctx, long delay,
+            BackupManagerConstants constants) {
         callingUid = Binder.getCallingUid();
     }
 }
diff --git a/services/tests/mockingservicestests/src/com/android/server/AppStateTrackerTest.java b/services/tests/mockingservicestests/src/com/android/server/AppStateTrackerTest.java
index c8e6782..4a48468 100644
--- a/services/tests/mockingservicestests/src/com/android/server/AppStateTrackerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/AppStateTrackerTest.java
@@ -85,6 +85,7 @@
 import java.util.concurrent.TimeUnit;
 import java.util.function.Consumer;
 
+import androidx.test.filters.FlakyTest;
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
@@ -793,6 +794,7 @@
     }
 
     @Test
+    @FlakyTest(bugId = 114098433)
     public void testAllListeners() throws Exception {
         final AppStateTrackerTestable instance = newInstance();
         callStart(instance);
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
index f1cd0cd..57ee6dc 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
@@ -33,6 +33,7 @@
 import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyLong;
@@ -43,7 +44,12 @@
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 
+import android.app.ActivityManager;
+import android.app.ActivityManagerInternal;
 import android.app.AlarmManager;
+import android.app.AppGlobals;
+import android.app.IActivityManager;
+import android.app.IUidObserver;
 import android.app.job.JobInfo;
 import android.app.usage.UsageStatsManager;
 import android.app.usage.UsageStatsManagerInternal;
@@ -56,13 +62,16 @@
 import android.os.BatteryManagerInternal;
 import android.os.Handler;
 import android.os.Looper;
+import android.os.RemoteException;
 import android.os.SystemClock;
+import android.util.SparseBooleanArray;
 
 import androidx.test.runner.AndroidJUnit4;
 
 import com.android.server.LocalServices;
 import com.android.server.job.JobSchedulerService;
 import com.android.server.job.JobSchedulerService.Constants;
+import com.android.server.job.JobStore;
 import com.android.server.job.controllers.QuotaController.ExecutionStats;
 import com.android.server.job.controllers.QuotaController.TimingSession;
 
@@ -96,9 +105,13 @@
     private BroadcastReceiver mChargingReceiver;
     private Constants mConstants;
     private QuotaController mQuotaController;
+    private int mSourceUid;
+    private IUidObserver mUidObserver;
 
     private MockitoSession mMockingSession;
     @Mock
+    private ActivityManagerInternal mActivityMangerInternal;
+    @Mock
     private AlarmManager mAlarmManager;
     @Mock
     private Context mContext;
@@ -107,6 +120,8 @@
     @Mock
     private UsageStatsManagerInternal mUsageStatsManager;
 
+    private JobStore mJobStore;
+
     @Before
     public void setUp() {
         mMockingSession = mockitoSession()
@@ -123,8 +138,17 @@
         when(mJobSchedulerService.getLock()).thenReturn(mJobSchedulerService);
         when(mJobSchedulerService.getConstants()).thenReturn(mConstants);
         // Called in QuotaController constructor.
+        IActivityManager activityManager = ActivityManager.getService();
+        spyOn(activityManager);
+        try {
+            doNothing().when(activityManager).registerUidObserver(any(), anyInt(), anyInt(), any());
+        } catch (RemoteException e) {
+            fail("registerUidObserver threw exception: " + e.getMessage());
+        }
         when(mContext.getMainLooper()).thenReturn(Looper.getMainLooper());
         when(mContext.getSystemService(Context.ALARM_SERVICE)).thenReturn(mAlarmManager);
+        doReturn(mActivityMangerInternal)
+                .when(() -> LocalServices.getService(ActivityManagerInternal.class));
         doReturn(mock(BatteryManagerInternal.class))
                 .when(() -> LocalServices.getService(BatteryManagerInternal.class));
         doReturn(mUsageStatsManager)
@@ -132,6 +156,9 @@
         // Used in JobStatus.
         doReturn(mock(PackageManagerInternal.class))
                 .when(() -> LocalServices.getService(PackageManagerInternal.class));
+        // Used in QuotaController.Handler.
+        mJobStore = JobStore.initAndGetForTesting(mContext, mContext.getFilesDir());
+        when(mJobSchedulerService.getJobStore()).thenReturn(mJobStore);
 
         // Freeze the clocks at 24 hours after this moment in time. Several tests create sessions
         // in the past, and QuotaController sometimes floors values at 0, so if the test time
@@ -150,10 +177,23 @@
         // Capture the listeners.
         ArgumentCaptor<BroadcastReceiver> receiverCaptor =
                 ArgumentCaptor.forClass(BroadcastReceiver.class);
+        ArgumentCaptor<IUidObserver> uidObserverCaptor =
+                ArgumentCaptor.forClass(IUidObserver.class);
         mQuotaController = new QuotaController(mJobSchedulerService);
 
         verify(mContext).registerReceiver(receiverCaptor.capture(), any());
         mChargingReceiver = receiverCaptor.getValue();
+        try {
+            verify(activityManager).registerUidObserver(
+                    uidObserverCaptor.capture(),
+                    eq(ActivityManager.UID_OBSERVER_PROCSTATE),
+                    eq(ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE),
+                    any());
+            mUidObserver = uidObserverCaptor.getValue();
+            mSourceUid = AppGlobals.getPackageManager().getPackageUid(SOURCE_PACKAGE, 0, 0);
+        } catch (RemoteException e) {
+            fail(e.getMessage());
+        }
     }
 
     @After
@@ -182,6 +222,25 @@
         mChargingReceiver.onReceive(mContext, intent);
     }
 
+    private void setProcessState(int procState) {
+        try {
+            doReturn(procState).when(mActivityMangerInternal).getUidProcessState(mSourceUid);
+            SparseBooleanArray foregroundUids = mQuotaController.getForegroundUids();
+            spyOn(foregroundUids);
+            mUidObserver.onUidStateChanged(mSourceUid, procState, 0);
+            if (procState <= ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE) {
+                verify(foregroundUids, timeout(SECOND_IN_MILLIS).times(1))
+                        .put(eq(mSourceUid), eq(true));
+                assertTrue(foregroundUids.get(mSourceUid));
+            } else {
+                verify(foregroundUids, timeout(SECOND_IN_MILLIS).times(1)).delete(eq(mSourceUid));
+                assertFalse(foregroundUids.get(mSourceUid));
+            }
+        } catch (RemoteException e) {
+            fail("registerUidObserver threw exception: " + e.getMessage());
+        }
+    }
+
     private void setStandbyBucket(int bucketIndex) {
         int bucket;
         switch (bucketIndex) {
@@ -204,9 +263,18 @@
                 anyLong())).thenReturn(bucket);
     }
 
-    private void setStandbyBucket(int bucketIndex, JobStatus job) {
+    private void setStandbyBucket(int bucketIndex, JobStatus... jobs) {
         setStandbyBucket(bucketIndex);
-        job.setStandbyBucket(bucketIndex);
+        for (JobStatus job : jobs) {
+            job.setStandbyBucket(bucketIndex);
+        }
+    }
+
+    private void trackJobs(JobStatus... jobs) {
+        for (JobStatus job : jobs) {
+            mJobStore.add(job);
+            mQuotaController.maybeStartTrackingJobLocked(job, null);
+        }
     }
 
     private JobStatus createJobStatus(String testTag, int jobId) {
@@ -214,8 +282,11 @@
                 new ComponentName(mContext, "TestQuotaJobService"))
                 .setMinimumLatency(Math.abs(jobId) + 1)
                 .build();
-        return JobStatus.createFromJobInfo(
+        JobStatus js = JobStatus.createFromJobInfo(
                 jobInfo, CALLING_UID, SOURCE_PACKAGE, SOURCE_USER_ID, testTag);
+        // Make sure tests aren't passing just because the default bucket is likely ACTIVE.
+        js.setStandbyBucket(FREQUENT_INDEX);
+        return js;
     }
 
     private TimingSession createTimingSession(long start, long duration, int count) {
@@ -709,6 +780,7 @@
         verify(mAlarmManager, never()).set(anyInt(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
 
         JobStatus jobStatus = createJobStatus("testMaybeScheduleStartAlarmLocked_Active", 1);
+        setStandbyBucket(standbyBucket, jobStatus);
         mQuotaController.maybeStartTrackingJobLocked(jobStatus, null);
         mQuotaController.prepareForExecutionLocked(jobStatus);
         advanceElapsedClock(5 * MINUTE_IN_MILLIS);
@@ -1339,19 +1411,23 @@
         setDischarging();
 
         JobStatus jobStatus = createJobStatus("testTimerTracking_AllForeground", 1);
-        jobStatus.uidActive = true;
+        setProcessState(ActivityManager.PROCESS_STATE_TOP);
         mQuotaController.maybeStartTrackingJobLocked(jobStatus, null);
 
         assertNull(mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
 
         mQuotaController.prepareForExecutionLocked(jobStatus);
         advanceElapsedClock(5 * SECOND_IN_MILLIS);
+        // Change to a state that should still be considered foreground.
+        setProcessState(ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE);
+        advanceElapsedClock(5 * SECOND_IN_MILLIS);
         mQuotaController.maybeStopTrackingJobLocked(jobStatus, null, false);
         assertNull(mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
     }
 
     /**
-     * Tests that Timers properly track overlapping foreground and background jobs.
+     * Tests that Timers properly track sessions when switching between foreground and background
+     * states.
      */
     @Test
     public void testTimerTracking_ForegroundAndBackground() {
@@ -1360,7 +1436,6 @@
         JobStatus jobBg1 = createJobStatus("testTimerTracking_ForegroundAndBackground", 1);
         JobStatus jobBg2 = createJobStatus("testTimerTracking_ForegroundAndBackground", 2);
         JobStatus jobFg3 = createJobStatus("testTimerTracking_ForegroundAndBackground", 3);
-        jobFg3.uidActive = true;
         mQuotaController.maybeStartTrackingJobLocked(jobBg1, null);
         mQuotaController.maybeStartTrackingJobLocked(jobBg2, null);
         mQuotaController.maybeStartTrackingJobLocked(jobFg3, null);
@@ -1368,6 +1443,7 @@
         List<TimingSession> expected = new ArrayList<>();
 
         // UID starts out inactive.
+        setProcessState(ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND);
         long start = JobSchedulerService.sElapsedRealtimeClock.millis();
         mQuotaController.prepareForExecutionLocked(jobBg1);
         advanceElapsedClock(10 * SECOND_IN_MILLIS);
@@ -1379,48 +1455,223 @@
 
         // Bg job starts while inactive, spans an entire active session, and ends after the
         // active session.
-        // Fg job starts after the bg job and ends before the bg job.
-        // Entire bg job duration should be counted since it started before active session. However,
-        // count should only be 1 since Timer shouldn't count fg jobs.
+        // App switching to foreground state then fg job starts.
+        // App remains in foreground state after coming to foreground, so there should only be one
+        // session.
         start = JobSchedulerService.sElapsedRealtimeClock.millis();
         mQuotaController.maybeStartTrackingJobLocked(jobBg2, null);
         mQuotaController.prepareForExecutionLocked(jobBg2);
         advanceElapsedClock(10 * SECOND_IN_MILLIS);
+        expected.add(createTimingSession(start, 10 * SECOND_IN_MILLIS, 1));
+        setProcessState(ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE);
         mQuotaController.prepareForExecutionLocked(jobFg3);
         advanceElapsedClock(10 * SECOND_IN_MILLIS);
         mQuotaController.maybeStopTrackingJobLocked(jobFg3, null, false);
         advanceElapsedClock(10 * SECOND_IN_MILLIS);
         mQuotaController.maybeStopTrackingJobLocked(jobBg2, null, false);
-        expected.add(createTimingSession(start, 30 * SECOND_IN_MILLIS, 1));
         assertEquals(expected, mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
 
         advanceElapsedClock(SECOND_IN_MILLIS);
 
         // Bg job 1 starts, then fg job starts. Bg job 1 job ends. Shortly after, uid goes
         // "inactive" and then bg job 2 starts. Then fg job ends.
-        // This should result in two TimingSessions with a count of one each.
+        // This should result in two TimingSessions:
+        //  * The first should have a count of 1
+        //  * The second should have a count of 2 since it will include both jobs
         start = JobSchedulerService.sElapsedRealtimeClock.millis();
         mQuotaController.maybeStartTrackingJobLocked(jobBg1, null);
         mQuotaController.maybeStartTrackingJobLocked(jobBg2, null);
         mQuotaController.maybeStartTrackingJobLocked(jobFg3, null);
+        setProcessState(ActivityManager.PROCESS_STATE_LAST_ACTIVITY);
         mQuotaController.prepareForExecutionLocked(jobBg1);
         advanceElapsedClock(10 * SECOND_IN_MILLIS);
+        expected.add(createTimingSession(start, 10 * SECOND_IN_MILLIS, 1));
+        setProcessState(ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE);
         mQuotaController.prepareForExecutionLocked(jobFg3);
         advanceElapsedClock(10 * SECOND_IN_MILLIS);
         mQuotaController.maybeStopTrackingJobLocked(jobBg1, jobBg1, true);
-        expected.add(createTimingSession(start, 20 * SECOND_IN_MILLIS, 1));
         advanceElapsedClock(10 * SECOND_IN_MILLIS); // UID "inactive" now
         start = JobSchedulerService.sElapsedRealtimeClock.millis();
+        setProcessState(ActivityManager.PROCESS_STATE_TOP_SLEEPING);
         mQuotaController.prepareForExecutionLocked(jobBg2);
         advanceElapsedClock(10 * SECOND_IN_MILLIS);
         mQuotaController.maybeStopTrackingJobLocked(jobFg3, null, false);
         advanceElapsedClock(10 * SECOND_IN_MILLIS);
         mQuotaController.maybeStopTrackingJobLocked(jobBg2, null, false);
-        expected.add(createTimingSession(start, 20 * SECOND_IN_MILLIS, 1));
+        expected.add(createTimingSession(start, 20 * SECOND_IN_MILLIS, 2));
         assertEquals(expected, mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
     }
 
     /**
+     * Tests that Timers properly track overlapping top and background jobs.
+     */
+    @Test
+    public void testTimerTracking_TopAndNonTop() {
+        setDischarging();
+
+        JobStatus jobBg1 = createJobStatus("testTimerTracking_TopAndNonTop", 1);
+        JobStatus jobBg2 = createJobStatus("testTimerTracking_TopAndNonTop", 2);
+        JobStatus jobFg1 = createJobStatus("testTimerTracking_TopAndNonTop", 3);
+        JobStatus jobTop = createJobStatus("testTimerTracking_TopAndNonTop", 4);
+        mQuotaController.maybeStartTrackingJobLocked(jobBg1, null);
+        mQuotaController.maybeStartTrackingJobLocked(jobBg2, null);
+        mQuotaController.maybeStartTrackingJobLocked(jobFg1, null);
+        mQuotaController.maybeStartTrackingJobLocked(jobTop, null);
+        assertNull(mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
+        List<TimingSession> expected = new ArrayList<>();
+
+        // UID starts out inactive.
+        setProcessState(ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND);
+        long start = JobSchedulerService.sElapsedRealtimeClock.millis();
+        mQuotaController.prepareForExecutionLocked(jobBg1);
+        advanceElapsedClock(10 * SECOND_IN_MILLIS);
+        mQuotaController.maybeStopTrackingJobLocked(jobBg1, jobBg1, true);
+        expected.add(createTimingSession(start, 10 * SECOND_IN_MILLIS, 1));
+        assertEquals(expected, mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
+
+        advanceElapsedClock(SECOND_IN_MILLIS);
+
+        // Bg job starts while inactive, spans an entire active session, and ends after the
+        // active session.
+        // App switching to top state then fg job starts.
+        // App remains in top state after coming to top, so there should only be one
+        // session.
+        start = JobSchedulerService.sElapsedRealtimeClock.millis();
+        mQuotaController.maybeStartTrackingJobLocked(jobBg2, null);
+        mQuotaController.prepareForExecutionLocked(jobBg2);
+        advanceElapsedClock(10 * SECOND_IN_MILLIS);
+        expected.add(createTimingSession(start, 10 * SECOND_IN_MILLIS, 1));
+        setProcessState(ActivityManager.PROCESS_STATE_TOP);
+        mQuotaController.prepareForExecutionLocked(jobTop);
+        advanceElapsedClock(10 * SECOND_IN_MILLIS);
+        mQuotaController.maybeStopTrackingJobLocked(jobTop, null, false);
+        advanceElapsedClock(10 * SECOND_IN_MILLIS);
+        mQuotaController.maybeStopTrackingJobLocked(jobBg2, null, false);
+        assertEquals(expected, mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
+
+        advanceElapsedClock(SECOND_IN_MILLIS);
+
+        // Bg job 1 starts, then top job starts. Bg job 1 job ends. Then app goes to
+        // foreground_service and a new job starts. Shortly after, uid goes
+        // "inactive" and then bg job 2 starts. Then top job ends, followed by bg and fg jobs.
+        // This should result in two TimingSessions:
+        //  * The first should have a count of 1
+        //  * The second should have a count of 2, which accounts for the bg2 and fg, but not top
+        //    jobs.
+        start = JobSchedulerService.sElapsedRealtimeClock.millis();
+        mQuotaController.maybeStartTrackingJobLocked(jobBg1, null);
+        mQuotaController.maybeStartTrackingJobLocked(jobBg2, null);
+        mQuotaController.maybeStartTrackingJobLocked(jobTop, null);
+        setProcessState(ActivityManager.PROCESS_STATE_LAST_ACTIVITY);
+        mQuotaController.prepareForExecutionLocked(jobBg1);
+        advanceElapsedClock(10 * SECOND_IN_MILLIS);
+        expected.add(createTimingSession(start, 10 * SECOND_IN_MILLIS, 1));
+        setProcessState(ActivityManager.PROCESS_STATE_TOP);
+        mQuotaController.prepareForExecutionLocked(jobTop);
+        advanceElapsedClock(10 * SECOND_IN_MILLIS);
+        mQuotaController.maybeStopTrackingJobLocked(jobBg1, jobBg1, true);
+        advanceElapsedClock(5 * SECOND_IN_MILLIS);
+        setProcessState(ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE);
+        mQuotaController.prepareForExecutionLocked(jobFg1);
+        advanceElapsedClock(5 * SECOND_IN_MILLIS);
+        setProcessState(ActivityManager.PROCESS_STATE_TOP);
+        advanceElapsedClock(10 * SECOND_IN_MILLIS); // UID "inactive" now
+        start = JobSchedulerService.sElapsedRealtimeClock.millis();
+        setProcessState(ActivityManager.PROCESS_STATE_TOP_SLEEPING);
+        mQuotaController.prepareForExecutionLocked(jobBg2);
+        advanceElapsedClock(10 * SECOND_IN_MILLIS);
+        mQuotaController.maybeStopTrackingJobLocked(jobTop, null, false);
+        advanceElapsedClock(10 * SECOND_IN_MILLIS);
+        mQuotaController.maybeStopTrackingJobLocked(jobBg2, null, false);
+        mQuotaController.maybeStopTrackingJobLocked(jobFg1, null, false);
+        expected.add(createTimingSession(start, 20 * SECOND_IN_MILLIS, 2));
+        assertEquals(expected, mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
+    }
+
+    /**
+     * Tests that TOP jobs aren't stopped when an app runs out of quota.
+     */
+    @Test
+    public void testTracking_OutOfQuota_ForegroundAndBackground() {
+        setDischarging();
+
+        JobStatus jobBg = createJobStatus("testTracking_OutOfQuota_ForegroundAndBackground", 1);
+        JobStatus jobTop = createJobStatus("testTracking_OutOfQuota_ForegroundAndBackground", 2);
+        trackJobs(jobBg, jobTop);
+        setStandbyBucket(WORKING_INDEX, jobTop, jobBg); // 2 hour window
+        // Now the package only has 20 seconds to run.
+        final long remainingTimeMs = 20 * SECOND_IN_MILLIS;
+        mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+                createTimingSession(
+                        JobSchedulerService.sElapsedRealtimeClock.millis() - HOUR_IN_MILLIS,
+                        10 * MINUTE_IN_MILLIS - remainingTimeMs, 1));
+
+        InOrder inOrder = inOrder(mJobSchedulerService);
+
+        // UID starts out inactive.
+        setProcessState(ActivityManager.PROCESS_STATE_SERVICE);
+        // Start the job.
+        mQuotaController.prepareForExecutionLocked(jobBg);
+        advanceElapsedClock(remainingTimeMs / 2);
+        // New job starts after UID is in the foreground. Since the app is now in the foreground, it
+        // should continue to have remainingTimeMs / 2 time remaining.
+        setProcessState(ActivityManager.PROCESS_STATE_TOP);
+        mQuotaController.prepareForExecutionLocked(jobTop);
+        advanceElapsedClock(remainingTimeMs);
+
+        // Wait for some extra time to allow for job processing.
+        inOrder.verify(mJobSchedulerService,
+                timeout(remainingTimeMs + 2 * SECOND_IN_MILLIS).times(0))
+                .onControllerStateChanged();
+        assertEquals(remainingTimeMs / 2, mQuotaController.getRemainingExecutionTimeLocked(jobBg));
+        assertEquals(remainingTimeMs / 2, mQuotaController.getRemainingExecutionTimeLocked(jobTop));
+        // Go to a background state.
+        setProcessState(ActivityManager.PROCESS_STATE_TOP_SLEEPING);
+        advanceElapsedClock(remainingTimeMs / 2 + 1);
+        inOrder.verify(mJobSchedulerService,
+                timeout(remainingTimeMs / 2 + 2 * SECOND_IN_MILLIS).times(1))
+                .onControllerStateChanged();
+        // Top job should still be allowed to run.
+        assertFalse(jobBg.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA));
+        assertTrue(jobTop.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA));
+
+        // New jobs to run.
+        JobStatus jobBg2 = createJobStatus("testTracking_OutOfQuota_ForegroundAndBackground", 3);
+        JobStatus jobTop2 = createJobStatus("testTracking_OutOfQuota_ForegroundAndBackground", 4);
+        JobStatus jobFg = createJobStatus("testTracking_OutOfQuota_ForegroundAndBackground", 5);
+        setStandbyBucket(WORKING_INDEX, jobBg2, jobTop2, jobFg);
+
+        advanceElapsedClock(20 * SECOND_IN_MILLIS);
+        setProcessState(ActivityManager.PROCESS_STATE_TOP);
+        inOrder.verify(mJobSchedulerService, timeout(SECOND_IN_MILLIS).times(1))
+                .onControllerStateChanged();
+        trackJobs(jobFg, jobTop);
+        mQuotaController.prepareForExecutionLocked(jobTop);
+        assertTrue(jobTop.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA));
+        assertTrue(jobFg.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA));
+        assertTrue(jobBg.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA));
+
+        // App still in foreground so everything should be in quota.
+        advanceElapsedClock(20 * SECOND_IN_MILLIS);
+        setProcessState(ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE);
+        assertTrue(jobTop.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA));
+        assertTrue(jobFg.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA));
+        assertTrue(jobBg.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA));
+
+        advanceElapsedClock(20 * SECOND_IN_MILLIS);
+        setProcessState(ActivityManager.PROCESS_STATE_SERVICE);
+        inOrder.verify(mJobSchedulerService, timeout(SECOND_IN_MILLIS).times(1))
+                .onControllerStateChanged();
+        // App is now in background and out of quota. Fg should now change to out of quota since it
+        // wasn't started. Top should remain in quota since it started when the app was in TOP.
+        assertTrue(jobTop.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA));
+        assertFalse(jobFg.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA));
+        assertFalse(jobBg.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA));
+        trackJobs(jobBg2);
+        assertFalse(jobBg2.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA));
+    }
+
+    /**
      * Tests that a job is properly updated and JobSchedulerService is notified when a job reaches
      * its quota.
      */
diff --git a/services/tests/rescueparty/Android.bp b/services/tests/rescueparty/Android.bp
new file mode 100644
index 0000000..6733af4
--- /dev/null
+++ b/services/tests/rescueparty/Android.bp
@@ -0,0 +1,23 @@
+// 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.
+
+cc_test {
+    name: "log_rescueparty_reset_event_reported",
+    srcs: ["log_rescueparty_reset_event_reported.cpp"],
+    shared_libs: [
+        "libbase",
+        "libstatslog",
+    ],
+    gtest: false,
+}
diff --git a/services/tests/rescueparty/how_to_run.txt b/services/tests/rescueparty/how_to_run.txt
new file mode 100644
index 0000000..9528d39
--- /dev/null
+++ b/services/tests/rescueparty/how_to_run.txt
@@ -0,0 +1,9 @@
+# Per http://go/westworld-local-development#step3-test-atom-and-metric-locally-on-device ,
+# In one terminal:
+make statsd_testdrive
+./out/host/linux-x86/bin/statsd_testdrive 122
+
+# In another terminal:
+mma -j $(nproc) log_rescueparty_reset_event_reported
+adb push $OUT/testcases/log_rescueparty_reset_event_reported/arm64/log_rescueparty_reset_event_reported /data
+adb shell /data/log_rescueparty_reset_event_reported 1234
diff --git a/services/tests/rescueparty/log_rescueparty_reset_event_reported.cpp b/services/tests/rescueparty/log_rescueparty_reset_event_reported.cpp
new file mode 100644
index 0000000..4aea917
--- /dev/null
+++ b/services/tests/rescueparty/log_rescueparty_reset_event_reported.cpp
@@ -0,0 +1,24 @@
+// 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.
+
+#include <statslog.h>
+#include <cstdlib>
+
+int main(int argc, const char** argv) {
+    int level = 0;
+    if (argc > 1) {
+        level = std::atoi(argv[1]);
+    }
+    android::util::stats_write(android::util::RESCUE_PARTY_RESET_REPORTED, level);
+}
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityManagerInternalTest.java b/services/tests/servicestests/src/com/android/server/am/ActivityManagerInternalTest.java
index e4fe599..00a60b9 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityManagerInternalTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityManagerInternalTest.java
@@ -117,7 +117,7 @@
         thread2.assertWaiting("Unexpected state for " + record2);
         thread2.interrupt();
 
-        mAms.mActiveUids.clear();
+        mAms.mProcessList.mActiveUids.clear();
     }
 
     private UidRecord addActiveUidRecord(int uid, long curProcStateSeq,
@@ -126,7 +126,7 @@
         record.lastNetworkUpdatedProcStateSeq = lastNetworkUpdatedProcStateSeq;
         record.curProcStateSeq = curProcStateSeq;
         record.waitingForNetwork = true;
-        mAms.mActiveUids.put(uid, record);
+        mAms.mProcessList.mActiveUids.put(uid, record);
         return record;
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java
index 2cc338c..419c736 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java
@@ -253,7 +253,7 @@
         final UidRecord uidRec = new UidRecord(uid, null /* atmInternal */);
         uidRec.waitingForNetwork = true;
         uidRec.hasInternetPermission = true;
-        mAms.mActiveUids.put(uid, uidRec);
+        mAms.mProcessList.mActiveUids.put(uid, uidRec);
 
         final ProcessRecord appRec = new ProcessRecord(mAms, new ApplicationInfo(), TAG, uid);
         appRec.thread = Mockito.mock(IApplicationThread.class);
@@ -715,7 +715,8 @@
 
         // Verify that when uid state changes to CHANGE_GONE or CHANGE_GONE_IDLE, then it
         // will be removed from validateUids.
-        assertNotEquals("validateUids should not be empty", 0, mAms.mValidateUids.size());
+        assertNotEquals("validateUids should not be empty", 0,
+                mAms.mValidateUids.size());
         for (int i = 0; i < pendingItemsForUids.size(); ++i) {
             final UidRecord.ChangeItem item = pendingItemsForUids.get(i);
             // Assign CHANGE_GONE_IDLE to some items and CHANGE_GONE to the others, using even/odd
@@ -853,7 +854,7 @@
         record.curProcStateSeq = curProcStateSeq;
         record.lastDispatchedProcStateSeq = lastDispatchedProcStateSeq;
         record.lastNetworkUpdatedProcStateSeq = lastNetworkUpdatedProcStateSeq;
-        mAms.mActiveUids.put(Process.myUid(), record);
+        mAms.mProcessList.mActiveUids.put(Process.myUid(), record);
 
         CustomThread thread = new CustomThread(record.networkStateLock, new Runnable() {
             @Override
@@ -876,7 +877,7 @@
             thread.assertTerminated(errMsg);
         }
 
-        mAms.mActiveUids.clear();
+        mAms.mProcessList.mActiveUids.clear();
     }
 
     private static class TestHandler extends Handler {
diff --git a/services/tests/servicestests/src/com/android/server/am/AppErrorDialogTest.java b/services/tests/servicestests/src/com/android/server/am/AppErrorDialogTest.java
index 8109b1c..1dfce51 100644
--- a/services/tests/servicestests/src/com/android/server/am/AppErrorDialogTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/AppErrorDialogTest.java
@@ -26,6 +26,7 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.server.appop.AppOpsService;
+import com.android.server.wm.ActivityTaskManagerService;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -62,13 +63,15 @@
                 return false;
             }
         });
+        mService.mActivityTaskManager = new ActivityTaskManagerService(mContext);
+        mService.mActivityTaskManager.initialize(null, null, mContext.getMainLooper());
     }
 
     @Test
     @UiThreadTest
     public void testCreateWorks() {
         AppErrorDialog.Data data = new AppErrorDialog.Data();
-        data.proc = new ProcessRecord(null, mContext.getApplicationInfo(), "name", 12345);
+        data.proc = new ProcessRecord(mService, mContext.getApplicationInfo(), "name", 12345);
         data.result = new AppErrorResult();
 
         AppErrorDialog dialog = new AppErrorDialog(mContext, mService, data);
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 db83505..6a9a121 100644
--- a/services/tests/servicestests/src/com/android/server/backup/TrampolineTest.java
+++ b/services/tests/servicestests/src/com/android/server/backup/TrampolineTest.java
@@ -1149,31 +1149,31 @@
 
     @Test
     public void beginFullBackup_calledBeforeInitialize_ignored() throws RemoteException {
-        mTrampoline.beginFullBackup(new FullBackupJob());
+        mTrampoline.beginFullBackup(mUserId, new FullBackupJob());
         verifyNoMoreInteractions(mBackupManagerServiceMock);
     }
 
     @Test
     public void beginFullBackup_forwarded() throws RemoteException {
         FullBackupJob fullBackupJob = new FullBackupJob();
-        when(mBackupManagerServiceMock.beginFullBackup(fullBackupJob)).thenReturn(true);
+        when(mBackupManagerServiceMock.beginFullBackup(mUserId, fullBackupJob)).thenReturn(true);
 
         mTrampoline.initializeService();
-        assertTrue(mTrampoline.beginFullBackup(fullBackupJob));
-        verify(mBackupManagerServiceMock).beginFullBackup(fullBackupJob);
+        assertTrue(mTrampoline.beginFullBackup(mUserId, fullBackupJob));
+        verify(mBackupManagerServiceMock).beginFullBackup(mUserId, fullBackupJob);
     }
 
     @Test
     public void endFullBackup_calledBeforeInitialize_ignored() throws RemoteException {
-        mTrampoline.endFullBackup();
+        mTrampoline.endFullBackup(mUserId);
         verifyNoMoreInteractions(mBackupManagerServiceMock);
     }
 
     @Test
     public void endFullBackup_forwarded() throws RemoteException {
         mTrampoline.initializeService();
-        mTrampoline.endFullBackup();
-        verify(mBackupManagerServiceMock).endFullBackup();
+        mTrampoline.endFullBackup(mUserId);
+        verify(mBackupManagerServiceMock).endFullBackup(mUserId);
     }
 
     @Test
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/ArcInitiationActionFromAvrTest.java b/services/tests/servicestests/src/com/android/server/hdmi/ArcInitiationActionFromAvrTest.java
index 8b0e8ab..eb9b98e 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/ArcInitiationActionFromAvrTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/ArcInitiationActionFromAvrTest.java
@@ -30,6 +30,7 @@
 import androidx.test.filters.SmallTest;
 
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
@@ -147,6 +148,7 @@
         mTestLooper.dispatchAll();
     }
 
+    @Ignore("b/120845532")
     @Test
     public void arcInitiation_requestActiveSource() {
         mSendCecCommandSuccess = true;
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/FakeNativeWrapper.java b/services/tests/servicestests/src/com/android/server/hdmi/FakeNativeWrapper.java
index 4255e37..8607ec6 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/FakeNativeWrapper.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/FakeNativeWrapper.java
@@ -50,6 +50,7 @@
 
     private final List<HdmiCecMessage> mResultMessages = new ArrayList<>();
     private int mMyPhysicalAddress = 0;
+    private HdmiPortInfo[] mHdmiPortInfo = null;
 
     @Override
     public long nativeInit(HdmiCecController handler, MessageQueue messageQueue) {
@@ -92,9 +93,11 @@
 
     @Override
     public HdmiPortInfo[] nativeGetPortInfos(long controllerPtr) {
-        HdmiPortInfo[] hdmiPortInfo = new HdmiPortInfo[1];
-        hdmiPortInfo[0] = new HdmiPortInfo(1, 1, 0x1000, true, true, true);
-        return hdmiPortInfo;
+        if (mHdmiPortInfo == null) {
+            mHdmiPortInfo = new HdmiPortInfo[1];
+            mHdmiPortInfo[0] = new HdmiPortInfo(1, 1, 0x1000, true, true, true);
+        }
+        return mHdmiPortInfo;
     }
 
     @Override
@@ -131,4 +134,10 @@
     protected void setPhysicalAddress(int physicalAddress) {
         mMyPhysicalAddress = physicalAddress;
     }
+
+    @VisibleForTesting
+    protected void setPortInfo(HdmiPortInfo[] hdmiPortInfo) {
+        mHdmiPortInfo = new HdmiPortInfo[hdmiPortInfo.length];
+        System.arraycopy(hdmiPortInfo, 0, mHdmiPortInfo, 0, hdmiPortInfo.length);
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecControllerTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecControllerTest.java
index 93a09dc..5d8131f 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecControllerTest.java
@@ -40,7 +40,6 @@
 import com.android.server.hdmi.HdmiCecController.AllocateAddressCallback;
 
 import org.junit.Before;
-import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
@@ -142,7 +141,6 @@
         assertEquals(ADDR_UNREGISTERED, mLogicalAddress);
     }
 
-    @Ignore("b/110413065 Support multiple device types 4 and 5.")
     @Test
     public void testAllocatLogicalAddress_PlaybackPreferredNotOccupied() {
         mHdmiCecController.allocateLogicalAddress(DEVICE_PLAYBACK, ADDR_PLAYBACK_1, mCallback);
@@ -158,7 +156,6 @@
         assertEquals(ADDR_PLAYBACK_2, mLogicalAddress);
     }
 
-    @Ignore("b/110413065 Support multiple device types 4 and 5.")
     @Test
     public void testAllocatLogicalAddress_PlaybackNoPreferredNotOcuppied() {
         mHdmiCecController.allocateLogicalAddress(DEVICE_PLAYBACK, ADDR_UNREGISTERED, mCallback);
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
index b47f269..3a6cdc2 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
@@ -15,9 +15,13 @@
  */
 package com.android.server.hdmi;
 
+import static android.hardware.hdmi.HdmiControlManager.DEVICE_EVENT_ADD_DEVICE;
+import static android.hardware.hdmi.HdmiControlManager.DEVICE_EVENT_UPDATE_DEVICE;
+
 import static com.android.server.hdmi.Constants.ADDR_AUDIO_SYSTEM;
 import static com.android.server.hdmi.Constants.ADDR_BROADCAST;
 import static com.android.server.hdmi.Constants.ADDR_PLAYBACK_1;
+import static com.android.server.hdmi.Constants.ADDR_PLAYBACK_2;
 import static com.android.server.hdmi.Constants.ADDR_TUNER_1;
 import static com.android.server.hdmi.Constants.ADDR_TV;
 import static com.android.server.hdmi.HdmiControlService.INITIATED_BY_ENABLE_CEC;
@@ -25,9 +29,10 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import android.hardware.hdmi.HdmiDeviceInfo;
+import android.hardware.hdmi.HdmiPortInfo;
 import android.media.AudioManager;
 import android.os.Looper;
-import android.os.SystemProperties;
 import android.os.test.TestLooper;
 
 import androidx.test.InstrumentationRegistry;
@@ -42,7 +47,6 @@
 import org.junit.runners.JUnit4;
 
 import java.util.ArrayList;
-
 @SmallTest
 @RunWith(JUnit4.class)
 /** Tests for {@link HdmiCecLocalDeviceAudioSystem} class. */
@@ -63,7 +67,15 @@
     private int mMusicVolume;
     private int mMusicMaxVolume;
     private boolean mMusicMute;
-    private int mAvrPhysicalAddress;
+    private static final int SELF_PHYSICAL_ADDRESS = 0x2000;
+    private static final int HDMI_1_PHYSICAL_ADDRESS = 0x2100;
+    private static final int HDMI_2_PHYSICAL_ADDRESS = 0x2200;
+    private static final int HDMI_3_PHYSICAL_ADDRESS = 0x2300;
+    private int mInvokeDeviceEventState;
+    private HdmiDeviceInfo mDeviceInfo;
+    private boolean mMutingEnabled;
+    private boolean mArcSupport;
+    private HdmiPortInfo[] mHdmiPortInfo;
 
     @Before
     public void setUp() {
@@ -127,6 +139,29 @@
 
                 @Override
                 void wakeUp() {}
+
+                @Override
+                void invokeDeviceEventListeners(HdmiDeviceInfo device, int status) {
+                    mDeviceInfo = device;
+                    mInvokeDeviceEventState = status;
+                }
+
+                @Override
+                void writeStringSetting(String key, String value) {
+                    // do nothing
+                }
+
+                @Override
+                boolean readBooleanSetting(String key, boolean defVal) {
+                    switch (key) {
+                        case Constants.PROPERTY_SYSTEM_AUDIO_MODE_MUTING_ENABLE:
+                            return mMutingEnabled;
+                        case Constants.PROPERTY_ARC_SUPPORT:
+                            return mArcSupport;
+                        default:
+                            return defVal;
+                    }
+                }
             };
 
         mMyLooper = mTestLooper.getLooper();
@@ -136,11 +171,17 @@
             void setIsActiveSource(boolean on) {
                 mIsActiveSource = on;
             }
+
+            @Override
+            protected int getPreferredAddress() {
+                return ADDR_PLAYBACK_1;
+            }
         };
         mHdmiCecLocalDeviceAudioSystem.init();
         mHdmiCecLocalDevicePlayback.init();
         mHdmiControlService.setIoLooper(mMyLooper);
         mNativeWrapper = new FakeNativeWrapper();
+        mNativeWrapper.setPhysicalAddress(SELF_PHYSICAL_ADDRESS);
         mHdmiCecController =
             HdmiCecController.createWithNativeWrapper(mHdmiControlService, mNativeWrapper);
         mHdmiControlService.setCecController(mHdmiCecController);
@@ -148,15 +189,30 @@
         mHdmiControlService.setMessageValidator(new HdmiCecMessageValidator(mHdmiControlService));
         mLocalDevices.add(mHdmiCecLocalDeviceAudioSystem);
         mLocalDevices.add(mHdmiCecLocalDevicePlayback);
+        mHdmiCecLocalDeviceAudioSystem.setRoutingControlFeatureEnables(true);
+        mHdmiPortInfo = new HdmiPortInfo[4];
+        mHdmiPortInfo[0] =
+            new HdmiPortInfo(
+                0, HdmiPortInfo.PORT_INPUT, SELF_PHYSICAL_ADDRESS, true, false, false);
+        mHdmiPortInfo[1] =
+            new HdmiPortInfo(
+                2, HdmiPortInfo.PORT_INPUT, HDMI_1_PHYSICAL_ADDRESS, true, false, false);
+        mHdmiPortInfo[2] =
+            new HdmiPortInfo(
+                1, HdmiPortInfo.PORT_INPUT, HDMI_2_PHYSICAL_ADDRESS, true, false, false);
+        mHdmiPortInfo[3] =
+            new HdmiPortInfo(
+                4, HdmiPortInfo.PORT_INPUT, HDMI_3_PHYSICAL_ADDRESS, true, false, false);
+        mNativeWrapper.setPortInfo(mHdmiPortInfo);
         mHdmiControlService.initPortInfo();
         // No TV device interacts with AVR so system audio control won't be turned on here
         mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
         mTestLooper.dispatchAll();
         mNativeWrapper.clearResultMessages();
-        mAvrPhysicalAddress  = 0x2000;
-        mNativeWrapper.setPhysicalAddress(mAvrPhysicalAddress);
-        SystemProperties.set(Constants.PROPERTY_ARC_SUPPORT, "true");
-        SystemProperties.set(Constants.PROPERTY_SYSTEM_AUDIO_MODE_MUTING_ENABLE, "true");
+        mMutingEnabled = true;
+        mArcSupport = true;
+        mInvokeDeviceEventState = 0;
+        mDeviceInfo = null;
     }
 
     @Test
@@ -224,6 +280,8 @@
         assertThat(mNativeWrapper.getOnlyResultMessage()).isEqualTo(expectedMessage);
     }
 
+    // Testing device has sadConfig.xml
+    @Ignore("b/120845532")
     @Test
     public void handleRequestShortAudioDescriptor_noAudioDeviceInfo() throws Exception {
         HdmiCecMessage expectedMessage =
@@ -394,51 +452,6 @@
     }
 
     @Test
-    public void pathToPort_isMe() throws Exception {
-        int targetPhysicalAddress = 0x1000;
-        mNativeWrapper.setPhysicalAddress(0x1000);
-        assertThat(mHdmiCecLocalDeviceAudioSystem
-                .getLocalPortFromPhysicalAddress(targetPhysicalAddress))
-                .isEqualTo(0);
-    }
-
-    @Test
-    public void pathToPort_isBelow() throws Exception {
-        int targetPhysicalAddress = 0x1100;
-        mNativeWrapper.setPhysicalAddress(0x1000);
-        assertThat(mHdmiCecLocalDeviceAudioSystem
-                .getLocalPortFromPhysicalAddress(targetPhysicalAddress))
-                .isEqualTo(1);
-    }
-
-    @Test
-    public void pathToPort_neitherMeNorBelow() throws Exception {
-        int targetPhysicalAddress = 0x3000;
-        mNativeWrapper.setPhysicalAddress(0x2000);
-        assertThat(mHdmiCecLocalDeviceAudioSystem
-                .getLocalPortFromPhysicalAddress(targetPhysicalAddress))
-                .isEqualTo(-1);
-
-        targetPhysicalAddress = 0x2200;
-        mNativeWrapper.setPhysicalAddress(0x3300);
-        assertThat(mHdmiCecLocalDeviceAudioSystem
-                .getLocalPortFromPhysicalAddress(targetPhysicalAddress))
-                .isEqualTo(-1);
-
-        targetPhysicalAddress = 0x2213;
-        mNativeWrapper.setPhysicalAddress(0x2212);
-        assertThat(mHdmiCecLocalDeviceAudioSystem
-                .getLocalPortFromPhysicalAddress(targetPhysicalAddress))
-                .isEqualTo(-1);
-
-        targetPhysicalAddress = 0x2340;
-        mNativeWrapper.setPhysicalAddress(0x2310);
-        assertThat(mHdmiCecLocalDeviceAudioSystem
-                .getLocalPortFromPhysicalAddress(targetPhysicalAddress))
-                .isEqualTo(-1);
-    }
-
-    @Test
     public void handleRequestArcInitiate_isNotDirectConnectedToTv() throws Exception {
         HdmiCecMessage message =
                 HdmiCecMessageBuilder.buildRequestArcInitiation(ADDR_TV, ADDR_AUDIO_SYSTEM);
@@ -510,7 +523,7 @@
                         ADDR_TV,
                         Constants.MESSAGE_REQUEST_ARC_INITIATION,
                         Constants.ABORT_UNRECOGNIZED_OPCODE);
-        SystemProperties.set(Constants.PROPERTY_ARC_SUPPORT, "false");
+        mArcSupport = false;
 
         assertThat(mHdmiCecLocalDeviceAudioSystem.handleRequestArcInitiate(message)).isTrue();
         mTestLooper.dispatchAll();
@@ -521,7 +534,7 @@
         HdmiCecMessage message =
                 HdmiCecMessageBuilder.buildSystemAudioModeRequest(
                         ADDR_TUNER_1, ADDR_AUDIO_SYSTEM,
-                        mAvrPhysicalAddress, true);
+                        SELF_PHYSICAL_ADDRESS, true);
         HdmiCecMessage expectedMessage =
                 HdmiCecMessageBuilder.buildFeatureAbortCommand(
                         ADDR_AUDIO_SYSTEM,
@@ -539,7 +552,7 @@
         HdmiCecMessage message =
                 HdmiCecMessageBuilder.buildSystemAudioModeRequest(
                         ADDR_TUNER_1, ADDR_AUDIO_SYSTEM,
-                        mAvrPhysicalAddress, true);
+                        SELF_PHYSICAL_ADDRESS, true);
         HdmiCecMessage expectedMessage =
                 HdmiCecMessageBuilder.buildSetSystemAudioMode(
                         ADDR_AUDIO_SYSTEM, Constants.ADDR_BROADCAST, true);
@@ -565,15 +578,14 @@
             .isEqualTo(expectedActiveSource);
     }
 
-    @Ignore("b/110413065 Support multiple device types 4 and 5.")
     @Test
     public void handleRoutingChange_currentActivePortIsHome() {
         HdmiCecMessage message =
-                HdmiCecMessageBuilder.buildRoutingChange(ADDR_TV, 0x3000, mAvrPhysicalAddress);
+                HdmiCecMessageBuilder.buildRoutingChange(ADDR_TV, 0x3000, SELF_PHYSICAL_ADDRESS);
 
         HdmiCecMessage expectedMessage =
-                HdmiCecMessageBuilder.buildActiveSource(ADDR_PLAYBACK_1, mAvrPhysicalAddress);
-        ActiveSource expectedActiveSource = ActiveSource.of(ADDR_PLAYBACK_1, mAvrPhysicalAddress);
+                HdmiCecMessageBuilder.buildActiveSource(ADDR_PLAYBACK_1, SELF_PHYSICAL_ADDRESS);
+        ActiveSource expectedActiveSource = ActiveSource.of(ADDR_PLAYBACK_1, SELF_PHYSICAL_ADDRESS);
         int expectedLocalActivePort = Constants.CEC_SWITCH_HOME;
 
         assertThat(mHdmiCecLocalDeviceAudioSystem.handleRoutingChange(message)).isTrue();
@@ -588,17 +600,18 @@
     @Test
     public void handleRoutingInformation_currentActivePortIsHDMI1() {
         HdmiCecMessage message =
-                HdmiCecMessageBuilder.buildRoutingInformation(ADDR_TV, 0x2000);
-        mHdmiCecLocalDeviceAudioSystem.setRoutingPort(Constants.CEC_SWITCH_HDMI1);
+                HdmiCecMessageBuilder.buildRoutingInformation(ADDR_TV, SELF_PHYSICAL_ADDRESS);
+        mHdmiCecLocalDeviceAudioSystem.setRoutingPort(mHdmiPortInfo[1].getId());
         HdmiCecMessage expectedMessage =
-                HdmiCecMessageBuilder.buildRoutingInformation(ADDR_AUDIO_SYSTEM, 0x2100);
+                HdmiCecMessageBuilder.buildRoutingInformation(
+                        ADDR_AUDIO_SYSTEM, HDMI_1_PHYSICAL_ADDRESS);
 
         assertThat(mHdmiCecLocalDeviceAudioSystem.handleRoutingInformation(message)).isTrue();
         mTestLooper.dispatchAll();
         assertThat(mNativeWrapper.getOnlyResultMessage()).isEqualTo(expectedMessage);
     }
 
-    @Ignore("b/110413065 Support multiple device types 4 and 5.")
+    @Ignore("b/120845532")
     @Test
     public void handleRoutingChange_homeIsActive_playbackSendActiveSource() {
         HdmiCecMessage message =
@@ -611,4 +624,72 @@
         mTestLooper.dispatchAll();
         assertThat(mNativeWrapper.getResultMessages()).contains(expectedMessage);
     }
+
+    @Test
+    public void updateCecDevice_deviceNotExists_addDevice() {
+        assertThat(mInvokeDeviceEventState).isNotEqualTo(DEVICE_EVENT_ADD_DEVICE);
+        HdmiDeviceInfo newDevice = new HdmiDeviceInfo(
+                ADDR_PLAYBACK_1, 0x2100, 2, HdmiDeviceInfo.DEVICE_PLAYBACK,
+                Constants.UNKNOWN_VENDOR_ID, HdmiUtils.getDefaultDeviceName(ADDR_PLAYBACK_1));
+
+        mHdmiCecLocalDeviceAudioSystem.updateCecDevice(newDevice);
+        assertThat(mDeviceInfo).isEqualTo(newDevice);
+        assertThat(mHdmiCecLocalDeviceAudioSystem
+            .getCecDeviceInfo(newDevice.getLogicalAddress())).isEqualTo(newDevice);
+        assertThat(mInvokeDeviceEventState).isEqualTo(DEVICE_EVENT_ADD_DEVICE);
+    }
+
+    @Test
+    public void updateCecDevice_deviceExists_doNothing() {
+        mInvokeDeviceEventState = 0;
+        HdmiDeviceInfo oldDevice = new HdmiDeviceInfo(
+                ADDR_PLAYBACK_1, 0x2100, 2, HdmiDeviceInfo.DEVICE_PLAYBACK,
+                Constants.UNKNOWN_VENDOR_ID, HdmiUtils.getDefaultDeviceName(ADDR_PLAYBACK_1));
+        mHdmiCecLocalDeviceAudioSystem.addDeviceInfo(oldDevice);
+
+        mHdmiCecLocalDeviceAudioSystem.updateCecDevice(oldDevice);
+        assertThat(mInvokeDeviceEventState).isEqualTo(0);
+    }
+
+    @Test
+    public void updateCecDevice_deviceInfoDifferent_updateDevice() {
+        assertThat(mInvokeDeviceEventState).isNotEqualTo(DEVICE_EVENT_UPDATE_DEVICE);
+        HdmiDeviceInfo oldDevice = new HdmiDeviceInfo(
+                ADDR_PLAYBACK_1, 0x2100, 2, HdmiDeviceInfo.DEVICE_PLAYBACK,
+                Constants.UNKNOWN_VENDOR_ID, HdmiUtils.getDefaultDeviceName(ADDR_PLAYBACK_1));
+        mHdmiCecLocalDeviceAudioSystem.addDeviceInfo(oldDevice);
+
+        HdmiDeviceInfo differentDevice = new HdmiDeviceInfo(
+                ADDR_PLAYBACK_1, 0x2300, 4, HdmiDeviceInfo.DEVICE_PLAYBACK,
+                Constants.UNKNOWN_VENDOR_ID, HdmiUtils.getDefaultDeviceName(ADDR_PLAYBACK_1));
+
+        mHdmiCecLocalDeviceAudioSystem.updateCecDevice(differentDevice);
+        assertThat(mDeviceInfo).isEqualTo(differentDevice);
+        assertThat(mHdmiCecLocalDeviceAudioSystem
+            .getCecDeviceInfo(differentDevice.getLogicalAddress())).isEqualTo(differentDevice);
+        assertThat(mInvokeDeviceEventState).isEqualTo(DEVICE_EVENT_UPDATE_DEVICE);
+    }
+
+    @Test
+    public void handleReportPhysicalAddress_differentPath_addDevice() {
+        assertThat(mInvokeDeviceEventState).isNotEqualTo(DEVICE_EVENT_ADD_DEVICE);
+        HdmiDeviceInfo oldDevice = new HdmiDeviceInfo(
+                ADDR_PLAYBACK_1, 0x2100, 2, HdmiDeviceInfo.DEVICE_PLAYBACK,
+                Constants.UNKNOWN_VENDOR_ID, HdmiUtils.getDefaultDeviceName(ADDR_PLAYBACK_1));
+        mHdmiCecLocalDeviceAudioSystem.addDeviceInfo(oldDevice);
+
+        HdmiDeviceInfo differentDevice = new HdmiDeviceInfo(
+                ADDR_PLAYBACK_2, 0x2200, 1, HdmiDeviceInfo.DEVICE_PLAYBACK,
+                Constants.UNKNOWN_VENDOR_ID, HdmiUtils.getDefaultDeviceName(ADDR_PLAYBACK_2));
+        HdmiCecMessage reportPhysicalAddress = HdmiCecMessageBuilder
+                .buildReportPhysicalAddressCommand(
+                        ADDR_PLAYBACK_2, 0x2200, HdmiDeviceInfo.DEVICE_PLAYBACK);
+        mHdmiCecLocalDeviceAudioSystem.handleReportPhysicalAddress(reportPhysicalAddress);
+
+        mTestLooper.dispatchAll();
+        assertThat(mDeviceInfo).isEqualTo(differentDevice);
+        assertThat(mHdmiCecLocalDeviceAudioSystem
+            .getCecDeviceInfo(differentDevice.getLogicalAddress())).isEqualTo(differentDevice);
+        assertThat(mInvokeDeviceEventState).isEqualTo(DEVICE_EVENT_ADD_DEVICE);
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
index 792c617..feae4ee 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
@@ -81,7 +81,8 @@
         mNativeWrapper.setPhysicalAddress(mPlaybackPhysicalAddress);
     }
 
-    @Ignore
+    // Playback device does not handle routing control related feature right now
+    @Ignore("b/120845532")
     @Test
     public void handleSetStreamPath_underCurrentDevice() {
         assertThat(mHdmiCecLocalDevicePlayback.getLocalActivePath()).isEqualTo(0);
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
index 18c9a65..1f66074 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
@@ -20,13 +20,18 @@
 
 import static com.android.server.hdmi.HdmiControlService.INITIATED_BY_ENABLE_CEC;
 
+import static com.google.common.truth.Truth.assertThat;
+
 import static junit.framework.Assert.assertFalse;
 import static junit.framework.Assert.assertTrue;
 
+import android.hardware.hdmi.HdmiPortInfo;
 import android.os.Looper;
 import android.os.test.TestLooper;
+
 import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.SmallTest;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -102,6 +107,7 @@
     private TestLooper mTestLooper = new TestLooper();
     private ArrayList<HdmiCecLocalDevice> mLocalDevices = new ArrayList<>();
     private boolean mStandbyMessageReceived;
+    private HdmiPortInfo[] mHdmiPortInfo;
 
     @Before
     public void SetUp() {
@@ -131,6 +137,16 @@
 
         mLocalDevices.add(mMyAudioSystemDevice);
         mLocalDevices.add(mMyPlaybackDevice);
+        mHdmiPortInfo = new HdmiPortInfo[4];
+        mHdmiPortInfo[0] =
+            new HdmiPortInfo(1, HdmiPortInfo.PORT_INPUT, 0x2100, true, false, false);
+        mHdmiPortInfo[1] =
+            new HdmiPortInfo(2, HdmiPortInfo.PORT_INPUT, 0x2200, true, false, false);
+        mHdmiPortInfo[2] =
+            new HdmiPortInfo(3, HdmiPortInfo.PORT_INPUT, 0x2000, true, false, false);
+        mHdmiPortInfo[3] =
+            new HdmiPortInfo(4, HdmiPortInfo.PORT_INPUT, 0x3000, true, false, false);
+        mNativeWrapper.setPortInfo(mHdmiPortInfo);
         mHdmiControlService.initPortInfo();
         mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
 
@@ -159,4 +175,27 @@
         assertTrue(mMyPlaybackDevice.isDisabled());
         assertTrue(mMyAudioSystemDevice.isDisabled());
     }
+
+    @Test
+    public void pathToPort_pathExists_weAreNonTv() {
+        mNativeWrapper.setPhysicalAddress(0x2000);
+        mHdmiControlService.initPortInfo();
+        assertThat(mHdmiControlService.pathToPortId(0x2120)).isEqualTo(1);
+        assertThat(mHdmiControlService.pathToPortId(0x2234)).isEqualTo(2);
+    }
+
+    @Test
+    public void pathToPort_pathExists_weAreTv() {
+        mNativeWrapper.setPhysicalAddress(0x0000);
+        mHdmiControlService.initPortInfo();
+        assertThat(mHdmiControlService.pathToPortId(0x2120)).isEqualTo(3);
+        assertThat(mHdmiControlService.pathToPortId(0x3234)).isEqualTo(4);
+    }
+
+    @Test
+    public void pathToPort_pathInvalid() {
+        mNativeWrapper.setPhysicalAddress(0x2000);
+        mHdmiControlService.initPortInfo();
+        assertThat(mHdmiControlService.pathToPortId(0x1000)).isEqualTo(Constants.INVALID_PORT_ID);
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiUtilsTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiUtilsTest.java
new file mode 100644
index 0000000..985c647
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiUtilsTest.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.server.hdmi;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.util.Slog;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.server.hdmi.HdmiUtils.CodecSad;
+import com.android.server.hdmi.HdmiUtils.DeviceConfig;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.List;
+
+@SmallTest
+@RunWith(JUnit4.class)
+/** Tests for {@link HdmiUtils} class. */
+public class HdmiUtilsTest {
+
+    private static final String TAG = "HdmiUtilsTest";
+
+    private final String mExampleXML =
+            "<!-- A sample Short Audio Descriptor configuration xml -->"
+                    + "<config version=\"1.0\" xmlns:xi=\"http://www.w3.org/2001/XInclude\">"
+                    + "<device type=\"VX_AUDIO_DEVICE_IN_HDMI_ARC\">"
+                    + "<supportedFormat format=\"AUDIO_FORMAT_LPCM\" descriptor=\"011a03\"/>"
+                    + "<supportedFormat format=\"AUDIO_FORMAT_DD\" descriptor=\"0d0506\"/>"
+                    + "</device>"
+                    + "<device type=\"AUDIO_DEVICE_IN_SPDIF\">"
+                    + "<supportedFormat format=\"AUDIO_FORMAT_LPCM\" descriptor=\"010203\"/>"
+                    + "<supportedFormat format=\"AUDIO_FORMAT_DD\" descriptor=\"040506\"/>"
+                    + "</device>"
+                    + "</config>";
+
+    @Test
+    public void pathToPort_isMe() {
+        int targetPhysicalAddress = 0x1000;
+        int myPhysicalAddress = 0x1000;
+        assertThat(HdmiUtils.getLocalPortFromPhysicalAddress(
+                targetPhysicalAddress, myPhysicalAddress)).isEqualTo(
+                        HdmiUtils.TARGET_SAME_PHYSICAL_ADDRESS);
+    }
+
+    @Test
+    public void pathToPort_isDirectlyBelow() {
+        int targetPhysicalAddress = 0x1100;
+        int myPhysicalAddress = 0x1000;
+        assertThat(HdmiUtils.getLocalPortFromPhysicalAddress(
+                targetPhysicalAddress, myPhysicalAddress)).isEqualTo(1);
+    }
+
+    @Test
+    public void pathToPort_isBelow() {
+        int targetPhysicalAddress = 0x1110;
+        int myPhysicalAddress = 0x1000;
+        assertThat(HdmiUtils.getLocalPortFromPhysicalAddress(
+                targetPhysicalAddress, myPhysicalAddress)).isEqualTo(1);
+    }
+
+    @Test
+    public void pathToPort_neitherMeNorBelow() {
+        int targetPhysicalAddress = 0x3000;
+        int myPhysicalAddress = 0x2000;
+        assertThat(HdmiUtils.getLocalPortFromPhysicalAddress(
+                targetPhysicalAddress, myPhysicalAddress)).isEqualTo(
+                        HdmiUtils.TARGET_NOT_UNDER_LOCAL_DEVICE);
+
+        targetPhysicalAddress = 0x2200;
+        myPhysicalAddress = 0x3300;
+        assertThat(HdmiUtils.getLocalPortFromPhysicalAddress(
+                targetPhysicalAddress, myPhysicalAddress)).isEqualTo(
+                        HdmiUtils.TARGET_NOT_UNDER_LOCAL_DEVICE);
+
+        targetPhysicalAddress = 0x2213;
+        myPhysicalAddress = 0x2212;
+        assertThat(HdmiUtils.getLocalPortFromPhysicalAddress(
+                targetPhysicalAddress, myPhysicalAddress)).isEqualTo(
+                        HdmiUtils.TARGET_NOT_UNDER_LOCAL_DEVICE);
+
+        targetPhysicalAddress = 0x2340;
+        myPhysicalAddress = 0x2310;
+        assertThat(HdmiUtils.getLocalPortFromPhysicalAddress(
+                targetPhysicalAddress, myPhysicalAddress)).isEqualTo(
+                        HdmiUtils.TARGET_NOT_UNDER_LOCAL_DEVICE);
+    }
+
+    @Test
+    public void parseSampleXML() {
+        List<DeviceConfig> config = new ArrayList<>();
+        try {
+            config = HdmiUtils.ShortAudioDescriptorXmlParser.parse(
+                    new ByteArrayInputStream(mExampleXML.getBytes(StandardCharsets.UTF_8)));
+        } catch (IOException e) {
+            Slog.e(TAG, e.getMessage(), e);
+        } catch (XmlPullParserException e) {
+            Slog.e(TAG, e.getMessage(), e);
+        }
+
+        CodecSad expectedCodec1 = new CodecSad(Constants.AUDIO_CODEC_LPCM, "011a03");
+        CodecSad expectedCodec2 = new CodecSad(Constants.AUDIO_CODEC_DD, "0d0506");
+        CodecSad expectedCodec3 = new CodecSad(Constants.AUDIO_CODEC_LPCM, "010203");
+        CodecSad expectedCodec4 = new CodecSad(Constants.AUDIO_CODEC_DD, "040506");
+
+        List<CodecSad> expectedList1 = new ArrayList<>();
+        expectedList1.add(expectedCodec1);
+        expectedList1.add(expectedCodec2);
+
+        List<CodecSad> expectedList2 = new ArrayList<>();
+        expectedList2.add(expectedCodec3);
+        expectedList2.add(expectedCodec4);
+
+        DeviceConfig expectedDevice1 = new DeviceConfig(
+                "VX_AUDIO_DEVICE_IN_HDMI_ARC", expectedList1);
+        DeviceConfig expectedDevice2 = new DeviceConfig(
+                "AUDIO_DEVICE_IN_SPDIF", expectedList2);
+
+        List<DeviceConfig> expectedConfig = new ArrayList<>();
+        expectedConfig.add(expectedDevice1);
+        expectedConfig.add(expectedDevice2);
+
+        assertThat(config).isEqualTo(expectedConfig);
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioInitiationActionFromAvrTest.java b/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioInitiationActionFromAvrTest.java
index bd297ee..440a49a 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioInitiationActionFromAvrTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioInitiationActionFromAvrTest.java
@@ -31,6 +31,7 @@
 import androidx.test.filters.SmallTest;
 
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
@@ -150,6 +151,11 @@
                             int sourceAddress, int physicalAddress) {
                         mBroadcastActiveSource = true;
                     }
+
+                    @Override
+                    int pathToPortId(int path) {
+                        return -1;
+                    }
                 };
         mHdmiCecLocalDeviceAudioSystem =
                 new HdmiCecLocalDeviceAudioSystem(hdmiControlService) {
@@ -270,6 +276,7 @@
         assertTrue(mHdmiCecLocalDeviceAudioSystem.isSystemAudioActivated());
     }
 
+    @Ignore("b/120845532")
     @Test
     public void testIsPlaybackDevice_cannotReceiveActiveSource() {
         resetTestVariables();
@@ -282,10 +289,10 @@
         mTestLooper.dispatchAll();
 
         assertThat(mMsgRequestActiveSourceCount).isEqualTo(1);
-        assertThat(mMsgSetSystemAudioModeCount).isEqualTo(1);
-        assertThat(mQueryTvSystemAudioModeSupportCount).isEqualTo(1);
-        assertThat(mHdmiCecLocalDeviceAudioSystem.isSystemAudioActivated()).isTrue();
         assertThat(mBroadcastActiveSource).isTrue();
+        assertThat(mQueryTvSystemAudioModeSupportCount).isEqualTo(1);
+        assertThat(mMsgSetSystemAudioModeCount).isEqualTo(1);
+        assertThat(mHdmiCecLocalDeviceAudioSystem.isSystemAudioActivated()).isTrue();
     }
 
     private void resetTestVariables() {
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
index 517b5ad..6d28ed1 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
@@ -35,6 +35,7 @@
 import android.annotation.NonNull;
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
 import android.content.pm.PackageParser;
 import android.content.pm.PackageUserState;
 import android.content.pm.SuspendDialogInfo;
@@ -226,8 +227,8 @@
         settingsUnderTest.mPackages.put(PACKAGE_NAME_3, createPackageSetting(PACKAGE_NAME_3));
         // now read and verify
         settingsUnderTest.readPackageRestrictionsLPr(0);
-        final PackageUserState readPus1 = settingsUnderTest.mPackages.get(PACKAGE_NAME_1).
-                readUserState(0);
+        final PackageUserState readPus1 = settingsUnderTest.mPackages.get(PACKAGE_NAME_1)
+                .readUserState(0);
         assertThat(readPus1.suspended, is(true));
         assertThat(readPus1.suspendingPackage, equalTo("suspendingPackage1"));
         assertThat(readPus1.dialogInfo, equalTo(dialogInfo1));
@@ -235,16 +236,16 @@
         assertThat(BaseBundle.kindofEquals(readPus1.suspendedLauncherExtras, launcherExtras1),
                 is(true));
 
-        final PackageUserState readPus2 = settingsUnderTest.mPackages.get(PACKAGE_NAME_2).
-                readUserState(0);
+        final PackageUserState readPus2 = settingsUnderTest.mPackages.get(PACKAGE_NAME_2)
+                .readUserState(0);
         assertThat(readPus2.suspended, is(true));
         assertThat(readPus2.suspendingPackage, equalTo("suspendingPackage2"));
         assertThat(readPus2.dialogInfo, is(nullValue()));
         assertThat(readPus2.suspendedAppExtras, is(nullValue()));
         assertThat(readPus2.suspendedLauncherExtras, is(nullValue()));
 
-        final PackageUserState readPus3 = settingsUnderTest.mPackages.get(PACKAGE_NAME_3).
-                readUserState(0);
+        final PackageUserState readPus3 = settingsUnderTest.mPackages.get(PACKAGE_NAME_3)
+                .readUserState(0);
         assertThat(readPus3.suspended, is(false));
         assertThat(readPus3.suspendingPackage, is(nullValue()));
         assertThat(readPus3.dialogInfo, is(nullValue()));
@@ -254,11 +255,59 @@
 
     @Test
     public void testPackageRestrictionsSuspendedDefault() {
-        final PackageSetting defaultSetting =  createPackageSetting(PACKAGE_NAME_1);
+        final PackageSetting defaultSetting = createPackageSetting(PACKAGE_NAME_1);
         assertThat(defaultSetting.getSuspended(0), is(false));
     }
 
     @Test
+    public void testReadWritePackageRestrictions_distractionFlags() {
+        final Context context = InstrumentationRegistry.getTargetContext();
+        final Settings settingsUnderTest = new Settings(context.getFilesDir(), null, new Object());
+        final PackageSetting ps1 = createPackageSetting(PACKAGE_NAME_1);
+        final PackageSetting ps2 = createPackageSetting(PACKAGE_NAME_2);
+        final PackageSetting ps3 = createPackageSetting(PACKAGE_NAME_3);
+
+        final int distractionFlags1 = PackageManager.RESTRICTION_HIDE_FROM_SUGGESTIONS;
+        ps1.setDistractionFlags(distractionFlags1, 0);
+        settingsUnderTest.mPackages.put(PACKAGE_NAME_1, ps1);
+
+        final int distractionFlags2 = PackageManager.RESTRICTION_HIDE_NOTIFICATIONS
+                | PackageManager.RESTRICTION_HIDE_FROM_SUGGESTIONS;
+        ps2.setDistractionFlags(distractionFlags2, 0);
+        settingsUnderTest.mPackages.put(PACKAGE_NAME_2, ps2);
+
+        final int distractionFlags3 = PackageManager.RESTRICTION_NONE;
+        ps3.setDistractionFlags(distractionFlags3, 0);
+        settingsUnderTest.mPackages.put(PACKAGE_NAME_3, ps3);
+
+        settingsUnderTest.writePackageRestrictionsLPr(0);
+
+        settingsUnderTest.mPackages.clear();
+        settingsUnderTest.mPackages.put(PACKAGE_NAME_1, createPackageSetting(PACKAGE_NAME_1));
+        settingsUnderTest.mPackages.put(PACKAGE_NAME_2, createPackageSetting(PACKAGE_NAME_2));
+        settingsUnderTest.mPackages.put(PACKAGE_NAME_3, createPackageSetting(PACKAGE_NAME_3));
+        // now read and verify
+        settingsUnderTest.readPackageRestrictionsLPr(0);
+        final PackageUserState readPus1 = settingsUnderTest.mPackages.get(PACKAGE_NAME_1)
+                .readUserState(0);
+        assertThat(readPus1.distractionFlags, is(distractionFlags1));
+
+        final PackageUserState readPus2 = settingsUnderTest.mPackages.get(PACKAGE_NAME_2)
+                .readUserState(0);
+        assertThat(readPus2.distractionFlags, is(distractionFlags2));
+
+        final PackageUserState readPus3 = settingsUnderTest.mPackages.get(PACKAGE_NAME_3)
+                .readUserState(0);
+        assertThat(readPus3.distractionFlags, is(distractionFlags3));
+    }
+
+    @Test
+    public void testPackageRestrictionsDistractionFlagsDefault() {
+        final PackageSetting defaultSetting = createPackageSetting(PACKAGE_NAME_1);
+        assertThat(defaultSetting.getDistractionFlags(0), is(PackageManager.RESTRICTION_NONE));
+    }
+
+    @Test
     public void testEnableDisable() {
         // Write the package files and make sure they're parsed properly the first time
         writeOldFiles();
@@ -692,6 +741,7 @@
         assertThat(userState.notLaunched, is(notLaunched));
         assertThat(userState.stopped, is(stopped));
         assertThat(userState.suspended, is(false));
+        assertThat(userState.distractionFlags, is(0));
         if (oldUserState != null) {
             assertThat(userState.equals(oldUserState), is(not(userStateChanged)));
         }
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageUserStateTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageUserStateTest.java
index f0ed612..8eaf35f 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageUserStateTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageUserStateTest.java
@@ -22,6 +22,7 @@
 import static org.hamcrest.CoreMatchers.is;
 import static org.junit.Assert.assertThat;
 
+import android.content.pm.PackageManager;
 import android.content.pm.PackageUserState;
 import android.content.pm.SuspendDialogInfo;
 import android.os.PersistableBundle;
@@ -227,4 +228,19 @@
         assertThat(testUserState1.equals(testUserState2), is(true));
     }
 
+    @Test
+    public void testPackageUserState06() {
+        final PackageUserState userState1 = new PackageUserState();
+        assertThat(userState1.distractionFlags, is(PackageManager.RESTRICTION_NONE));
+        userState1.distractionFlags = PackageManager.RESTRICTION_HIDE_FROM_SUGGESTIONS;
+
+        final PackageUserState copyOfUserState1 = new PackageUserState(userState1);
+        assertThat(userState1.distractionFlags, is(copyOfUserState1.distractionFlags));
+        assertThat(userState1.equals(copyOfUserState1), is(true));
+
+        final PackageUserState userState2 = new PackageUserState(userState1);
+        userState2.distractionFlags = PackageManager.RESTRICTION_HIDE_NOTIFICATIONS;
+        assertThat(userState1.equals(userState2), is(false));
+    }
+
 }
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
index 823b7a5..3ebc6ad 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
@@ -544,6 +544,18 @@
         switchUser(-1, UserHandle.of(startUser), false);
     }
 
+    public void testSwitchUserByHandle_ThrowsException() {
+        synchronized (mUserSwitchLock) {
+            try {
+                ActivityManager am = getContext().getSystemService(ActivityManager.class);
+                am.switchUser(null);
+                fail("Expected IllegalArgumentException on passing in a null UserHandle.");
+            } catch (IllegalArgumentException expected) {
+                // Do nothing - exception is expected.
+            }
+        }
+    }
+
     @MediumTest
     public void testConcurrentUserCreate() throws Exception {
         int userCount = mUserManager.getUserCount();
diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/DexLoggerTests.java b/services/tests/servicestests/src/com/android/server/pm/dex/DexLoggerTests.java
index 3b6b48b..f817e8e 100644
--- a/services/tests/servicestests/src/com/android/server/pm/dex/DexLoggerTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/dex/DexLoggerTests.java
@@ -77,7 +77,6 @@
 
     @Mock IPackageManager mPM;
     @Mock Installer mInstaller;
-    private final Object mInstallLock = new Object();
 
     private PackageDynamicCodeLoading mPackageDynamicCodeLoading;
     private DexLogger mDexLogger;
@@ -103,7 +102,7 @@
         };
 
         // For test purposes capture log messages as well as sending to the event log.
-        mDexLogger = new DexLogger(mPM, mInstaller, mInstallLock, mPackageDynamicCodeLoading) {
+        mDexLogger = new DexLogger(mPM, mInstaller, mPackageDynamicCodeLoading) {
             @Override
                 void writeDclEvent(int uid, String message) {
                     super.writeDclEvent(uid, message);
diff --git a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
index 41d5691..8171469 100644
--- a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
@@ -61,6 +61,7 @@
 import android.view.Display;
 
 import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.FlakyTest;
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
@@ -416,6 +417,7 @@
     }
 
     @Test
+    @FlakyTest(bugId = 119774928)
     public void testEnabledState() throws Exception {
         TestParoleListener paroleListener = new TestParoleListener();
         mController.addListener(paroleListener);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 83c1c76..94b21af 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -3511,9 +3511,9 @@
     }
 
     @Test
-    public void testAppOverlay() throws Exception {
-        mBinderService.setAppOverlaysAllowed(PKG, mUid, false);
-        assertFalse(mBinderService.areAppOverlaysAllowedForPackage(PKG, mUid));
+    public void testBubble() throws Exception {
+        mBinderService.setBubblesAllowed(PKG, mUid, false);
+        assertFalse(mBinderService.areBubblesAllowedForPackage(PKG, mUid));
     }
 
     @Test
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java
index 1458266..b9ae7d5 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java
@@ -56,8 +56,8 @@
 import android.provider.Settings;
 import android.service.notification.Adjustment;
 import android.service.notification.StatusBarNotification;
+import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
-import android.test.suitebuilder.annotation.SmallTest;
 
 import com.android.internal.R;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
@@ -87,14 +87,7 @@
     private final String channelId = "channel";
     private NotificationChannel channel =
             new NotificationChannel(channelId, "test", NotificationManager.IMPORTANCE_DEFAULT);
-    private final String channelIdLong =
-            "give_a_developer_a_string_argument_and_who_knows_what_they_will_pass_in_there";
     private final String groupId = "group";
-    private final String groupIdOverride = "other_group";
-    private final String groupIdLong =
-            "0|com.foo.bar|g:content://com.foo.bar.ui/account%3A-0000000/account/";
-    private NotificationChannel channelLongId =
-            new NotificationChannel(channelIdLong, "long", NotificationManager.IMPORTANCE_DEFAULT);
     private NotificationChannel defaultChannel =
             new NotificationChannel(NotificationChannel.DEFAULT_CHANNEL_ID, "test",
                     NotificationManager.IMPORTANCE_UNSPECIFIED);
@@ -408,73 +401,27 @@
     }
 
     @Test
-    public void testLogmakerShortChannel() {
+    public void testLogMaker() {
+        long timestamp = 1000L;
         StatusBarNotification sbn = getNotification(PKG_O, true /* noisy */,
                 true /* defaultSound */, false /* buzzy */, false /* defaultBuzz */,
                 false /* lights */, false /* defaultLights */, null /* group */);
         NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
-        final LogMaker logMaker = record.getLogMaker();
+        final LogMaker logMaker = record.getLogMaker(timestamp);
+
+        assertNull(logMaker.getTaggedData(MetricsEvent.NOTIFICATION_SHADE_INDEX));
         assertEquals(channelId,
                 (String) logMaker.getTaggedData(MetricsEvent.FIELD_NOTIFICATION_CHANNEL_ID));
         assertEquals(channel.getImportance(),
                 logMaker.getTaggedData(MetricsEvent.FIELD_NOTIFICATION_CHANNEL_IMPORTANCE));
-    }
-
-    @Test
-    public void testLogmakerLongChannel() {
-        StatusBarNotification sbn = getNotification(PKG_O, true /* noisy */,
-        true /* defaultSound */, false /* buzzy */, false /* defaultBuzz */,
-        false /* lights */, false /*defaultLights */, null /* group */);
-        NotificationRecord record = new NotificationRecord(mMockContext, sbn, channelLongId);
-        final String loggedId = (String)
-            record.getLogMaker().getTaggedData(MetricsEvent.FIELD_NOTIFICATION_CHANNEL_ID);
-        assertEquals(channelIdLong.substring(0,10), loggedId.substring(0, 10));
-    }
-
-    @Test
-    public void testLogmakerNoGroup() {
-        StatusBarNotification sbn = getNotification(PKG_O, true /* noisy */,
-                true /* defaultSound */, false /* buzzy */, false /* defaultBuzz */,
-                false /* lights */, false /*defaultLights */, null /* group */);
-        NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
-        assertNull(record.getLogMaker().getTaggedData(MetricsEvent.FIELD_NOTIFICATION_GROUP_ID));
-    }
-
-    @Test
-    public void testLogmakerShortGroup() {
-        StatusBarNotification sbn = getNotification(PKG_O, true /* noisy */,
-                true /* defaultSound */, false /* buzzy */, false /* defaultBuzz */,
-                false /* lights */, false /* defaultLights */, groupId /* group */);
-        NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
-        assertEquals(groupId,
-                record.getLogMaker().getTaggedData(MetricsEvent.FIELD_NOTIFICATION_GROUP_ID));
-    }
-
-    @Test
-    public void testLogmakerLongGroup() {
-        StatusBarNotification sbn = getNotification(PKG_O, true /* noisy */,
-                true /* defaultSound */, false /* buzzy */, false /* defaultBuzz */,
-                false /* lights */, false /* defaultLights */, groupIdLong /* group */);
-        NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
-        final String loggedId = (String)
-                record.getLogMaker().getTaggedData(MetricsEvent.FIELD_NOTIFICATION_GROUP_ID);
-        assertEquals(groupIdLong.substring(0,10), loggedId.substring(0, 10));
-    }
-
-    @Test
-    public void testLogmakerOverrideGroup() {
-        StatusBarNotification sbn = getNotification(PKG_O, true /* noisy */,
-                true /* defaultSound */, false /* buzzy */, false /* defaultBuzz */,
-                false /* lights */, false /* defaultLights */, groupId /* group */);
-        NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
-        assertEquals(groupId,
-                record.getLogMaker().getTaggedData(MetricsEvent.FIELD_NOTIFICATION_GROUP_ID));
-        record.setOverrideGroupKey(groupIdOverride);
-        assertEquals(groupIdOverride,
-                record.getLogMaker().getTaggedData(MetricsEvent.FIELD_NOTIFICATION_GROUP_ID));
-        record.setOverrideGroupKey(null);
-        assertEquals(groupId,
-                record.getLogMaker().getTaggedData(MetricsEvent.FIELD_NOTIFICATION_GROUP_ID));
+        assertEquals(record.getLifespanMs(timestamp),
+                (int) logMaker.getTaggedData(MetricsEvent.NOTIFICATION_SINCE_CREATE_MILLIS));
+        assertEquals(record.getFreshnessMs(timestamp),
+                (int) logMaker.getTaggedData(MetricsEvent.NOTIFICATION_SINCE_UPDATE_MILLIS));
+        assertEquals(record.getExposureMs(timestamp),
+                (int) logMaker.getTaggedData(MetricsEvent.NOTIFICATION_SINCE_VISIBLE_MILLIS));
+        assertEquals(record.getInterruptionMs(timestamp),
+                (int) logMaker.getTaggedData(MetricsEvent.NOTIFICATION_SINCE_INTERRUPTION_MILLIS));
     }
 
     @Test
@@ -845,4 +792,52 @@
 
         assertFalse(record.isNewEnoughForAlerting(record.mUpdateTimeMs + (1000 * 60 * 60)));
     }
+
+    @Test
+    public void testIgnoreImportanceAdjustmentsForOemLockedChannels() {
+        NotificationChannel channel = new NotificationChannel("a", "a", IMPORTANCE_DEFAULT);
+        channel.setImportanceLockedByOEM(true);
+
+        StatusBarNotification sbn = getNotification(PKG_O, true /* noisy */,
+                true /* defaultSound */, false /* buzzy */, false /* defaultBuzz */,
+                false /* lights */, false /* defaultLights */, groupId /* group */);
+        NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
+
+        assertEquals(IMPORTANCE_DEFAULT, record.getImportance());
+
+        Bundle bundle = new Bundle();
+        bundle.putInt(Adjustment.KEY_IMPORTANCE, IMPORTANCE_LOW);
+        Adjustment adjustment = new Adjustment(
+                PKG_O, record.getKey(), bundle, "", record.getUserId());
+
+        record.addAdjustment(adjustment);
+        record.applyAdjustments();
+        record.calculateImportance();
+
+        assertEquals(IMPORTANCE_DEFAULT, record.getImportance());
+    }
+
+    @Test
+    public void testApplyImportanceAdjustmentsForNonOemLockedChannels() {
+        NotificationChannel channel = new NotificationChannel("a", "a", IMPORTANCE_DEFAULT);
+        channel.setImportanceLockedByOEM(false);
+
+        StatusBarNotification sbn = getNotification(PKG_O, true /* noisy */,
+                true /* defaultSound */, false /* buzzy */, false /* defaultBuzz */,
+                false /* lights */, false /* defaultLights */, groupId /* group */);
+        NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
+
+        assertEquals(IMPORTANCE_DEFAULT, record.getImportance());
+
+        Bundle bundle = new Bundle();
+        bundle.putInt(Adjustment.KEY_IMPORTANCE, IMPORTANCE_LOW);
+        Adjustment adjustment = new Adjustment(
+                PKG_O, record.getKey(), bundle, "", record.getUserId());
+
+        record.addAdjustment(adjustment);
+        record.applyAdjustments();
+        record.calculateImportance();
+
+        assertEquals(IMPORTANCE_LOW, record.getImportance());
+    }
 }
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
index 0b73481..24a1f8c 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
@@ -809,7 +809,7 @@
         channel.setBypassDnd(true);
         channel.setLockscreenVisibility(Notification.VISIBILITY_SECRET);
         channel.setShowBadge(true);
-        channel.setAllowAppOverlay(false);
+        channel.setAllowBubbles(false);
         int lockMask = 0;
         for (int i = 0; i < NotificationChannel.LOCKABLE_FIELDS.length; i++) {
             lockMask |= NotificationChannel.LOCKABLE_FIELDS[i];
@@ -826,7 +826,7 @@
         assertFalse(savedChannel.canBypassDnd());
         assertFalse(Notification.VISIBILITY_SECRET == savedChannel.getLockscreenVisibility());
         assertEquals(channel.canShowBadge(), savedChannel.canShowBadge());
-        assertEquals(channel.canOverlayApps(), savedChannel.canOverlayApps());
+        assertEquals(channel.canBubble(), savedChannel.canBubble());
 
         verify(mHandler, never()).requestSort();
     }
@@ -840,7 +840,7 @@
         channel.setBypassDnd(true);
         channel.setLockscreenVisibility(Notification.VISIBILITY_SECRET);
         channel.setShowBadge(true);
-        channel.setAllowAppOverlay(false);
+        channel.setAllowBubbles(false);
         int lockMask = 0;
         for (int i = 0; i < NotificationChannel.LOCKABLE_FIELDS.length; i++) {
             lockMask |= NotificationChannel.LOCKABLE_FIELDS[i];
@@ -857,7 +857,7 @@
         assertFalse(savedChannel.canBypassDnd());
         assertFalse(Notification.VISIBILITY_SECRET == savedChannel.getLockscreenVisibility());
         assertEquals(channel.canShowBadge(), savedChannel.canShowBadge());
-        assertEquals(channel.canOverlayApps(), savedChannel.canOverlayApps());
+        assertEquals(channel.canBubble(), savedChannel.canBubble());
     }
 
     @Test
@@ -969,16 +969,16 @@
     }
 
     @Test
-    public void testLockFields_appOverlay() {
+    public void testLockFields_allowBubble() {
         mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, getChannel(), true, false);
         assertEquals(0,
                 mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, getChannel().getId(), false)
                         .getUserLockedFields());
 
         final NotificationChannel update = getChannel();
-        update.setAllowAppOverlay(false);
+        update.setAllowBubbles(false);
         mHelper.updateNotificationChannel(PKG_N_MR1, UID_N_MR1, update, true);
-        assertEquals(NotificationChannel.USER_LOCKED_ALLOW_APP_OVERLAY,
+        assertEquals(NotificationChannel.USER_LOCKED_ALLOW_BUBBLE,
                 mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, update.getId(), false)
                         .getUserLockedFields());
     }
@@ -2161,30 +2161,147 @@
     }
 
     @Test
-    public void testAllowAppOverlay_defaults() throws Exception {
-        assertTrue(mHelper.areAppOverlaysAllowed(PKG_O, UID_O));
+    public void testAllowBubbles_defaults() throws Exception {
+        assertTrue(mHelper.areBubblessAllowed(PKG_O, UID_O));
 
         ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false);
         mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper);
         loadStreamXml(baos, false);
 
-        assertTrue(mHelper.areAppOverlaysAllowed(PKG_O, UID_O));
+        assertTrue(mHelper.areBubblessAllowed(PKG_O, UID_O));
         assertEquals(0, mHelper.getAppLockedFields(PKG_O, UID_O));
     }
 
     @Test
-    public void testAllowAppOverlay_xml() throws Exception {
-        mHelper.setAppOverlaysAllowed(PKG_O, UID_O, false);
-        assertFalse(mHelper.areAppOverlaysAllowed(PKG_O, UID_O));
-        assertEquals(PreferencesHelper.LockableAppFields.USER_LOCKED_APP_OVERLAY,
+    public void testAllowBubbles_xml() throws Exception {
+        mHelper.setBubblesAllowed(PKG_O, UID_O, false);
+        assertFalse(mHelper.areBubblessAllowed(PKG_O, UID_O));
+        assertEquals(PreferencesHelper.LockableAppFields.USER_LOCKED_BUBBLE,
                 mHelper.getAppLockedFields(PKG_O, UID_O));
 
         ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false);
         mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper);
         loadStreamXml(baos, false);
 
-        assertFalse(mHelper.areAppOverlaysAllowed(PKG_O, UID_O));
-        assertEquals(PreferencesHelper.LockableAppFields.USER_LOCKED_APP_OVERLAY,
+        assertFalse(mHelper.areBubblessAllowed(PKG_O, UID_O));
+        assertEquals(PreferencesHelper.LockableAppFields.USER_LOCKED_BUBBLE,
                 mHelper.getAppLockedFields(PKG_O, UID_O));
     }
+
+    @Test
+    public void testLockChannelsForOEM_emptyList() {
+        mHelper.lockChannelsForOEM(null);
+        mHelper.lockChannelsForOEM(new String[0]);
+        // no exception
+    }
+
+    @Test
+    public void testLockChannelsForOEM_appWide() {
+        NotificationChannel a = new NotificationChannel("a", "a", IMPORTANCE_HIGH);
+        NotificationChannel b = new NotificationChannel("b", "b", IMPORTANCE_LOW);
+        NotificationChannel c = new NotificationChannel("c", "c", IMPORTANCE_DEFAULT);
+        // different uids, same package
+        mHelper.createNotificationChannel(PKG_O, 3, a, true, false);
+        mHelper.createNotificationChannel(PKG_O, 3, b, false, false);
+        mHelper.createNotificationChannel(PKG_O, 30, c, true, true);
+
+        mHelper.lockChannelsForOEM(new String[] {PKG_O});
+
+        assertTrue(mHelper.getNotificationChannel(PKG_O, 3, a.getId(), false)
+                .isImportanceLockedByOEM());
+        assertTrue(mHelper.getNotificationChannel(PKG_O, 3, b.getId(), false)
+                .isImportanceLockedByOEM());
+        assertTrue(mHelper.getNotificationChannel(PKG_O, 30, c.getId(), false)
+                .isImportanceLockedByOEM());
+    }
+
+    @Test
+    public void testLockChannelsForOEM_onlyGivenPkg() {
+        NotificationChannel a = new NotificationChannel("a", "a", IMPORTANCE_HIGH);
+        NotificationChannel b = new NotificationChannel("b", "b", IMPORTANCE_LOW);
+        mHelper.createNotificationChannel(PKG_O, 3, a, true, false);
+        mHelper.createNotificationChannel(PKG_N_MR1, 30, b, false, false);
+
+        mHelper.lockChannelsForOEM(new String[] {PKG_O});
+
+        assertTrue(mHelper.getNotificationChannel(PKG_O, 3, a.getId(), false)
+                .isImportanceLockedByOEM());
+        assertFalse(mHelper.getNotificationChannel(PKG_N_MR1, 30, b.getId(), false)
+                .isImportanceLockedByOEM());
+    }
+
+    @Test
+    public void testLockChannelsForOEM_channelSpecific() {
+        NotificationChannel a = new NotificationChannel("a", "a", IMPORTANCE_HIGH);
+        NotificationChannel b = new NotificationChannel("b", "b", IMPORTANCE_LOW);
+        NotificationChannel c = new NotificationChannel("c", "c", IMPORTANCE_DEFAULT);
+        // different uids, same package
+        mHelper.createNotificationChannel(PKG_O, 3, a, true, false);
+        mHelper.createNotificationChannel(PKG_O, 3, b, false, false);
+        mHelper.createNotificationChannel(PKG_O, 30, c, true, true);
+
+        mHelper.lockChannelsForOEM(new String[] {PKG_O + ":b", PKG_O + ":c"});
+
+        assertFalse(mHelper.getNotificationChannel(PKG_O, 3, a.getId(), false)
+                .isImportanceLockedByOEM());
+        assertTrue(mHelper.getNotificationChannel(PKG_O, 3, b.getId(), false)
+                .isImportanceLockedByOEM());
+        assertTrue(mHelper.getNotificationChannel(PKG_O, 30, c.getId(), false)
+                .isImportanceLockedByOEM());
+    }
+
+    @Test
+    public void testLockChannelsForOEM_channelDoesNotExistYet_appWide() {
+        NotificationChannel a = new NotificationChannel("a", "a", IMPORTANCE_HIGH);
+        NotificationChannel b = new NotificationChannel("b", "b", IMPORTANCE_LOW);
+        mHelper.createNotificationChannel(PKG_O, 3, a, true, false);
+
+        mHelper.lockChannelsForOEM(new String[] {PKG_O});
+
+        assertTrue(mHelper.getNotificationChannel(PKG_O, 3, a.getId(), false)
+                .isImportanceLockedByOEM());
+
+        mHelper.createNotificationChannel(PKG_O, 3, b, true, false);
+        assertTrue(mHelper.getNotificationChannel(PKG_O, 3, b.getId(), false)
+                .isImportanceLockedByOEM());
+    }
+
+    @Test
+    public void testLockChannelsForOEM_channelDoesNotExistYet_channelSpecific() {
+        NotificationChannel a = new NotificationChannel("a", "a", IMPORTANCE_HIGH);
+        NotificationChannel b = new NotificationChannel("b", "b", IMPORTANCE_LOW);
+        mHelper.createNotificationChannel(PKG_O, UID_O, a, true, false);
+
+        mHelper.lockChannelsForOEM(new String[] {PKG_O + ":a", PKG_O + ":b"});
+
+        assertTrue(mHelper.getNotificationChannel(PKG_O, UID_O, a.getId(), false)
+                .isImportanceLockedByOEM());
+
+        mHelper.createNotificationChannel(PKG_O, UID_O, b, true, false);
+        assertTrue(mHelper.getNotificationChannel(PKG_O, UID_O, b.getId(), false)
+                .isImportanceLockedByOEM());
+    }
+
+    @Test
+    public void testUpdateNotificationChannel_oemLockedImportance() {
+        NotificationChannel a = new NotificationChannel("a", "a", IMPORTANCE_HIGH);
+        mHelper.createNotificationChannel(PKG_O, UID_O, a, true, false);
+
+        mHelper.lockChannelsForOEM(new String[] {PKG_O});
+
+        NotificationChannel update = new NotificationChannel("a", "a", IMPORTANCE_NONE);
+        update.setAllowBubbles(false);
+
+        mHelper.updateNotificationChannel(PKG_O, UID_O, update, true);
+
+        assertEquals(IMPORTANCE_HIGH,
+                mHelper.getNotificationChannel(PKG_O, UID_O, a.getId(), false).getImportance());
+        assertEquals(false,
+                mHelper.getNotificationChannel(PKG_O, UID_O, a.getId(), false).canBubble());
+
+        mHelper.updateNotificationChannel(PKG_O, UID_O, update, true);
+
+        assertEquals(IMPORTANCE_HIGH,
+                mHelper.getNotificationChannel(PKG_O, UID_O, a.getId(), false).getImportance());
+    }
 }
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/RankingHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/RankingHelperTest.java
index 5638cb36..c79e1db0 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/RankingHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/RankingHelperTest.java
@@ -15,8 +15,11 @@
  */
 package com.android.server.notification;
 
+import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
 import static android.app.NotificationManager.IMPORTANCE_LOW;
 
+import static junit.framework.TestCase.assertEquals;
+
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Matchers.anyInt;
@@ -153,7 +156,7 @@
                 .build();
         mRecordGroupGSortA = new NotificationRecord(mContext, new StatusBarNotification(
                 PKG, PKG, 1, null, 0, 0, mNotiGroupGSortA, user,
-                null, System.currentTimeMillis()), getDefaultChannel());
+                null, System.currentTimeMillis()), getLowChannel());
 
         mNotiGroupGSortB = new Notification.Builder(mContext, TEST_CHANNEL_ID)
                 .setContentTitle("B")
@@ -163,7 +166,7 @@
                 .build();
         mRecordGroupGSortB = new NotificationRecord(mContext, new StatusBarNotification(
                 PKG, PKG, 1, null, 0, 0, mNotiGroupGSortB, user,
-                null, System.currentTimeMillis()), getDefaultChannel());
+                null, System.currentTimeMillis()), getLowChannel());
 
         mNotiNoGroup = new Notification.Builder(mContext, TEST_CHANNEL_ID)
                 .setContentTitle("C")
@@ -171,7 +174,7 @@
                 .build();
         mRecordNoGroup = new NotificationRecord(mContext, new StatusBarNotification(
                 PKG, PKG, 1, null, 0, 0, mNotiNoGroup, user,
-                null, System.currentTimeMillis()), getDefaultChannel());
+                null, System.currentTimeMillis()), getLowChannel());
 
         mNotiNoGroup2 = new Notification.Builder(mContext, TEST_CHANNEL_ID)
                 .setContentTitle("D")
@@ -179,7 +182,7 @@
                 .build();
         mRecordNoGroup2 = new NotificationRecord(mContext, new StatusBarNotification(
                 PKG, PKG, 1, null, 0, 0, mNotiNoGroup2, user,
-                null, System.currentTimeMillis()), getDefaultChannel());
+                null, System.currentTimeMillis()), getLowChannel());
 
         mNotiNoGroupSortA = new Notification.Builder(mContext, TEST_CHANNEL_ID)
                 .setContentTitle("E")
@@ -188,7 +191,7 @@
                 .build();
         mRecordNoGroupSortA = new NotificationRecord(mContext, new StatusBarNotification(
                 PKG, PKG, 1, null, 0, 0, mNotiNoGroupSortA, user,
-                null, System.currentTimeMillis()), getDefaultChannel());
+                null, System.currentTimeMillis()), getLowChannel());
 
         mAudioAttributes = new AudioAttributes.Builder()
                 .setContentType(AudioAttributes.CONTENT_TYPE_UNKNOWN)
@@ -197,11 +200,16 @@
                 .build();
     }
 
-    private NotificationChannel getDefaultChannel() {
+    private NotificationChannel getLowChannel() {
         return new NotificationChannel(NotificationChannel.DEFAULT_CHANNEL_ID, "name",
                 IMPORTANCE_LOW);
     }
 
+    private NotificationChannel getDefaultChannel() {
+        return new NotificationChannel(NotificationChannel.DEFAULT_CHANNEL_ID, "name",
+                IMPORTANCE_DEFAULT);
+    }
+
     @Test
     public void testSortShouldRespectCritical() throws Exception {
         ArrayList<NotificationRecord> notificationList = new ArrayList<NotificationRecord>(7);
@@ -285,4 +293,40 @@
         ArrayList<NotificationRecord> notificationList = new ArrayList<NotificationRecord>();
         mHelper.sort(notificationList);
     }
+    
+    @Test
+    public void testGroupNotifications_highestIsProxy() {
+        ArrayList<NotificationRecord> notificationList = new ArrayList<>();
+        // this should be the last in the list, except it's in a group with a high child
+        Notification lowSummaryN = new Notification.Builder(mContext, "")
+                .setGroup("group")
+                .setGroupSummary(true)
+                .build();
+        NotificationRecord lowSummary = new NotificationRecord(mContext, new StatusBarNotification(
+                PKG, PKG, 1, "summary", 0, 0, lowSummaryN, USER,
+                null, System.currentTimeMillis()), getLowChannel());
+        notificationList.add(lowSummary);
+
+        Notification lowN = new Notification.Builder(mContext, "").build();
+        NotificationRecord low = new NotificationRecord(mContext, new StatusBarNotification(
+                PKG, PKG, 1, "low", 0, 0, lowN, USER,
+                null, System.currentTimeMillis()), getLowChannel());
+        low.setContactAffinity(0.5f);
+        notificationList.add(low);
+
+        Notification highChildN = new Notification.Builder(mContext, "")
+                .setGroup("group")
+                .setGroupSummary(false)
+                .build();
+        NotificationRecord highChild = new NotificationRecord(mContext, new StatusBarNotification(
+                PKG, PKG, 1, "child", 0, 0, highChildN, USER,
+                null, System.currentTimeMillis()), getDefaultChannel());
+        notificationList.add(highChild);
+
+        mHelper.sort(notificationList);
+
+        assertEquals(lowSummary, notificationList.get(0));
+        assertEquals(highChild, notificationList.get(1));
+        assertEquals(low, notificationList.get(2));
+    }
 }
diff --git a/services/tests/uiservicestests/src/com/android/server/slice/SlicePermissionManagerTest.java b/services/tests/uiservicestests/src/com/android/server/slice/SlicePermissionManagerTest.java
index b315e51..efefee1 100644
--- a/services/tests/uiservicestests/src/com/android/server/slice/SlicePermissionManagerTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/slice/SlicePermissionManagerTest.java
@@ -16,6 +16,7 @@
 
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
 
 import android.content.ContentProvider;
 import android.content.ContentResolver;
@@ -26,6 +27,7 @@
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 import android.testing.TestableLooper.RunWithLooper;
+import android.util.Log;
 import android.util.Xml.Encoding;
 
 import com.android.server.UiServiceTestCase;
@@ -46,10 +48,12 @@
 @RunWith(AndroidTestingRunner.class)
 @RunWithLooper
 public class SlicePermissionManagerTest extends UiServiceTestCase {
+    private static final String TAG = "SlicePerManTest";
 
     @Test
     public void testGrant() {
-        File sliceDir = new File(mContext.getDataDir(), "system/slices");
+        File sliceDir = new File(mContext.getCacheDir(), "testGrantSlices");
+        Log.v(TAG, "testGrant: slice permissions stored in " + sliceDir.getAbsolutePath());
         SlicePermissionManager permissions = new SlicePermissionManager(mContext,
                 TestableLooper.get(this).getLooper(), sliceDir);
         Uri uri = new Builder().scheme(ContentResolver.SCHEME_CONTENT)
@@ -59,11 +63,15 @@
         permissions.grantSliceAccess("my.pkg", 0, "provider.pkg", 0, uri);
 
         assertTrue(permissions.hasPermission("my.pkg", 0, uri));
+
+        // Cleanup.
+        assertTrue(FileUtils.deleteContentsAndDir(sliceDir));
     }
 
     @Test
     public void testBackup() throws XmlPullParserException, IOException {
-        File sliceDir = new File(mContext.getDataDir(), "system/slices");
+        File sliceDir = new File(mContext.getCacheDir(), "testBackupSlices");
+        Log.v(TAG, "testBackup: slice permissions stored in " + sliceDir.getAbsolutePath());
         Uri uri = new Builder().scheme(ContentResolver.SCHEME_CONTENT)
                 .authority("authority")
                 .path("something").build();
@@ -90,7 +98,10 @@
                 TestableLooper.get(this).getLooper());
         permissions.readRestore(parser);
 
-        assertTrue(permissions.hasFullAccess("com.android.mypkg", 10));
+        if (!permissions.hasFullAccess("com.android.mypkg", 10)) {
+            fail("com.android.mypkg@10 did not have full access. backup file: "
+                    + output.toString());
+        }
         assertTrue(permissions.hasPermission("com.android.otherpkg", 0,
                 ContentProvider.maybeAddUserId(uri, 1)));
         permissions.removePkg("com.android.lastpkg", 1);
@@ -102,8 +113,9 @@
     }
 
     @Test
-    public void testInvalid() throws Exception {
-        File sliceDir = new File(mContext.getCacheDir(), "slices-test");
+    public void testInvalid() {
+        File sliceDir = new File(mContext.getCacheDir(), "testInvalidSlices");
+        Log.v(TAG, "testInvalid: slice permissions stored in " + sliceDir.getAbsolutePath());
         if (!sliceDir.exists()) {
             sliceDir.mkdir();
         }
@@ -118,7 +130,8 @@
 
             @Override
             public void writeTo(XmlSerializer out) throws IOException {
-                throw new RuntimeException("this doesn't work");
+                throw new RuntimeException("this RuntimeException inside junk.writeTo() "
+                        + "should be caught and suppressed by surrounding code");
             }
         };
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index 8f9d2ba..06360c2 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -41,9 +41,11 @@
 import android.app.ActivityOptions;
 import android.app.servertransaction.ClientTransaction;
 import android.app.servertransaction.PauseActivityItem;
+import android.content.pm.ActivityInfo;
 import android.content.res.Configuration;
 import android.graphics.Rect;
 import android.platform.test.annotations.Presubmit;
+import android.util.MergedConfiguration;
 import android.util.MutableBoolean;
 
 import androidx.test.filters.MediumTest;
@@ -68,7 +70,7 @@
     @Before
     public void setUp() throws Exception {
         setupActivityTaskManagerService();
-        mStack = new StackBuilder(mRootActivityContainer).build();
+        mStack = (TestActivityStack) new StackBuilder(mRootActivityContainer).build();
         mTask = mStack.getChildAt(0);
         mActivity = mTask.getTopActivity();
     }
@@ -251,4 +253,72 @@
         assertEquals(prevSeq + 1, appWindowTokenRequestedOrientation.seq);
         verify(mActivity.mAppWindowToken).onMergedOverrideConfigurationChanged();
     }
+
+    @Test
+    public void testSetsRelaunchReason_NotDragResizing() {
+        mActivity.setState(ActivityStack.ActivityState.RESUMED, "Testing");
+
+        mTask.onRequestedOverrideConfigurationChanged(mTask.getConfiguration());
+        mActivity.setLastReportedConfiguration(new MergedConfiguration(new Configuration(),
+                mActivity.getConfiguration()));
+
+        mActivity.info.configChanges &= ~ActivityInfo.CONFIG_ORIENTATION;
+        final Configuration newConfig = new Configuration(mTask.getConfiguration());
+        newConfig.orientation = newConfig.orientation == Configuration.ORIENTATION_PORTRAIT
+                ? Configuration.ORIENTATION_LANDSCAPE : Configuration.ORIENTATION_PORTRAIT;
+        mTask.onRequestedOverrideConfigurationChanged(newConfig);
+
+        mActivity.mRelaunchReason = ActivityTaskManagerService.RELAUNCH_REASON_NONE;
+
+        mActivity.ensureActivityConfiguration(0, false, false);
+
+        assertEquals(ActivityTaskManagerService.RELAUNCH_REASON_WINDOWING_MODE_RESIZE,
+                mActivity.mRelaunchReason);
+    }
+
+    @Test
+    public void testSetsRelaunchReason_DragResizing() {
+        mActivity.setState(ActivityStack.ActivityState.RESUMED, "Testing");
+
+        mTask.onRequestedOverrideConfigurationChanged(mTask.getConfiguration());
+        mActivity.setLastReportedConfiguration(new MergedConfiguration(new Configuration(),
+                mActivity.getConfiguration()));
+
+        mActivity.info.configChanges &= ~ActivityInfo.CONFIG_ORIENTATION;
+        final Configuration newConfig = new Configuration(mTask.getConfiguration());
+        newConfig.orientation = newConfig.orientation == Configuration.ORIENTATION_PORTRAIT
+                ? Configuration.ORIENTATION_LANDSCAPE : Configuration.ORIENTATION_PORTRAIT;
+        mTask.onRequestedOverrideConfigurationChanged(newConfig);
+
+        doReturn(true).when(mTask.getTask()).isDragResizing();
+
+        mActivity.mRelaunchReason = ActivityTaskManagerService.RELAUNCH_REASON_NONE;
+
+        mActivity.ensureActivityConfiguration(0, false, false);
+
+        assertEquals(ActivityTaskManagerService.RELAUNCH_REASON_FREE_RESIZE,
+                mActivity.mRelaunchReason);
+    }
+
+    @Test
+    public void testSetsRelaunchReason_NonResizeConfigChanges() {
+        mActivity.setState(ActivityStack.ActivityState.RESUMED, "Testing");
+
+        mTask.onRequestedOverrideConfigurationChanged(mTask.getConfiguration());
+        mActivity.setLastReportedConfiguration(new MergedConfiguration(new Configuration(),
+                mActivity.getConfiguration()));
+
+        mActivity.info.configChanges &= ~ActivityInfo.CONFIG_FONT_SCALE;
+        final Configuration newConfig = new Configuration(mTask.getConfiguration());
+        newConfig.fontScale = 5;
+        mTask.onRequestedOverrideConfigurationChanged(newConfig);
+
+        mActivity.mRelaunchReason =
+                ActivityTaskManagerService.RELAUNCH_REASON_WINDOWING_MODE_RESIZE;
+
+        mActivity.ensureActivityConfiguration(0, false, false);
+
+        assertEquals(ActivityTaskManagerService.RELAUNCH_REASON_NONE,
+                mActivity.mRelaunchReason);
+    }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
index b2a2869..fda23e9 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
@@ -36,6 +36,8 @@
 import static com.android.server.wm.ActivityStack.ActivityState.RESUMED;
 import static com.android.server.wm.ActivityStack.ActivityState.STOPPING;
 import static com.android.server.wm.ActivityStack.REMOVE_TASK_MODE_DESTROYING;
+import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_FREE_RESIZE;
+import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_WINDOWING_MODE_RESIZE;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -264,13 +266,13 @@
 
         // Do not move display to back because there is still another stack.
         stack2.moveToBack("testMoveStackToBackIncludingParent", stack2.topTask());
-        verify(stack2.getWindowContainerController()).positionChildAtBottom(any(),
+        verify(stack2.getTaskStack()).positionChildAtBottom(any(),
                 eq(false) /* includingParents */);
 
         // Also move display to back because there is only one stack left.
         display.removeChild(stack1);
         stack2.moveToBack("testMoveStackToBackIncludingParent", stack2.topTask());
-        verify(stack2.getWindowContainerController()).positionChildAtBottom(any(),
+        verify(stack2.getTaskStack()).positionChildAtBottom(any(),
                 eq(true) /* includingParents */);
     }
 
@@ -687,6 +689,62 @@
     }
 
     @Test
+    public void testHandleAppDied_RelaunchesAfterCrashDuringWindowingModeResize() {
+        final ActivityRecord activity = new ActivityBuilder(mService).setTask(mTask).build();
+
+        activity.mRelaunchReason = RELAUNCH_REASON_WINDOWING_MODE_RESIZE;
+        activity.launchCount = 1;
+        activity.haveState = false;
+
+        mStack.handleAppDiedLocked(activity.app);
+
+        assertEquals(1, mTask.mActivities.size());
+        assertEquals(1, mStack.getAllTasks().size());
+    }
+
+    @Test
+    public void testHandleAppDied_NotRelaunchAfterThreeCrashesDuringWindowingModeResize() {
+        final ActivityRecord activity = new ActivityBuilder(mService).setTask(mTask).build();
+
+        activity.mRelaunchReason = RELAUNCH_REASON_WINDOWING_MODE_RESIZE;
+        activity.launchCount = 3;
+        activity.haveState = false;
+
+        mStack.handleAppDiedLocked(activity.app);
+
+        assertThat(mTask.mActivities).isEmpty();
+        assertThat(mStack.getAllTasks()).isEmpty();
+    }
+
+    @Test
+    public void testHandleAppDied_RelaunchesAfterCrashDuringFreeResize() {
+        final ActivityRecord activity = new ActivityBuilder(mService).setTask(mTask).build();
+
+        activity.mRelaunchReason = RELAUNCH_REASON_FREE_RESIZE;
+        activity.launchCount = 1;
+        activity.haveState = false;
+
+        mStack.handleAppDiedLocked(activity.app);
+
+        assertEquals(1, mTask.mActivities.size());
+        assertEquals(1, mStack.getAllTasks().size());
+    }
+
+    @Test
+    public void testHandleAppDied_NotRelaunchAfterThreeCrashesDuringFreeResize() {
+        final ActivityRecord activity = new ActivityBuilder(mService).setTask(mTask).build();
+
+        activity.mRelaunchReason = RELAUNCH_REASON_FREE_RESIZE;
+        activity.launchCount = 3;
+        activity.haveState = false;
+
+        mStack.handleAppDiedLocked(activity.app);
+
+        assertThat(mTask.mActivities).isEmpty();
+        assertThat(mStack.getAllTasks()).isEmpty();
+    }
+
+    @Test
     public void testFinishCurrentActivity() {
         // Create 2 activities on a new display.
         final ActivityDisplay display = addNewActivityDisplayAt(ActivityDisplay.POSITION_TOP);
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
index 7a6b2b5..898d107 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
@@ -143,7 +143,7 @@
                 .setStack(mService.mRootActivityContainer.getDefaultDisplay().createStack(
                         WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, true /* onTop */))
                 .build();
-        assertThat((Object) task2.getStack()).isInstanceOf(PinnedActivityStack.class);
+        assertThat((Object) task2.getStack()).isInstanceOf(ActivityStack.class);
         mStarter.updateBounds(task2, bounds);
 
         verify(mService, times(1)).resizeStack(eq(task2.getStack().mStackId),
@@ -643,10 +643,10 @@
             boolean hasForegroundActivities, boolean callerIsRecents,
             boolean callerIsTempWhitelisted) {
         // window visibility
-        doReturn(callingUidHasVisibleWindow).when(mService.mWindowManager).isAnyWindowVisibleForUid(
-                callingUid);
-        doReturn(realCallingUidHasVisibleWindow).when(mService.mWindowManager)
-                .isAnyWindowVisibleForUid(realCallingUid);
+        doReturn(callingUidHasVisibleWindow).when(mService.mWindowManager.mRoot)
+                .isAnyNonToastWindowVisibleForUid(callingUid);
+        doReturn(realCallingUidHasVisibleWindow).when(mService.mWindowManager.mRoot)
+                .isAnyNonToastWindowVisibleForUid(realCallingUid);
         // process importance
         doReturn(callingUidProcState).when(mService).getUidStateLocked(callingUid);
         doReturn(realCallingUidProcState).when(mService).getUidStateLocked(realCallingUid);
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
index 5589ca1..d462e69 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
@@ -39,9 +39,6 @@
 import static com.android.server.wm.ActivityStack.REMOVE_TASK_MODE_DESTROYING;
 import static com.android.server.wm.ActivityStackSupervisor.ON_TOP;
 
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.doCallRealMethod;
-
 import android.app.ActivityManagerInternal;
 import android.app.ActivityOptions;
 import android.app.IApplicationThread;
@@ -66,11 +63,11 @@
 import android.view.DisplayInfo;
 
 import com.android.internal.app.IVoiceInteractor;
-import com.android.server.appop.AppOpsService;
 import com.android.server.AttributeCache;
 import com.android.server.ServiceThread;
 import com.android.server.am.ActivityManagerService;
 import com.android.server.am.PendingIntentController;
+import com.android.server.appop.AppOpsService;
 import com.android.server.firewall.IntentFirewall;
 import com.android.server.uri.UriGrantsManagerInternal;
 
@@ -377,7 +374,7 @@
                 mStack.addTask(task, true, "creating test task");
                 task.setStack(mStack);
                 task.setTask();
-                mStack.getWindowContainerController().mContainer.addChild(task.mTask, 0);
+                mStack.getTaskStack().addChild(task.mTask, 0);
             }
 
             task.touchActiveTime();
@@ -632,7 +629,7 @@
 
         @SuppressWarnings("TypeParameterUnusedInFormals")
         @Override
-        <T extends ActivityStack> T createStackUnchecked(int windowingMode, int activityType,
+        ActivityStack createStackUnchecked(int windowingMode, int activityType,
                 int stackId, boolean onTop) {
             return new StackBuilder(mSupervisor.mRootActivityContainer).setDisplay(this)
                     .setWindowingMode(windowingMode).setActivityType(activityType)
@@ -681,10 +678,9 @@
      * method is called. Note that its functionality depends on the implementations of the
      * construction arguments.
      */
-    protected static class TestActivityStack<T extends StackWindowController>
-            extends ActivityStack<T> {
+    protected static class TestActivityStack
+            extends ActivityStack {
         private int mOnActivityRemovedFromStackCount = 0;
-        private T mContainerController;
 
         static final int IS_TRANSLUCENT_UNSET = 0;
         static final int IS_TRANSLUCENT_FALSE = 1;
@@ -724,20 +720,20 @@
         }
 
         @Override
-        protected T createStackWindowController(int displayId, boolean onTop, Rect outBounds) {
-            mContainerController = (T) WindowTestUtils.createMockStackWindowContainerController();
+        protected void createTaskStack(int displayId, boolean onTop, Rect outBounds) {
+            mTaskStack = WindowTestUtils.createMockTaskStack();
 
             // Primary pinned stacks require a non-empty out bounds to be set or else all tasks
             // will be moved to the full screen stack.
             if (getWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
                 outBounds.set(0, 0, 100, 100);
             }
-            return mContainerController;
+
         }
 
         @Override
-        T getWindowContainerController() {
-            return mContainerController;
+        TaskStack getTaskStack() {
+            return mTaskStack;
         }
 
         void setIsTranslucent(boolean isTranslucent) {
@@ -827,27 +823,23 @@
         }
 
         @SuppressWarnings("TypeParameterUnusedInFormals")
-        <T extends ActivityStack> T build() {
+        ActivityStack build() {
             final int stackId = mStackId >= 0 ? mStackId : mDisplay.getNextStackId();
             if (mWindowingMode == WINDOWING_MODE_PINNED) {
-                return (T) new PinnedActivityStack(mDisplay, stackId,
-                        mRootActivityContainer.mStackSupervisor, mOnTop) {
+                return new ActivityStack(mDisplay, stackId, mRootActivityContainer.mStackSupervisor,
+                        mWindowingMode, ACTIVITY_TYPE_STANDARD, mOnTop) {
                     @Override
                     Rect getDefaultPictureInPictureBounds(float aspectRatio) {
                         return new Rect(50, 50, 100, 100);
                     }
 
                     @Override
-                    PinnedStackWindowController createStackWindowController(int displayId,
-                            boolean onTop, Rect outBounds) {
-                        PinnedStackWindowController controller =
-                                mock(PinnedStackWindowController.class);
-                        controller.mContainer = mock(TaskStack.class);
-                        return controller;
+                    void createTaskStack(int displayId, boolean onTop, Rect outBounds) {
+                        mTaskStack = mock(TaskStack.class);
                     }
                 };
             } else {
-                return (T) new TestActivityStack(mDisplay, stackId,
+                return new TestActivityStack(mDisplay, stackId,
                         mRootActivityContainer.mStackSupervisor, mWindowingMode,
                         mActivityType, mOnTop, mCreateActivity);
             }
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
index fa42289..0c6e632 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
@@ -39,7 +39,9 @@
 
 import androidx.test.filters.SmallTest;
 
+import org.junit.AfterClass;
 import org.junit.Before;
+import org.junit.BeforeClass;
 import org.junit.Test;
 
 /**
@@ -52,15 +54,34 @@
 @Presubmit
 public class AppTransitionTests extends WindowTestsBase {
 
+    private static RootWindowContainer sOriginalRootWindowContainer;
+
     private DisplayContent mDc;
 
+    @BeforeClass
+    public static void setUpRootWindowContainerMock() {
+        final WindowManagerService wm = WmServiceUtils.getWindowManagerService();
+        // For unit test, we don't need to test performSurfacePlacement to prevent some abnormal
+        // interaction with surfaceflinger native side.
+        sOriginalRootWindowContainer = wm.mRoot;
+        // Creating spied mock of RootWindowContainer shouldn't be done in @Before, since it will
+        // create unnecessary nested spied objects chain, because WindowManagerService object under
+        // test is a single instance shared among all tests that extend WindowTestsBase class.
+        // Instead it should be done once before running all tests in this test class.
+        wm.mRoot = spy(wm.mRoot);
+        doNothing().when(wm.mRoot).performSurfacePlacement(anyBoolean());
+    }
+
+    @AfterClass
+    public static void tearDownRootWindowContainerMock() {
+        final WindowManagerService wm = WmServiceUtils.getWindowManagerService();
+        wm.mRoot = sOriginalRootWindowContainer;
+        sOriginalRootWindowContainer = null;
+    }
+
     @Before
     public void setUp() throws Exception {
         mDc = mWm.getDefaultDisplayContentLocked();
-        // For unit test,  we don't need to test performSurfacePlacement to prevent some
-        // abnormal interaction with surfaceflinger native side.
-        mWm.mRoot = spy(mWm.mRoot);
-        doNothing().when(mWm.mRoot).performSurfacePlacement(anyBoolean());
     }
 
     @Test
@@ -160,7 +181,7 @@
         assertTrue(dc1.mOpeningApps.size() > 0);
 
         // Move stack to another display.
-        stack1.getController().reparent(dc2.getDisplayId(),  new Rect(), true);
+        stack1.reparent(dc2.getDisplayId(),  new Rect(), true);
 
         // Verify if token are cleared from both pending transition list in former display.
         assertFalse(dc1.mOpeningApps.contains(token1));
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index 3826fac..f399463 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -59,6 +59,7 @@
 import android.platform.test.annotations.Presubmit;
 import android.util.DisplayMetrics;
 import android.view.DisplayCutout;
+import android.view.DisplayInfo;
 import android.view.Gravity;
 import android.view.MotionEvent;
 import android.view.Surface;
@@ -584,15 +585,17 @@
 
     @Test
     public void testOnDescendantOrientationRequestChanged() {
-        final DisplayContent dc = createNewDisplay();
+        final DisplayInfo info = new DisplayInfo();
+        info.logicalWidth = 1080;
+        info.logicalHeight = 1920;
+        info.logicalDensityDpi = 240;
+        final DisplayContent dc = createNewDisplay(info);
+        dc.configureDisplayPolicy();
         mWm.mAtmService.mRootActivityContainer = mock(RootActivityContainer.class);
-        final int newOrientation = dc.getLastOrientation() == SCREEN_ORIENTATION_LANDSCAPE
-                ? SCREEN_ORIENTATION_PORTRAIT
-                : SCREEN_ORIENTATION_LANDSCAPE;
 
         final WindowState window = createWindow(null /* parent */, TYPE_BASE_APPLICATION, dc, "w");
         window.getTask().mTaskRecord = mock(TaskRecord.class, ExtendedMockito.RETURNS_DEEP_STUBS);
-        window.mAppToken.setOrientation(newOrientation);
+        window.mAppToken.mOrientation = SCREEN_ORIENTATION_LANDSCAPE;
 
         ActivityRecord activityRecord = mock(ActivityRecord.class);
 
@@ -603,21 +606,22 @@
         verify(mWm.mAtmService).updateDisplayOverrideConfigurationLocked(captor.capture(),
                 same(activityRecord), anyBoolean(), eq(dc.getDisplayId()));
         final Configuration newDisplayConfig = captor.getValue();
-        assertEquals(Configuration.ORIENTATION_PORTRAIT, newDisplayConfig.orientation);
+        assertEquals(Configuration.ORIENTATION_LANDSCAPE, newDisplayConfig.orientation);
     }
 
     @Test
     public void testOnDescendantOrientationRequestChanged_FrozenToUserRotation() {
-        final DisplayContent dc = createNewDisplay();
+        final DisplayInfo info = new DisplayInfo();
+        info.logicalWidth = 1080;
+        info.logicalHeight = 1920;
+        info.logicalDensityDpi = 240;
+        final DisplayContent dc = createNewDisplay(info);
         dc.getDisplayRotation().setFixedToUserRotation(true);
         mWm.mAtmService.mRootActivityContainer = mock(RootActivityContainer.class);
-        final int newOrientation = dc.getLastOrientation() == SCREEN_ORIENTATION_LANDSCAPE
-                ? SCREEN_ORIENTATION_PORTRAIT
-                : SCREEN_ORIENTATION_LANDSCAPE;
 
         final WindowState window = createWindow(null /* parent */, TYPE_BASE_APPLICATION, dc, "w");
         window.getTask().mTaskRecord = mock(TaskRecord.class, ExtendedMockito.RETURNS_DEEP_STUBS);
-        window.mAppToken.setOrientation(newOrientation);
+        window.mAppToken.mOrientation = SCREEN_ORIENTATION_LANDSCAPE;
 
         ActivityRecord activityRecord = mock(ActivityRecord.class);
 
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 198e7ce..d05711e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java
@@ -33,7 +33,6 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
 
 import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
 import android.content.ContentResolver;
@@ -612,23 +611,6 @@
     // ========================
     // Non-rotation API Tests
     // ========================
-    @Test
-    public void testRespectsAppRequestedOrientationByDefault() throws Exception {
-        mBuilder.build();
-
-        assertTrue("Display rotation should respect app requested orientation by"
-                + " default.", mTarget.respectAppRequestedOrientation());
-    }
-
-    @Test
-    public void testNotRespectAppRequestedOrientation_FixedToUserRotation() throws Exception {
-        mBuilder.build();
-        mTarget.setFixedToUserRotation(true);
-
-        assertFalse("Display rotation shouldn't respect app requested orientation if"
-                + " fixed to user rotation.", mTarget.respectAppRequestedOrientation());
-    }
-
     /**
      * Call {@link DisplayRotation#configure(int, int, int, int)} to configure {@link #mTarget}
      * according to given parameters.
diff --git a/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java
index bb3ab35..68d8bc0 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java
@@ -94,8 +94,8 @@
     private WindowState createDropTargetWindow(String name, int ownerId) {
         final WindowTestUtils.TestAppWindowToken token = WindowTestUtils.createTestAppWindowToken(
                 mDisplayContent);
-        final TaskStack stack = createStackControllerOnStackOnDisplay(
-                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, mDisplayContent).mContainer;
+        final TaskStack stack = createTaskStackOnDisplay(
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, mDisplayContent);
         final Task task = createTaskInStack(stack, ownerId);
         task.addChild(token, 0);
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
index 58302d6..a3f535a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
@@ -323,8 +323,8 @@
     public void testFindTaskToMoveToFrontWhenRecentsOnTop() {
         // Create stack/task on default display.
         final ActivityDisplay display = mRootActivityContainer.getDefaultDisplay();
-        final TestActivityStack targetStack =
-                new StackBuilder(mRootActivityContainer).setOnTop(false).build();
+        final TestActivityStack targetStack = (TestActivityStack) new StackBuilder(
+                mRootActivityContainer).setOnTop(false).build();
         final TaskRecord targetTask = targetStack.getChildAt(0);
 
         // Create Recents on top of the display.
diff --git a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
new file mode 100644
index 0000000..45fe5d2
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
+import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
+import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+
+/**
+ * Tests for RootWindowContainer.
+ *
+ * Build/Install/Run:
+ *  atest WmTests:RootWindowContainerTests
+ */
+@SmallTest
+@Presubmit
+public class RootWindowContainerTests extends WindowTestsBase {
+
+    private static final int FAKE_CALLING_UID = 667;
+
+    @Test
+    public void testIsAnyNonToastWindowVisibleForUid_oneToastOneNonToastBothVisible() {
+        final WindowState toastyToast = createWindow(null, TYPE_TOAST, "toast", FAKE_CALLING_UID);
+        final WindowState app = createWindow(null, TYPE_APPLICATION, "app", FAKE_CALLING_UID);
+        toastyToast.mHasSurface = true;
+        app.mHasSurface = true;
+
+        assertTrue(toastyToast.isVisible());
+        assertTrue(app.isVisible());
+        assertTrue(mWm.mRoot.isAnyNonToastWindowVisibleForUid(FAKE_CALLING_UID));
+    }
+
+    @Test
+    public void testIsAnyNonToastWindowVisibleForUid_onlyToastVisible() {
+        final WindowState toastyToast = createWindow(null, TYPE_TOAST, "toast", FAKE_CALLING_UID);
+        toastyToast.mHasSurface = true;
+
+        assertTrue(toastyToast.isVisible());
+        assertFalse(mWm.mRoot.isAnyNonToastWindowVisibleForUid(FAKE_CALLING_UID));
+    }
+
+    @Test
+    public void testIsAnyNonToastWindowVisibleForUid_aFewNonToastButNoneVisible() {
+        final WindowState topBar = createWindow(null, TYPE_STATUS_BAR, "topBar", FAKE_CALLING_UID);
+        final WindowState app = createWindow(null, TYPE_APPLICATION, "app", FAKE_CALLING_UID);
+
+        assertFalse(topBar.isVisible());
+        assertFalse(app.isVisible());
+        assertFalse(mWm.mRoot.isAnyNonToastWindowVisibleForUid(FAKE_CALLING_UID));
+    }
+}
+
diff --git a/services/tests/wmtests/src/com/android/server/wm/StackWindowControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/StackWindowControllerTests.java
deleted file mode 100644
index 5690b58..0000000
--- a/services/tests/wmtests/src/com/android/server/wm/StackWindowControllerTests.java
+++ /dev/null
@@ -1,105 +0,0 @@
-/*
- * Copyright (C) 2017 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.wm;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
-
-import android.graphics.Rect;
-import android.platform.test.annotations.Presubmit;
-
-import androidx.test.filters.SmallTest;
-
-import org.junit.Test;
-
-/**
- * Test class for {@link StackWindowController}.
- *
- * Build/Install/Run:
- *  atest FrameworksServicesTests:StackWindowControllerTests
- */
-@SmallTest
-@Presubmit
-public class StackWindowControllerTests extends WindowTestsBase {
-    @Test
-    public void testRemoveContainer() {
-        final StackWindowController stackController =
-                createStackControllerOnDisplay(mDisplayContent);
-        final WindowTestUtils.TestTask task = WindowTestUtils.createTestTask(stackController);
-
-        final TaskStack stack = stackController.mContainer;
-        assertNotNull(stack);
-        assertNotNull(task);
-        stackController.removeContainer();
-        // Assert that the container was removed.
-        assertNull(stackController.mContainer);
-        assertNull(stack.getDisplayContent());
-        assertNull(task.getDisplayContent());
-        assertNull(task.mStack);
-    }
-
-    @Test
-    public void testRemoveContainer_deferRemoval() {
-        final StackWindowController stackController =
-                createStackControllerOnDisplay(mDisplayContent);
-        final WindowTestUtils.TestTask task = WindowTestUtils.createTestTask(stackController);
-
-        final TaskStack stack = stackController.mContainer;
-        // Stack removal is deferred if one of its child is animating.
-        task.setLocalIsAnimating(true);
-
-        stackController.removeContainer();
-        // For the case of deferred removal the stack controller will no longer be connected to the
-        // container, but the task controller will still be connected to the its container until
-        // the stack window container is removed.
-        assertNull(stackController.mContainer);
-        assertNull(stack.getController());
-        assertNotNull(task);
-
-        stack.removeImmediately();
-        // After removing, the task will be isolated.
-        assertNull(task.getParent());
-        assertEquals(task.getChildCount(), 0);
-        assertNull(task.getController());
-    }
-
-    @Test
-    public void testReparent() {
-        // Create first stack on primary display.
-        final StackWindowController stack1Controller =
-                createStackControllerOnDisplay(mDisplayContent);
-        final TaskStack stack1 = stack1Controller.mContainer;
-        final WindowTestUtils.TestTask task1 = WindowTestUtils.createTestTask(stack1Controller);
-        task1.mOnDisplayChangedCalled = false;
-
-        // Create second display and put second stack on it.
-        final DisplayContent dc = createNewDisplay();
-        final StackWindowController stack2Controller =
-                createStackControllerOnDisplay(dc);
-        final TaskStack stack2 = stack2Controller.mContainer;
-
-        // Reparent
-        stack1Controller.reparent(dc.getDisplayId(), new Rect(), true /* onTop */);
-        assertEquals(dc, stack1.getDisplayContent());
-        final int stack1PositionInParent = stack1.getParent().mChildren.indexOf(stack1);
-        final int stack2PositionInParent = stack1.getParent().mChildren.indexOf(stack2);
-        assertEquals(stack1PositionInParent, stack2PositionInParent + 1);
-        assertTrue(task1.mOnDisplayChangedCalled);
-    }
-}
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
index 8a98cbe..cdb578d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
@@ -217,12 +217,17 @@
         info.logicalHeight = fullScreenBounds.height();
         ActivityDisplay display = addNewActivityDisplayAt(info, POSITION_TOP);
         assertTrue(mRootActivityContainer.getActivityDisplay(display.mDisplayId) != null);
+        // Override display orientation. Normally this is available via DisplayContent, but DC
+        // is mocked-out.
+        display.getRequestedOverrideConfiguration().orientation =
+                Configuration.ORIENTATION_LANDSCAPE;
+        display.onRequestedOverrideConfigurationChanged(
+                display.getRequestedOverrideConfiguration());
         ActivityStack stack = new StackBuilder(mRootActivityContainer)
                 .setWindowingMode(WINDOWING_MODE_FULLSCREEN).setDisplay(display).build();
         TaskRecord task = stack.getChildAt(0);
-        ActivityRecord root = task.getRootActivity();
-        ActivityRecord top = new ActivityBuilder(mService).setTask(task).setStack(stack).build();
-        assertEquals(root, task.getRootActivity());
+        ActivityRecord root = task.getTopActivity();
+        assertEquals(root, task.getTopActivity());
 
         assertEquals(fullScreenBounds, task.getBounds());
 
@@ -233,16 +238,22 @@
         assertTrue(task.getBounds().width() < task.getBounds().height());
         assertEquals(fullScreenBounds.height(), task.getBounds().height());
 
-        // Setting non-root app has no effect
-        setActivityRequestedOrientation(root, SCREEN_ORIENTATION_LANDSCAPE);
-        assertTrue(task.getBounds().width() < task.getBounds().height());
+        // Top activity gets used
+        ActivityRecord top = new ActivityBuilder(mService).setTask(task).setStack(stack).build();
+        assertEquals(top, task.getTopActivity());
+        setActivityRequestedOrientation(top, SCREEN_ORIENTATION_LANDSCAPE);
+        assertTrue(task.getBounds().width() > task.getBounds().height());
+        assertEquals(task.getBounds().width(), fullScreenBounds.width());
 
         // Setting app to unspecified restores
-        setActivityRequestedOrientation(root, SCREEN_ORIENTATION_UNSPECIFIED);
+        setActivityRequestedOrientation(top, SCREEN_ORIENTATION_UNSPECIFIED);
         assertEquals(fullScreenBounds, task.getBounds());
 
         // Setting app to fixed landscape and changing display
-        setActivityRequestedOrientation(root, SCREEN_ORIENTATION_LANDSCAPE);
+        setActivityRequestedOrientation(top, SCREEN_ORIENTATION_LANDSCAPE);
+        // simulate display orientation changing (normally done via DisplayContent)
+        display.getRequestedOverrideConfiguration().orientation =
+                Configuration.ORIENTATION_PORTRAIT;
         display.setBounds(fullScreenBoundsPort);
         assertTrue(task.getBounds().width() > task.getBounds().height());
         assertEquals(fullScreenBoundsPort.width(), task.getBounds().width());
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskStackContainersTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskStackContainersTests.java
index f01e9f0..74ccccc 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskStackContainersTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskStackContainersTests.java
@@ -47,8 +47,8 @@
 
     @Before
     public void setUp() throws Exception {
-        mPinnedStack = createStackControllerOnStackOnDisplay(
-                WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, mDisplayContent).mContainer;
+        mPinnedStack = createTaskStackOnDisplay(
+                WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, mDisplayContent);
         // Stack should contain visible app window to be considered visible.
         final Task pinnedTask = createTaskInStack(mPinnedStack, 0 /* userId */);
         assertFalse(mPinnedStack.isVisible());
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskStackTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskStackTests.java
index 7ac3318..2554237 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskStackTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskStackTests.java
@@ -20,8 +20,12 @@
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
 
+import android.graphics.Rect;
 import android.platform.test.annotations.Presubmit;
 
 import androidx.test.filters.SmallTest;
@@ -106,4 +110,62 @@
         assertNull(stack.getDisplayContent());
         assertNull(task.mStack);
     }
+
+    @Test
+    public void testRemoveContainer() {
+        final TaskStack stack = createTaskStackOnDisplay(mDisplayContent);
+        final WindowTestUtils.TestTask task = WindowTestUtils.createTestTask(stack);
+
+        assertNotNull(stack);
+        assertNotNull(task);
+        stack.removeIfPossible();
+        // Assert that the container was removed.
+        assertNull(stack.getParent());
+        assertEquals(0, stack.getChildCount());
+        assertNull(stack.getDisplayContent());
+        assertNull(task.getDisplayContent());
+        assertNull(task.mStack);
+    }
+
+    @Test
+    public void testRemoveContainer_deferRemoval() {
+        final TaskStack stack = createTaskStackOnDisplay(mDisplayContent);
+        final WindowTestUtils.TestTask task = WindowTestUtils.createTestTask(stack);
+
+        // Stack removal is deferred if one of its child is animating.
+        task.setLocalIsAnimating(true);
+
+        stack.removeIfPossible();
+        // For the case of deferred removal the task controller will still be connected to the its
+        // container until the stack window container is removed.
+        assertNotNull(stack.getParent());
+        assertNotEquals(0, stack.getChildCount());
+        assertNotNull(task);
+
+        stack.removeImmediately();
+        // After removing, the task will be isolated.
+        assertNull(task.getParent());
+        assertEquals(0, task.getChildCount());
+        assertNull(task.getController());
+    }
+
+    @Test
+    public void testReparent() {
+        // Create first stack on primary display.
+        final TaskStack stack1 = createTaskStackOnDisplay(mDisplayContent);
+        final WindowTestUtils.TestTask task1 = WindowTestUtils.createTestTask(stack1);
+        task1.mOnDisplayChangedCalled = false;
+
+        // Create second display and put second stack on it.
+        final DisplayContent dc = createNewDisplay();
+        final TaskStack stack2 = createTaskStackOnDisplay(dc);
+
+        // Reparent
+        stack1.reparent(dc.getDisplayId(), new Rect(), true /* onTop */);
+        assertEquals(dc, stack1.getDisplayContent());
+        final int stack1PositionInParent = stack1.getParent().mChildren.indexOf(stack1);
+        final int stack2PositionInParent = stack1.getParent().mChildren.indexOf(stack2);
+        assertEquals(stack1PositionInParent, stack2PositionInParent + 1);
+        assertTrue(task1.mOnDisplayChangedCalled);
+    }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
index 3e32ed3..ae211d3 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
@@ -40,8 +40,7 @@
 
     @Test
     public void testRemoveContainer() {
-        final StackWindowController stackController1 =
-                createStackControllerOnDisplay(mDisplayContent);
+        final TaskStack stackController1 = createTaskStackOnDisplay(mDisplayContent);
         final WindowTestUtils.TestTask task = WindowTestUtils.createTestTask(stackController1);
         final WindowTestUtils.TestAppWindowToken appToken =
                 WindowTestUtils.createAppWindowTokenInTask(mDisplayContent, task);
@@ -49,14 +48,13 @@
         task.removeIfPossible();
         // Assert that the container was removed.
         assertNull(task.getParent());
-        assertEquals(task.getChildCount(), 0);
+        assertEquals(0, task.getChildCount());
         assertNull(appToken.getParent());
     }
 
     @Test
     public void testRemoveContainer_deferRemoval() {
-        final StackWindowController stackController1 =
-                createStackControllerOnDisplay(mDisplayContent);
+        final TaskStack stackController1 = createTaskStackOnDisplay(mDisplayContent);
         final WindowTestUtils.TestTask task = WindowTestUtils.createTestTask(stackController1);
         final WindowTestUtils.TestAppWindowToken appToken =
                 WindowTestUtils.createAppWindowTokenInTask(mDisplayContent, task);
@@ -67,22 +65,20 @@
         // For the case of deferred removal the task will still be connected to the its app token
         // until the task window container is removed.
         assertNotNull(task.getParent());
-        assertNotEquals(task.getChildCount(), 0);
+        assertNotEquals(0, task.getChildCount());
         assertNotNull(appToken.getParent());
 
         task.removeImmediately();
         assertNull(task.getParent());
-        assertEquals(task.getChildCount(), 0);
+        assertEquals(0, task.getChildCount());
         assertNull(appToken.getParent());
     }
 
     @Test
     public void testReparent() {
-        final StackWindowController stackController1 =
-                createStackControllerOnDisplay(mDisplayContent);
+        final TaskStack stackController1 = createTaskStackOnDisplay(mDisplayContent);
         final WindowTestUtils.TestTask task = WindowTestUtils.createTestTask(stackController1);
-        final StackWindowController stackController2 =
-                createStackControllerOnDisplay(mDisplayContent);
+        final TaskStack stackController2 = createTaskStackOnDisplay(mDisplayContent);
         final WindowTestUtils.TestTask task2 = WindowTestUtils.createTestTask(stackController2);
 
         boolean gotException = false;
@@ -93,20 +89,17 @@
         }
         assertTrue("Should not be able to reparent to the same parent", gotException);
 
-        final StackWindowController stackController3 =
-                createStackControllerOnDisplay(mDisplayContent);
-        stackController3.setContainer(null);
         gotException = false;
         try {
-            task.reparent(stackController3, 0, false/* moveParents */);
+            task.reparent(null, 0, false/* moveParents */);
         } catch (IllegalArgumentException e) {
             gotException = true;
         }
-        assertTrue("Should not be able to reparent to a stack that doesn't have a container",
+        assertTrue("Should not be able to reparent to a stack that doesn't exist",
                 gotException);
 
         task.reparent(stackController2, 0, false/* moveParents */);
-        assertEquals(stackController2.mContainer, task.getParent());
+        assertEquals(stackController2, task.getParent());
         assertEquals(0, task.positionInParent());
         assertEquals(1, task2.positionInParent());
     }
@@ -114,20 +107,17 @@
     @Test
     public void testReparent_BetweenDisplays() {
         // Create first stack on primary display.
-        final StackWindowController stack1Controller =
-                createStackControllerOnDisplay(mDisplayContent);
-        final TaskStack stack1 = stack1Controller.mContainer;
-        final WindowTestUtils.TestTask task = WindowTestUtils.createTestTask(stack1Controller);
+        final TaskStack stack1 = createTaskStackOnDisplay(mDisplayContent);
+        final WindowTestUtils.TestTask task = WindowTestUtils.createTestTask(stack1);
         task.mOnDisplayChangedCalled = false;
         assertEquals(mDisplayContent, stack1.getDisplayContent());
 
         // Create second display and put second stack on it.
         final DisplayContent dc = createNewDisplay();
-        final StackWindowController stack2Controller = createStackControllerOnDisplay(dc);
-        final TaskStack stack2 = stack2Controller.mContainer;
-        final WindowTestUtils.TestTask task2 = WindowTestUtils.createTestTask(stack2Controller);
+        final TaskStack stack2 = createTaskStackOnDisplay(dc);
+        final WindowTestUtils.TestTask task2 = WindowTestUtils.createTestTask(stack2);
         // Reparent and check state
-        task.reparent(stack2Controller, 0, false /* moveParents */);
+        task.reparent(stack2, 0, false /* moveParents */);
         assertEquals(stack2, task.getParent());
         assertEquals(0, task.positionInParent());
         assertEquals(1, task2.positionInParent());
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java
index d9ef10d..0a4a8a4 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java
@@ -16,12 +16,15 @@
 
 package com.android.server.wm;
 
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.view.DisplayCutout.BOUNDS_POSITION_TOP;
 import static android.view.DisplayCutout.fromBoundingRect;
 import static android.view.WindowManager.LayoutParams.MATCH_PARENT;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
 
 import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.mock;
 
 import android.app.ActivityManager.TaskDescription;
 import android.content.res.Configuration;
@@ -58,7 +61,7 @@
         boolean mDockedResizingForTest = false;
         WindowStateWithTask(WindowManagerService wm, IWindow iWindow, WindowToken windowToken,
                 WindowManager.LayoutParams attrs, Task t) {
-            super(wm, null, iWindow, windowToken, null, 0, 0, attrs, 0, 0,
+            super(wm, mock(Session.class), iWindow, windowToken, null, 0, 0, attrs, 0, 0,
                     false /* ownerCanAddInternalSystemWindow */);
             mTask = t;
         }
@@ -113,9 +116,9 @@
 
     @Before
     public void setUp() throws Exception {
-        mWindowToken = WindowTestUtils.createTestAppWindowToken(
-                mWm.getDefaultDisplayContentLocked());
-        mStubStack = new TaskStack(mWm, 0, null);
+        mWindowToken = createAppWindowToken(mWm.getDefaultDisplayContentLocked(),
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
+        mStubStack = WindowTestUtils.createMockTaskStack();
     }
 
     // Do not use this function directly in the tests below. Instead, use more explicit function
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java
index 20fc5ae..44e998b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java
@@ -103,12 +103,10 @@
     }
 
     /**
-     * Creates a mock instance of {@link StackWindowController}.
+     * Creates a mock instance of {@link TestTaskStack}.
      */
-    public static StackWindowController createMockStackWindowContainerController() {
-        StackWindowController controller = mock(StackWindowController.class);
-        controller.mContainer = mock(TestTaskStack.class);
-        return controller;
+    public static TaskStack createMockTaskStack() {
+        return mock(TestTaskStack.class);
     }
 
     /** Creates a {@link Task} and adds it to the specified {@link TaskStack}. */
@@ -201,6 +199,11 @@
         }
 
         @Override
+        void onParentSet() {
+            // Do nothing.
+        }
+
+        @Override
         boolean isOnTop() {
             return mOnTop;
         }
@@ -283,10 +286,9 @@
         }
     }
 
-    public static TestTask createTestTask(StackWindowController stackWindowController) {
-        return new TestTask(sNextTaskId++, stackWindowController.mContainer, 0,
-                stackWindowController.mService, RESIZE_MODE_UNRESIZEABLE, false,
-                mock(TaskRecord.class));
+    public static TestTask createTestTask(TaskStack stack) {
+        return new TestTask(sNextTaskId++, stack, 0, stack.mWmService, RESIZE_MODE_UNRESIZEABLE,
+                false, mock(TaskRecord.class));
     }
 
     public static class TestIApplicationToken implements IApplicationToken {
@@ -334,5 +336,10 @@
 
             mHasSurface = hadSurface;
         }
+
+        @Override
+        void onParentSet() {
+            // Do nothing;
+        }
     }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
index 638cb03..89c1551 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -41,7 +41,6 @@
 
 import android.content.Context;
 import android.content.res.Configuration;
-import android.graphics.Rect;
 import android.hardware.display.DisplayManagerGlobal;
 import android.testing.DexmakerShareClassLoaderRule;
 import android.util.Log;
@@ -240,8 +239,7 @@
 
     WindowTestUtils.TestAppWindowToken createTestAppWindowToken(DisplayContent dc, int
             windowingMode, int activityType) {
-        final TaskStack stack = createStackControllerOnStackOnDisplay(windowingMode, activityType,
-                dc).mContainer;
+        final TaskStack stack = createTaskStackOnDisplay(windowingMode, activityType, dc);
         final Task task = createTaskInStack(stack, 0 /* userId */);
         final WindowTestUtils.TestAppWindowToken appWindowToken =
                 WindowTestUtils.createTestAppWindowToken(dc);
@@ -257,6 +255,14 @@
         }
     }
 
+    WindowState createWindow(WindowState parent, int type, String name, int ownerId) {
+        synchronized (mWm.mGlobalLock) {
+            return (parent == null)
+                    ? createWindow(parent, type, mDisplayContent, name, ownerId)
+                    : createWindow(parent, type, parent.mToken, name, ownerId);
+        }
+    }
+
     WindowState createWindowOnStack(WindowState parent, int windowingMode, int activityType,
             int type, DisplayContent dc, String name) {
         synchronized (mWm.mGlobalLock) {
@@ -277,7 +283,16 @@
         synchronized (mWm.mGlobalLock) {
             final WindowToken token = createWindowToken(
                     dc, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, type);
-            return createWindow(parent, type, token, name);
+            return createWindow(parent, type, token, name, 0 /* ownerId */);
+        }
+    }
+
+    WindowState createWindow(WindowState parent, int type, DisplayContent dc, String name,
+            int ownerId) {
+        synchronized (mWm.mGlobalLock) {
+            final WindowToken token = createWindowToken(
+                    dc, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, type);
+            return createWindow(parent, type, token, name, ownerId);
         }
     }
 
@@ -299,6 +314,14 @@
     }
 
     WindowState createWindow(WindowState parent, int type, WindowToken token, String name,
+            int ownerId) {
+        synchronized (mWm.mGlobalLock) {
+            return createWindow(parent, type, token, name, ownerId,
+                    false /* ownerCanAddInternalSystemWindow */);
+        }
+    }
+
+    WindowState createWindow(WindowState parent, int type, WindowToken token, String name,
             int ownerId, boolean ownerCanAddInternalSystemWindow) {
         return createWindow(parent, type, token, name, ownerId, ownerCanAddInternalSystemWindow,
                 mWm, mMockSession, mIWindow);
@@ -325,28 +348,20 @@
     /** Creates a {@link TaskStack} and adds it to the specified {@link DisplayContent}. */
     TaskStack createTaskStackOnDisplay(DisplayContent dc) {
         synchronized (mWm.mGlobalLock) {
-            return createStackControllerOnDisplay(dc).mContainer;
+            return createTaskStackOnDisplay(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, dc);
         }
     }
 
-    StackWindowController createStackControllerOnDisplay(DisplayContent dc) {
-        synchronized (mWm.mGlobalLock) {
-            return createStackControllerOnStackOnDisplay(
-                    WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, dc);
-        }
-    }
-
-    StackWindowController createStackControllerOnStackOnDisplay(
-            int windowingMode, int activityType, DisplayContent dc) {
+    TaskStack createTaskStackOnDisplay(int windowingMode, int activityType, DisplayContent dc) {
         synchronized (mWm.mGlobalLock) {
             final Configuration overrideConfig = new Configuration();
             overrideConfig.windowConfiguration.setWindowingMode(windowingMode);
             overrideConfig.windowConfiguration.setActivityType(activityType);
             final int stackId = ++sNextStackId;
-            final StackWindowController controller = new StackWindowController(stackId, null,
-                    dc.getDisplayId(), true /* onTop */, new Rect(), mWm);
-            controller.onRequestedOverrideConfigurationChanged(overrideConfig);
-            return controller;
+            final TaskStack stack = new TaskStack(mWm, stackId, mock(ActivityStack.class));
+            dc.setStackOnDisplay(stackId, true, stack);
+            stack.onRequestedOverrideConfigurationChanged(overrideConfig);
+            return stack;
         }
     }
 
diff --git a/services/usage/java/com/android/server/usage/IntervalStats.java b/services/usage/java/com/android/server/usage/IntervalStats.java
index 94cc650..9a5bd13 100644
--- a/services/usage/java/com/android/server/usage/IntervalStats.java
+++ b/services/usage/java/com/android/server/usage/IntervalStats.java
@@ -22,6 +22,7 @@
 import static android.app.usage.UsageEvents.Event.CONFIGURATION_CHANGE;
 import static android.app.usage.UsageEvents.Event.CONTINUE_PREVIOUS_DAY;
 import static android.app.usage.UsageEvents.Event.CONTINUING_FOREGROUND_SERVICE;
+import static android.app.usage.UsageEvents.Event.DEVICE_SHUTDOWN;
 import static android.app.usage.UsageEvents.Event.END_OF_DAY;
 import static android.app.usage.UsageEvents.Event.FLUSH_TO_DISK;
 import static android.app.usage.UsageEvents.Event.FOREGROUND_SERVICE_START;
@@ -66,7 +67,7 @@
     public final ArrayMap<String, UsageStats> packageStats = new ArrayMap<>();
     public final ArrayMap<Configuration, ConfigurationStats> configurations = new ArrayMap<>();
     public Configuration activeConfiguration;
-    public EventList events;
+    public EventList events = new EventList();
 
     // A string cache. This is important as when we're parsing XML files, we don't want to
     // keep hundreds of strings that have the same contents. We will read the string
@@ -112,6 +113,9 @@
 
     }
 
+    public IntervalStats() {
+    }
+
     /**
      * Gets the UsageStats object for the given package, or creates one and adds it internally.
      */
@@ -253,6 +257,7 @@
             case ROLLOVER_FOREGROUND_SERVICE:
             case CONTINUE_PREVIOUS_DAY:
             case CONTINUING_FOREGROUND_SERVICE:
+            case DEVICE_SHUTDOWN:
                 return true;
         }
         return false;
@@ -281,8 +286,9 @@
     @VisibleForTesting
     public void update(String packageName, String className, long timeStamp, int eventType,
             int instanceId) {
-        if (eventType == FLUSH_TO_DISK) {
-            // FLUSH_TO_DISK are sent to all packages.
+        if (eventType == DEVICE_SHUTDOWN
+                || eventType == FLUSH_TO_DISK) {
+            // DEVICE_SHUTDOWN and FLUSH_TO_DISK are sent to all packages.
             final int size = packageStats.size();
             for (int i = 0; i < size; i++) {
                 UsageStats usageStats = packageStats.valueAt(i);
@@ -321,9 +327,6 @@
      */
     @VisibleForTesting
     public void addEvent(Event event) {
-        if (events == null) {
-            events = new EventList();
-        }
         // Cache common use strings
         event.mPackage = getCachedStringRef(event.mPackage);
         if (event.mClass != null) {
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index f146370..76a3aa8 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -18,6 +18,7 @@
 
 import static android.app.usage.UsageEvents.Event.CHOOSER_ACTION;
 import static android.app.usage.UsageEvents.Event.CONFIGURATION_CHANGE;
+import static android.app.usage.UsageEvents.Event.DEVICE_SHUTDOWN;
 import static android.app.usage.UsageEvents.Event.FLUSH_TO_DISK;
 import static android.app.usage.UsageEvents.Event.NOTIFICATION_INTERRUPTION;
 import static android.app.usage.UsageEvents.Event.SHORTCUT_INVOCATION;
@@ -416,11 +417,30 @@
      */
     void shutdown() {
         synchronized (mLock) {
+            mHandler.removeMessages(MSG_REPORT_EVENT);
+            Event event = new Event(DEVICE_SHUTDOWN, SystemClock.elapsedRealtime());
+            // orderly shutdown, the last event is DEVICE_SHUTDOWN.
+            reportEventToAllUserId(event);
             flushToDiskLocked();
         }
     }
 
     /**
+     * After power button is pressed for 3.5 seconds
+     * (as defined in {@link com.android.internal.R.integer#config_veryLongPressTimeout}),
+     * report DEVICE_SHUTDOWN event and persist the database. If the power button is pressed for 10
+     * seconds and the device is shutdown, the database is already persisted and we are not losing
+     * data.
+     * This method is called from PhoneWindowManager, do not synchronize on mLock otherwise
+     * PhoneWindowManager may be blocked.
+     */
+    void prepareForPossibleShutdown() {
+        Event event = new Event(DEVICE_SHUTDOWN, SystemClock.elapsedRealtime());
+        mHandler.obtainMessage(MSG_REPORT_EVENT_TO_ALL_USERID, event).sendToTarget();
+        mHandler.sendEmptyMessage(MSG_FLUSH_TO_DISK);
+    }
+
+    /**
      * Called by the Binder stub.
      */
     void reportEvent(Event event, int userId) {
@@ -1487,6 +1507,11 @@
         }
 
         @Override
+        public void prepareForPossibleShutdown() {
+            UsageStatsService.this.prepareForPossibleShutdown();
+        }
+
+        @Override
         public void addAppIdleStateChangeListener(AppIdleStateChangeListener listener) {
             mAppStandby.addListener(listener);
             listener.onParoleStateChanged(isAppIdleParoleOn());
diff --git a/services/usage/java/com/android/server/usage/UserUsageStatsService.java b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
index 5128ae1..2d1098c7 100644
--- a/services/usage/java/com/android/server/usage/UserUsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
@@ -16,6 +16,7 @@
 
 package com.android.server.usage;
 
+import static android.app.usage.UsageEvents.Event.DEVICE_SHUTDOWN;
 import static android.app.usage.UsageStatsManager.INTERVAL_BEST;
 import static android.app.usage.UsageStatsManager.INTERVAL_COUNT;
 import static android.app.usage.UsageStatsManager.INTERVAL_DAILY;
@@ -134,6 +135,18 @@
             updateRolloverDeadline();
         }
 
+        // During system reboot, add a DEVICE_SHUTDOWN event to the end of event list, the timestamp
+        // is last time UsageStatsDatabase is persisted to disk.
+        final IntervalStats currentDailyStats = mCurrentStats[INTERVAL_DAILY];
+        if (currentDailyStats != null) {
+            final int size = currentDailyStats.events.size();
+            if (size == 0 || currentDailyStats.events.get(size - 1).mEventType != DEVICE_SHUTDOWN) {
+                // The last event in event list is not DEVICE_SHUTDOWN, then we insert one.
+                final Event event = new Event(DEVICE_SHUTDOWN, currentDailyStats.lastTimeSaved);
+                currentDailyStats.addEvent(event);
+            }
+        }
+
         if (mDatabase.isNewUpdate()) {
             notifyNewUpdate();
         }
@@ -175,7 +188,9 @@
                 // ACTIVITY_STOPPED.
                 && event.mEventType != Event.ACTIVITY_DESTROYED
                 // FLUSH_TO_DISK is a private event.
-                && event.mEventType != Event.FLUSH_TO_DISK) {
+                && event.mEventType != Event.FLUSH_TO_DISK
+                // DEVICE_SHUTDOWN is added to event list after reboot.
+                && event.mEventType != Event.DEVICE_SHUTDOWN) {
             currentDailyStats.addEvent(event);
         }
 
@@ -393,10 +408,6 @@
                     @Override
                     public void combine(IntervalStats stats, boolean mutable,
                             List<Event> accumulatedResult) {
-                        if (stats.events == null) {
-                            return;
-                        }
-
                         final int startIndex = stats.events.firstIndexOnOrAfter(beginTime);
                         final int size = stats.events.size();
                         for (int i = startIndex; i < size; i++) {
@@ -434,10 +445,6 @@
         names.add(packageName);
         final List<Event> results = queryStats(INTERVAL_DAILY,
                 beginTime, endTime, (stats, mutable, accumulatedResult) -> {
-                    if (stats.events == null) {
-                        return;
-                    }
-
                     final int startIndex = stats.events.firstIndexOnOrAfter(beginTime);
                     final int size = stats.events.size();
                     for (int i = startIndex; i < size; i++) {
@@ -696,10 +703,6 @@
                     @Override
                     public void combine(IntervalStats stats, boolean mutable,
                             List<Event> accumulatedResult) {
-                        if (stats.events == null) {
-                            return;
-                        }
-
                         final int startIndex = stats.events.firstIndexOnOrAfter(beginTime);
                         final int size = stats.events.size();
                         for (int i = startIndex; i < size; i++) {
@@ -925,10 +928,12 @@
                 return "SCREEN_INTERACTIVE";
             case Event.SCREEN_NON_INTERACTIVE:
                 return "SCREEN_NON_INTERACTIVE";
-            case UsageEvents.Event.KEYGUARD_SHOWN:
+            case Event.KEYGUARD_SHOWN:
                 return "KEYGUARD_SHOWN";
-            case UsageEvents.Event.KEYGUARD_HIDDEN:
+            case Event.KEYGUARD_HIDDEN:
                 return "KEYGUARD_HIDDEN";
+            case Event.DEVICE_SHUTDOWN:
+                return "DEVICE_SHUTDOWN";
             default:
                 return "UNKNOWN_TYPE_" + eventType;
         }
diff --git a/services/usb/java/com/android/server/usb/UsbHostManager.java b/services/usb/java/com/android/server/usb/UsbHostManager.java
index 904d55e..00c7548 100644
--- a/services/usb/java/com/android/server/usb/UsbHostManager.java
+++ b/services/usb/java/com/android/server/usb/UsbHostManager.java
@@ -418,12 +418,10 @@
                         parser.getRawDescriptors());
 
                 // Stats collection
-                if (parser.hasAudioInterface()) {
-                    StatsLog.write(StatsLog.USB_DEVICE_ATTACHED, newDevice.getVendorId(),
-                            newDevice.getProductId(), parser.hasAudioInterface(),
-                            parser.hasHIDInterface(), parser.hasStorageInterface(),
-                            StatsLog.USB_DEVICE_ATTACHED__STATE__STATE_CONNECTED, 0);
-                }
+                StatsLog.write(StatsLog.USB_DEVICE_ATTACHED, newDevice.getVendorId(),
+                        newDevice.getProductId(), parser.hasAudioInterface(),
+                        parser.hasHIDInterface(), parser.hasStorageInterface(),
+                        StatsLog.USB_DEVICE_ATTACHED__STATE__STATE_CONNECTED, 0);
             }
         }
 
@@ -455,14 +453,12 @@
                 if (current != null) {
                     UsbDescriptorParser parser = new UsbDescriptorParser(deviceAddress,
                             current.mDescriptors);
-                    if (parser.hasAudioInterface()) {
                         // Stats collection
-                        StatsLog.write(StatsLog.USB_DEVICE_ATTACHED, device.getVendorId(),
-                                device.getProductId(), parser.hasAudioInterface(),
-                                parser.hasHIDInterface(),  parser.hasStorageInterface(),
-                                StatsLog.USB_DEVICE_ATTACHED__STATE__STATE_DISCONNECTED,
-                                System.currentTimeMillis() - current.mTimestamp);
-                    }
+                    StatsLog.write(StatsLog.USB_DEVICE_ATTACHED, device.getVendorId(),
+                            device.getProductId(), parser.hasAudioInterface(),
+                            parser.hasHIDInterface(),  parser.hasStorageInterface(),
+                            StatsLog.USB_DEVICE_ATTACHED__STATE__STATE_DISCONNECTED,
+                            System.currentTimeMillis() - current.mTimestamp);
                 }
             } else {
                 Slog.d(TAG, "Removed device at " + deviceAddress + " was already gone");
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
index bbf3d45..613c4ff 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
@@ -371,14 +371,15 @@
         }
 
         private boolean shouldEnableService(Context context) {
-            // VoiceInteractionService should not be enabled on any low RAM devices
-            // or devices that have not declared the recognition feature, unless the
-            // device's configuration has explicitly set the config flag for a fixed
+            // VoiceInteractionService should not be enabled on devices that have not declared the
+            // recognition feature (including low-ram devices where notLowRam="true" takes effect),
+            // unless the device's configuration has explicitly set the config flag for a fixed
             // voice interaction service.
-            return (!ActivityManager.isLowRamDeviceStatic()
-                            && context.getPackageManager().hasSystemFeature(
-                                    PackageManager.FEATURE_VOICE_RECOGNIZERS)) ||
-                    getForceVoiceInteractionServicePackage(context.getResources()) != null;
+            if (getForceVoiceInteractionServicePackage(context.getResources()) != null) {
+                return true;
+            }
+            return context.getPackageManager()
+                    .hasSystemFeature(PackageManager.FEATURE_VOICE_RECOGNIZERS);
         }
 
         private String getForceVoiceInteractionServicePackage(Resources res) {
diff --git a/startop/view_compiler/Android.bp b/startop/view_compiler/Android.bp
index 2fc3a0d..7dc83c3 100644
--- a/startop/view_compiler/Android.bp
+++ b/startop/view_compiler/Android.bp
@@ -46,7 +46,7 @@
     },
 }
 
-cc_library_host_static {
+cc_library_static {
     name: "libviewcompiler",
     defaults: ["viewcompiler_defaults"],
     srcs: [
@@ -58,9 +58,10 @@
         "util.cc",
         "layout_validation.cc",
     ],
+    host_supported: true,
 }
 
-cc_binary_host {
+cc_binary {
     name: "viewcompiler",
     defaults: ["viewcompiler_defaults"],
     srcs: [
@@ -70,6 +71,7 @@
         "libgflags",
         "libviewcompiler",
     ],
+    host_supported: true
 }
 
 cc_test_host {
diff --git a/startop/view_compiler/apk_layout_compiler.cc b/startop/view_compiler/apk_layout_compiler.cc
index e95041b..09cdbd5 100644
--- a/startop/view_compiler/apk_layout_compiler.cc
+++ b/startop/view_compiler/apk_layout_compiler.cc
@@ -79,9 +79,9 @@
   return visitor.can_compile();
 }
 
-void CompileApkLayouts(const std::string& filename, CompilationTarget target,
-                       std::ostream& target_out) {
-  auto assets = android::ApkAssets::Load(filename);
+namespace {
+void CompileApkAssetsLayouts(const std::unique_ptr<const android::ApkAssets>& assets,
+                             CompilationTarget target, std::ostream& target_out) {
   android::AssetManager2 resources;
   resources.SetApkAssets({assets.get()});
 
@@ -155,5 +155,20 @@
     target_out.write(image.ptr<const char>(), image.size());
   }
 }
+}  // namespace
+
+void CompileApkLayouts(const std::string& filename, CompilationTarget target,
+                       std::ostream& target_out) {
+  auto assets = android::ApkAssets::Load(filename);
+  CompileApkAssetsLayouts(assets, target, target_out);
+}
+
+void CompileApkLayoutsFd(android::base::unique_fd fd, CompilationTarget target,
+                         std::ostream& target_out) {
+  constexpr const char* friendly_name{"viewcompiler assets"};
+  auto assets = android::ApkAssets::LoadFromFd(
+      std::move(fd), friendly_name, /*system=*/false, /*force_shared_lib=*/false);
+  CompileApkAssetsLayouts(assets, target, target_out);
+}
 
 }  // namespace startop
diff --git a/startop/view_compiler/apk_layout_compiler.h b/startop/view_compiler/apk_layout_compiler.h
index c85ddd6..03bd545 100644
--- a/startop/view_compiler/apk_layout_compiler.h
+++ b/startop/view_compiler/apk_layout_compiler.h
@@ -19,12 +19,16 @@
 
 #include <string>
 
+#include "android-base/unique_fd.h"
+
 namespace startop {
 
 enum class CompilationTarget { kJavaLanguage, kDex };
 
 void CompileApkLayouts(const std::string& filename, CompilationTarget target,
                        std::ostream& target_out);
+void CompileApkLayoutsFd(android::base::unique_fd fd, CompilationTarget target,
+                         std::ostream& target_out);
 
 }  // namespace startop
 
diff --git a/startop/view_compiler/main.cc b/startop/view_compiler/main.cc
index 871a421..11ecde2 100644
--- a/startop/view_compiler/main.cc
+++ b/startop/view_compiler/main.cc
@@ -49,6 +49,7 @@
 
 DEFINE_bool(apk, false, "Compile layouts in an APK");
 DEFINE_bool(dex, false, "Generate a DEX file instead of Java");
+DEFINE_int32(infd, -1, "Read input from the given file descriptor");
 DEFINE_string(out, kStdoutFilename, "Where to write the generated class");
 DEFINE_string(package, "", "The package name for the generated class (required)");
 
@@ -95,7 +96,7 @@
 int main(int argc, char** argv) {
   constexpr size_t kProgramName = 0;
   constexpr size_t kFileNameParam = 1;
-  constexpr size_t kNumRequiredArgs = 2;
+  constexpr size_t kNumRequiredArgs = 1;
 
   gflags::SetUsageMessage(
       "Compile XML layout files into equivalent Java language code\n"
@@ -104,12 +105,11 @@
   gflags::ParseCommandLineFlags(&argc, &argv, /*remove_flags*/ true);
 
   gflags::CommandLineFlagInfo cmd = gflags::GetCommandLineFlagInfoOrDie("package");
-  if (argc != kNumRequiredArgs || cmd.is_default) {
+  if (argc < kNumRequiredArgs || cmd.is_default) {
     gflags::ShowUsageWithFlags(argv[kProgramName]);
     return 1;
   }
 
-  const char* const filename = argv[kFileNameParam];
   const bool is_stdout = FLAGS_out == kStdoutFilename;
 
   std::ofstream outfile;
@@ -118,13 +118,23 @@
   }
 
   if (FLAGS_apk) {
-    startop::CompileApkLayouts(
-        filename,
-        FLAGS_dex ? startop::CompilationTarget::kDex : startop::CompilationTarget::kJavaLanguage,
-        is_stdout ? std::cout : outfile);
+    const startop::CompilationTarget target =
+        FLAGS_dex ? startop::CompilationTarget::kDex : startop::CompilationTarget::kJavaLanguage;
+    if (FLAGS_infd >= 0) {
+      startop::CompileApkLayoutsFd(
+          android::base::unique_fd{FLAGS_infd}, target, is_stdout ? std::cout : outfile);
+    } else {
+      if (argc < 2) {
+        gflags::ShowUsageWithFlags(argv[kProgramName]);
+        return 1;
+      }
+      const char* const filename = argv[kFileNameParam];
+      startop::CompileApkLayouts(filename, target, is_stdout ? std::cout : outfile);
+    }
     return 0;
   }
 
+  const char* const filename = argv[kFileNameParam];
   const string layout_name = startop::util::FindLayoutNameFromFilename(filename);
 
   XMLDocument xml;
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index 8425603..05d5a13 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -505,6 +505,14 @@
             "android.telecom.extra.ORIGINAL_CONNECTION_ID";
 
     /**
+     * Boolean connection extra key set on the extras passed to
+     * {@link Connection#sendConnectionEvent} which indicates that audio is present
+     * on the RTT call when the extra value is true.
+     */
+    public static final String EXTRA_IS_RTT_AUDIO_PRESENT =
+            "android.telecom.extra.IS_RTT_AUDIO_PRESENT";
+
+    /**
      * Connection event used to inform Telecom that it should play the on hold tone.  This is used
      * to play a tone when the peer puts the current call on hold.  Sent to Telecom via
      * {@link #sendConnectionEvent(String, Bundle)}.
@@ -619,6 +627,13 @@
      */
     public static final String EXTRA_SIP_INVITE = "android.telecom.extra.SIP_INVITE";
 
+    /**
+     * Connection event used to inform an {@link InCallService} that the RTT audio indication
+     * has changed.
+     */
+    public static final String EVENT_RTT_AUDIO_INDICATION_CHANGED =
+            "android.telecom.event.RTT_AUDIO_INDICATION_CHANGED";
+
     // Flag controlling whether PII is emitted into the logs
     private static final boolean PII_DEBUG = Log.isLoggable(android.util.Log.DEBUG);
 
diff --git a/telephony/java/android/telephony/AvailableNetworkInfo.java b/telephony/java/android/telephony/AvailableNetworkInfo.java
index fe07370..4da79b3 100644
--- a/telephony/java/android/telephony/AvailableNetworkInfo.java
+++ b/telephony/java/android/telephony/AvailableNetworkInfo.java
@@ -110,6 +110,7 @@
     private AvailableNetworkInfo(Parcel in) {
         mSubId = in.readInt();
         mPriority = in.readInt();
+        mMccMncs = new ArrayList<>();
         in.readStringList(mMccMncs);
     }
 
diff --git a/telephony/java/android/telephony/CallAttributes.aidl b/telephony/java/android/telephony/CallAttributes.aidl
new file mode 100644
index 0000000..69127df
--- /dev/null
+++ b/telephony/java/android/telephony/CallAttributes.aidl
@@ -0,0 +1,20 @@
+/*
+ * 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.telephony;
+
+parcelable CallAttributes;
+
diff --git a/telephony/java/android/telephony/CallAttributes.java b/telephony/java/android/telephony/CallAttributes.java
new file mode 100644
index 0000000..2b99ce1
--- /dev/null
+++ b/telephony/java/android/telephony/CallAttributes.java
@@ -0,0 +1,151 @@
+/*
+ * 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.telephony;
+
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.telephony.TelephonyManager.NetworkType;
+
+import java.util.Objects;
+
+/**
+ * Contains information about a call's attributes as passed up from the HAL. If there are multiple
+ * ongoing calls, the CallAttributes will pertain to the call in the foreground.
+ * @hide
+ */
+@SystemApi
+public class CallAttributes implements Parcelable {
+    private PreciseCallState mPreciseCallState;
+    @NetworkType
+    private int mNetworkType; // TelephonyManager.NETWORK_TYPE_* ints
+    private CallQuality mCallQuality;
+
+
+    public CallAttributes(PreciseCallState state, @NetworkType int networkType,
+            CallQuality callQuality) {
+        this.mPreciseCallState = state;
+        this.mNetworkType = networkType;
+        this.mCallQuality = callQuality;
+    }
+
+    @Override
+    public String toString() {
+        return "mPreciseCallState=" + mPreciseCallState + " mNetworkType=" + mNetworkType
+                + " mCallQuality=" + mCallQuality;
+    }
+
+    private CallAttributes(Parcel in) {
+        mPreciseCallState = (PreciseCallState) in.readValue(mPreciseCallState.getClass()
+                .getClassLoader());
+        mNetworkType = in.readInt();
+        mCallQuality = (CallQuality) in.readValue(mCallQuality.getClass().getClassLoader());
+    }
+
+    // getters
+    /**
+     * Returns the {@link PreciseCallState} of the call.
+     */
+    public PreciseCallState getPreciseCallState() {
+        return mPreciseCallState;
+    }
+
+    /**
+     * Returns the {@link TelephonyManager#NetworkType} of the call.
+     *
+     * @see TelephonyManager#NETWORK_TYPE_UNKNOWN
+     * @see TelephonyManager#NETWORK_TYPE_GPRS
+     * @see TelephonyManager#NETWORK_TYPE_EDGE
+     * @see TelephonyManager#NETWORK_TYPE_UMTS
+     * @see TelephonyManager#NETWORK_TYPE_CDMA
+     * @see TelephonyManager#NETWORK_TYPE_EVDO_0
+     * @see TelephonyManager#NETWORK_TYPE_EVDO_A
+     * @see TelephonyManager#NETWORK_TYPE_1xRTT
+     * @see TelephonyManager#NETWORK_TYPE_HSDPA
+     * @see TelephonyManager#NETWORK_TYPE_HSUPA
+     * @see TelephonyManager#NETWORK_TYPE_HSPA
+     * @see TelephonyManager#NETWORK_TYPE_IDEN
+     * @see TelephonyManager#NETWORK_TYPE_EVDO_B
+     * @see TelephonyManager#NETWORK_TYPE_LTE
+     * @see TelephonyManager#NETWORK_TYPE_EHRPD
+     * @see TelephonyManager#NETWORK_TYPE_HSPAP
+     * @see TelephonyManager#NETWORK_TYPE_GSM
+     * @see TelephonyManager#NETWORK_TYPE_TD_SCDMA
+     * @see TelephonyManager#NETWORK_TYPE_IWLAN
+     * @see TelephonyManager#NETWORK_TYPE_LTE_CA
+     * @see TelephonyManager#NETWORK_TYPE_NR
+     */
+    @NetworkType
+    public int getNetworkType() {
+        return mNetworkType;
+    }
+
+    /**
+     * Returns the {#link CallQuality} of the call.
+     */
+    public CallQuality getCallQuality() {
+        return mCallQuality;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mPreciseCallState, mNetworkType, mCallQuality);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (o == null || !(o instanceof CallAttributes) || hashCode() != o.hashCode()) {
+            return false;
+        }
+
+        if (this == o) {
+            return true;
+        }
+
+        CallAttributes s = (CallAttributes) o;
+
+        return (mPreciseCallState == s.mPreciseCallState
+                && mNetworkType == s.mNetworkType
+                && mCallQuality == s.mCallQuality);
+    }
+
+    /**
+     * {@link Parcelable#describeContents}
+     */
+    public @Parcelable.ContentsFlags int describeContents() {
+        return 0;
+    }
+
+    /**
+     * {@link Parcelable#writeToParcel}
+     */
+    public void writeToParcel(Parcel dest, @Parcelable.WriteFlags int flags) {
+        mPreciseCallState.writeToParcel(dest, flags);
+        dest.writeInt(mNetworkType);
+        mCallQuality.writeToParcel(dest, flags);
+    }
+
+    public static final Parcelable.Creator<CallAttributes> CREATOR = new Parcelable.Creator() {
+        public CallAttributes createFromParcel(Parcel in) {
+            return new CallAttributes(in);
+        }
+
+        public CallAttributes[] newArray(int size) {
+            return new CallAttributes[size];
+        }
+    };
+}
diff --git a/telephony/java/android/telephony/CallQuality.aidl b/telephony/java/android/telephony/CallQuality.aidl
new file mode 100644
index 0000000..f54355f
--- /dev/null
+++ b/telephony/java/android/telephony/CallQuality.aidl
@@ -0,0 +1,20 @@
+/*
+ * 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.telephony;
+
+parcelable CallQuality;
+
diff --git a/telephony/java/android/telephony/CallQuality.java b/telephony/java/android/telephony/CallQuality.java
new file mode 100644
index 0000000..b27f6b4
--- /dev/null
+++ b/telephony/java/android/telephony/CallQuality.java
@@ -0,0 +1,341 @@
+/*
+ * 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.telephony;
+
+import android.annotation.IntDef;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+
+/**
+ * Parcelable object to handle call quality.
+ * <p>
+ * Currently this supports IMS calls.
+ * <p>
+ * It provides the call quality level, duration, and additional information related to RTP packets,
+ * jitter and delay.
+ * <p>
+ * If there are multiple active calls, the CallQuality will pertain to the call in the foreground.
+ *
+ * @hide
+ */
+@SystemApi
+public final class CallQuality implements Parcelable {
+
+    // Constants representing the call quality level (see #CallQuality);
+    public static final int CALL_QUALITY_EXCELLENT = 0;
+    public static final int CALL_QUALITY_GOOD = 1;
+    public static final int CALL_QUALITY_FAIR = 2;
+    public static final int CALL_QUALITY_POOR = 3;
+    public static final int CALL_QUALITY_BAD = 4;
+    public static final int CALL_QUALITY_NOT_AVAILABLE = 5;
+
+    /**
+     * Call quality
+     * @hide
+     */
+    @IntDef(prefix = { "CALL_QUALITY_" }, value = {
+            CALL_QUALITY_EXCELLENT,
+            CALL_QUALITY_GOOD,
+            CALL_QUALITY_FAIR,
+            CALL_QUALITY_POOR,
+            CALL_QUALITY_BAD,
+            CALL_QUALITY_NOT_AVAILABLE,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface CallQualityLevel {}
+
+    @CallQualityLevel
+    private int mDownlinkCallQualityLevel;
+    @CallQualityLevel
+    private int mUplinkCallQualityLevel;
+    private int mCallDuration;
+    private int mNumRtpPacketsTransmitted;
+    private int mNumRtpPacketsReceived;
+    private int mNumRtpPacketsTransmittedLost;
+    private int mNumRtpPacketsNotReceived;
+    private int mAverageRelativeJitter;
+    private int mMaxRelativeJitter;
+    private int mAverageRoundTripTime;
+    private int mCodecType;
+
+    /** @hide **/
+    public CallQuality(Parcel in) {
+        mDownlinkCallQualityLevel = in.readInt();
+        mUplinkCallQualityLevel = in.readInt();
+        mCallDuration = in.readInt();
+        mNumRtpPacketsTransmitted = in.readInt();
+        mNumRtpPacketsReceived = in.readInt();
+        mNumRtpPacketsTransmittedLost = in.readInt();
+        mNumRtpPacketsNotReceived = in.readInt();
+        mAverageRelativeJitter = in.readInt();
+        mMaxRelativeJitter = in.readInt();
+        mAverageRoundTripTime = in.readInt();
+        mCodecType = in.readInt();
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param callQualityLevel the call quality level (see #CallQualityLevel)
+     * @param callDuration the call duration in milliseconds
+     * @param numRtpPacketsTransmitted RTP packets sent to network
+     * @param numRtpPacketsReceived RTP packets received from network
+     * @param numRtpPacketsTransmittedLost RTP packets which were lost in network and never
+     * transmitted
+     * @param numRtpPacketsNotReceived RTP packets which were lost in network and never recieved
+     * @param averageRelativeJitter average relative jitter in milliseconds
+     * @param maxRelativeJitter maximum relative jitter in milliseconds
+     * @param averageRoundTripTime average round trip delay in milliseconds
+     * @param codecType the codec type
+     */
+    public CallQuality(
+            @CallQualityLevel int downlinkCallQualityLevel,
+            @CallQualityLevel int uplinkCallQualityLevel,
+            int callDuration,
+            int numRtpPacketsTransmitted,
+            int numRtpPacketsReceived,
+            int numRtpPacketsTransmittedLost,
+            int numRtpPacketsNotReceived,
+            int averageRelativeJitter,
+            int maxRelativeJitter,
+            int averageRoundTripTime,
+            int codecType) {
+        this.mDownlinkCallQualityLevel = downlinkCallQualityLevel;
+        this.mUplinkCallQualityLevel = uplinkCallQualityLevel;
+        this.mCallDuration = callDuration;
+        this.mNumRtpPacketsTransmitted = numRtpPacketsTransmitted;
+        this.mNumRtpPacketsReceived = numRtpPacketsReceived;
+        this.mNumRtpPacketsTransmittedLost = numRtpPacketsTransmittedLost;
+        this.mNumRtpPacketsNotReceived = numRtpPacketsNotReceived;
+        this.mAverageRelativeJitter = averageRelativeJitter;
+        this.mMaxRelativeJitter = maxRelativeJitter;
+        this.mAverageRoundTripTime = averageRoundTripTime;
+        this.mCodecType = codecType;
+    }
+
+    // getters
+    /**
+     * Returns the downlink CallQualityLevel for a given ongoing call.
+     */
+    @CallQualityLevel
+    public int getDownlinkCallQualityLevel() {
+        return mDownlinkCallQualityLevel;
+    }
+
+    /**
+     * Returns the uplink CallQualityLevel for a given ongoing call.
+     */
+    @CallQualityLevel
+    public int getUplinkCallQualityLevel() {
+        return mUplinkCallQualityLevel;
+    }
+
+    /**
+     * Returns the duration of the call, in milliseconds.
+     */
+    public int getCallDuration() {
+        return mCallDuration;
+    }
+
+    /**
+     * Returns the total number of RTP packets transmitted by this device for a given ongoing call.
+     */
+    public int getNumRtpPacketsTransmitted() {
+        return mNumRtpPacketsTransmitted;
+    }
+
+    /**
+     * Returns the total number of RTP packets received by this device for a given ongoing call.
+     */
+    public int getNumRtpPacketsReceived() {
+        return mNumRtpPacketsReceived;
+    }
+
+    /**
+     * Returns the number of RTP packets which were sent by this device but were lost in the
+     * network before reaching the other party.
+     */
+    public int getNumRtpPacketsTransmittedLost() {
+        return mNumRtpPacketsTransmittedLost;
+    }
+
+    /**
+     * Returns the number of RTP packets which were sent by the other party but were lost in the
+     * network before reaching this device.
+     */
+    public int getNumRtpPacketsNotReceived() {
+        return mNumRtpPacketsNotReceived;
+    }
+
+    /**
+     * Returns the average relative jitter in milliseconds. Jitter represents the amount of variance
+     * in interarrival time of packets, for example, if two packets are sent 2 milliseconds apart
+     * but received 3 milliseconds apart, the relative jitter between those packets is 1
+     * millisecond.
+     *
+     * <p>See RFC 3550 for more information on jitter calculations.
+     */
+    public int getAverageRelativeJitter() {
+        return mAverageRelativeJitter;
+    }
+
+    /**
+     * Returns the maximum relative jitter for a given ongoing call. Jitter represents the amount of
+     * variance in interarrival time of packets, for example, if two packets are sent 2 milliseconds
+     * apart but received 3 milliseconds apart, the relative jitter between those packets is 1
+     * millisecond.
+     *
+     * <p>See RFC 3550 for more information on jitter calculations.
+     */
+    public int getMaxRelativeJitter() {
+        return mMaxRelativeJitter;
+    }
+
+    /**
+     * Returns the average round trip time in milliseconds.
+     */
+    public int getAverageRoundTripTime() {
+        return mAverageRoundTripTime;
+    }
+
+    /**
+     * Returns the codec type. This value corresponds to the AUDIO_QUALITY_* constants in
+     * {@link ImsStreamMediaProfile}.
+     *
+     * @see ImsStreamMediaProfile#AUDIO_QUALITY_NONE
+     * @see ImsStreamMediaProfile#AUDIO_QUALITY_AMR
+     * @see ImsStreamMediaProfile#AUDIO_QUALITY_AMR_WB
+     * @see ImsStreamMediaProfile#AUDIO_QUALITY_QCELP13K
+     * @see ImsStreamMediaProfile#AUDIO_QUALITY_EVRC
+     * @see ImsStreamMediaProfile#AUDIO_QUALITY_EVRC_B
+     * @see ImsStreamMediaProfile#AUDIO_QUALITY_EVRC_WB
+     * @see ImsStreamMediaProfile#AUDIO_QUALITY_EVRC_NW
+     * @see ImsStreamMediaProfile#AUDIO_QUALITY_GSM_EFR
+     * @see ImsStreamMediaProfile#AUDIO_QUALITY_GSM_FR
+     * @see ImsStreamMediaProfile#AUDIO_QUALITY_GSM_HR
+     * @see ImsStreamMediaProfile#AUDIO_QUALITY_G711U
+     * @see ImsStreamMediaProfile#AUDIO_QUALITY_G723
+     * @see ImsStreamMediaProfile#AUDIO_QUALITY_G711A
+     * @see ImsStreamMediaProfile#AUDIO_QUALITY_G722
+     * @see ImsStreamMediaProfile#AUDIO_QUALITY_G711AB
+     * @see ImsStreamMediaProfile#AUDIO_QUALITY_G729
+     * @see ImsStreamMediaProfile#AUDIO_QUALITY_EVS_NB
+     * @see ImsStreamMediaProfile#AUDIO_QUALITY_EVS_WB
+     * @see ImsStreamMediaProfile#AUDIO_QUALITY_EVS_SWB
+     * @see ImsStreamMediaProfile#AUDIO_QUALITY_EVS_FB
+     */
+    public int getCodecType() {
+        return mCodecType;
+    }
+
+    // Parcelable things
+    @Override
+    public String toString() {
+        return "CallQuality: {downlinkCallQualityLevel=" + mDownlinkCallQualityLevel
+                + " uplinkCallQualityLevel=" + mUplinkCallQualityLevel
+                + " callDuration=" + mCallDuration
+                + " numRtpPacketsTransmitted=" + mNumRtpPacketsTransmitted
+                + " numRtpPacketsReceived=" + mNumRtpPacketsReceived
+                + " numRtpPacketsTransmittedLost=" + mNumRtpPacketsTransmittedLost
+                + " numRtpPacketsNotReceived=" + mNumRtpPacketsNotReceived
+                + " averageRelativeJitter=" + mAverageRelativeJitter
+                + " maxRelativeJitter=" + mMaxRelativeJitter
+                + " averageRoundTripTime=" + mAverageRoundTripTime
+                + " codecType=" + mCodecType
+                + "}";
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(
+                mDownlinkCallQualityLevel,
+                mUplinkCallQualityLevel,
+                mCallDuration,
+                mNumRtpPacketsTransmitted,
+                mNumRtpPacketsReceived,
+                mNumRtpPacketsTransmittedLost,
+                mNumRtpPacketsNotReceived,
+                mAverageRelativeJitter,
+                mMaxRelativeJitter,
+                mAverageRoundTripTime,
+                mCodecType);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (o == null || !(o instanceof CallQuality) || hashCode() != o.hashCode()) {
+            return false;
+        }
+
+        if (this == o) {
+            return true;
+        }
+
+        CallQuality s = (CallQuality) o;
+
+        return (mDownlinkCallQualityLevel == s.mDownlinkCallQualityLevel
+                && mUplinkCallQualityLevel == s.mUplinkCallQualityLevel
+                && mCallDuration == s.mCallDuration
+                && mNumRtpPacketsTransmitted == s.mNumRtpPacketsTransmitted
+                && mNumRtpPacketsReceived == s.mNumRtpPacketsReceived
+                && mNumRtpPacketsTransmittedLost == s.mNumRtpPacketsTransmittedLost
+                && mNumRtpPacketsNotReceived == s.mNumRtpPacketsNotReceived
+                && mAverageRelativeJitter == s.mAverageRelativeJitter
+                && mMaxRelativeJitter == s.mMaxRelativeJitter
+                && mAverageRoundTripTime == s.mAverageRoundTripTime
+                && mCodecType == s.mCodecType);
+    }
+
+    /**
+     * {@link Parcelable#describeContents}
+     */
+    public @Parcelable.ContentsFlags int describeContents() {
+        return 0;
+    }
+
+    /**
+     * {@link Parcelable#writeToParcel}
+     */
+    public void writeToParcel(Parcel dest, @Parcelable.WriteFlags int flags) {
+        dest.writeInt(mDownlinkCallQualityLevel);
+        dest.writeInt(mUplinkCallQualityLevel);
+        dest.writeInt(mCallDuration);
+        dest.writeInt(mNumRtpPacketsTransmitted);
+        dest.writeInt(mNumRtpPacketsReceived);
+        dest.writeInt(mNumRtpPacketsTransmittedLost);
+        dest.writeInt(mNumRtpPacketsNotReceived);
+        dest.writeInt(mAverageRelativeJitter);
+        dest.writeInt(mMaxRelativeJitter);
+        dest.writeInt(mAverageRoundTripTime);
+        dest.writeInt(mCodecType);
+    }
+
+    public static final Parcelable.Creator<CallQuality> CREATOR = new Parcelable.Creator() {
+        public CallQuality createFromParcel(Parcel in) {
+            return new CallQuality(in);
+        }
+
+        public CallQuality[] newArray(int size) {
+            return new CallQuality[size];
+        }
+    };
+}
diff --git a/telephony/java/android/telephony/CellConfigLte.java b/telephony/java/android/telephony/CellConfigLte.java
index 35769f0..eafbfbc 100644
--- a/telephony/java/android/telephony/CellConfigLte.java
+++ b/telephony/java/android/telephony/CellConfigLte.java
@@ -34,6 +34,11 @@
     }
 
     /** @hide */
+    public CellConfigLte(android.hardware.radio.V1_4.CellConfigLte cellConfig) {
+        mIsEndcAvailable = cellConfig.isEndcAvailable;
+    }
+
+    /** @hide */
     public CellConfigLte(boolean isEndcAvailable) {
         mIsEndcAvailable = isEndcAvailable;
     }
diff --git a/telephony/java/android/telephony/CellInfo.java b/telephony/java/android/telephony/CellInfo.java
index b761bd7..8ce5c54 100644
--- a/telephony/java/android/telephony/CellInfo.java
+++ b/telephony/java/android/telephony/CellInfo.java
@@ -19,8 +19,10 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.UnsupportedAppUsage;
+import android.hardware.radio.V1_4.CellInfo.Info;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.os.SystemClock;
 
 import com.android.internal.annotations.VisibleForTesting;
 
@@ -318,6 +320,13 @@
     }
 
     /** @hide */
+    protected CellInfo(android.hardware.radio.V1_4.CellInfo ci) {
+        this.mRegistered = ci.isRegistered;
+        this.mTimeStamp = SystemClock.elapsedRealtimeNanos();
+        this.mCellConnectionStatus = ci.connectionStatus;
+    }
+
+    /** @hide */
     public static CellInfo create(android.hardware.radio.V1_0.CellInfo ci) {
         if (ci == null) return null;
         switch(ci.cellInfoType) {
@@ -342,4 +351,17 @@
             default: return null;
         }
     }
+
+    /** @hide */
+    public static CellInfo create(android.hardware.radio.V1_4.CellInfo ci) {
+        if (ci == null) return null;
+        switch (ci.info.getDiscriminator()) {
+            case Info.hidl_discriminator.gsm: return new CellInfoGsm(ci);
+            case Info.hidl_discriminator.cdma: return new CellInfoCdma(ci);
+            case Info.hidl_discriminator.lte: return new CellInfoLte(ci);
+            case Info.hidl_discriminator.wcdma: return new CellInfoWcdma(ci);
+            case Info.hidl_discriminator.tdscdma: return new CellInfoTdscdma(ci);
+            default: return null;
+        }
+    }
 }
diff --git a/telephony/java/android/telephony/CellInfoCdma.java b/telephony/java/android/telephony/CellInfoCdma.java
index c9f07da..4440108 100644
--- a/telephony/java/android/telephony/CellInfoCdma.java
+++ b/telephony/java/android/telephony/CellInfoCdma.java
@@ -67,6 +67,15 @@
             new CellSignalStrengthCdma(cic.signalStrengthCdma, cic.signalStrengthEvdo);
     }
 
+    /** @hide */
+    public CellInfoCdma(android.hardware.radio.V1_4.CellInfo ci) {
+        super(ci);
+        final android.hardware.radio.V1_2.CellInfoCdma cic = ci.info.cdma();
+        mCellIdentityCdma = new CellIdentityCdma(cic.cellIdentityCdma);
+        mCellSignalStrengthCdma =
+                new CellSignalStrengthCdma(cic.signalStrengthCdma, cic.signalStrengthEvdo);
+    }
+
     @Override
     public CellIdentityCdma getCellIdentity() {
         return mCellIdentityCdma;
diff --git a/telephony/java/android/telephony/CellInfoGsm.java b/telephony/java/android/telephony/CellInfoGsm.java
index ad16dfa..248adfc 100644
--- a/telephony/java/android/telephony/CellInfoGsm.java
+++ b/telephony/java/android/telephony/CellInfoGsm.java
@@ -63,6 +63,14 @@
         mCellSignalStrengthGsm = new CellSignalStrengthGsm(cig.signalStrengthGsm);
     }
 
+    /** @hide */
+    public CellInfoGsm(android.hardware.radio.V1_4.CellInfo ci) {
+        super(ci);
+        final android.hardware.radio.V1_2.CellInfoGsm cig = ci.info.gsm();
+        mCellIdentityGsm = new CellIdentityGsm(cig.cellIdentityGsm);
+        mCellSignalStrengthGsm = new CellSignalStrengthGsm(cig.signalStrengthGsm);
+    }
+
     @Override
     public CellIdentityGsm getCellIdentity() {
         return mCellIdentityGsm;
diff --git a/telephony/java/android/telephony/CellInfoLte.java b/telephony/java/android/telephony/CellInfoLte.java
index 7593831..8e8ce8a 100644
--- a/telephony/java/android/telephony/CellInfoLte.java
+++ b/telephony/java/android/telephony/CellInfoLte.java
@@ -70,6 +70,15 @@
         mCellConfig = new CellConfigLte();
     }
 
+    /** @hide */
+    public CellInfoLte(android.hardware.radio.V1_4.CellInfo ci) {
+        super(ci);
+        final android.hardware.radio.V1_4.CellInfoLte cil = ci.info.lte();
+        mCellIdentityLte = new CellIdentityLte(cil.base.cellIdentityLte);
+        mCellSignalStrengthLte = new CellSignalStrengthLte(cil.base.signalStrengthLte);
+        mCellConfig = new CellConfigLte(cil.cellConfig);
+    }
+
     @Override
     public CellIdentityLte getCellIdentity() {
         if (DBG) log("getCellIdentity: " + mCellIdentityLte);
diff --git a/telephony/java/android/telephony/CellInfoTdscdma.java b/telephony/java/android/telephony/CellInfoTdscdma.java
index a8c49b7..2ab38fb 100644
--- a/telephony/java/android/telephony/CellInfoTdscdma.java
+++ b/telephony/java/android/telephony/CellInfoTdscdma.java
@@ -64,6 +64,14 @@
         mCellSignalStrengthTdscdma = new CellSignalStrengthTdscdma(cit.signalStrengthTdscdma);
     }
 
+    /** @hide */
+    public CellInfoTdscdma(android.hardware.radio.V1_4.CellInfo ci) {
+        super(ci);
+        final android.hardware.radio.V1_2.CellInfoTdscdma cit = ci.info.tdscdma();
+        mCellIdentityTdscdma = new CellIdentityTdscdma(cit.cellIdentityTdscdma);
+        mCellSignalStrengthTdscdma = new CellSignalStrengthTdscdma(cit.signalStrengthTdscdma);
+    }
+
     @Override public CellIdentityTdscdma getCellIdentity() {
         return mCellIdentityTdscdma;
     }
diff --git a/telephony/java/android/telephony/CellInfoWcdma.java b/telephony/java/android/telephony/CellInfoWcdma.java
index a427e80..65e0470 100644
--- a/telephony/java/android/telephony/CellInfoWcdma.java
+++ b/telephony/java/android/telephony/CellInfoWcdma.java
@@ -63,6 +63,14 @@
         mCellSignalStrengthWcdma = new CellSignalStrengthWcdma(ciw.signalStrengthWcdma);
     }
 
+    /** @hide */
+    public CellInfoWcdma(android.hardware.radio.V1_4.CellInfo ci) {
+        super(ci);
+        final android.hardware.radio.V1_2.CellInfoWcdma ciw = ci.info.wcdma();
+        mCellIdentityWcdma = new CellIdentityWcdma(ciw.cellIdentityWcdma);
+        mCellSignalStrengthWcdma = new CellSignalStrengthWcdma(ciw.signalStrengthWcdma);
+    }
+
     @Override
     public CellIdentityWcdma getCellIdentity() {
         return mCellIdentityWcdma;
diff --git a/telephony/java/android/telephony/ICellInfoCallback.aidl b/telephony/java/android/telephony/ICellInfoCallback.aidl
index 7fb62682..ee3c1b1 100644
--- a/telephony/java/android/telephony/ICellInfoCallback.aidl
+++ b/telephony/java/android/telephony/ICellInfoCallback.aidl
@@ -16,6 +16,7 @@
 
 package android.telephony;
 
+import android.os.ParcelableException;
 import android.telephony.CellInfo;
 
 import java.util.List;
@@ -27,4 +28,5 @@
 oneway interface ICellInfoCallback
 {
     void onCellInfo(in List<CellInfo> state);
+    void onError(in int errorCode, in ParcelableException detail);
 }
diff --git a/telephony/java/android/telephony/PhoneStateListener.java b/telephony/java/android/telephony/PhoneStateListener.java
index e27b385..c816701 100644
--- a/telephony/java/android/telephony/PhoneStateListener.java
+++ b/telephony/java/android/telephony/PhoneStateListener.java
@@ -296,7 +296,7 @@
 
     /**
      *  Listen for changes to preferred data subId.
-     *  See {@link SubscriptionManager#setPreferredData(int)}
+     *  See {@link SubscriptionManager#setPreferredDataSubId(int)}
      *  for more details.
      *
      *  @see #onPreferredDataSubIdChanged
@@ -335,6 +335,18 @@
     @SystemApi
     public static final int LISTEN_CALL_DISCONNECT_CAUSES                  = 0x02000000;
 
+    /**
+     * Listen for changes to the call attributes of a currently active call.
+     * {@more}
+     * Requires Permission: {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE
+     * READ_PRECISE_PHONE_STATE}
+     *
+     * @see #onCallAttributesChanged
+     * @hide
+     */
+    @SystemApi
+    public static final int LISTEN_CALL_ATTRIBUTES_CHANGED                 = 0x04000000;
+
     /*
      * Subscription used to listen to the phone state changes
      * @hide
@@ -683,6 +695,17 @@
     }
 
     /**
+     * Callback invoked when the call attributes changes. Requires
+     * the READ_PRIVILEGED_PHONE_STATE permission.
+     * @param callAttributes the call attributes
+     * @hide
+     */
+    @SystemApi
+    public void onCallAttributesChanged(CallAttributes callAttributes) {
+        // default implementation empty
+    }
+
+    /**
      * Callback invoked when modem radio power state changes. Requires
      * the READ_PRIVILEGED_PHONE_STATE permission.
      * @param state the modem radio power state
@@ -941,6 +964,14 @@
                     () -> mExecutor.execute(() -> psl.onRadioPowerStateChanged(state)));
         }
 
+        public void onCallAttributesChanged(CallAttributes callAttributes) {
+            PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
+            if (psl == null) return;
+
+            Binder.withCleanCallingIdentity(
+                    () -> mExecutor.execute(() -> psl.onCallAttributesChanged(callAttributes)));
+        }
+
         public void onPreferredDataSubIdChanged(int subId) {
             PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
             if (psl == null) return;
diff --git a/telephony/java/android/telephony/SignalStrength.java b/telephony/java/android/telephony/SignalStrength.java
index 2271069..e77042d 100644
--- a/telephony/java/android/telephony/SignalStrength.java
+++ b/telephony/java/android/telephony/SignalStrength.java
@@ -24,6 +24,8 @@
 import android.os.Parcelable;
 import android.os.PersistableBundle;
 
+import java.util.ArrayList;
+import java.util.List;
 import java.util.Objects;
 
 /**
@@ -178,6 +180,35 @@
         return mLte;
     }
 
+    /**
+     * Returns a List of CellSignalStrength Components of this SignalStrength Report.
+     *
+     * Use this API to access underlying
+     * {@link android.telephony#CellSignalStrength CellSignalStrength} objects that provide more
+     * granular information about the SignalStrength report. Only valid (non-empty)
+     * CellSignalStrengths will be returned. The order of any returned elements is not guaranteed,
+     * and the list may contain more than one instance of a CellSignalStrength type.
+     *
+     * @return a List of CellSignalStrength or an empty List if there are no valid measurements.
+     *
+     * @see android.telephony#CellSignalStrength
+     * @see android.telephony#CellSignalStrengthNr
+     * @see android.telephony#CellSignalStrengthLte
+     * @see android.telephony#CellSignalStrengthTdscdma
+     * @see android.telephony#CellSignalStrengthWcdma
+     * @see android.telephony#CellSignalStrengthCdma
+     * @see android.telephony#CellSignalStrengthGsm
+     */
+    public @NonNull List<CellSignalStrength> getCellSignalStrengths() {
+        List<CellSignalStrength> cssList = new ArrayList<>(2); // Usually have 2 or fewer elems
+        if (mLte.isValid()) cssList.add(mLte);
+        if (mCdma.isValid()) cssList.add(mCdma);
+        if (mTdscdma.isValid()) cssList.add(mTdscdma);
+        if (mWcdma.isValid()) cssList.add(mWcdma);
+        if (mGsm.isValid()) cssList.add(mGsm);
+        return cssList;
+    }
+
     /** @hide */
     public void updateLevel(PersistableBundle cc, ServiceState ss) {
         mLteRsrpBoost = ss.getLteEarfcnRsrpBoost();
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 34f7abd..9fa4c3c 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -2453,10 +2453,39 @@
      *
      */
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
-    public void setPreferredData(int subId) {
-        if (VDBG) logd("[setPreferredData]+ subId:" + subId);
-        setSubscriptionPropertyHelper(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID,
-                "setPreferredData", (iSub)-> iSub.setPreferredData(subId));
+    public void setPreferredDataSubscriptionId(int subId) {
+        if (VDBG) logd("[setPreferredDataSubscriptionId]+ subId:" + subId);
+        setSubscriptionPropertyHelper(DEFAULT_SUBSCRIPTION_ID, "setPreferredDataSubscriptionId",
+                (iSub)-> iSub.setPreferredDataSubscriptionId(subId));
+    }
+
+    /**
+     * Get which subscription is preferred for cellular data.
+     * It's also usually the subscription we set up internet connection on.
+     *
+     * PreferredData overwrites user setting of default data subscription. And it's used
+     * by AlternativeNetworkService or carrier apps to switch primary and CBRS
+     * subscription dynamically in multi-SIM devices.
+     *
+     * @return preferred subscription id for cellular data. {@link DEFAULT_SUBSCRIPTION_ID} if
+     * there's no prefered subscription.
+     *
+     * @hide
+     *
+     */
+    @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    public int getPreferredDataSubscriptionId() {
+        int preferredSubId = SubscriptionManager.DEFAULT_SUBSCRIPTION_ID;
+        try {
+            ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+            if (iSub != null) {
+                preferredSubId = iSub.getPreferredDataSubscriptionId();
+            }
+        } catch (RemoteException ex) {
+            // ignore it
+        }
+
+        return preferredSubId;
     }
 
     /**
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index b48a1ce..3311218 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -4898,19 +4898,53 @@
     /** Callback for providing asynchronous {@link CellInfo} on request */
     public abstract static class CellInfoCallback {
         /**
-         * Response to
+         * Success response to
          * {@link android.telephony.TelephonyManager#requestCellInfoUpdate requestCellInfoUpdate()}.
          *
-         * <p>Invoked when there is a response to
+         * Invoked when there is a response to
          * {@link android.telephony.TelephonyManager#requestCellInfoUpdate requestCellInfoUpdate()}
          * to provide a list of {@link CellInfo}. If no {@link CellInfo} is available then an empty
-         * list will be provided. If an error occurs, null will be provided.
+         * list will be provided. If an error occurs, null will be provided unless the onError
+         * callback is overridden.
          *
          * @param cellInfo a list of {@link CellInfo}, an empty list, or null.
          *
          * {@see android.telephony.TelephonyManager#getAllCellInfo getAllCellInfo()}
          */
-        public abstract void onCellInfo(List<CellInfo> cellInfo);
+        public abstract void onCellInfo(@NonNull List<CellInfo> cellInfo);
+
+        /** @hide */
+        @Retention(RetentionPolicy.SOURCE)
+        @IntDef(prefix = {"ERROR_"}, value = {ERROR_TIMEOUT, ERROR_MODEM_ERROR})
+        public @interface CellInfoCallbackError {}
+
+        /**
+         * The system timed out waiting for a response from the Radio.
+         */
+        public static final int ERROR_TIMEOUT = 1;
+
+        /**
+         * The modem returned a failure.
+         */
+        public static final int ERROR_MODEM_ERROR = 2;
+
+        /**
+         * Error response to
+         * {@link android.telephony.TelephonyManager#requestCellInfoUpdate requestCellInfoUpdate()}.
+         *
+         * Invoked when an error condition prevents updated {@link CellInfo} from being fetched
+         * and returned from the modem. Callers of requestCellInfoUpdate() should override this
+         * function to receive detailed status information in the event of an error. By default,
+         * this function will invoke onCellInfo() with null.
+         *
+         * @param errorCode an error code indicating the type of failure.
+         * @param detail a Throwable object with additional detail regarding the failure if
+         *     available, otherwise null.
+         */
+        public void onError(@CellInfoCallbackError int errorCode, @Nullable Throwable detail) {
+            // By default, simply invoke the success callback with an empty list.
+            onCellInfo(new ArrayList<CellInfo>());
+        }
     };
 
     /**
@@ -4937,6 +4971,12 @@
                             Binder.withCleanCallingIdentity(() ->
                                     executor.execute(() -> callback.onCellInfo(cellInfo)));
                         }
+
+                        public void onError(int errorCode, android.os.ParcelableException detail) {
+                            Binder.withCleanCallingIdentity(() ->
+                                    executor.execute(() -> callback.onError(
+                                            errorCode, detail.getCause())));
+                        }
                     }, getOpPackageName());
 
         } catch (RemoteException ex) {
@@ -4971,6 +5011,12 @@
                             Binder.withCleanCallingIdentity(() ->
                                     executor.execute(() -> callback.onCellInfo(cellInfo)));
                         }
+
+                        public void onError(int errorCode, android.os.ParcelableException detail) {
+                            Binder.withCleanCallingIdentity(() ->
+                                    executor.execute(() -> callback.onError(
+                                            errorCode, detail.getCause())));
+                        }
                     }, getOpPackageName(), workSource);
         } catch (RemoteException ex) {
         }
@@ -9864,10 +9910,11 @@
         try {
             IOns iOpportunisticNetworkService = getIOns();
             if (iOpportunisticNetworkService != null) {
-                return iOpportunisticNetworkService.setPreferredData(subId, pkgForDebug);
+                return iOpportunisticNetworkService
+                        .setPreferredDataSubscriptionId(subId, pkgForDebug);
             }
         } catch (RemoteException ex) {
-            Rlog.e(TAG, "setPreferredData RemoteException", ex);
+            Rlog.e(TAG, "setPreferredDataSubscriptionId RemoteException", ex);
         }
         return false;
     }
@@ -9888,10 +9935,10 @@
         try {
             IOns iOpportunisticNetworkService = getIOns();
             if (iOpportunisticNetworkService != null) {
-                subId = iOpportunisticNetworkService.getPreferredData(pkgForDebug);
+                subId = iOpportunisticNetworkService.getPreferredDataSubscriptionId(pkgForDebug);
             }
         } catch (RemoteException ex) {
-            Rlog.e(TAG, "getPreferredData RemoteException", ex);
+            Rlog.e(TAG, "getPreferredDataSubscriptionId RemoteException", ex);
         }
         return subId;
     }
@@ -9916,7 +9963,7 @@
         boolean ret = false;
         try {
             IOns iOpportunisticNetworkService = getIOns();
-            if (iOpportunisticNetworkService != null) {
+            if (iOpportunisticNetworkService != null && availableNetworks != null) {
                 ret = iOpportunisticNetworkService.updateAvailableNetworks(availableNetworks,
                         pkgForDebug);
             }
diff --git a/telephony/java/android/telephony/euicc/EuiccManager.java b/telephony/java/android/telephony/euicc/EuiccManager.java
index cc9befe..42a788d 100644
--- a/telephony/java/android/telephony/euicc/EuiccManager.java
+++ b/telephony/java/android/telephony/euicc/EuiccManager.java
@@ -30,6 +30,7 @@
 import android.os.Bundle;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.telephony.TelephonyManager;
 
 import com.android.internal.telephony.euicc.IEuiccController;
 
@@ -40,7 +41,11 @@
  * EuiccManager is the application interface to eUICCs, or eSIMs/embedded SIMs.
  *
  * <p>You do not instantiate this class directly; instead, you retrieve an instance through
- * {@link Context#getSystemService(String)} and {@link Context#EUICC_SERVICE}.
+ * {@link Context#getSystemService(String)} and {@link Context#EUICC_SERVICE}. This instance will be
+ * created using the default eUICC.
+ *
+ * <p>On a device with multiple eUICCs, you may want to create multiple EuiccManagers. To do this
+ * you can call {@link #createForCardId}.
  *
  * <p>See {@link #isEnabled} before attempting to use these APIs.
  */
@@ -318,10 +323,29 @@
     public static final int EUICC_OTA_STATUS_UNAVAILABLE = 5;
 
     private final Context mContext;
+    private final int mCardId;
 
     /** @hide */
     public EuiccManager(Context context) {
         mContext = context;
+        TelephonyManager tm = (TelephonyManager)
+                context.getSystemService(Context.TELEPHONY_SERVICE);
+        mCardId = tm.getCardIdForDefaultEuicc();
+    }
+
+    /** @hide */
+    private EuiccManager(Context context, int cardId) {
+        mContext = context;
+        mCardId = cardId;
+    }
+
+    /**
+     * Create a new EuiccManager object pinned to the given card ID.
+     *
+     * @return an EuiccManager that uses the given card ID for all calls.
+     */
+    public EuiccManager createForCardId(int cardId) {
+        return new EuiccManager(mContext, cardId);
     }
 
     /**
@@ -344,7 +368,8 @@
      * Returns the EID identifying the eUICC hardware.
      *
      * <p>Requires that the calling app has carrier privileges on the active subscription on the
-     * eUICC.
+     * current eUICC. A calling app with carrier privileges for one eUICC may not necessarily have
+     * access to the EID of another eUICC.
      *
      * @return the EID. May be null if {@link #isEnabled()} is false or the eUICC is not ready.
      */
@@ -354,7 +379,7 @@
             return null;
         }
         try {
-            return getIEuiccController().getEid();
+            return getIEuiccController().getEid(mCardId);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -377,7 +402,7 @@
             return EUICC_OTA_STATUS_UNAVAILABLE;
         }
         try {
-            return getIEuiccController().getOtaStatus();
+            return getIEuiccController().getOtaStatus(mCardId);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -387,10 +412,10 @@
      * Attempt to download the given {@link DownloadableSubscription}.
      *
      * <p>Requires the {@code android.Manifest.permission#WRITE_EMBEDDED_SUBSCRIPTIONS} permission,
-     * or the calling app must be authorized to manage both the currently-active subscription and
-     * the subscription to be downloaded according to the subscription metadata. Without the former,
-     * an {@link #EMBEDDED_SUBSCRIPTION_RESULT_RESOLVABLE_ERROR} will be returned in the callback
-     * intent to prompt the user to accept the download.
+     * or the calling app must be authorized to manage both the currently-active subscription on the
+     * current eUICC and the subscription to be downloaded according to the subscription metadata.
+     * Without the former, an {@link #EMBEDDED_SUBSCRIPTION_RESULT_RESOLVABLE_ERROR} will be
+     * returned in the callback intent to prompt the user to accept the download.
      *
      * @param subscription the subscription to download.
      * @param switchAfterDownload if true, the profile will be activated upon successful download.
@@ -404,7 +429,7 @@
             return;
         }
         try {
-            getIEuiccController().downloadSubscription(subscription, switchAfterDownload,
+            getIEuiccController().downloadSubscription(mCardId, subscription, switchAfterDownload,
                     mContext.getOpPackageName(), null /* resolvedBundle */, callbackIntent);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
@@ -471,7 +496,7 @@
             return;
         }
         try {
-            getIEuiccController().continueOperation(resolutionIntent, resolutionExtras);
+            getIEuiccController().continueOperation(mCardId, resolutionIntent, resolutionExtras);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -503,8 +528,8 @@
             return;
         }
         try {
-            getIEuiccController().getDownloadableSubscriptionMetadata(
-                    subscription, mContext.getOpPackageName(), callbackIntent);
+            getIEuiccController().getDownloadableSubscriptionMetadata(mCardId, subscription,
+                    mContext.getOpPackageName(), callbackIntent);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -533,7 +558,7 @@
             return;
         }
         try {
-            getIEuiccController().getDefaultDownloadableSubscriptionList(
+            getIEuiccController().getDefaultDownloadableSubscriptionList(mCardId,
                     mContext.getOpPackageName(), callbackIntent);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
@@ -552,7 +577,7 @@
             return null;
         }
         try {
-            return getIEuiccController().getEuiccInfo();
+            return getIEuiccController().getEuiccInfo(mCardId);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -578,7 +603,7 @@
             return;
         }
         try {
-            getIEuiccController().deleteSubscription(
+            getIEuiccController().deleteSubscription(mCardId,
                     subscriptionId, mContext.getOpPackageName(), callbackIntent);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
@@ -606,7 +631,7 @@
             return;
         }
         try {
-            getIEuiccController().switchToSubscription(
+            getIEuiccController().switchToSubscription(mCardId,
                     subscriptionId, mContext.getOpPackageName(), callbackIntent);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
@@ -632,7 +657,7 @@
             return;
         }
         try {
-            getIEuiccController().updateSubscriptionNickname(
+            getIEuiccController().updateSubscriptionNickname(mCardId,
                     subscriptionId, nickname, mContext.getOpPackageName(), callbackIntent);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
@@ -656,7 +681,7 @@
             return;
         }
         try {
-            getIEuiccController().eraseSubscriptions(callbackIntent);
+            getIEuiccController().eraseSubscriptions(mCardId, callbackIntent);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -686,7 +711,7 @@
             return;
         }
         try {
-            getIEuiccController().retainSubscriptionsForFactoryReset(callbackIntent);
+            getIEuiccController().retainSubscriptionsForFactoryReset(mCardId, callbackIntent);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
diff --git a/telephony/java/android/telephony/ims/ImsCallSession.java b/telephony/java/android/telephony/ims/ImsCallSession.java
index df903cc2..397d5d9 100644
--- a/telephony/java/android/telephony/ims/ImsCallSession.java
+++ b/telephony/java/android/telephony/ims/ImsCallSession.java
@@ -443,6 +443,13 @@
         public void callSessionRttMessageReceived(String rttMessage) {
             // no-op
         }
+
+        /**
+         * While in call, there has been a change in RTT audio indicator.
+         */
+        public void callSessionRttAudioIndicatorChanged(ImsStreamMediaProfile profile) {
+            // no-op
+        }
     }
 
     private final IImsCallSession miSession;
@@ -1397,6 +1404,16 @@
                 mListener.callSessionRttMessageReceived(rttMessage);
             }
         }
+
+        /**
+         * While in call, there has been a change in RTT audio indicator.
+         */
+        @Override
+        public void callSessionRttAudioIndicatorChanged(ImsStreamMediaProfile profile) {
+            if (mListener != null) {
+                mListener.callSessionRttAudioIndicatorChanged(profile);
+            }
+        }
     }
 
     /**
diff --git a/telephony/java/android/telephony/ims/ImsCallSessionListener.java b/telephony/java/android/telephony/ims/ImsCallSessionListener.java
index a7f124a..a4696a3 100644
--- a/telephony/java/android/telephony/ims/ImsCallSessionListener.java
+++ b/telephony/java/android/telephony/ims/ImsCallSessionListener.java
@@ -599,5 +599,18 @@
             throw new RuntimeException(e);
         }
     }
+
+    /**
+     * While in call, there has been a change in RTT audio indicator.
+     *
+     * @param profile updated ImsStreamMediaProfile
+     */
+    public void callSessionRttAudioIndicatorChanged(ImsStreamMediaProfile profile) {
+        try {
+            mListener.callSessionRttAudioIndicatorChanged(profile);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
 }
 
diff --git a/telephony/java/android/telephony/ims/ImsStreamMediaProfile.java b/telephony/java/android/telephony/ims/ImsStreamMediaProfile.java
index 52d72b5..837ef54 100644
--- a/telephony/java/android/telephony/ims/ImsStreamMediaProfile.java
+++ b/telephony/java/android/telephony/ims/ImsStreamMediaProfile.java
@@ -97,6 +97,9 @@
     // Rtt related information
     /** @hide */
     public int mRttMode;
+    // RTT Audio Speech Indicator
+    /** @hide */
+    public boolean mHasRttAudioSpeech = false;
 
     /** @hide */
     public ImsStreamMediaProfile(Parcel in) {
@@ -197,7 +200,8 @@
                 ", audioDirection=" + mAudioDirection +
                 ", videoQuality=" + mVideoQuality +
                 ", videoDirection=" + mVideoDirection +
-                ", rttMode=" + mRttMode + " }";
+                ", rttMode=" + mRttMode +
+                ", hasRttAudioSpeech=" + mHasRttAudioSpeech + " }";
     }
 
     @Override
@@ -212,6 +216,7 @@
         out.writeInt(mVideoQuality);
         out.writeInt(mVideoDirection);
         out.writeInt(mRttMode);
+        out.writeBoolean(mHasRttAudioSpeech);
     }
 
     private void readFromParcel(Parcel in) {
@@ -220,6 +225,7 @@
         mVideoQuality = in.readInt();
         mVideoDirection = in.readInt();
         mRttMode = in.readInt();
+        mHasRttAudioSpeech = in.readBoolean();
     }
 
     public static final Creator<ImsStreamMediaProfile> CREATOR =
@@ -250,6 +256,10 @@
         mRttMode = rttMode;
     }
 
+    public void setRttAudioSpeech(boolean audioOn) {
+        mHasRttAudioSpeech = audioOn;
+    }
+
     public int getAudioQuality() {
         return mAudioQuality;
     }
@@ -269,4 +279,8 @@
     public int getRttMode() {
         return mRttMode;
     }
+
+    public boolean getRttAudioSpeech() {
+        return mHasRttAudioSpeech;
+    }
 }
diff --git a/telephony/java/android/telephony/ims/aidl/IImsCallSessionListener.aidl b/telephony/java/android/telephony/ims/aidl/IImsCallSessionListener.aidl
index f25b4b1..d0b31e1 100644
--- a/telephony/java/android/telephony/ims/aidl/IImsCallSessionListener.aidl
+++ b/telephony/java/android/telephony/ims/aidl/IImsCallSessionListener.aidl
@@ -138,4 +138,10 @@
      * @param rttMessage Received RTT message
      */
     void callSessionRttMessageReceived(in String rttMessage);
+
+    /*
+     * While in call, there has been a change in RTT audio indicator.
+     * @param profile updated ImsStreamMediaProfile
+     */
+    void callSessionRttAudioIndicatorChanged(in ImsStreamMediaProfile profile);
 }
diff --git a/telephony/java/android/telephony/ims/compat/stub/ImsCallSessionImplBase.java b/telephony/java/android/telephony/ims/compat/stub/ImsCallSessionImplBase.java
index 23de2fd..bc58e46 100644
--- a/telephony/java/android/telephony/ims/compat/stub/ImsCallSessionImplBase.java
+++ b/telephony/java/android/telephony/ims/compat/stub/ImsCallSessionImplBase.java
@@ -591,5 +591,11 @@
         public void callSessionRttMessageReceived(String rttMessage) throws RemoteException {
             mNewListener.callSessionRttMessageReceived(rttMessage);
         }
+
+        @Override
+        public void callSessionRttAudioIndicatorChanged(ImsStreamMediaProfile profile)
+                throws RemoteException {
+            mNewListener.callSessionRttAudioIndicatorChanged(profile);
+        }
     }
 }
diff --git a/telephony/java/com/android/ims/internal/IImsCallSessionListener.aidl b/telephony/java/com/android/ims/internal/IImsCallSessionListener.aidl
index a8e8b7dd..bbb27af 100644
--- a/telephony/java/com/android/ims/internal/IImsCallSessionListener.aidl
+++ b/telephony/java/com/android/ims/internal/IImsCallSessionListener.aidl
@@ -152,4 +152,10 @@
      * @param rttMessage Received RTT message
      */
     void callSessionRttMessageReceived(in String rttMessage);
+
+    /*
+     * While in call, there has been a change in RTT audio indicator.
+     * @param profile updated ImsStreamMediaProfile
+     */
+    void callSessionRttAudioIndicatorChanged(in ImsStreamMediaProfile profile);
 }
diff --git a/telephony/java/com/android/internal/telephony/DctConstants.java b/telephony/java/com/android/internal/telephony/DctConstants.java
index 96f7a1b..3408291 100644
--- a/telephony/java/com/android/internal/telephony/DctConstants.java
+++ b/telephony/java/com/android/internal/telephony/DctConstants.java
@@ -92,6 +92,7 @@
     public static final int EVENT_DATA_RECONNECT = BASE + 47;
     public static final int EVENT_ROAMING_SETTING_CHANGE = BASE + 48;
     public static final int EVENT_DATA_SERVICE_BINDING_CHANGED = BASE + 49;
+    public static final int EVENT_DEVICE_PROVISIONED_CHANGE = BASE + 50;
 
     /***** Constants *****/
 
diff --git a/telephony/java/com/android/internal/telephony/IOns.aidl b/telephony/java/com/android/internal/telephony/IOns.aidl
index d6779f1..0e3d12b 100755
--- a/telephony/java/com/android/internal/telephony/IOns.aidl
+++ b/telephony/java/com/android/internal/telephony/IOns.aidl
@@ -66,7 +66,7 @@
      * @return true if request is accepted, else false.
      *
      */
-    boolean setPreferredData(int subId, String callingPackage);
+    boolean setPreferredDataSubscriptionId(int subId, String callingPackage);
 
     /**
      * Get preferred opportunistic data subscription Id
@@ -78,7 +78,7 @@
      * subscription id
      *
      */
-    int getPreferredData(String callingPackage);
+    int getPreferredDataSubscriptionId(String callingPackage);
 
     /**
      * Update availability of a list of networks in the current location.
diff --git a/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl b/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl
index 00cf9c3..3dbebe8 100644
--- a/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl
+++ b/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl
@@ -17,14 +17,15 @@
 package com.android.internal.telephony;
 
 import android.os.Bundle;
-import android.telephony.ServiceState;
-import android.telephony.SignalStrength;
+import android.telephony.CallAttributes;
 import android.telephony.CellInfo;
 import android.telephony.DataConnectionRealTimeInfo;
 import android.telephony.PhoneCapability;
 import android.telephony.PhysicalChannelConfig;
 import android.telephony.PreciseCallState;
 import android.telephony.PreciseDataConnectionState;
+import android.telephony.ServiceState;
+import android.telephony.SignalStrength;
 import android.telephony.emergency.EmergencyNumber;
 
 oneway interface IPhoneStateListener {
@@ -54,6 +55,7 @@
     void onPhoneCapabilityChanged(in PhoneCapability capability);
     void onPreferredDataSubIdChanged(in int subId);
     void onRadioPowerStateChanged(in int state);
+    void onCallAttributesChanged(in CallAttributes callAttributes);
     void onEmergencyNumberListChanged(in Map emergencyNumberList);
     void onCallDisconnectCauseChanged(in int disconnectCause, in int preciseDisconnectCause);
 }
diff --git a/telephony/java/com/android/internal/telephony/ISub.aidl b/telephony/java/com/android/internal/telephony/ISub.aidl
index d169b7d..577ddbd 100755
--- a/telephony/java/com/android/internal/telephony/ISub.aidl
+++ b/telephony/java/com/android/internal/telephony/ISub.aidl
@@ -200,7 +200,15 @@
      * @hide
      *
      */
-    int setPreferredData(int subId);
+    int setPreferredDataSubscriptionId(int subId);
+
+    /**
+     * Get which subscription is preferred for cellular data.
+     *
+     * @hide
+     *
+     */
+    int getPreferredDataSubscriptionId();
 
     /**
      * Get User downloaded Profiles.
diff --git a/telephony/java/com/android/internal/telephony/TelephonyIntents.java b/telephony/java/com/android/internal/telephony/TelephonyIntents.java
index 2a648bd..8523554 100644
--- a/telephony/java/com/android/internal/telephony/TelephonyIntents.java
+++ b/telephony/java/com/android/internal/telephony/TelephonyIntents.java
@@ -501,4 +501,18 @@
      */
     public static final String ACTION_LINE1_NUMBER_ERROR_DETECTED =
             "com.android.internal.telephony.ACTION_LINE1_NUMBER_ERROR_DETECTED";
+
+    /**
+     * Broadcast action to notify radio bug.
+     *
+     * Requires the READ_PRIVILEGED_PHONE_STATE permission.
+     *
+     * @hide
+     */
+    public static final String ACTION_REPORT_RADIO_BUG =
+            "com.android.internal.telephony.ACTION_REPORT_RADIO_BUG";
+
+    // ACTION_REPORT_RADIO_BUG extra keys
+    public static final String EXTRA_SLOT_ID = "slotId";
+    public static final String EXTRA_RADIO_BUG_TYPE = "radioBugType";
 }
diff --git a/telephony/java/com/android/internal/telephony/euicc/IEuiccController.aidl b/telephony/java/com/android/internal/telephony/euicc/IEuiccController.aidl
index dd40d56..14a36c8 100644
--- a/telephony/java/com/android/internal/telephony/euicc/IEuiccController.aidl
+++ b/telephony/java/com/android/internal/telephony/euicc/IEuiccController.aidl
@@ -24,22 +24,25 @@
 
 /** @hide */
 interface IEuiccController {
-    oneway void continueOperation(in Intent resolutionIntent, in Bundle resolutionExtras);
-    oneway void getDownloadableSubscriptionMetadata(in DownloadableSubscription subscription,
+    oneway void continueOperation(int cardId, in Intent resolutionIntent,
+            in Bundle resolutionExtras);
+    oneway void getDownloadableSubscriptionMetadata(int cardId,
+            in DownloadableSubscription subscription,
         String callingPackage, in PendingIntent callbackIntent);
-    oneway void getDefaultDownloadableSubscriptionList(
+    oneway void getDefaultDownloadableSubscriptionList(int cardId,
         String callingPackage, in PendingIntent callbackIntent);
-    String getEid();
-    int getOtaStatus();
-    oneway void downloadSubscription(in DownloadableSubscription subscription,
-        boolean switchAfterDownload, String callingPackage, in Bundle resolvedBundle, in PendingIntent callbackIntent);
-    EuiccInfo getEuiccInfo();
-    oneway void deleteSubscription(int subscriptionId, String callingPackage,
+    String getEid(int cardId);
+    int getOtaStatus(int cardId);
+    oneway void downloadSubscription(int cardId, in DownloadableSubscription subscription,
+        boolean switchAfterDownload, String callingPackage, in Bundle resolvedBundle,
         in PendingIntent callbackIntent);
-    oneway void switchToSubscription(int subscriptionId, String callingPackage,
+    EuiccInfo getEuiccInfo(int cardId);
+    oneway void deleteSubscription(int cardId, int subscriptionId, String callingPackage,
         in PendingIntent callbackIntent);
-    oneway void updateSubscriptionNickname(int subscriptionId, String nickname,
+    oneway void switchToSubscription(int cardId, int subscriptionId, String callingPackage,
+        in PendingIntent callbackIntent);
+    oneway void updateSubscriptionNickname(int cardId, int subscriptionId, String nickname,
         String callingPackage, in PendingIntent callbackIntent);
-    oneway void eraseSubscriptions(in PendingIntent callbackIntent);
-    oneway void retainSubscriptionsForFactoryReset(in PendingIntent callbackIntent);
-}
\ No newline at end of file
+    oneway void eraseSubscriptions(int cardId, in PendingIntent callbackIntent);
+    oneway void retainSubscriptionsForFactoryReset(int cardId, in PendingIntent callbackIntent);
+}
diff --git a/test-base/api/current.txt b/test-base/api/current.txt
index 7ebd6aa..91fcca5 100644
--- a/test-base/api/current.txt
+++ b/test-base/api/current.txt
@@ -48,6 +48,9 @@
     method public abstract void startTiming(boolean);
   }
 
+  public abstract deprecated class RepetitiveTest implements java.lang.annotation.Annotation {
+  }
+
   public abstract deprecated class UiThreadTest implements java.lang.annotation.Annotation {
   }
 
diff --git a/test-base/src/android/test/RepetitiveTest.java b/test-base/src/android/test/RepetitiveTest.java
index 6a7130e..13e89d2 100644
--- a/test-base/src/android/test/RepetitiveTest.java
+++ b/test-base/src/android/test/RepetitiveTest.java
@@ -26,8 +26,10 @@
  * When the annotation is present, the test method is executed the number of times specified by
  * numIterations and defaults to 1.
  *
- * {@hide} Not needed for public API.
+ * @deprecated New tests should be written using the
+ * <a href="{@docRoot}tools/testing-support-library/index.html">Android Testing Support Library</a>.
  */
+@Deprecated
 @Target(ElementType.METHOD)
 @Retention(RetentionPolicy.RUNTIME)
 public @interface RepetitiveTest {
@@ -37,4 +39,4 @@
      * @return The total number of iterations, the default is 1.
      */
     int numIterations() default 1;
-}
\ No newline at end of file
+}
diff --git a/test-legacy/Android.bp b/test-legacy/Android.bp
index 833c714..a69f422 100644
--- a/test-legacy/Android.bp
+++ b/test-legacy/Android.bp
@@ -25,7 +25,7 @@
     static_libs: [
         "android.test.base-minus-junit",
         "android.test.runner-minus-junit",
-        "android.test.mock.impl",
+        "android.test.mock_static",
     ],
 
     no_framework_libs: true,
diff --git a/test-mock/Android.bp b/test-mock/Android.bp
index e1d6e01..43b765d 100644
--- a/test-mock/Android.bp
+++ b/test-mock/Android.bp
@@ -30,3 +30,19 @@
     srcs_lib_whitelist_pkgs: ["android"],
     compile_dex: true,
 }
+
+// Build the android.test.mock_static library
+// ==========================================
+// This is only intended for inclusion in the legacy-android-test.
+// Must not be used elewhere.
+java_library_static {
+    name: "android.test.mock_static",
+
+    java_version: "1.8",
+    srcs: ["src/**/*.java"],
+
+    no_framework_libs: true,
+    libs: [
+        "framework",
+    ],
+}
diff --git a/tests/PackageWatchdog/Android.mk b/tests/PackageWatchdog/Android.mk
index b53f27d..1c1c2a4 100644
--- a/tests/PackageWatchdog/Android.mk
+++ b/tests/PackageWatchdog/Android.mk
@@ -23,7 +23,7 @@
     junit \
     frameworks-base-testutils \
     android-support-test \
-    services
+    services.core
 
 LOCAL_JAVA_LIBRARIES := \
     android.test.runner
diff --git a/tests/RollbackTest/src/com/android/tests/rollback/RollbackTest.java b/tests/RollbackTest/src/com/android/tests/rollback/RollbackTest.java
index 0ccfb19..77cd9d8 100644
--- a/tests/RollbackTest/src/com/android/tests/rollback/RollbackTest.java
+++ b/tests/RollbackTest/src/com/android/tests/rollback/RollbackTest.java
@@ -108,6 +108,10 @@
             }
 
             // The app should not be available for rollback.
+            // TODO: See if there is a way to remove this race condition
+            // between when the app is uninstalled and when the previously
+            // available rollback, if any, is removed.
+            Thread.sleep(1000);
             assertNull(rm.getAvailableRollback(TEST_APP_A));
             assertFalse(rm.getPackagesWithAvailableRollbacks().contains(TEST_APP_A));
 
@@ -125,6 +129,10 @@
             assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_A));
 
             // The app should now be available for rollback.
+            // TODO: See if there is a way to remove this race condition
+            // between when the app is installed and when the rollback
+            // is made available.
+            Thread.sleep(1000);
             assertTrue(rm.getPackagesWithAvailableRollbacks().contains(TEST_APP_A));
             RollbackInfo rollback = rm.getAvailableRollback(TEST_APP_A);
             assertNotNull(rollback);
@@ -182,6 +190,8 @@
 
             RollbackManager rm = RollbackTestUtils.getRollbackManager();
 
+            // TODO: Test this with multi-package rollback, not just single
+            // package rollback.
             // Prep installation of TEST_APP_A
             RollbackTestUtils.uninstall(TEST_APP_A);
             RollbackTestUtils.install("RollbackTestAppAv1.apk", false);
@@ -189,6 +199,10 @@
             assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_A));
 
             // The app should now be available for rollback.
+            // TODO: See if there is a way to remove this race condition
+            // between when the app is installed and when the rollback
+            // is made available.
+            Thread.sleep(1000);
             assertTrue(rm.getPackagesWithAvailableRollbacks().contains(TEST_APP_A));
             RollbackInfo rollback = rm.getAvailableRollback(TEST_APP_A);
             assertNotNull(rollback);
@@ -263,6 +277,10 @@
             assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_A));
 
             // The app should now be available for rollback.
+            // TODO: See if there is a way to remove this race condition
+            // between when the app is installed and when the rollback
+            // is made available.
+            Thread.sleep(1000);
             assertTrue(rm.getPackagesWithAvailableRollbacks().contains(TEST_APP_A));
             RollbackInfo rollback = rm.getAvailableRollback(TEST_APP_A);
             assertNotNull(rollback);
@@ -405,6 +423,10 @@
 
             // Both test apps should now be available for rollback, and the
             // targetPackage returned for rollback should be correct.
+            // TODO: See if there is a way to remove this race condition
+            // between when the app is installed and when the rollback
+            // is made available.
+            Thread.sleep(1000);
             RollbackInfo rollbackA = rm.getAvailableRollback(TEST_APP_A);
             assertNotNull(rollbackA);
             assertEquals(TEST_APP_A, rollbackA.targetPackage.packageName);
@@ -491,7 +513,7 @@
      * TODO: Stop ignoring this test once support for multi-package rollback
      * is implemented.
      */
-    @Ignore @Test
+    @Test
     public void testMultiPackage() throws Exception {
         try {
             RollbackTestUtils.adoptShellPermissionIdentity(
diff --git a/tests/net/java/android/net/LinkPropertiesTest.java b/tests/net/java/android/net/LinkPropertiesTest.java
index f82b380..299fbef 100644
--- a/tests/net/java/android/net/LinkPropertiesTest.java
+++ b/tests/net/java/android/net/LinkPropertiesTest.java
@@ -18,6 +18,7 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
@@ -33,6 +34,9 @@
 import android.system.OsConstants;
 import android.util.ArraySet;
 
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
 import java.net.InetAddress;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -41,9 +45,6 @@
 import java.util.List;
 import java.util.Set;
 
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
 @RunWith(AndroidJUnit4.class)
 @SmallTest
 public class LinkPropertiesTest {
@@ -504,6 +505,40 @@
     }
 
     @Test
+    public void testNat64Prefix() throws Exception {
+        LinkProperties lp = new LinkProperties();
+        lp.addLinkAddress(LINKADDRV4);
+        lp.addLinkAddress(LINKADDRV6);
+
+        assertNull(lp.getNat64Prefix());
+
+        IpPrefix p = new IpPrefix("64:ff9b::/96");
+        lp.setNat64Prefix(p);
+        assertEquals(p, lp.getNat64Prefix());
+
+        p = new IpPrefix("2001:db8:a:b:1:2:3::/96");
+        lp.setNat64Prefix(p);
+        assertEquals(p, lp.getNat64Prefix());
+
+        p = new IpPrefix("2001:db8:a:b:1:2::/80");
+        try {
+            lp.setNat64Prefix(p);
+        } catch (IllegalArgumentException expected) {
+        }
+
+        p = new IpPrefix("64:ff9b::/64");
+        try {
+            lp.setNat64Prefix(p);
+        } catch (IllegalArgumentException expected) {
+        }
+
+        assertEquals(new IpPrefix("2001:db8:a:b:1:2:3::/96"), lp.getNat64Prefix());
+
+        lp.setNat64Prefix(null);
+        assertNull(lp.getNat64Prefix());
+    }
+
+    @Test
     public void testIsProvisioned() {
         LinkProperties lp4 = new LinkProperties();
         assertFalse("v4only:empty", lp4.isProvisioned());
@@ -814,8 +849,20 @@
         assertEquals(new ArraySet<>(expectRemoved), (new ArraySet<>(result.removed)));
     }
 
+    private void assertParcelingIsLossless(LinkProperties source) {
+        Parcel p = Parcel.obtain();
+        source.writeToParcel(p, /* flags */ 0);
+        p.setDataPosition(0);
+        final byte[] marshalled = p.marshall();
+        p = Parcel.obtain();
+        p.unmarshall(marshalled, 0, marshalled.length);
+        p.setDataPosition(0);
+        LinkProperties dest = LinkProperties.CREATOR.createFromParcel(p);
+        assertEquals(source, dest);
+    }
+
     @Test
-    public void testLinkPropertiesParcelable() {
+    public void testLinkPropertiesParcelable() throws Exception {
         LinkProperties source = new LinkProperties();
         source.setInterfaceName(NAME);
         // set 2 link addresses
@@ -833,15 +880,14 @@
 
         source.setMtu(MTU);
 
-        Parcel p = Parcel.obtain();
-        source.writeToParcel(p, /* flags */ 0);
-        p.setDataPosition(0);
-        final byte[] marshalled = p.marshall();
-        p = Parcel.obtain();
-        p.unmarshall(marshalled, 0, marshalled.length);
-        p.setDataPosition(0);
-        LinkProperties dest = LinkProperties.CREATOR.createFromParcel(p);
+        source.setNat64Prefix(new IpPrefix("2001:db8:1:2:64:64::/96"));
 
-        assertEquals(source, dest);
+        assertParcelingIsLossless(source);
+    }
+
+    @Test
+    public void testParcelUninitialized() throws Exception {
+        LinkProperties empty = new LinkProperties();
+        assertParcelingIsLossless(empty);
     }
 }
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index bf39644..2a92a7d 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -4683,7 +4683,7 @@
         mCellNetworkAgent.sendLinkProperties(cellLp);
         mCellNetworkAgent.connect(true);
         networkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
-        verify(mNetworkManagementService, times(1)).startClatd(MOBILE_IFNAME);
+        verify(mMockNetd, times(1)).clatdStart(MOBILE_IFNAME);
         Nat464Xlat clat = mService.getNat464Xlat(mCellNetworkAgent);
 
         // Clat iface up, expect stack link updated.
@@ -4710,7 +4710,7 @@
         mCellNetworkAgent.sendLinkProperties(cellLp);
         waitForIdle();
         networkCallback.expectCallback(CallbackState.LINK_PROPERTIES, mCellNetworkAgent);
-        verify(mNetworkManagementService, times(1)).stopClatd(MOBILE_IFNAME);
+        verify(mMockNetd, times(1)).clatdStop(MOBILE_IFNAME);
 
         // Clat iface removed, expect linkproperties revert to original one
         clat.interfaceRemoved(CLAT_PREFIX + MOBILE_IFNAME);
diff --git a/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java b/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java
index 4c52d81..9578ded 100644
--- a/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java
+++ b/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java
@@ -32,11 +32,13 @@
 import android.content.Context;
 import android.content.res.Resources;
 import android.net.ConnectivityManager;
+import android.net.INetd;
 import android.net.Network;
 import android.net.NetworkCapabilities;
 import android.net.NetworkInfo;
 import android.net.NetworkMisc;
 import android.net.NetworkStack;
+import android.os.INetworkManagementService;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
 import android.text.format.DateUtils;
@@ -66,6 +68,8 @@
     LingerMonitor mMonitor;
 
     @Mock ConnectivityService mConnService;
+    @Mock INetd mNetd;
+    @Mock INetworkManagementService mNMS;
     @Mock Context mCtx;
     @Mock NetworkMisc mMisc;
     @Mock NetworkNotificationManager mNotifier;
@@ -352,7 +356,7 @@
         caps.addCapability(0);
         caps.addTransportType(transport);
         NetworkAgentInfo nai = new NetworkAgentInfo(null, null, new Network(netId), info, null,
-                caps, 50, mCtx, null, mMisc, mConnService);
+                caps, 50, mCtx, null, mMisc, mConnService, mNetd, mNMS);
         nai.everValidated = true;
         return nai;
     }
diff --git a/tests/net/java/com/android/server/connectivity/Nat464XlatTest.java b/tests/net/java/com/android/server/connectivity/Nat464XlatTest.java
index bf42412..07b1d05 100644
--- a/tests/net/java/com/android/server/connectivity/Nat464XlatTest.java
+++ b/tests/net/java/com/android/server/connectivity/Nat464XlatTest.java
@@ -17,9 +17,7 @@
 package com.android.server.connectivity;
 
 import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
-import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.eq;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
@@ -27,6 +25,7 @@
 import static org.mockito.Mockito.when;
 
 import android.net.ConnectivityManager;
+import android.net.INetd;
 import android.net.InterfaceConfiguration;
 import android.net.LinkAddress;
 import android.net.LinkProperties;
@@ -57,6 +56,7 @@
 
     @Mock ConnectivityService mConnectivity;
     @Mock NetworkMisc mMisc;
+    @Mock INetd mNetd;
     @Mock INetworkManagementService mNms;
     @Mock InterfaceConfiguration mConfig;
     @Mock NetworkAgentInfo mNai;
@@ -65,7 +65,7 @@
     Handler mHandler;
 
     Nat464Xlat makeNat464Xlat() {
-        return new Nat464Xlat(mNms, mNai);
+        return new Nat464Xlat(mNai, mNetd, mNms);
     }
 
     @Before
@@ -129,7 +129,7 @@
         nat.start();
 
         verify(mNms).registerObserver(eq(nat));
-        verify(mNms).startClatd(eq(BASE_IFACE));
+        verify(mNetd).clatdStart(eq(BASE_IFACE));
 
         // Stacked interface up notification arrives.
         nat.interfaceLinkStateChanged(STACKED_IFACE, true);
@@ -144,7 +144,7 @@
         // ConnectivityService stops clat (Network disconnects, IPv4 addr appears, ...).
         nat.stop();
 
-        verify(mNms).stopClatd(eq(BASE_IFACE));
+        verify(mNetd).clatdStop(eq(BASE_IFACE));
 
         // Stacked interface removed notification arrives.
         nat.interfaceRemoved(STACKED_IFACE);
@@ -156,7 +156,7 @@
         assertFalse(c.getValue().getAllInterfaceNames().contains(STACKED_IFACE));
         assertIdle(nat);
 
-        verifyNoMoreInteractions(mNms, mConnectivity);
+        verifyNoMoreInteractions(mNetd, mNms, mConnectivity);
     }
 
     @Test
@@ -168,7 +168,7 @@
         nat.start();
 
         verify(mNms).registerObserver(eq(nat));
-        verify(mNms).startClatd(eq(BASE_IFACE));
+        verify(mNetd).clatdStart(eq(BASE_IFACE));
 
         // Stacked interface up notification arrives.
         nat.interfaceLinkStateChanged(STACKED_IFACE, true);
@@ -185,7 +185,7 @@
         mLooper.dispatchNext();
 
         verify(mNms).unregisterObserver(eq(nat));
-        verify(mNms).stopClatd(eq(BASE_IFACE));
+        verify(mNetd).clatdStop(eq(BASE_IFACE));
         verify(mConnectivity, times(2)).handleUpdateLinkProperties(eq(mNai), c.capture());
         assertTrue(c.getValue().getStackedLinks().isEmpty());
         assertFalse(c.getValue().getAllInterfaceNames().contains(STACKED_IFACE));
@@ -194,7 +194,7 @@
         // ConnectivityService stops clat: no-op.
         nat.stop();
 
-        verifyNoMoreInteractions(mNms, mConnectivity);
+        verifyNoMoreInteractions(mNetd, mNms, mConnectivity);
     }
 
     @Test
@@ -205,13 +205,13 @@
         nat.start();
 
         verify(mNms).registerObserver(eq(nat));
-        verify(mNms).startClatd(eq(BASE_IFACE));
+        verify(mNetd).clatdStart(eq(BASE_IFACE));
 
         // ConnectivityService immediately stops clat (Network disconnects, IPv4 addr appears, ...)
         nat.stop();
 
         verify(mNms).unregisterObserver(eq(nat));
-        verify(mNms).stopClatd(eq(BASE_IFACE));
+        verify(mNetd).clatdStop(eq(BASE_IFACE));
         assertIdle(nat);
 
         // In-flight interface up notification arrives: no-op
@@ -225,7 +225,7 @@
 
         assertIdle(nat);
 
-        verifyNoMoreInteractions(mNms, mConnectivity);
+        verifyNoMoreInteractions(mNetd, mNms, mConnectivity);
     }
 
     @Test
@@ -236,16 +236,16 @@
         nat.start();
 
         verify(mNms).registerObserver(eq(nat));
-        verify(mNms).startClatd(eq(BASE_IFACE));
+        verify(mNetd).clatdStart(eq(BASE_IFACE));
 
         // ConnectivityService immediately stops clat (Network disconnects, IPv4 addr appears, ...)
         nat.stop();
 
         verify(mNms).unregisterObserver(eq(nat));
-        verify(mNms).stopClatd(eq(BASE_IFACE));
+        verify(mNetd).clatdStop(eq(BASE_IFACE));
         assertIdle(nat);
 
-        verifyNoMoreInteractions(mNms, mConnectivity);
+        verifyNoMoreInteractions(mNetd, mNms, mConnectivity);
     }
 
     static void assertIdle(Nat464Xlat nat) {
diff --git a/tests/net/java/com/android/server/net/ipmemorystore/IpMemoryStoreServiceTest.java b/tests/net/java/com/android/server/net/ipmemorystore/IpMemoryStoreServiceTest.java
index 859a54d..e63c3b0 100644
--- a/tests/net/java/com/android/server/net/ipmemorystore/IpMemoryStoreServiceTest.java
+++ b/tests/net/java/com/android/server/net/ipmemorystore/IpMemoryStoreServiceTest.java
@@ -16,6 +16,9 @@
 
 package com.android.server.net.ipmemorystore;
 
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.doReturn;
+
 import android.content.Context;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
@@ -26,6 +29,8 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
+import java.io.File;
+
 /** Unit tests for {@link IpMemoryStoreServiceTest}. */
 @SmallTest
 @RunWith(AndroidJUnit4.class)
@@ -36,6 +41,7 @@
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
+        doReturn(new File("/tmp/test.db")).when(mMockContext).getDatabasePath(anyString());
     }
 
     @Test
diff --git a/tests/net/java/com/android/server/net/ipmemorystore/RelevanceUtilsTests.java b/tests/net/java/com/android/server/net/ipmemorystore/RelevanceUtilsTests.java
new file mode 100644
index 0000000..8d367e2
--- /dev/null
+++ b/tests/net/java/com/android/server/net/ipmemorystore/RelevanceUtilsTests.java
@@ -0,0 +1,149 @@
+/*
+ * 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.net.ipmemorystore;
+
+import static com.android.server.net.ipmemorystore.RelevanceUtils.CAPPED_RELEVANCE;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/** Unit tests for {@link RelevanceUtils}. */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class RelevanceUtilsTests {
+    @Test
+    public void testComputeRelevanceForTargetDate() {
+        final long dayInMillis = 24L * 60 * 60 * 1000;
+        final long base = 1_000_000L; // any given point in time
+        // Relevance when the network expires in 1000 years must be capped
+        assertEquals(CAPPED_RELEVANCE, RelevanceUtils.computeRelevanceForTargetDate(
+                base + 1000L * dayInMillis, base));
+        // Relevance when expiry is before the date must be 0
+        assertEquals(0, RelevanceUtils.computeRelevanceForTargetDate(base - 1, base));
+        // Make sure the relevance for a given target date is higher if the expiry is further
+        // in the future
+        assertTrue(RelevanceUtils.computeRelevanceForTargetDate(base + 100 * dayInMillis, base)
+                < RelevanceUtils.computeRelevanceForTargetDate(base + 150 * dayInMillis, base));
+
+        // Make sure the relevance falls slower as the expiry is closing in. This is to ensure
+        // the decay is indeed logarithmic.
+        final int relevanceAtExpiry = RelevanceUtils.computeRelevanceForTargetDate(base, base);
+        final int relevance50DaysBeforeExpiry =
+                RelevanceUtils.computeRelevanceForTargetDate(base + 50 * dayInMillis, base);
+        final int relevance100DaysBeforeExpiry =
+                RelevanceUtils.computeRelevanceForTargetDate(base + 100 * dayInMillis, base);
+        final int relevance150DaysBeforeExpiry =
+                RelevanceUtils.computeRelevanceForTargetDate(base + 150 * dayInMillis, base);
+        assertEquals(0, relevanceAtExpiry);
+        assertTrue(relevance50DaysBeforeExpiry - relevanceAtExpiry
+                < relevance100DaysBeforeExpiry - relevance50DaysBeforeExpiry);
+        assertTrue(relevance100DaysBeforeExpiry - relevance50DaysBeforeExpiry
+                < relevance150DaysBeforeExpiry - relevance100DaysBeforeExpiry);
+    }
+
+    @Test
+    public void testIncreaseRelevance() {
+        long expiry = System.currentTimeMillis();
+
+        final long firstBump = RelevanceUtils.bumpExpiryDate(expiry);
+        // Though a few milliseconds might have elapsed, the first bump should push the duration
+        // to days in the future, so unless this test takes literal days between these two lines,
+        // this should always pass.
+        assertTrue(firstBump > expiry);
+
+        expiry = 0;
+        long lastDifference = Long.MAX_VALUE;
+        // The relevance should be capped in at most this many steps. Otherwise, fail.
+        final int steps = 1000;
+        for (int i = 0; i < steps; ++i) {
+            final long newExpiry = RelevanceUtils.bumpExpiryDuration(expiry);
+            if (newExpiry == expiry) {
+                // The relevance should be capped. Make sure it is, then exit without failure.
+                assertEquals(newExpiry, RelevanceUtils.CAPPED_RELEVANCE_LIFETIME_MS);
+                return;
+            }
+            // Make sure the new expiry is further in the future than last time.
+            assertTrue(newExpiry > expiry);
+            // Also check that it was not bumped as much as the last bump, because the
+            // decay must be exponential.
+            assertTrue(newExpiry - expiry < lastDifference);
+            lastDifference = newExpiry - expiry;
+            expiry = newExpiry;
+        }
+        fail("Relevance failed to go to the maximum value after " + steps + " bumps");
+    }
+
+    @Test
+    public void testContinuity() {
+        final long expiry = System.currentTimeMillis();
+
+        // Relevance at expiry and after expiry should be the cap.
+        final int relevanceBeforeMaxLifetime = RelevanceUtils.computeRelevanceForTargetDate(expiry,
+                expiry - (RelevanceUtils.CAPPED_RELEVANCE_LIFETIME_MS + 1_000_000));
+        assertEquals(relevanceBeforeMaxLifetime, CAPPED_RELEVANCE);
+        final int relevanceForMaxLifetime = RelevanceUtils.computeRelevanceForTargetDate(expiry,
+                expiry - RelevanceUtils.CAPPED_RELEVANCE_LIFETIME_MS);
+        assertEquals(relevanceForMaxLifetime, CAPPED_RELEVANCE);
+
+        // If the max relevance is reached at the cap lifetime, one millisecond less than this
+        // should be very close. Strictly speaking this is a bit brittle, but it should be
+        // good enough for the purposes of the memory store.
+        final int relevanceForOneMillisecLessThanCap = RelevanceUtils.computeRelevanceForTargetDate(
+                expiry, expiry - RelevanceUtils.CAPPED_RELEVANCE_LIFETIME_MS + 1);
+        assertTrue(relevanceForOneMillisecLessThanCap <= CAPPED_RELEVANCE);
+        assertTrue(relevanceForOneMillisecLessThanCap >= CAPPED_RELEVANCE - 10);
+
+        // Likewise the relevance one millisecond before expiry should be very close to 0. It's
+        // fine if it rounds down to 0.
+        final int relevanceOneMillisecBeforeExpiry = RelevanceUtils.computeRelevanceForTargetDate(
+                expiry, expiry - 1);
+        assertTrue(relevanceOneMillisecBeforeExpiry <= 10);
+        assertTrue(relevanceOneMillisecBeforeExpiry >= 0);
+
+        final int relevanceAtExpiry = RelevanceUtils.computeRelevanceForTargetDate(expiry, expiry);
+        assertEquals(relevanceAtExpiry, 0);
+        final int relevanceAfterExpiry = RelevanceUtils.computeRelevanceForTargetDate(expiry,
+                expiry + 1_000_000);
+        assertEquals(relevanceAfterExpiry, 0);
+    }
+
+    // testIncreaseRelevance makes sure bumping the expiry continuously always yields a
+    // monotonically increasing date as a side effect, but this tests that the relevance (as
+    // opposed to the expiry date) increases monotonically with increasing periods.
+    @Test
+    public void testMonotonicity() {
+        // Hopefully the relevance is granular enough to give a different value for every one
+        // of this number of steps.
+        final int steps = 40;
+        final long expiry = System.currentTimeMillis();
+
+        int lastRelevance = -1;
+        for (int i = 0; i < steps; ++i) {
+            final long date = expiry - i * (RelevanceUtils.CAPPED_RELEVANCE_LIFETIME_MS / steps);
+            final int relevance = RelevanceUtils.computeRelevanceForTargetDate(expiry, date);
+            assertTrue(relevance > lastRelevance);
+            lastRelevance = relevance;
+        }
+    }
+}
diff --git a/tools/aapt2/Android.bp b/tools/aapt2/Android.bp
index 0580df6..7783e10 100644
--- a/tools/aapt2/Android.bp
+++ b/tools/aapt2/Android.bp
@@ -115,6 +115,7 @@
         "optimize/MultiApkGenerator.cpp",
         "optimize/ResourceDeduper.cpp",
         "optimize/ResourceFilter.cpp",
+        "optimize/ResourcePathShortener.cpp",
         "optimize/VersionCollapser.cpp",
         "process/SymbolTable.cpp",
         "split/TableSplitter.cpp",
diff --git a/tools/aapt2/LoadedApk.cpp b/tools/aapt2/LoadedApk.cpp
index b353ff0..45719ef 100644
--- a/tools/aapt2/LoadedApk.cpp
+++ b/tools/aapt2/LoadedApk.cpp
@@ -223,8 +223,17 @@
     io::IFile* file = iterator->Next();
     std::string path = file->GetSource().path;
 
+    std::string output_path = path;
+    bool is_resource = path.find("res/") == 0;
+    if (is_resource) {
+      auto it = options.shortened_path_map.find(path);
+      if (it != options.shortened_path_map.end()) {
+        output_path = it->second;
+      }
+    }
+
     // Skip resources that are not referenced if requested.
-    if (path.find("res/") == 0 && referenced_resources.find(path) == referenced_resources.end()) {
+    if (is_resource && referenced_resources.find(output_path) == referenced_resources.end()) {
       if (context->IsVerbose()) {
         context->GetDiagnostics()->Note(DiagMessage()
                                         << "Removing resource '" << path << "' from APK.");
@@ -283,7 +292,8 @@
         return false;
       }
     } else {
-      if (!io::CopyFileToArchivePreserveCompression(context, file, path, writer)) {
+      if (!io::CopyFileToArchivePreserveCompression(
+              context, file, output_path, writer)) {
         return false;
       }
     }
diff --git a/tools/aapt2/ResourceUtils.cpp b/tools/aapt2/ResourceUtils.cpp
index c6f9152..ab4805f 100644
--- a/tools/aapt2/ResourceUtils.cpp
+++ b/tools/aapt2/ResourceUtils.cpp
@@ -16,6 +16,7 @@
 
 #include "ResourceUtils.h"
 
+#include <algorithm>
 #include <sstream>
 
 #include "android-base/stringprintf.h"
@@ -503,6 +504,14 @@
   if (entry.first == trimmed_str) {
     return entry.second;
   }
+
+  // Try parsing codename from "[codename].[preview_sdk_fingerprint]" value.
+  const StringPiece::const_iterator begin = std::begin(trimmed_str);
+  const StringPiece::const_iterator end = std::end(trimmed_str);
+  const StringPiece::const_iterator codename_end = std::find(begin, end, '.');
+  if (codename_end != end && entry.first == trimmed_str.substr(begin, codename_end)) {
+    return entry.second;
+  }
   return {};
 }
 
diff --git a/tools/aapt2/ResourceUtils_test.cpp b/tools/aapt2/ResourceUtils_test.cpp
index 5ce4640..9018b0f 100644
--- a/tools/aapt2/ResourceUtils_test.cpp
+++ b/tools/aapt2/ResourceUtils_test.cpp
@@ -16,6 +16,7 @@
 
 #include "ResourceUtils.h"
 
+#include "SdkConstants.h"
 #include "Resource.h"
 #include "test/Test.h"
 
@@ -212,6 +213,17 @@
               Pointee(ValueEq(BinaryPrimitive(Res_value::TYPE_FLOAT, expected_float_flattened))));
 }
 
+TEST(ResourceUtilsTest, ParseSdkVersionWithCodename) {
+  const android::StringPiece codename =
+      GetDevelopmentSdkCodeNameAndVersion().first;
+  const int version = GetDevelopmentSdkCodeNameAndVersion().second;
+
+  EXPECT_THAT(ResourceUtils::ParseSdkVersion(codename), Eq(Maybe<int>(version)));
+  EXPECT_THAT(
+      ResourceUtils::ParseSdkVersion(codename.to_string() + ".fingerprint"),
+      Eq(Maybe<int>(version)));
+}
+
 TEST(ResourceUtilsTest, StringBuilderWhitespaceRemoval) {
   EXPECT_THAT(ResourceUtils::StringBuilder()
                   .AppendText("    hey guys ")
diff --git a/tools/aapt2/cmd/Optimize.cpp b/tools/aapt2/cmd/Optimize.cpp
index 328b0be..2e6af18 100644
--- a/tools/aapt2/cmd/Optimize.cpp
+++ b/tools/aapt2/cmd/Optimize.cpp
@@ -41,6 +41,7 @@
 #include "optimize/MultiApkGenerator.h"
 #include "optimize/ResourceDeduper.h"
 #include "optimize/ResourceFilter.h"
+#include "optimize/ResourcePathShortener.h"
 #include "optimize/VersionCollapser.h"
 #include "split/TableSplitter.h"
 #include "util/Files.h"
@@ -52,6 +53,7 @@
 using ::android::ResTable_config;
 using ::android::StringPiece;
 using ::android::base::ReadFileToString;
+using ::android::base::WriteStringToFile;
 using ::android::base::StringAppendF;
 using ::android::base::StringPrintf;
 
@@ -143,6 +145,21 @@
       return 1;
     }
 
+    if (options_.shorten_resource_paths) {
+      ResourcePathShortener shortener(options_.table_flattener_options.shortened_path_map);
+      if (!shortener.Consume(context_, apk->GetResourceTable())) {
+        context_->GetDiagnostics()->Error(DiagMessage() << "failed shortening resource paths");
+        return 1;
+      }
+      if (options_.shortened_paths_map_path
+          && !WriteShortenedPathsMap(options_.table_flattener_options.shortened_path_map,
+                                      options_.shortened_paths_map_path.value())) {
+        context_->GetDiagnostics()->Error(DiagMessage()
+                                          << "failed to write shortened resource paths to file");
+        return 1;
+      }
+    }
+
     // Adjust the SplitConstraints so that their SDK version is stripped if it is less than or
     // equal to the minSdk.
     options_.split_constraints =
@@ -264,6 +281,15 @@
                                         ArchiveEntry::kAlign, writer);
   }
 
+  bool WriteShortenedPathsMap(const std::map<std::string, std::string> &path_map,
+                               const std::string &file_path) {
+    std::stringstream ss;
+    for (auto it = path_map.cbegin(); it != path_map.cend(); ++it) {
+      ss << it->first << " -> " << it->second << "\n";
+    }
+    return WriteStringToFile(ss.str(), file_path);
+  }
+
   OptimizeOptions options_;
   OptimizeContext* context_;
 };
diff --git a/tools/aapt2/cmd/Optimize.h b/tools/aapt2/cmd/Optimize.h
index d07305b..7f4a3ed 100644
--- a/tools/aapt2/cmd/Optimize.h
+++ b/tools/aapt2/cmd/Optimize.h
@@ -55,6 +55,12 @@
   // Set of artifacts to keep when generating multi-APK splits. If the list is empty, all artifacts
   // are kept and will be written as output.
   std::unordered_set<std::string> kept_artifacts;
+
+  // Whether or not to shorten resource paths in the APK.
+  bool shorten_resource_paths;
+
+  // Path to the output map of original resource paths to shortened paths.
+  Maybe<std::string> shortened_paths_map_path;
 };
 
 class OptimizeCommand : public Command {
@@ -101,6 +107,12 @@
     AddOptionalSwitch("--enable-resource-obfuscation",
         "Enables obfuscation of key string pool to single value",
         &options_.table_flattener_options.collapse_key_stringpool);
+    AddOptionalSwitch("--enable-resource-path-shortening",
+        "Enables shortening of the path of the resources inside the APK.",
+        &options_.shorten_resource_paths);
+    AddOptionalFlag("--resource-path-shortening-map",
+        "Path to output the map of old resource paths to shortened paths.",
+        &options_.shortened_paths_map_path);
     AddOptionalSwitch("-v", "Enables verbose logging", &verbose_);
   }
 
@@ -109,6 +121,9 @@
  private:
   OptimizeOptions options_;
 
+  bool WriteObfuscatedPathsMap(const std::map<std::string, std::string> &path_map,
+                               const std::string &file_path);
+
   Maybe<std::string> config_path_;
   Maybe<std::string> whitelist_path_;
   Maybe<std::string> resources_config_path_;
@@ -122,4 +137,4 @@
 
 }// namespace aapt
 
-#endif //AAPT2_OPTIMIZE_H
\ No newline at end of file
+#endif //AAPT2_OPTIMIZE_H
diff --git a/tools/aapt2/format/binary/BinaryResourceParser.cpp b/tools/aapt2/format/binary/BinaryResourceParser.cpp
index c496ff0..7d4c6f3 100644
--- a/tools/aapt2/format/binary/BinaryResourceParser.cpp
+++ b/tools/aapt2/format/binary/BinaryResourceParser.cpp
@@ -42,6 +42,19 @@
 
 namespace {
 
+static std::u16string strcpy16_dtoh(const char16_t* src, size_t len) {
+  size_t utf16_len = strnlen16(src, len);
+  if (utf16_len == 0) {
+    return {};
+  }
+  std::u16string dst;
+  dst.resize(utf16_len);
+  for (size_t i = 0; i < utf16_len; i++) {
+    dst[i] = util::DeviceToHost16(src[i]);
+  }
+  return dst;
+}
+
 // Visitor that converts a reference's resource ID to a resource name, given a mapping from
 // resource ID to resource name.
 class ReferenceIdToNameVisitor : public DescendingValueVisitor {
@@ -176,12 +189,8 @@
   }
 
   // Extract the package name.
-  size_t len = strnlen16((const char16_t*)package_header->name, arraysize(package_header->name));
-  std::u16string package_name;
-  package_name.resize(len);
-  for (size_t i = 0; i < len; i++) {
-    package_name[i] = util::DeviceToHost16(package_header->name[i]);
-  }
+  std::u16string package_name = strcpy16_dtoh((const char16_t*)package_header->name,
+                                              arraysize(package_header->name));
 
   ResourceTablePackage* package =
       table_->CreatePackage(util::Utf16ToUtf8(package_name), static_cast<uint8_t>(package_id));
@@ -435,6 +444,11 @@
   }
 
   auto overlayable = std::make_shared<Overlayable>();
+  overlayable->name = util::Utf16ToUtf8(strcpy16_dtoh((const char16_t*)header->name,
+                                                      arraysize(header->name)));
+  overlayable->actor = util::Utf16ToUtf8(strcpy16_dtoh((const char16_t*)header->actor,
+                                                       arraysize(header->name)));
+  overlayable->source = source_.WithLine(0);
 
   ResChunkPullParser parser(GetChunkData(chunk),
                             GetChunkDataLen(chunk));
diff --git a/tools/aapt2/format/binary/TableFlattener.cpp b/tools/aapt2/format/binary/TableFlattener.cpp
index 931d57b..c4ecbaf 100644
--- a/tools/aapt2/format/binary/TableFlattener.cpp
+++ b/tools/aapt2/format/binary/TableFlattener.cpp
@@ -217,9 +217,10 @@
   size_t entry_count_ = 0;
 };
 
-struct PolicyChunk {
-  uint32_t policy_flags;
-  std::set<ResourceId> ids;
+struct OverlayableChunk {
+  std::string actor;
+  Source source;
+  std::map<OverlayableItem::PolicyFlags, std::set<ResourceId>> policy_ids;
 };
 
 class PackageFlattener {
@@ -421,8 +422,9 @@
     return sorted_entries;
   }
 
-  void FlattenOverlayable(BigBuffer* buffer) {
-    std::vector<PolicyChunk> policies;
+  bool FlattenOverlayable(BigBuffer* buffer) {
+    std::set<ResourceId> seen_ids;
+    std::map<std::string, OverlayableChunk> overlayable_chunks;
 
     CHECK(bool(package_->id)) << "package must have an ID set when flattening <overlayable>";
     for (auto& type : package_->types) {
@@ -433,79 +435,119 @@
           continue;
         }
 
-        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 & OverlayableItem::Policy::kSystem) {
-          policy_flags |= ResTable_overlayable_policy_header::POLICY_SYSTEM_PARTITION;
-        }
-        if (overlayable.policies & OverlayableItem::Policy::kVendor) {
-          policy_flags |= ResTable_overlayable_policy_header::POLICY_VENDOR_PARTITION;
-        }
-        if (overlayable.policies & OverlayableItem::Policy::kProduct) {
-          policy_flags |= ResTable_overlayable_policy_header::POLICY_PRODUCT_PARTITION;
-        }
-        if (overlayable.policies & OverlayableItem::Policy::kProductServices) {
-          policy_flags |= ResTable_overlayable_policy_header::POLICY_PRODUCT_SERVICES_PARTITION;
+        OverlayableItem& item = entry->overlayable_item.value();
+
+        // Resource ids should only appear once in the resource table
+        ResourceId id = android::make_resid(package_->id.value(), type->id.value(),
+                                            entry->id.value());
+        CHECK(seen_ids.find(id) == seen_ids.end())
+            << "multiple overlayable definitions found for resource "
+            << ResourceName(package_->name, type->type, entry->name).to_string();
+        seen_ids.insert(id);
+
+        // Find the overlayable chunk with the specified name
+        OverlayableChunk* overlayable_chunk = nullptr;
+        auto iter = overlayable_chunks.find(item.overlayable->name);
+        if (iter == overlayable_chunks.end()) {
+          OverlayableChunk chunk{item.overlayable->actor, item.overlayable->source};
+          overlayable_chunk =
+              &overlayable_chunks.insert({item.overlayable->name, chunk}).first->second;
+        } else {
+          OverlayableChunk& chunk = iter->second;
+          if (!(chunk.source == item.overlayable->source)) {
+            // The name of an overlayable set of resources must be unique
+            context_->GetDiagnostics()->Error(DiagMessage(item.overlayable->source)
+                                                  << "duplicate overlayable name"
+                                                  << item.overlayable->name << "'");
+            context_->GetDiagnostics()->Error(DiagMessage(chunk.source)
+                                                  << "previous declaration here");
+            return false;
+          }
+
+          CHECK(chunk.actor == item.overlayable->actor);
+          overlayable_chunk = &chunk;
         }
 
-        if (overlayable.policies == OverlayableItem::Policy::kNone) {
+        uint32_t policy_flags = 0;
+        if (item.policies == OverlayableItem::Policy::kNone) {
           // Encode overlayable entries defined without a policy as publicly overlayable
           policy_flags |= ResTable_overlayable_policy_header::POLICY_PUBLIC;
-        }
-
-        // Find the overlayable policy chunk with the same policies as the entry
-        PolicyChunk* policy_chunk = nullptr;
-        for (PolicyChunk& policy : policies) {
-          if (policy.policy_flags == policy_flags) {
-            policy_chunk = &policy;
-            break;
+        } else {
+          if (item.policies & OverlayableItem::Policy::kPublic) {
+            policy_flags |= ResTable_overlayable_policy_header::POLICY_PUBLIC;
+          }
+          if (item.policies & OverlayableItem::Policy::kSystem) {
+            policy_flags |= ResTable_overlayable_policy_header::POLICY_SYSTEM_PARTITION;
+          }
+          if (item.policies & OverlayableItem::Policy::kVendor) {
+            policy_flags |= ResTable_overlayable_policy_header::POLICY_VENDOR_PARTITION;
+          }
+          if (item.policies & OverlayableItem::Policy::kProduct) {
+            policy_flags |= ResTable_overlayable_policy_header::POLICY_PRODUCT_PARTITION;
+          }
+          if (item.policies & OverlayableItem::Policy::kProductServices) {
+            policy_flags |= ResTable_overlayable_policy_header::POLICY_PRODUCT_SERVICES_PARTITION;
           }
         }
 
-        // Create a new policy chunk if an existing one with the same policy cannot be found
-        if (policy_chunk == nullptr) {
-          PolicyChunk p;
-          p.policy_flags = policy_flags;
-          policies.push_back(p);
-          policy_chunk = &policies.back();
+        auto policy = overlayable_chunk->policy_ids.find(policy_flags);
+        if (policy != overlayable_chunk->policy_ids.end()) {
+          policy->second.insert(id);
+        } else {
+          overlayable_chunk->policy_ids.insert(
+              std::make_pair(policy_flags, std::set<ResourceId>{id}));
         }
-
-        policy_chunk->ids.insert(android::make_resid(package_->id.value(), type->id.value(),
-                                                     entry->id.value()));
       }
     }
 
-    if (policies.empty()) {
-      // Only write the overlayable chunk if the APK has overlayable entries
-      return;
-    }
+    for (auto& overlayable_pair : overlayable_chunks) {
+      std::string name = overlayable_pair.first;
+      OverlayableChunk& overlayable = overlayable_pair.second;
 
-    ChunkWriter writer(buffer);
-    writer.StartChunk<ResTable_overlayable_header>(RES_TABLE_OVERLAYABLE_TYPE);
-
-    // Write each policy block for the overlayable
-    for (PolicyChunk& policy : policies) {
-      ChunkWriter policy_writer(buffer);
-      ResTable_overlayable_policy_header* policy_type =
-          policy_writer.StartChunk<ResTable_overlayable_policy_header>(
-              RES_TABLE_OVERLAYABLE_POLICY_TYPE);
-      policy_type->policy_flags = util::HostToDevice32(policy.policy_flags);
-      policy_type->entry_count = util::HostToDevice32(static_cast<uint32_t>(policy.ids.size()));
-
-      // Write the ids after the policy header
-      ResTable_ref* id_block = policy_writer.NextBlock<ResTable_ref>(policy.ids.size());
-      for (const ResourceId& id : policy.ids) {
-        id_block->ident = util::HostToDevice32(id.id);
-        id_block++;
+      // Write the header of the overlayable chunk
+      ChunkWriter overlayable_writer(buffer);
+      auto* overlayable_type =
+          overlayable_writer.StartChunk<ResTable_overlayable_header>(RES_TABLE_OVERLAYABLE_TYPE);
+      if (name.size() >= arraysize(overlayable_type->name)) {
+        diag_->Error(DiagMessage() << "overlayable name '" << name
+                                   << "' exceeds maximum length ("
+                                   << arraysize(overlayable_type->name)
+                                   << " utf16 characters)");
+        return false;
       }
+      strcpy16_htod(overlayable_type->name, arraysize(overlayable_type->name),
+                    util::Utf8ToUtf16(name));
 
-      policy_writer.Finish();
+      if (overlayable.actor.size() >= arraysize(overlayable_type->actor)) {
+        diag_->Error(DiagMessage() << "overlayable name '" << overlayable.actor
+                                   << "' exceeds maximum length ("
+                                   << arraysize(overlayable_type->actor)
+                                   << " utf16 characters)");
+        return false;
+      }
+      strcpy16_htod(overlayable_type->actor, arraysize(overlayable_type->actor),
+                    util::Utf8ToUtf16(overlayable.actor));
+
+      // Write each policy block for the overlayable
+      for (auto& policy_ids : overlayable.policy_ids) {
+        ChunkWriter policy_writer(buffer);
+        auto* policy_type = policy_writer.StartChunk<ResTable_overlayable_policy_header>(
+            RES_TABLE_OVERLAYABLE_POLICY_TYPE);
+        policy_type->policy_flags = util::HostToDevice32(static_cast<uint32_t>(policy_ids.first));
+        policy_type->entry_count = util::HostToDevice32(static_cast<uint32_t>(
+                                                            policy_ids.second.size()));
+        // Write the ids after the policy header
+        auto* id_block = policy_writer.NextBlock<ResTable_ref>(policy_ids.second.size());
+        for (const ResourceId& id : policy_ids.second) {
+          id_block->ident = util::HostToDevice32(id.id);
+          id_block++;
+        }
+        policy_writer.Finish();
+      }
+      overlayable_writer.Finish();
     }
 
-    writer.Finish();
+    return true;
   }
 
   bool FlattenTypeSpec(ResourceTableType* type, std::vector<ResourceEntry*>* sorted_entries,
diff --git a/tools/aapt2/format/binary/TableFlattener.h b/tools/aapt2/format/binary/TableFlattener.h
index 635cb21..71330e3 100644
--- a/tools/aapt2/format/binary/TableFlattener.h
+++ b/tools/aapt2/format/binary/TableFlattener.h
@@ -46,6 +46,9 @@
 
   // When true, sort the entries in the values string pool by priority and configuration.
   bool sort_stringpool_entries = true;
+
+  // Map from original resource paths to shortened resource paths.
+  std::map<std::string, std::string> shortened_path_map;
 };
 
 class TableFlattener : public IResourceTableConsumer {
diff --git a/tools/aapt2/format/binary/TableFlattener_test.cpp b/tools/aapt2/format/binary/TableFlattener_test.cpp
index a5fb6fd..18fecf6 100644
--- a/tools/aapt2/format/binary/TableFlattener_test.cpp
+++ b/tools/aapt2/format/binary/TableFlattener_test.cpp
@@ -657,36 +657,36 @@
 TEST_F(TableFlattenerTest, FlattenMultipleOverlayablePolicies) {
   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;
+  OverlayableItem overlayable_item_zero(overlayable);
+  overlayable_item_zero.policies |= OverlayableItem::Policy::kProduct;
+  overlayable_item_zero.policies |= OverlayableItem::Policy::kSystem;
+  overlayable_item_zero.policies |= OverlayableItem::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;
+  OverlayableItem overlayable_item_one(overlayable);
+  overlayable_item_one.policies |= OverlayableItem::Policy::kPublic;
+  overlayable_item_one.policies |= OverlayableItem::Policy::kProductServices;
 
   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;
+  OverlayableItem overlayable_item_two(overlayable);
+  overlayable_item_two.policies |= OverlayableItem::Policy::kProduct;
+  overlayable_item_two.policies |= OverlayableItem::Policy::kSystem;
+  overlayable_item_two.policies |= OverlayableItem::Policy::kVendor;
 
   std::string name_three = "com.app.test:integer/overlayable_three_item";
-  OverlayableItem overlayable_three_item(overlayable);
+  OverlayableItem overlayable_item_three(overlayable);
 
   std::unique_ptr<ResourceTable> table =
       test::ResourceTableBuilder()
           .SetPackageId("com.app.test", 0x7f)
           .AddSimple(name_zero, ResourceId(0x7f020000))
-          .SetOverlayable(name_zero, overlayable_zero_item)
+          .SetOverlayable(name_zero, overlayable_item_zero)
           .AddSimple(name_one, ResourceId(0x7f020001))
-          .SetOverlayable(name_one, overlayable_one_item)
+          .SetOverlayable(name_one, overlayable_item_one)
           .AddSimple(name_two, ResourceId(0x7f020002))
-          .SetOverlayable(name_two, overlayable_two_item)
+          .SetOverlayable(name_two, overlayable_item_two)
           .AddSimple(name_three, ResourceId(0x7f020003))
-          .SetOverlayable(name_three, overlayable_three_item)
+          .SetOverlayable(name_three, overlayable_item_three)
           .Build();
 
   ResourceTable output_table;
@@ -724,6 +724,84 @@
   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);
+  EXPECT_EQ(overlayable_item.overlayable->name, "TestName");
+  EXPECT_EQ(overlayable_item.overlayable->actor, "overlay://theme");
+  EXPECT_EQ(overlayable_item.policies, OverlayableItem::Policy::kPublic);
+}
+
+TEST_F(TableFlattenerTest, FlattenMultipleOverlayable) {
+  auto group = std::make_shared<Overlayable>("TestName", "overlay://theme");
+  std::string name_zero = "com.app.test:integer/overlayable_zero";
+  OverlayableItem overlayable_item_zero(group);
+  overlayable_item_zero.policies |= OverlayableItem::Policy::kProduct;
+  overlayable_item_zero.policies |= OverlayableItem::Policy::kSystem;
+  overlayable_item_zero.policies |= OverlayableItem::Policy::kProductServices;
+
+  auto group_one = std::make_shared<Overlayable>("OtherName", "overlay://customization");
+  std::string name_one = "com.app.test:integer/overlayable_one";
+  OverlayableItem overlayable_item_one(group_one);
+  overlayable_item_one.policies |= OverlayableItem::Policy::kPublic;
+  overlayable_item_one.policies |= OverlayableItem::Policy::kProductServices;
+
+  std::string name_two = "com.app.test:integer/overlayable_two";
+  OverlayableItem overlayable_item_two(group);
+  overlayable_item_two.policies |= OverlayableItem::Policy::kProduct;
+  overlayable_item_two.policies |= OverlayableItem::Policy::kSystem;
+  overlayable_item_two.policies |= OverlayableItem::Policy::kVendor;
+
+  std::string name_three = "com.app.test:integer/overlayable_three";
+  OverlayableItem overlayable_item_three(group_one);
+  std::unique_ptr<ResourceTable> table =
+      test::ResourceTableBuilder()
+          .SetPackageId("com.app.test", 0x7f)
+          .AddSimple(name_zero, ResourceId(0x7f020000))
+          .SetOverlayable(name_zero, overlayable_item_zero)
+          .AddSimple(name_one, ResourceId(0x7f020001))
+          .SetOverlayable(name_one, overlayable_item_one)
+          .AddSimple(name_two, ResourceId(0x7f020002))
+          .SetOverlayable(name_two, overlayable_item_two)
+          .AddSimple(name_three, ResourceId(0x7f020003))
+          .SetOverlayable(name_three, overlayable_item_three)
+          .Build();
+  ResourceTable output_table;
+  ASSERT_TRUE(Flatten(context_.get(), {}, table.get(), &output_table));
+  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_item);
+  OverlayableItem& result_overlayable = search_result.value().entry->overlayable_item.value();
+  EXPECT_EQ(result_overlayable.overlayable->name, "TestName");
+  EXPECT_EQ(result_overlayable.overlayable->actor, "overlay://theme");
+  EXPECT_EQ(result_overlayable.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_item);
+  result_overlayable = search_result.value().entry->overlayable_item.value();
+  EXPECT_EQ(result_overlayable.overlayable->name, "OtherName");
+  EXPECT_EQ(result_overlayable.overlayable->actor, "overlay://customization");
+  EXPECT_EQ(result_overlayable.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_item);
+  result_overlayable = search_result.value().entry->overlayable_item.value();
+  EXPECT_EQ(result_overlayable.overlayable->name, "TestName");
+  EXPECT_EQ(result_overlayable.overlayable->actor, "overlay://theme");
+  EXPECT_EQ(result_overlayable.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_item);
+  result_overlayable = search_result.value().entry->overlayable_item.value();
+  EXPECT_EQ(result_overlayable.overlayable->name, "OtherName");
+  EXPECT_EQ(result_overlayable.overlayable->actor, "overlay://customization");
+  EXPECT_EQ(result_overlayable.policies, OverlayableItem::Policy::kPublic);
 }
 
 }  // namespace aapt
diff --git a/tools/aapt2/link/ManifestFixer.cpp b/tools/aapt2/link/ManifestFixer.cpp
index 85bf6f2..5812ec4 100644
--- a/tools/aapt2/link/ManifestFixer.cpp
+++ b/tools/aapt2/link/ManifestFixer.cpp
@@ -213,6 +213,27 @@
   return true;
 }
 
+static bool AddDeprecatedUsesFeatures(xml::Element* el, SourcePathDiagnostics* diag) {
+  if (xml::Attribute* attr = el->FindAttribute(xml::kSchemaAndroid, "name")) {
+    if (attr->value.empty()) {
+      return true;
+    }
+
+    // Add "android.hardware.fingerprint" when "android.hardware.biometric.fingerprint" is found,
+    // since the former is deprecated in Q and the latter is not present pre-Q. (see b/115639644)
+    if (attr->value == "android.hardware.biometrics.fingerprint") {
+      auto element = el->CloneElement([&](const xml::Element& el, xml::Element* out_el) {
+        xml::Attribute* cloned_attr = out_el->FindOrCreateAttribute(xml::kSchemaAndroid, "name");
+        cloned_attr->value = "android.hardware.fingerprint";
+      });
+
+      el->parent->AppendChild(std::move(element));
+    }
+  }
+
+  return true;
+}
+
 bool ManifestFixer::BuildRules(xml::XmlActionExecutor* executor,
                                IDiagnostics* diag) {
   // First verify some options.
@@ -247,6 +268,7 @@
   // Common <uses-feature> actions.
   xml::XmlNodeAction uses_feature_action;
   uses_feature_action.Action(VerifyUsesFeature);
+  uses_feature_action.Action(AddDeprecatedUsesFeatures);
 
   // Common component actions.
   xml::XmlNodeAction component_action;
diff --git a/tools/aapt2/link/ManifestFixer_test.cpp b/tools/aapt2/link/ManifestFixer_test.cpp
index adea627..fcc9f55 100644
--- a/tools/aapt2/link/ManifestFixer_test.cpp
+++ b/tools/aapt2/link/ManifestFixer_test.cpp
@@ -832,4 +832,36 @@
   EXPECT_THAT(Verify(input), NotNull());
 }
 
+TEST_F(ManifestFixerTest, UsesFeatureAddDeprecated) {
+  std::string input = R"(
+      <manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="android">
+        <uses-feature android:name="android.hardware.biometrics.fingerprint" />
+        <feature-group>
+          <uses-feature android:name="android.hardware.biometrics.fingerprint" />
+        </feature-group>
+      </manifest>)";
+
+  std::unique_ptr<xml::XmlResource> manifest = Verify(input);
+  ASSERT_THAT(manifest, NotNull());
+  EXPECT_THAT(manifest->root->FindChildWithAttribute("", "uses-feature",
+                                                     xml::kSchemaAndroid, "name",
+                                                     "android.hardware.biometrics.fingerprint"),
+              Ne(nullptr));
+  EXPECT_THAT(manifest->root->FindChildWithAttribute("", "uses-feature",
+                                                     xml::kSchemaAndroid, "name",
+                                                     "android.hardware.fingerprint"),
+              Ne(nullptr));
+
+  xml::Element* feature_group = manifest->root->FindChild("", "feature-group");
+  ASSERT_THAT(feature_group, Ne(nullptr));
+
+  EXPECT_THAT(feature_group->FindChildWithAttribute("", "uses-feature", xml::kSchemaAndroid, "name",
+                                                    "android.hardware.biometrics.fingerprint"),
+              Ne(nullptr));
+  EXPECT_THAT(feature_group->FindChildWithAttribute("", "uses-feature", xml::kSchemaAndroid, "name",
+                                                    "android.hardware.fingerprint"),
+              Ne(nullptr));
+}
+
 }  // namespace aapt
diff --git a/tools/aapt2/optimize/ResourcePathShortener.cpp b/tools/aapt2/optimize/ResourcePathShortener.cpp
new file mode 100644
index 0000000..c5df3dd
--- /dev/null
+++ b/tools/aapt2/optimize/ResourcePathShortener.cpp
@@ -0,0 +1,112 @@
+/*
+ * 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 "optimize/ResourcePathShortener.h"
+
+#include <math.h>
+#include <unordered_set>
+
+#include "androidfw/StringPiece.h"
+
+#include "ResourceTable.h"
+#include "ValueVisitor.h"
+
+
+static const std::string base64_chars =
+             "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+             "abcdefghijklmnopqrstuvwxyz"
+             "0123456789-_";
+
+namespace aapt {
+
+ResourcePathShortener::ResourcePathShortener(
+    std::map<std::string, std::string>& path_map_out)
+    : path_map_(path_map_out) {
+}
+
+std::string ShortenFileName(const android::StringPiece& file_path, int output_length) {
+  std::size_t hash_num = std::hash<android::StringPiece>{}(file_path);
+  std::string result = "";
+  // Convert to (modified) base64 so that it is a proper file path.
+  for (int i = 0; i < output_length; i++) {
+    uint8_t sextet = hash_num & 0x3f;
+    hash_num >>= 6;
+    result += base64_chars[sextet];
+  }
+  return result;
+}
+
+
+// Calculate the optimal hash length such that an average of 10% of resources
+// collide in their shortened path.
+// Reference: http://matt.might.net/articles/counting-hash-collisions/
+int OptimalShortenedLength(int num_resources) {
+  int num_chars = 2;
+  double N = 64*64; // hash space when hash is 2 chars long
+  double max_collisions = num_resources * 0.1;
+  while (num_resources - N + N * pow((N - 1) / N, num_resources) > max_collisions) {
+    N *= 64;
+    num_chars++;
+  }
+  return num_chars;
+}
+
+std::string GetShortenedPath(const android::StringPiece& shortened_filename,
+    const android::StringPiece& extension, int collision_count) {
+  std::string shortened_path = "res/" + shortened_filename.to_string();
+  if (collision_count > 0) {
+    shortened_path += std::to_string(collision_count);
+  }
+  shortened_path += extension;
+  return shortened_path;
+}
+
+bool ResourcePathShortener::Consume(IAaptContext* context, ResourceTable* table) {
+  // used to detect collisions
+  std::unordered_set<std::string> shortened_paths;
+  std::unordered_set<FileReference*> file_refs;
+  for (auto& package : table->packages) {
+    for (auto& type : package->types) {
+      for (auto& entry : type->entries) {
+        for (auto& config_value : entry->values) {
+          FileReference* file_ref = ValueCast<FileReference>(config_value->value.get());
+          if (file_ref) {
+            file_refs.insert(file_ref);
+          }
+        }
+      }
+    }
+  }
+  int num_chars = OptimalShortenedLength(file_refs.size());
+  for (auto& file_ref : file_refs) {
+    android::StringPiece res_subdir, actual_filename, extension;
+    util::ExtractResFilePathParts(*file_ref->path, &res_subdir, &actual_filename, &extension);
+
+    std::string shortened_filename = ShortenFileName(*file_ref->path, num_chars);
+    int collision_count = 0;
+    std::string shortened_path = GetShortenedPath(shortened_filename, extension, collision_count);
+    while (shortened_paths.find(shortened_path) != shortened_paths.end()) {
+      collision_count++;
+      shortened_path = GetShortenedPath(shortened_filename, extension, collision_count);
+    }
+    shortened_paths.insert(shortened_path);
+    path_map_.insert({*file_ref->path, shortened_path});
+    file_ref->path = table->string_pool.MakeRef(shortened_path, file_ref->path.GetContext());
+  }
+  return true;
+}
+
+}  // namespace aapt
diff --git a/tools/aapt2/optimize/ResourcePathShortener.h b/tools/aapt2/optimize/ResourcePathShortener.h
new file mode 100644
index 0000000..f1074ef
--- /dev/null
+++ b/tools/aapt2/optimize/ResourcePathShortener.h
@@ -0,0 +1,44 @@
+/*
+ * 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 AAPT_OPTIMIZE_RESOURCEPATHSHORTENER_H
+#define AAPT_OPTIMIZE_RESOURCEPATHSHORTENER_H
+
+#include <map>
+
+#include "android-base/macros.h"
+
+#include "process/IResourceTableConsumer.h"
+
+namespace aapt {
+
+class ResourceTable;
+
+// Maps resources in the apk to shortened paths.
+class ResourcePathShortener : public IResourceTableConsumer {
+ public:
+  explicit ResourcePathShortener(std::map<std::string, std::string>& path_map_out);
+
+  bool Consume(IAaptContext* context, ResourceTable* table) override;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(ResourcePathShortener);
+  std::map<std::string, std::string>& path_map_;
+};
+
+} // namespace aapt
+
+#endif  // AAPT_OPTIMIZE_RESOURCEPATHSHORTENER_H
diff --git a/tools/aapt2/optimize/ResourcePathShortener_test.cpp b/tools/aapt2/optimize/ResourcePathShortener_test.cpp
new file mode 100644
index 0000000..88cadc7
--- /dev/null
+++ b/tools/aapt2/optimize/ResourcePathShortener_test.cpp
@@ -0,0 +1,67 @@
+/*
+ * 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 "optimize/ResourcePathShortener.h"
+
+#include "ResourceTable.h"
+#include "test/Test.h"
+
+using ::aapt::test::GetValue;
+using ::testing::Not;
+using ::testing::NotNull;
+using ::testing::Eq;
+
+namespace aapt {
+
+TEST(ResourcePathShortenerTest, FileRefPathsChangedInResourceTable) {
+  std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
+
+  std::unique_ptr<ResourceTable> table =
+      test::ResourceTableBuilder()
+          .AddFileReference("android:drawable/xmlfile", "res/drawables/xmlfile.xml")
+          .AddFileReference("android:drawable/xmlfile2", "res/drawables/xmlfile2.xml")
+          .AddString("android:string/string", "res/should/still/be/the/same.png")
+          .Build();
+
+  std::map<std::string, std::string> path_map;
+  ASSERT_TRUE(ResourcePathShortener(path_map).Consume(context.get(), table.get()));
+
+  // Expect that the path map is populated
+  ASSERT_THAT(path_map.find("res/drawables/xmlfile.xml"), Not(Eq(path_map.end())));
+  ASSERT_THAT(path_map.find("res/drawables/xmlfile2.xml"), Not(Eq(path_map.end())));
+
+  // The file paths were changed
+  EXPECT_THAT(path_map.at("res/drawables/xmlfile.xml"), Not(Eq("res/drawables/xmlfile.xml")));
+  EXPECT_THAT(path_map.at("res/drawables/xmlfile2.xml"), Not(Eq("res/drawables/xmlfile2.xml")));
+
+  // Different file paths should remain different
+  EXPECT_THAT(path_map["res/drawables/xmlfile.xml"],
+              Not(Eq(path_map["res/drawables/xmlfile2.xml"])));
+
+  FileReference* ref =
+      GetValue<FileReference>(table.get(), "android:drawable/xmlfile");
+  ASSERT_THAT(ref, NotNull());
+  // The map correctly points to the new location of the file
+  EXPECT_THAT(path_map["res/drawables/xmlfile.xml"], Eq(*ref->path));
+
+  // Strings should not be affected, only file paths
+  EXPECT_THAT(
+      *GetValue<String>(table.get(), "android:string/string")->value,
+              Eq("res/should/still/be/the/same.png"));
+  EXPECT_THAT(path_map.find("res/should/still/be/the/same.png"), Eq(path_map.end()));
+}
+
+}   // namespace aapt
diff --git a/tools/apilint/apilint.py b/tools/apilint/apilint.py
index acf1f1e..d1fe43e 100644
--- a/tools/apilint/apilint.py
+++ b/tools/apilint/apilint.py
@@ -297,7 +297,7 @@
 class V2LineParser(object):
     __slots__ = ["tokenized", "current", "len"]
 
-    MODIFIERS = set("public protected internal private abstract default static final transient volatile synchronized".split())
+    MODIFIERS = set("public protected internal private abstract default static final transient volatile synchronized native operator sealed strictfp infix inline suspend vararg".split())
     JAVA_LANG_TYPES = set("AbstractMethodError AbstractStringBuilder Appendable ArithmeticException ArrayIndexOutOfBoundsException ArrayStoreException AssertionError AutoCloseable Boolean BootstrapMethodError Byte Character CharSequence Class ClassCastException ClassCircularityError ClassFormatError ClassLoader ClassNotFoundException Cloneable CloneNotSupportedException Comparable Compiler Deprecated Double Enum EnumConstantNotPresentException Error Exception ExceptionInInitializerError Float FunctionalInterface IllegalAccessError IllegalAccessException IllegalArgumentException IllegalMonitorStateException IllegalStateException IllegalThreadStateException IncompatibleClassChangeError IndexOutOfBoundsException InheritableThreadLocal InstantiationError InstantiationException Integer InternalError InterruptedException Iterable LinkageError Long Math NegativeArraySizeException NoClassDefFoundError NoSuchFieldError NoSuchFieldException NoSuchMethodError NoSuchMethodException NullPointerException Number NumberFormatException Object OutOfMemoryError Override Package package-info.java Process ProcessBuilder ProcessEnvironment ProcessImpl Readable ReflectiveOperationException Runnable Runtime RuntimeException RuntimePermission SafeVarargs SecurityException SecurityManager Short StackOverflowError StackTraceElement StrictMath String StringBuffer StringBuilder StringIndexOutOfBoundsException SuppressWarnings System Thread ThreadDeath ThreadGroup ThreadLocal Throwable TypeNotPresentException UNIXProcess UnknownError UnsatisfiedLinkError UnsupportedClassVersionError UnsupportedOperationException VerifyError VirtualMachineError Void".split())
 
     def __init__(self, raw):
@@ -355,7 +355,7 @@
         self.parse_eof()
 
     def parse_into_field(self, field):
-        kind = self.parse_token("field")
+        kind = self.parse_one_of("field", "property")
         field.split = [kind]
         annotations = self.parse_annotations()
         if "@Deprecated" in annotations:
@@ -442,13 +442,19 @@
         return None
 
     def parse_type(self):
+        self.parse_annotations()
         type = self.parse_token()
+        if type[-1] == '.':
+            self.parse_annotations()
+            type += self.parse_token()
         if type in V2LineParser.JAVA_LANG_TYPES:
             type = "java.lang." + type
         self.parse_matching_paren("<", ">")
         while True:
             t = self.lookahead()
-            if t == "[]":
+            if t == "@":
+                self.parse_annotation()
+            elif t == "[]":
                 type += self.parse_token()
             elif self.parse_kotlin_nullability() is not None:
                 pass  # discard nullability for now
@@ -478,17 +484,23 @@
             self.parse_token(",")
 
     def parse_arg(self):
+        self.parse_if("vararg")  # kotlin vararg
         self.parse_annotations()
         type = self.parse_arg_type()
         l = self.lookahead()
         if l != "," and l != ")":
-            self.parse_token()  # kotlin argument name
+            if self.lookahead() != '=':
+                self.parse_token()  # kotlin argument name
             if self.parse_if('='): # kotlin default value
-                (self.parse_matching_paren('(', ')') or
-                 self.parse_matching_paren('{', '}') or
-                 self.parse_token() and self.parse_matching_paren('(', ')'))
+                self.parse_expression()
         return type
 
+    def parse_expression(self):
+        while not self.lookahead() in [')', ',', ';']:
+            (self.parse_matching_paren('(', ')') or
+            self.parse_matching_paren('{', '}') or
+            self.parse_token())
+
     def parse_throws(self):
         ret = []
         if self.parse_if("throws"):
@@ -516,7 +528,7 @@
 
     def parse_annotation_default(self):
         if self.parse_if("default"):
-            self.parse_value()
+            self.parse_expression()
 
     def parse_value(self):
         if self.lookahead() == "{":
@@ -599,7 +611,7 @@
             clazz.ctors.append(Method(clazz, line, raw, blame, sig_format=sig_format))
         elif raw.startswith("    method"):
             clazz.methods.append(Method(clazz, line, raw, blame, sig_format=sig_format))
-        elif raw.startswith("    field"):
+        elif raw.startswith("    field") or raw.startswith("    property"):
             clazz.fields.append(Field(clazz, line, raw, blame, sig_format=sig_format))
         elif raw.startswith("  }") and clazz:
             yield clazz
@@ -942,7 +954,7 @@
             else:
                 error(clazz, f, "F2", "Bare fields must be marked final, or add accessors if mutable")
 
-        if not "static" in f.split:
+        if "static" not in f.split and "property" not in f.split:
             if not re.match("[a-z]([a-zA-Z]+)?", f.name):
                 error(clazz, f, "S1", "Non-static fields must be named using myField style")
 
@@ -1650,7 +1662,7 @@
         binary.add(op)
 
     for m in clazz.methods:
-        if 'static' in m.split:
+        if 'static' in m.split or 'operator' in m.split:
             continue
 
         # https://kotlinlang.org/docs/reference/operator-overloading.html#unary-prefix-operators
diff --git a/tools/apilint/apilint_test.py b/tools/apilint/apilint_test.py
index 081e98d..fde61a9 100644
--- a/tools/apilint/apilint_test.py
+++ b/tools/apilint/apilint_test.py
@@ -225,11 +225,12 @@
         self.assertEquals('pkg.SuppressLint', cls.fullname)
 
     def test_parse_method(self):
-        m = self._method("method @Deprecated public static <T> Class<T>[][] name("
+        m = self._method("method @Deprecated public static native <T> Class<T>[][] name("
                          + "Class<T[]>[][], Class<T[][][]>[][]...) throws Exception, T;")
         self.assertTrue('static' in m.split)
         self.assertTrue('public' in m.split)
         self.assertTrue('method' in m.split)
+        self.assertTrue('native' in m.split)
         self.assertTrue('deprecated' in m.split)
         self.assertEquals('java.lang.Class[][]', m.typ)
         self.assertEquals('name', m.name)
@@ -248,6 +249,7 @@
         self._method('method abstract String category() default "";', cls=cls)
         self._method('method abstract boolean deepExport() default false;', cls=cls)
         self._method('method abstract ViewDebug.FlagToString[] flagMapping() default {};', cls=cls)
+        self._method('method abstract ViewDebug.FlagToString[] flagMapping() default (double)java.lang.Float.NEGATIVE_INFINITY;', cls=cls)
 
     def test_parse_string_field(self):
         f = self._field('field @Deprecated public final String SOME_NAME = "value";')
@@ -286,5 +288,44 @@
         self._method("method <T> T name(T a = 1, T b = A(1), Lambda f = { false }, N? n = null, "
                          + """double c = (1/0), float d = 1.0f, String s = "heyo", char c = 'a');""")
 
+    def test_kotlin_operator(self):
+        self._method('method public operator void unaryPlus(androidx.navigation.NavDestination);')
+        self._method('method public static operator androidx.navigation.NavDestination get(androidx.navigation.NavGraph, @IdRes int id);')
+        self._method('method public static operator <T> T get(androidx.navigation.NavigatorProvider, kotlin.reflect.KClass<T> clazz);')
+
+    def test_kotlin_property(self):
+        self._field('property public VM value;')
+        self._field('property public final String? action;')
+
+    def test_kotlin_varargs(self):
+        self._method('method public void error(int p = "42", Integer int2 = "null", int p1 = "42", vararg String args);')
+
+    def test_kotlin_default_values(self):
+        self._method('method public void foo(String! = null, String! = "Hello World", int = 42);')
+        self._method('method void method(String, String firstArg = "hello", int secondArg = "42", String thirdArg = "world");')
+        self._method('method void method(String, String firstArg = "hello", int secondArg = "42");')
+        self._method('method void method(String, String firstArg = "hello");')
+        self._method('method void edit(android.Type, boolean commit = false, Function1<? super Editor,kotlin.Unit> action);')
+        self._method('method <K, V> LruCache<K,V> lruCache(int maxSize, Function2<? super K,? super V,java.lang.Integer> sizeOf = { _, _ -> 1 }, Function1<? extends V> create = { (V)null }, Function4<kotlin.Unit> onEntryRemoved = { _, _, _, _ ->  });')
+        self._method('method android.Bitmap? drawToBitmap(android.View, android.Config config = android.graphics.Bitmap.Config.ARGB_8888);')
+        self._method('method void emptyLambda(Function0<kotlin.Unit> sizeOf = {});')
+        self._method('method void method1(int p = 42, Integer? int2 = null, int p1 = 42, String str = "hello world", java.lang.String... args);')
+        self._method('method void method2(int p, int int2 = (2 * int) * some.other.pkg.Constants.Misc.SIZE);')
+        self._method('method void method3(String str, int p, int int2 = double(int) + str.length);')
+        self._method('method void print(test.pkg.Foo foo = test.pkg.Foo());')
+
+    def test_type_use_annotation(self):
+        self._method('method public static int codePointAt(char @NonNull [], int);')
+        self._method('method @NonNull public java.util.Set<java.util.Map.@NonNull Entry<K,V>> entrySet();')
+
+        m = self._method('method @NonNull public java.lang.annotation.@NonNull Annotation @NonNull [] getAnnotations();')
+        self.assertEquals('java.lang.annotation.Annotation[]', m.typ)
+
+        m = self._method('method @NonNull public abstract java.lang.annotation.@NonNull Annotation @NonNull [] @NonNull [] getParameterAnnotations();')
+        self.assertEquals('java.lang.annotation.Annotation[][]', m.typ)
+
+        m = self._method('method @NonNull public @NonNull String @NonNull [] split(@NonNull String, int);')
+        self.assertEquals('java.lang.String[]', m.typ)
+
 if __name__ == "__main__":
     unittest.main()
diff --git a/tools/signedconfig/prod_public.pem b/tools/signedconfig/prod_public.pem
new file mode 100644
index 0000000..8c10215
--- /dev/null
+++ b/tools/signedconfig/prod_public.pem
@@ -0,0 +1,5 @@
+-----BEGIN PUBLIC KEY-----
+MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE+lky6wKyGL6lE1VrD0YTMHwb0Xwc
++tzC8MvnrzVxodvTpVY/jV7V+Zktcx+pry43XPABFRXtbhTo+qykhyBA1g==
+-----END PUBLIC KEY-----
+
diff --git a/tools/signedconfig/verify_b64.sh b/tools/signedconfig/verify_b64.sh
index 8e1f58c..a4ac6a8 100755
--- a/tools/signedconfig/verify_b64.sh
+++ b/tools/signedconfig/verify_b64.sh
@@ -7,4 +7,30 @@
 # The arg values can be taken from the debug log for SignedConfigService when verbose logging is
 # enabled.
 
-openssl dgst -sha256 -verify $(dirname $0)/debug_public.pem -signature <(echo $2 | base64 -d) <(echo $1 | base64 -d)
+function verify() {
+  D=${1}
+  S=${2}
+  K=${3}
+  echo Trying ${K}
+  openssl dgst -sha256 -verify $(dirname $0)/${K} -signature <(echo ${S} | base64 -d) <(echo ${D} | base64 -d)
+}
+
+
+PROD_KEY_NAME=prod_public.pem
+DEBUG_KEY_NAME=debug_public.pem
+SIGNATURE="$2"
+DATA="$1"
+
+echo DATA: ${DATA}
+echo SIGNATURE: ${SIGNATURE}
+
+if verify "${DATA}" "${SIGNATURE}" "${PROD_KEY_NAME}"; then
+  echo Verified with ${PROD_KEY_NAME}
+  exit 0
+fi
+
+if verify "${DATA}" "${SIGNATURE}" "${DEBUG_KEY_NAME}"; then
+  echo Verified with ${DEBUG_KEY_NAME}
+  exit 0
+fi
+exit 1
diff --git a/wifi/java/android/net/wifi/DppStatusCallback.java b/wifi/java/android/net/wifi/DppStatusCallback.java
deleted file mode 100644
index fa2ab30..0000000
--- a/wifi/java/android/net/wifi/DppStatusCallback.java
+++ /dev/null
@@ -1,166 +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 android.net.wifi;
-
-import android.annotation.IntDef;
-import android.annotation.SystemApi;
-import android.os.Handler;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/**
- * DPP Status Callback. Use this callback to get status updates (success, failure, progress)
- * from the DPP operation started with {@link WifiManager#startDppAsConfiguratorInitiator(String,
- * int, int, Handler, DppStatusCallback)} or {@link WifiManager#startDppAsEnrolleeInitiator(String,
- * Handler, DppStatusCallback)}
- * @hide
- */
-@SystemApi
-public abstract class DppStatusCallback {
-    /**
-     * DPP Success event: Configuration sent (Configurator mode).
-     */
-    public static final int DPP_EVENT_SUCCESS_CONFIGURATION_SENT = 0;
-
-    /** @hide */
-    @IntDef(prefix = { "DPP_EVENT_SUCCESS_" }, value = {
-            DPP_EVENT_SUCCESS_CONFIGURATION_SENT,
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface DppSuccessStatusCode {}
-
-    /**
-     * DPP Progress event: Initial authentication with peer succeeded.
-     */
-    public static final int DPP_EVENT_PROGRESS_AUTHENTICATION_SUCCESS = 0;
-
-    /**
-     * DPP Progress event: Peer requires more time to process bootstrapping.
-     */
-    public static final int DPP_EVENT_PROGRESS_RESPONSE_PENDING = 1;
-
-    /** @hide */
-    @IntDef(prefix = { "DPP_EVENT_PROGRESS_" }, value = {
-            DPP_EVENT_PROGRESS_AUTHENTICATION_SUCCESS,
-            DPP_EVENT_PROGRESS_RESPONSE_PENDING,
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface DppProgressStatusCode {}
-
-    /**
-     * DPP Failure event: Scanned QR code is either not a DPP URI, or the DPP URI has errors.
-     */
-    public static final int DPP_EVENT_FAILURE_INVALID_URI = -1;
-
-    /**
-     * DPP Failure event: Bootstrapping/Authentication initialization process failure.
-     */
-    public static final int DPP_EVENT_FAILURE_AUTHENTICATION = -2;
-
-    /**
-     * DPP Failure event: Both devices are implementing the same role and are incompatible.
-     */
-    public static final int DPP_EVENT_FAILURE_NOT_COMPATIBLE = -3;
-
-    /**
-     * DPP Failure event: Configuration process has failed due to malformed message.
-     */
-    public static final int DPP_EVENT_FAILURE_CONFIGURATION = -4;
-
-    /**
-     * DPP Failure event: DPP request while in another DPP exchange.
-     */
-    public static final int DPP_EVENT_FAILURE_BUSY = -5;
-
-    /**
-     * DPP Failure event: No response from the peer.
-     */
-    public static final int DPP_EVENT_FAILURE_TIMEOUT = -6;
-
-    /**
-     * DPP Failure event: General protocol failure.
-     */
-    public static final int DPP_EVENT_FAILURE = -7;
-
-    /**
-     * DPP Failure event: Feature or option is not supported.
-     */
-    public static final int DPP_EVENT_FAILURE_NOT_SUPPORTED = -8;
-
-    /**
-     * DPP Failure event: Invalid network provided to DPP configurator.
-     * Network must either be WPA3-Personal (SAE) or WPA2-Personal (PSK).
-     */
-    public static final int DPP_EVENT_FAILURE_INVALID_NETWORK = -9;
-
-
-    /** @hide */
-    @IntDef(prefix = {"DPP_EVENT_FAILURE_"}, value = {
-            DPP_EVENT_FAILURE_INVALID_URI,
-            DPP_EVENT_FAILURE_AUTHENTICATION,
-            DPP_EVENT_FAILURE_NOT_COMPATIBLE,
-            DPP_EVENT_FAILURE_CONFIGURATION,
-            DPP_EVENT_FAILURE_BUSY,
-            DPP_EVENT_FAILURE_TIMEOUT,
-            DPP_EVENT_FAILURE,
-            DPP_EVENT_FAILURE_NOT_SUPPORTED,
-            DPP_EVENT_FAILURE_INVALID_NETWORK,
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface DppFailureStatusCode {
-    }
-
-    /**
-     * Called when local DPP Enrollee successfully receives a new Wi-Fi configuration from the
-     * peer DPP configurator. This callback marks the successful end of the DPP current DPP
-     * session, and no further callbacks will be called. This callback is the successful outcome
-     * of a DPP flow starting with {@link WifiManager#startDppAsEnrolleeInitiator(String, Handler,
-     * DppStatusCallback)}.
-     *
-     * @param newNetworkId New Wi-Fi configuration with a network ID received from the configurator
-     */
-    public abstract void onEnrolleeSuccess(int newNetworkId);
-
-    /**
-     * Called when a DPP success event takes place, except for when configuration is received from
-     * an external Configurator. The callback onSuccessConfigReceived will be used in this case.
-     * This callback marks the successful end of the current DPP session, and no further
-     * callbacks will be called. This callback is the successful outcome of a DPP flow starting with
-     * {@link WifiManager#startDppAsConfiguratorInitiator(String, int, int, Handler,
-     * DppStatusCallback)}.
-     *
-     * @param code DPP success status code.
-     */
-    public abstract void onConfiguratorSuccess(@DppSuccessStatusCode int code);
-
-    /**
-     * Called when a DPP Failure event takes place. This callback marks the unsuccessful end of the
-     * current DPP session, and no further callbacks will be called.
-     *
-     * @param code DPP failure status code.
-     */
-    public abstract void onFailure(@DppFailureStatusCode int code);
-
-    /**
-     * Called when DPP events that indicate progress take place. Can be used by UI elements
-     * to show progress.
-     *
-     * @param code DPP progress status code.
-     */
-    public abstract void onProgress(@DppProgressStatusCode int code);
-}
diff --git a/wifi/java/android/net/wifi/EasyConnectStatusCallback.java b/wifi/java/android/net/wifi/EasyConnectStatusCallback.java
new file mode 100644
index 0000000..3b4a6cd
--- /dev/null
+++ b/wifi/java/android/net/wifi/EasyConnectStatusCallback.java
@@ -0,0 +1,179 @@
+/*
+ * 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.wifi;
+
+import android.annotation.IntDef;
+import android.annotation.SystemApi;
+import android.os.Handler;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Easy Connect (DPP) Status Callback. Use this callback to get status updates (success, failure,
+ * progress) from the Easy Connect operation started with
+ * {@link WifiManager#startEasyConnectAsConfiguratorInitiator(String,
+ * int, int, Handler, EasyConnectStatusCallback)} or
+ * {@link WifiManager#startEasyConnectAsEnrolleeInitiator(String,
+ * Handler, EasyConnectStatusCallback)}
+ *
+ * @hide
+ */
+@SystemApi
+public abstract class EasyConnectStatusCallback {
+    /**
+     * Easy Connect Success event: Configuration sent (Configurator mode).
+     */
+    public static final int EASY_CONNECT_EVENT_SUCCESS_CONFIGURATION_SENT = 0;
+
+    /** @hide */
+    @IntDef(prefix = {"EASY_CONNECT_EVENT_SUCCESS_"}, value = {
+            EASY_CONNECT_EVENT_SUCCESS_CONFIGURATION_SENT,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface EasyConnectSuccessStatusCode {
+    }
+
+    /**
+     * Easy Connect Progress event: Initial authentication with peer succeeded.
+     */
+    public static final int EASY_CONNECT_EVENT_PROGRESS_AUTHENTICATION_SUCCESS = 0;
+
+    /**
+     * Easy Connect Progress event: Peer requires more time to process bootstrapping.
+     */
+    public static final int EASY_CONNECT_EVENT_PROGRESS_RESPONSE_PENDING = 1;
+
+    /** @hide */
+    @IntDef(prefix = {"EASY_CONNECT_EVENT_PROGRESS_"}, value = {
+            EASY_CONNECT_EVENT_PROGRESS_AUTHENTICATION_SUCCESS,
+            EASY_CONNECT_EVENT_PROGRESS_RESPONSE_PENDING,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface EasyConnectProgressStatusCode {
+    }
+
+    /**
+     * Easy Connect Failure event: Scanned QR code is either not a Easy Connect URI, or the Easy
+     * Connect URI has errors.
+     */
+    public static final int EASY_CONNECT_EVENT_FAILURE_INVALID_URI = -1;
+
+    /**
+     * Easy Connect Failure event: Bootstrapping/Authentication initialization process failure.
+     */
+    public static final int EASY_CONNECT_EVENT_FAILURE_AUTHENTICATION = -2;
+
+    /**
+     * Easy Connect Failure event: Both devices are implementing the same role and are incompatible.
+     */
+    public static final int EASY_CONNECT_EVENT_FAILURE_NOT_COMPATIBLE = -3;
+
+    /**
+     * Easy Connect Failure event: Configuration process has failed due to malformed message.
+     */
+    public static final int EASY_CONNECT_EVENT_FAILURE_CONFIGURATION = -4;
+
+    /**
+     * Easy Connect Failure event: Easy Connect request while in another Easy Connect exchange.
+     */
+    public static final int EASY_CONNECT_EVENT_FAILURE_BUSY = -5;
+
+    /**
+     * Easy Connect Failure event: No response from the peer.
+     */
+    public static final int EASY_CONNECT_EVENT_FAILURE_TIMEOUT = -6;
+
+    /**
+     * Easy Connect Failure event: General protocol failure.
+     */
+    public static final int EASY_CONNECT_EVENT_FAILURE = -7;
+
+    /**
+     * Easy Connect Failure event: Feature or option is not supported.
+     */
+    public static final int EASY_CONNECT_EVENT_FAILURE_NOT_SUPPORTED = -8;
+
+    /**
+     * Easy Connect Failure event: Invalid network provided to Easy Connect configurator.
+     * Network must either be WPA3-Personal (SAE) or WPA2-Personal (PSK).
+     */
+    public static final int EASY_CONNECT_EVENT_FAILURE_INVALID_NETWORK = -9;
+
+
+    /** @hide */
+    @IntDef(prefix = {"EASY_CONNECT_EVENT_FAILURE_"}, value = {
+            EASY_CONNECT_EVENT_FAILURE_INVALID_URI,
+            EASY_CONNECT_EVENT_FAILURE_AUTHENTICATION,
+            EASY_CONNECT_EVENT_FAILURE_NOT_COMPATIBLE,
+            EASY_CONNECT_EVENT_FAILURE_CONFIGURATION,
+            EASY_CONNECT_EVENT_FAILURE_BUSY,
+            EASY_CONNECT_EVENT_FAILURE_TIMEOUT,
+            EASY_CONNECT_EVENT_FAILURE,
+            EASY_CONNECT_EVENT_FAILURE_NOT_SUPPORTED,
+            EASY_CONNECT_EVENT_FAILURE_INVALID_NETWORK,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface EasyConnectFailureStatusCode {
+    }
+
+    /**
+     * Called when local Easy Connect Enrollee successfully receives a new Wi-Fi configuration from
+     * the
+     * peer Easy Connect configurator. This callback marks the successful end of the Easy Connect
+     * current Easy Connect
+     * session, and no further callbacks will be called. This callback is the successful outcome
+     * of a Easy Connect flow starting with
+     * {@link WifiManager#startEasyConnectAsEnrolleeInitiator(String,
+     * Handler,
+     * EasyConnectStatusCallback)}.
+     *
+     * @param newNetworkId New Wi-Fi configuration with a network ID received from the configurator
+     */
+    public abstract void onEnrolleeSuccess(int newNetworkId);
+
+    /**
+     * Called when a Easy Connect success event takes place, except for when configuration is
+     * received from
+     * an external Configurator. The callback onSuccessConfigReceived will be used in this case.
+     * This callback marks the successful end of the current Easy Connect session, and no further
+     * callbacks will be called. This callback is the successful outcome of a Easy Connect flow
+     * starting with
+     * {@link WifiManager#startEasyConnectAsConfiguratorInitiator(String, int, int, Handler,
+     * EasyConnectStatusCallback)}.
+     *
+     * @param code Easy Connect success status code.
+     */
+    public abstract void onConfiguratorSuccess(@EasyConnectSuccessStatusCode int code);
+
+    /**
+     * Called when a Easy Connect Failure event takes place. This callback marks the unsuccessful
+     * end of the
+     * current Easy Connect session, and no further callbacks will be called.
+     *
+     * @param code Easy Connect failure status code.
+     */
+    public abstract void onFailure(@EasyConnectFailureStatusCode int code);
+
+    /**
+     * Called when Easy Connect events that indicate progress take place. Can be used by UI elements
+     * to show progress.
+     *
+     * @param code Easy Connect progress status code.
+     */
+    public abstract void onProgress(@EasyConnectProgressStatusCode int code);
+}
diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index 28dd9b4..e019f28 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -145,11 +145,23 @@
          */
         public static final int SUITE_B_192 = 10;
 
+        /**
+         * WPA pre-shared key with stronger SHA256-based algorithms.
+         * @hide
+         */
+        public static final int WPA_PSK_SHA256 = 11;
+
+        /**
+         * WPA using EAP authentication with stronger SHA256-based algorithms.
+         * @hide
+         */
+        public static final int WPA_EAP_SHA256 = 12;
+
         public static final String varName = "key_mgmt";
 
         public static final String[] strings = { "NONE", "WPA_PSK", "WPA_EAP",
                 "IEEE8021X", "WPA2_PSK", "OSEN", "FT_PSK", "FT_EAP",
-                "SAE", "OWE", "SUITE_B_192"};
+                "SAE", "OWE", "SUITE_B_192", "WPA_PSK_SHA256", "WPA_EAP_SHA256" };
     }
 
     /**
@@ -1886,6 +1898,7 @@
         if (creatorName != null) sbuf.append(" cname=" + creatorName);
         if (lastUpdateUid != 0) sbuf.append(" luid=" + lastUpdateUid);
         if (lastUpdateName != null) sbuf.append(" lname=" + lastUpdateName);
+        if (updateIdentifier != null) sbuf.append(" updateIdentifier=" + updateIdentifier);
         sbuf.append(" lcuid=" + lastConnectUid);
         sbuf.append(" userApproved=" + userApprovedAsString(userApproved));
         sbuf.append(" noInternetAccessExpected=" + noInternetAccessExpected);
@@ -2281,6 +2294,7 @@
             mRandomizedMacAddress = source.mRandomizedMacAddress;
             macRandomizationSetting = source.macRandomizationSetting;
             requirePMF = source.requirePMF;
+            updateIdentifier = source.updateIdentifier;
         }
     }
 
diff --git a/wifi/java/android/net/wifi/WifiInfo.java b/wifi/java/android/net/wifi/WifiInfo.java
index af5ad51..840af5d 100644
--- a/wifi/java/android/net/wifi/WifiInfo.java
+++ b/wifi/java/android/net/wifi/WifiInfo.java
@@ -93,12 +93,22 @@
     private int mRssi;
 
     /**
-     * Link speed in Mbps
+     * The unit in which links speeds are expressed.
      */
     public static final String LINK_SPEED_UNITS = "Mbps";
     private int mLinkSpeed;
 
     /**
+     * Tx(transmit) Link speed in Mbps
+     */
+    private int mTxLinkSpeed;
+
+    /**
+     * Rx(receive) Link speed in Mbps
+     */
+    private int mRxLinkSpeed;
+
+    /**
      * Frequency in MHz
      */
     public static final String FREQUENCY_UNITS = "MHz";
@@ -192,6 +202,8 @@
         setNetworkId(-1);
         setRssi(INVALID_RSSI);
         setLinkSpeed(-1);
+        setTxLinkSpeedMbps(-1);
+        setRxLinkSpeedMbps(-1);
         setFrequency(-1);
         setMeteredHint(false);
         setEphemeral(false);
@@ -219,6 +231,8 @@
             mNetworkId = source.mNetworkId;
             mRssi = source.mRssi;
             mLinkSpeed = source.mLinkSpeed;
+            mTxLinkSpeed = source.mTxLinkSpeed;
+            mRxLinkSpeed = source.mRxLinkSpeed;
             mFrequency = source.mFrequency;
             mIpAddress = source.mIpAddress;
             mMacAddress = source.mMacAddress;
@@ -313,7 +327,7 @@
 
     /**
      * Returns the current link speed in {@link #LINK_SPEED_UNITS}.
-     * @return the link speed.
+     * @return the link speed or -1 if there is no valid value.
      * @see #LINK_SPEED_UNITS
      */
     public int getLinkSpeed() {
@@ -323,7 +337,39 @@
     /** @hide */
     @UnsupportedAppUsage
     public void setLinkSpeed(int linkSpeed) {
-        this.mLinkSpeed = linkSpeed;
+        mLinkSpeed = linkSpeed;
+    }
+
+    /**
+     * Returns the current transmit link speed in Mbps.
+     * @return the Tx link speed or -1 if there is no valid value.
+     */
+    public int getTxLinkSpeedMbps() {
+        return mTxLinkSpeed;
+    }
+
+    /**
+     * Update the last transmitted packet bit rate in Mbps.
+     * @hide
+     */
+    public void setTxLinkSpeedMbps(int txLinkSpeed) {
+        mTxLinkSpeed = txLinkSpeed;
+    }
+
+    /**
+     * Returns the current receive link speed in Mbps.
+     * @return the Rx link speed or -1 if there is no valid value.
+     */
+    public int getRxLinkSpeedMbps() {
+        return mRxLinkSpeed;
+    }
+
+    /**
+     * Update the last received packet bit rate in Mbps.
+     * @hide
+     */
+    public void setRxLinkSpeedMbps(int rxLinkSpeed) {
+        mRxLinkSpeed = rxLinkSpeed;
     }
 
     /**
@@ -529,17 +575,19 @@
         StringBuffer sb = new StringBuffer();
         String none = "<none>";
 
-        sb.append("SSID: ").append(mWifiSsid == null ? WifiSsid.NONE : mWifiSsid).
-            append(", BSSID: ").append(mBSSID == null ? none : mBSSID).
-            append(", MAC: ").append(mMacAddress == null ? none : mMacAddress).
-            append(", Supplicant state: ").
-            append(mSupplicantState == null ? none : mSupplicantState).
-            append(", RSSI: ").append(mRssi).
-            append(", Link speed: ").append(mLinkSpeed).append(LINK_SPEED_UNITS).
-            append(", Frequency: ").append(mFrequency).append(FREQUENCY_UNITS).
-            append(", Net ID: ").append(mNetworkId).
-            append(", Metered hint: ").append(mMeteredHint).
-            append(", score: ").append(Integer.toString(score));
+        sb.append("SSID: ").append(mWifiSsid == null ? WifiSsid.NONE : mWifiSsid)
+                .append(", BSSID: ").append(mBSSID == null ? none : mBSSID)
+                .append(", MAC: ").append(mMacAddress == null ? none : mMacAddress)
+                .append(", Supplicant state: ")
+                .append(mSupplicantState == null ? none : mSupplicantState)
+                .append(", RSSI: ").append(mRssi)
+                .append(", Link speed: ").append(mLinkSpeed).append(LINK_SPEED_UNITS)
+                .append(", Tx Link speed: ").append(mTxLinkSpeed).append(LINK_SPEED_UNITS)
+                .append(", Rx Link speed: ").append(mRxLinkSpeed).append(LINK_SPEED_UNITS)
+                .append(", Frequency: ").append(mFrequency).append(FREQUENCY_UNITS)
+                .append(", Net ID: ").append(mNetworkId)
+                .append(", Metered hint: ").append(mMeteredHint)
+                .append(", score: ").append(Integer.toString(score));
         return sb.toString();
     }
 
@@ -553,6 +601,8 @@
         dest.writeInt(mNetworkId);
         dest.writeInt(mRssi);
         dest.writeInt(mLinkSpeed);
+        dest.writeInt(mTxLinkSpeed);
+        dest.writeInt(mRxLinkSpeed);
         dest.writeInt(mFrequency);
         if (mIpAddress != null) {
             dest.writeByte((byte)1);
@@ -593,6 +643,8 @@
                 info.setNetworkId(in.readInt());
                 info.setRssi(in.readInt());
                 info.setLinkSpeed(in.readInt());
+                info.setTxLinkSpeedMbps(in.readInt());
+                info.setRxLinkSpeedMbps(in.readInt());
                 info.setFrequency(in.readInt());
                 if (in.readByte() == 1) {
                     try {
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 517bf3b..559f4ad 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -4586,93 +4586,100 @@
         }
     }
 
-    /* DPP - Device Provisioning Protocol AKA "Easy Connect" */
+    /* Easy Connect - AKA Device Provisioning Protocol (DPP) */
 
     /**
-     * DPP Network role: Station.
+     * Easy Connect Network role: Station.
+     *
      * @hide
      */
     @SystemApi
-    public static final int DPP_NETWORK_ROLE_STA = 0;
+    public static final int EASY_CONNECT_NETWORK_ROLE_STA = 0;
 
     /**
-     * DPP Network role: Access Point.
+     * Easy Connect Network role: Access Point.
+     *
      * @hide
      */
     @SystemApi
-    public static final int DPP_NETWORK_ROLE_AP = 1;
+    public static final int EASY_CONNECT_NETWORK_ROLE_AP = 1;
 
     /** @hide */
-    @IntDef(prefix = {"DPP_NETWORK_ROLE_"}, value = {
-            DPP_NETWORK_ROLE_STA,
-            DPP_NETWORK_ROLE_AP,
+    @IntDef(prefix = {"EASY_CONNECT_NETWORK_ROLE_"}, value = {
+            EASY_CONNECT_NETWORK_ROLE_STA,
+            EASY_CONNECT_NETWORK_ROLE_AP,
     })
     @Retention(RetentionPolicy.SOURCE)
-    public @interface DppNetworkRole {}
+    public @interface EasyConnectNetworkRole {
+    }
 
     /**
-     * Start DPP in Configurator-Initiator role. The current device will initiate DPP bootstrapping
-     * with a peer, and configure the peer with the SSID and password of the specified network using
-     * the DPP protocol on an encrypted link.
+     * Start Easy Connect (DPP) in Configurator-Initiator role. The current device will initiate
+     * Easy Connect bootstrapping with a peer, and configure the peer with the SSID and password of
+     * the specified network using the Easy Connect protocol on an encrypted link.
      *
-     * @param enrolleeUri URI of the Enrollee obtained separately (e.g. QR code scanning)
-     * @param selectedNetworkId Selected network ID to be sent to the peer
+     * @param enrolleeUri         URI of the Enrollee obtained separately (e.g. QR code scanning)
+     * @param selectedNetworkId   Selected network ID to be sent to the peer
      * @param enrolleeNetworkRole The network role of the enrollee
-     * @param callback Callback for status updates
-     * @param handler The handler on whose thread to execute the callbacks. Null for main thread.
+     * @param callback            Callback for status updates
+     * @param handler             The handler on whose thread to execute the callbacks. Null for
+     *                            main thread.
      * @hide
      */
     @SystemApi
     @RequiresPermission(anyOf = {
             android.Manifest.permission.NETWORK_SETTINGS,
             android.Manifest.permission.NETWORK_SETUP_WIZARD})
-    public void startDppAsConfiguratorInitiator(@NonNull String enrolleeUri,
-            int selectedNetworkId, @DppNetworkRole int enrolleeNetworkRole,
-            @Nullable Handler handler, @NonNull DppStatusCallback callback) {
+    public void startEasyConnectAsConfiguratorInitiator(@NonNull String enrolleeUri,
+            int selectedNetworkId, @EasyConnectNetworkRole int enrolleeNetworkRole,
+            @Nullable Handler handler, @NonNull EasyConnectStatusCallback callback) {
         Looper looper = (handler == null) ? Looper.getMainLooper() : handler.getLooper();
         Binder binder = new Binder();
         try {
             mService.startDppAsConfiguratorInitiator(binder, enrolleeUri, selectedNetworkId,
-                    enrolleeNetworkRole, new DppCallbackProxy(looper, callback));
+                    enrolleeNetworkRole, new EasyConnectCallbackProxy(looper, callback));
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
     }
 
     /**
-     * Start DPP in Enrollee-Initiator role. The current device will initiate DPP bootstrapping
-     * with a peer, and receive the SSID and password from the peer configurator.
+     * Start Easy Connect (DPP) in Enrollee-Initiator role. The current device will initiate Easy
+     * Connect bootstrapping with a peer, and receive the SSID and password from the peer
+     * configurator.
      *
      * @param configuratorUri URI of the Configurator obtained separately (e.g. QR code scanning)
-     * @param callback Callback for status updates
-     * @param handler The handler on whose thread to execute the callbacks. Null for main thread.
+     * @param callback        Callback for status updates
+     * @param handler         The handler on whose thread to execute the callbacks. Null for main
+     *                        thread.
      * @hide
      */
     @SystemApi
     @RequiresPermission(anyOf = {
             android.Manifest.permission.NETWORK_SETTINGS,
             android.Manifest.permission.NETWORK_SETUP_WIZARD})
-    public void startDppAsEnrolleeInitiator(@NonNull String configuratorUri,
-            @Nullable Handler handler, @NonNull DppStatusCallback callback) {
+    public void startEasyConnectAsEnrolleeInitiator(@NonNull String configuratorUri,
+            @Nullable Handler handler, @NonNull EasyConnectStatusCallback callback) {
         Looper looper = (handler == null) ? Looper.getMainLooper() : handler.getLooper();
         Binder binder = new Binder();
         try {
             mService.startDppAsEnrolleeInitiator(binder, configuratorUri,
-                     new DppCallbackProxy(looper, callback));
+                    new EasyConnectCallbackProxy(looper, callback));
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
     }
 
     /**
-     * Stop or abort a current DPP session.
+     * Stop or abort a current Easy Connect (DPP) session.
+     *
      * @hide
      */
     @SystemApi
     @RequiresPermission(anyOf = {
             android.Manifest.permission.NETWORK_SETTINGS,
             android.Manifest.permission.NETWORK_SETUP_WIZARD})
-    public void stopDppSession() {
+    public void stopEasyConnectSession() {
         try {
             /* Request lower layers to stop/abort and clear resources */
             mService.stopDppSession();
@@ -4682,48 +4689,50 @@
     }
 
     /**
-     * Helper class to support DPP callbacks
+     * Helper class to support Easy Connect (DPP) callbacks
+     *
      * @hide
      */
     @SystemApi
-    private static class DppCallbackProxy extends IDppCallback.Stub {
+    private static class EasyConnectCallbackProxy extends IDppCallback.Stub {
         private final Handler mHandler;
-        private final DppStatusCallback mDppStatusCallback;
+        private final EasyConnectStatusCallback mEasyConnectStatusCallback;
 
-        DppCallbackProxy(Looper looper, DppStatusCallback dppStatusCallback) {
+        EasyConnectCallbackProxy(Looper looper,
+                EasyConnectStatusCallback easyConnectStatusCallback) {
             mHandler = new Handler(looper);
-            mDppStatusCallback = dppStatusCallback;
+            mEasyConnectStatusCallback = easyConnectStatusCallback;
         }
 
         @Override
         public void onSuccessConfigReceived(int newNetworkId) {
-            Log.d(TAG, "DPP onSuccessConfigReceived callback");
+            Log.d(TAG, "Easy Connect onSuccessConfigReceived callback");
             mHandler.post(() -> {
-                mDppStatusCallback.onEnrolleeSuccess(newNetworkId);
+                mEasyConnectStatusCallback.onEnrolleeSuccess(newNetworkId);
             });
         }
 
         @Override
         public void onSuccess(int status) {
-            Log.d(TAG, "DPP onSuccess callback");
+            Log.d(TAG, "Easy Connect onSuccess callback");
             mHandler.post(() -> {
-                mDppStatusCallback.onConfiguratorSuccess(status);
+                mEasyConnectStatusCallback.onConfiguratorSuccess(status);
             });
         }
 
         @Override
         public void onFailure(int status) {
-            Log.d(TAG, "DPP onFailure callback");
+            Log.d(TAG, "Easy Connect onFailure callback");
             mHandler.post(() -> {
-                mDppStatusCallback.onFailure(status);
+                mEasyConnectStatusCallback.onFailure(status);
             });
         }
 
         @Override
         public void onProgress(int status) {
-            Log.d(TAG, "DPP onProgress callback");
+            Log.d(TAG, "Easy Connect onProgress callback");
             mHandler.post(() -> {
-                mDppStatusCallback.onProgress(status);
+                mEasyConnectStatusCallback.onProgress(status);
             });
         }
     }
diff --git a/wifi/java/android/net/wifi/aware/WifiAwareManager.java b/wifi/java/android/net/wifi/aware/WifiAwareManager.java
index 26a6c08..1fa1fd5 100644
--- a/wifi/java/android/net/wifi/aware/WifiAwareManager.java
+++ b/wifi/java/android/net/wifi/aware/WifiAwareManager.java
@@ -35,6 +35,7 @@
 import android.os.Message;
 import android.os.Process;
 import android.os.RemoteException;
+import android.text.TextUtils;
 import android.util.Log;
 
 import libcore.util.HexEncoding;
@@ -434,6 +435,8 @@
                 null, // peerMac (not used in this method)
                 pmk,
                 passphrase,
+                0, // no port info for deprecated IB APIs
+                -1, // no transport info for deprecated IB APIs
                 Process.myUid());
     }
 
@@ -473,6 +476,8 @@
                 peer,
                 pmk,
                 passphrase,
+                0, // no port info for OOB APIs
+                -1, // no transport protocol info for OOB APIs
                 Process.myUid());
     }
 
@@ -824,6 +829,8 @@
         private PeerHandle mPeerHandle;
         private String mPskPassphrase;
         private byte[] mPmk;
+        private int mPort = 0; // invalid value
+        private int mTransportProtocol = -1; // invalid value
 
         /**
          * Configure the {@link PublishDiscoverySession} or {@link SubscribeDiscoverySession}
@@ -902,6 +909,55 @@
         }
 
         /**
+         * Configure the port number which will be used to create a connection over this link. This
+         * configuration should only be done on the server device, e.g. the device creating the
+         * {@link java.net.ServerSocket}.
+         * <p>Notes:
+         * <ul>
+         *     <li>The server device must be the Publisher device!
+         *     <li>The port information can only be specified on secure links, specified using
+         *     {@link #setPskPassphrase(String)}.
+         * </ul>
+         *
+         * @param port A positive integer indicating the port to be used for communication.
+         * @return the current {@link NetworkSpecifierBuilder} builder, enabling chaining of builder
+         *         methods.
+         */
+        public @NonNull NetworkSpecifierBuilder setPort(int port) {
+            if (port <= 0 || port > 65535) {
+                throw new IllegalArgumentException("The port must be a positive value (0, 65535]");
+            }
+            mPort = port;
+            return this;
+        }
+
+        /**
+         * Configure the transport protocol which will be used to create a connection over this
+         * link. This configuration should only be done on the server device, e.g. the device
+         * creating the {@link java.net.ServerSocket} for TCP.
+         * <p>Notes:
+         * <ul>
+         *     <li>The server device must be the Publisher device!
+         *     <li>The transport protocol information can only be specified on secure links,
+         *     specified using {@link #setPskPassphrase(String)}.
+         * </ul>
+         * The transport protocol number is assigned by the Internet Assigned Numbers Authority
+         * (IANA) https://www.iana.org/assignments/protocol-numbers/protocol-numbers.xhtml.
+         *
+         * @param transportProtocol The transport protocol to be used for communication.
+         * @return the current {@link NetworkSpecifierBuilder} builder, enabling chaining of builder
+         *         methods.
+         */
+        public @NonNull NetworkSpecifierBuilder setTransportProtocol(int transportProtocol) {
+            if (transportProtocol < 0 || transportProtocol > 255) {
+                throw new IllegalArgumentException(
+                        "The transport protocol must be in range [0, 255]");
+            }
+            mTransportProtocol = transportProtocol;
+            return this;
+        }
+
+        /**
          * Create a {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(NetworkSpecifier)}
          * for a WiFi Aware connection (link) to the specified peer. The
          * {@link android.net.NetworkRequest.Builder#addTransportType(int)} should be set to
@@ -929,6 +985,18 @@
                     ? WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_INITIATOR
                     : WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_RESPONDER;
 
+            if (mPort != 0 || mTransportProtocol != -1) {
+                if (role != WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_RESPONDER) {
+                    throw new IllegalStateException(
+                            "Port and transport protocol information can only "
+                                    + "be specified on the Publisher device (which is the server");
+                }
+                if (TextUtils.isEmpty(mPskPassphrase) && mPmk == null) {
+                    throw new IllegalStateException("Port and transport protocol information can "
+                            + "only be specified on a secure link");
+                }
+            }
+
             if (role == WIFI_AWARE_DATA_PATH_ROLE_INITIATOR && mPeerHandle == null) {
                 throw new IllegalStateException("Null peerHandle!?");
             }
@@ -936,7 +1004,7 @@
             return new WifiAwareNetworkSpecifier(
                     WifiAwareNetworkSpecifier.NETWORK_SPECIFIER_TYPE_IB, role,
                     mDiscoverySession.mClientId, mDiscoverySession.mSessionId, mPeerHandle.peerId,
-                    null, mPmk, mPskPassphrase, Process.myUid());
+                    null, mPmk, mPskPassphrase, mPort, mTransportProtocol, Process.myUid());
         }
     }
 }
diff --git a/wifi/java/android/net/wifi/aware/WifiAwareNetworkInfo.java b/wifi/java/android/net/wifi/aware/WifiAwareNetworkInfo.java
index 0f29e08..b258906 100644
--- a/wifi/java/android/net/wifi/aware/WifiAwareNetworkInfo.java
+++ b/wifi/java/android/net/wifi/aware/WifiAwareNetworkInfo.java
@@ -38,17 +38,30 @@
  * android.net.NetworkCapabilities)} callback.
  * <p>
  * The Wi-Fi Aware-specific network information include the peer's scoped link-local IPv6 address
- * for the Wi-Fi Aware link. The scoped link-local IPv6 can then be used to create a
+ * for the Wi-Fi Aware link, as well as (optionally) the port and transport protocol specified by
+ * the peer.
+ * The scoped link-local IPv6, port, and transport protocol can then be used to create a
  * {@link java.net.Socket} connection to the peer.
+ * <p>
+ * Note: these are the peer's IPv6 and port information - not the local device's!
  */
 public final class WifiAwareNetworkInfo implements TransportInfo, Parcelable {
     private Inet6Address mIpv6Addr;
+    private int mPort = 0; // a value of 0 is considered invalid
+    private int mTransportProtocol = -1; // a value of -1 is considered invalid
 
     /** @hide */
     public WifiAwareNetworkInfo(Inet6Address ipv6Addr) {
         mIpv6Addr = ipv6Addr;
     }
 
+    /** @hide */
+    public WifiAwareNetworkInfo(Inet6Address ipv6Addr, int port, int transportProtocol) {
+        mIpv6Addr = ipv6Addr;
+        mPort = port;
+        mTransportProtocol = transportProtocol;
+    }
+
     /**
      * Get the scoped link-local IPv6 address of the Wi-Fi Aware peer (not of the local device!).
      *
@@ -59,6 +72,34 @@
         return mIpv6Addr;
     }
 
+    /**
+     * Get the port number to be used to create a network connection to the Wi-Fi Aware peer.
+     * The port information is provided by the app running on the peer which requested the
+     * connection, using the {@link WifiAwareManager.NetworkSpecifierBuilder#setPort(int)}.
+     *
+     * @return A port number on the peer. A value of 0 indicates that no port was specified by the
+     *         peer.
+     */
+    public int getPort() {
+        return mPort;
+    }
+
+    /**
+     * Get the transport protocol to be used to communicate over a network connection to the Wi-Fi
+     * Aware peer. The transport protocol is provided by the app running on the peer which requested
+     * the connection, using the
+     * {@link WifiAwareManager.NetworkSpecifierBuilder#setTransportProtocol(int)}.
+     * <p>
+     * The transport protocol number is assigned by the Internet Assigned Numbers Authority
+     * (IANA) https://www.iana.org/assignments/protocol-numbers/protocol-numbers.xhtml.
+     *
+     * @return A transport protocol id. A value of -1 indicates that no transport protocol was
+     *         specified by the peer.
+     */
+    public int getTransportProtocol() {
+        return mTransportProtocol;
+    }
+
     // parcelable methods
 
     @Override
@@ -71,6 +112,8 @@
         dest.writeByteArray(mIpv6Addr.getAddress());
         NetworkInterface ni = mIpv6Addr.getScopedInterface();
         dest.writeString(ni == null ? null : ni.getName());
+        dest.writeInt(mPort);
+        dest.writeInt(mTransportProtocol);
     }
 
     public static final Creator<WifiAwareNetworkInfo> CREATOR =
@@ -94,8 +137,10 @@
                         e.printStackTrace();
                         return null;
                     }
+                    int port = in.readInt();
+                    int transportProtocol = in.readInt();
 
-                    return new WifiAwareNetworkInfo(ipv6Addr);
+                    return new WifiAwareNetworkInfo(ipv6Addr, port, transportProtocol);
                 }
 
                 @Override
@@ -109,7 +154,9 @@
 
     @Override
     public String toString() {
-        return new StringBuilder("AwareNetworkInfo: IPv6=").append(mIpv6Addr).toString();
+        return new StringBuilder("AwareNetworkInfo: IPv6=").append(mIpv6Addr).append(
+                ", port=").append(mPort).append(", transportProtocol=").append(
+                mTransportProtocol).toString();
     }
 
     /** @hide */
@@ -124,12 +171,13 @@
         }
 
         WifiAwareNetworkInfo lhs = (WifiAwareNetworkInfo) obj;
-        return Objects.equals(mIpv6Addr, lhs.mIpv6Addr);
+        return Objects.equals(mIpv6Addr, lhs.mIpv6Addr) && mPort == lhs.mPort
+                && mTransportProtocol == lhs.mTransportProtocol;
     }
 
     /** @hide */
     @Override
     public int hashCode() {
-        return Objects.hash(mIpv6Addr);
+        return Objects.hash(mIpv6Addr, mPort, mTransportProtocol);
     }
 }
diff --git a/wifi/java/android/net/wifi/aware/WifiAwareNetworkSpecifier.java b/wifi/java/android/net/wifi/aware/WifiAwareNetworkSpecifier.java
index 6e37fcf..a93a6d5 100644
--- a/wifi/java/android/net/wifi/aware/WifiAwareNetworkSpecifier.java
+++ b/wifi/java/android/net/wifi/aware/WifiAwareNetworkSpecifier.java
@@ -19,7 +19,6 @@
 import android.net.NetworkSpecifier;
 import android.os.Parcel;
 import android.os.Parcelable;
-import android.util.Log;
 
 import java.util.Arrays;
 import java.util.Objects;
@@ -117,6 +116,32 @@
     public final String passphrase;
 
     /**
+     * The port information to be used for this link. This information will be communicated to the
+     * peer as part of the layer 2 link setup.
+     *
+     * Information only allowed on secure links since a single layer-2 link is set up for all
+     * requestors. Therefore if multiple apps on a single device request links to the same peer
+     * device they all get the same link. However, the link is only set up on the first request -
+     * hence only the first can transmit the port information. But we don't want to expose that
+     * information to other apps. Limiting to secure links would (usually) imply single app usage.
+     *
+     * @hide
+     */
+    public final int port;
+
+    /**
+     * The transport protocol information to be used for this link. This information will be
+     * communicated to the peer as part of the layer 2 link setup.
+     *
+     * Information only allowed on secure links since a single layer-2 link is set up for all
+     * requestors. Therefore if multiple apps on a single device request links to the same peer
+     * device they all get the same link. However, the link is only set up on the first request -
+     * hence only the first can transmit the port information. But we don't want to expose that
+     * information to other apps. Limiting to secure links would (usually) imply single app usage.
+     */
+    public final int transportProtocol;
+
+    /**
      * The UID of the process initializing this network specifier. Validated by receiver using
      * checkUidIfNecessary() and is used by satisfiedBy() to determine whether matches the
      * offered network.
@@ -127,7 +152,8 @@
 
     /** @hide */
     public WifiAwareNetworkSpecifier(int type, int role, int clientId, int sessionId, int peerId,
-            byte[] peerMac, byte[] pmk, String passphrase, int requestorUid) {
+            byte[] peerMac, byte[] pmk, String passphrase, int port, int transportProtocol,
+            int requestorUid) {
         this.type = type;
         this.role = role;
         this.clientId = clientId;
@@ -136,6 +162,8 @@
         this.peerMac = peerMac;
         this.pmk = pmk;
         this.passphrase = passphrase;
+        this.port = port;
+        this.transportProtocol = transportProtocol;
         this.requestorUid = requestorUid;
     }
 
@@ -152,6 +180,8 @@
                         in.createByteArray(), // peerMac
                         in.createByteArray(), // pmk
                         in.readString(), // passphrase
+                        in.readInt(), // port
+                        in.readInt(), // transportProtocol
                         in.readInt()); // requestorUid
                 }
 
@@ -186,6 +216,8 @@
         dest.writeByteArray(peerMac);
         dest.writeByteArray(pmk);
         dest.writeString(passphrase);
+        dest.writeInt(port);
+        dest.writeInt(transportProtocol);
         dest.writeInt(requestorUid);
     }
 
@@ -202,19 +234,8 @@
     /** @hide */
     @Override
     public int hashCode() {
-        int result = 17;
-
-        result = 31 * result + type;
-        result = 31 * result + role;
-        result = 31 * result + clientId;
-        result = 31 * result + sessionId;
-        result = 31 * result + peerId;
-        result = 31 * result + Arrays.hashCode(peerMac);
-        result = 31 * result + Arrays.hashCode(pmk);
-        result = 31 * result + Objects.hashCode(passphrase);
-        result = 31 * result + requestorUid;
-
-        return result;
+        return Objects.hash(type, role, clientId, sessionId, peerId, Arrays.hashCode(peerMac),
+                Arrays.hashCode(pmk), passphrase, port, transportProtocol, requestorUid);
     }
 
     /** @hide */
@@ -238,6 +259,8 @@
                 && Arrays.equals(peerMac, lhs.peerMac)
                 && Arrays.equals(pmk, lhs.pmk)
                 && Objects.equals(passphrase, lhs.passphrase)
+                && port == lhs.port
+                && transportProtocol == lhs.transportProtocol
                 && requestorUid == lhs.requestorUid;
     }
 
@@ -256,7 +279,8 @@
                 .append(", pmk=").append((pmk == null) ? "<null>" : "<non-null>")
                 // masking PII
                 .append(", passphrase=").append((passphrase == null) ? "<null>" : "<non-null>")
-                .append(", requestorUid=").append(requestorUid)
+                .append(", port=").append(port).append(", transportProtocol=")
+                .append(transportProtocol).append(", requestorUid=").append(requestorUid)
                 .append("]");
         return sb.toString();
     }
diff --git a/wifi/java/android/net/wifi/hotspot2/pps/Credential.java b/wifi/java/android/net/wifi/hotspot2/pps/Credential.java
index 7689fc3..59a7290 100644
--- a/wifi/java/android/net/wifi/hotspot2/pps/Credential.java
+++ b/wifi/java/android/net/wifi/hotspot2/pps/Credential.java
@@ -572,7 +572,7 @@
 
         @Override
         public int hashCode() {
-            return Objects.hash(mCertType, mCertSha256Fingerprint);
+            return Objects.hash(mCertType, Arrays.hashCode(mCertSha256Fingerprint));
         }
 
         @Override
@@ -842,24 +842,50 @@
     }
 
     /**
-     * CA (Certificate Authority) X509 certificate.
+     * CA (Certificate Authority) X509 certificates.
      */
-    private X509Certificate mCaCertificate = null;
+    private X509Certificate[] mCaCertificates = null;
+
     /**
      * Set the CA (Certification Authority) certificate associated with this credential.
      *
      * @param caCertificate The CA certificate to set to
      */
     public void setCaCertificate(X509Certificate caCertificate) {
-        mCaCertificate = caCertificate;
+        mCaCertificates = null;
+        if (caCertificate != null) {
+            mCaCertificates = new X509Certificate[] {caCertificate};
+        }
     }
+
+    /**
+     * Set the CA (Certification Authority) certificates associated with this credential.
+     *
+     * @param caCertificates The list of CA certificates to set to
+     * @hide
+     */
+    public void setCaCertificates(X509Certificate[] caCertificates) {
+        mCaCertificates = caCertificates;
+    }
+
     /**
      * Get the CA (Certification Authority) certificate associated with this credential.
      *
-     * @return CA certificate associated with this credential
+     * @return CA certificate associated with this credential, {@code null} if certificate is not
+     * set or certificate is more than one.
      */
     public X509Certificate getCaCertificate() {
-        return mCaCertificate;
+        return mCaCertificates == null || mCaCertificates.length > 1 ? null : mCaCertificates[0];
+    }
+
+    /**
+     * Get the CA (Certification Authority) certificates associated with this credential.
+     *
+     * @return The list of CA certificates associated with this credential
+     * @hide
+     */
+    public X509Certificate[] getCaCertificates() {
+        return mCaCertificates;
     }
 
     /**
@@ -933,7 +959,11 @@
                 mClientCertificateChain = Arrays.copyOf(source.mClientCertificateChain,
                                                         source.mClientCertificateChain.length);
             }
-            mCaCertificate = source.mCaCertificate;
+            if (source.mCaCertificates != null) {
+                mCaCertificates = Arrays.copyOf(source.mCaCertificates,
+                        source.mCaCertificates.length);
+            }
+
             mClientPrivateKey = source.mClientPrivateKey;
         }
     }
@@ -952,7 +982,7 @@
         dest.writeParcelable(mUserCredential, flags);
         dest.writeParcelable(mCertCredential, flags);
         dest.writeParcelable(mSimCredential, flags);
-        ParcelUtil.writeCertificate(dest, mCaCertificate);
+        ParcelUtil.writeCertificates(dest, mCaCertificates);
         ParcelUtil.writeCertificates(dest, mClientCertificateChain);
         ParcelUtil.writePrivateKey(dest, mClientPrivateKey);
     }
@@ -977,16 +1007,17 @@
                     : mCertCredential.equals(that.mCertCredential))
                 && (mSimCredential == null ? that.mSimCredential == null
                     : mSimCredential.equals(that.mSimCredential))
-                && isX509CertificateEquals(mCaCertificate, that.mCaCertificate)
+                && isX509CertificatesEquals(mCaCertificates, that.mCaCertificates)
                 && isX509CertificatesEquals(mClientCertificateChain, that.mClientCertificateChain)
                 && isPrivateKeyEquals(mClientPrivateKey, that.mClientPrivateKey);
     }
 
     @Override
     public int hashCode() {
-        return Objects.hash(mRealm, mCreationTimeInMillis, mExpirationTimeInMillis,
+        return Objects.hash(mCreationTimeInMillis, mExpirationTimeInMillis, mRealm,
                 mCheckAaaServerCertStatus, mUserCredential, mCertCredential, mSimCredential,
-                mCaCertificate, mClientCertificateChain, mClientPrivateKey);
+                mClientPrivateKey, Arrays.hashCode(mCaCertificates),
+                Arrays.hashCode(mClientCertificateChain));
     }
 
     @Override
@@ -1067,7 +1098,7 @@
                 credential.setUserCredential(in.readParcelable(null));
                 credential.setCertCredential(in.readParcelable(null));
                 credential.setSimCredential(in.readParcelable(null));
-                credential.setCaCertificate(ParcelUtil.readCertificate(in));
+                credential.setCaCertificates(ParcelUtil.readCertificates(in));
                 credential.setClientCertificateChain(ParcelUtil.readCertificates(in));
                 credential.setClientPrivateKey(ParcelUtil.readPrivateKey(in));
                 return credential;
@@ -1100,7 +1131,7 @@
 
         // CA certificate is required for R1 Passpoint profile.
         // For R2, it is downloaded using cert URL provided in PPS MO after validation completes.
-        if (isR1 && mCaCertificate == null) {
+        if (isR1 && mCaCertificates == null) {
             Log.d(TAG, "Missing CA Certificate for user credential");
             return false;
         }
@@ -1131,7 +1162,7 @@
         // Verify required key and certificates for certificate credential.
         // CA certificate is required for R1 Passpoint profile.
         // For R2, it is downloaded using cert URL provided in PPS MO after validation completes.
-        if (isR1 && mCaCertificate == null) {
+        if (isR1 && mCaCertificates == null) {
             Log.d(TAG, "Missing CA Certificate for certificate credential");
             return false;
         }
diff --git a/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java b/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java
index c744f18..7bff68a 100644
--- a/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java
@@ -59,6 +59,7 @@
         WifiConfiguration config = new WifiConfiguration();
         config.setPasspointManagementObjectTree(cookie);
         config.trusted = false;
+        config.updateIdentifier = "1234";
         MacAddress macBeforeParcel = config.getOrCreateRandomizedMacAddress();
         Parcel parcelW = Parcel.obtain();
         config.writeToParcel(parcelW, 0);
@@ -73,6 +74,7 @@
         // lacking a useful config.equals, check two fields near the end.
         assertEquals(cookie, reconfig.getMoTree());
         assertEquals(macBeforeParcel, reconfig.getOrCreateRandomizedMacAddress());
+        assertEquals(config.updateIdentifier, reconfig.updateIdentifier);
         assertFalse(reconfig.trusted);
 
         Parcel parcelWW = Parcel.obtain();
@@ -251,6 +253,18 @@
     }
 
     /**
+     * Verifies that updateIdentifier should be copied for copy constructor.
+     */
+    @Test
+    public void testUpdateIdentifierForCopyConstructor() {
+        WifiConfiguration config = new WifiConfiguration();
+        config.updateIdentifier = "1234";
+        WifiConfiguration copyConfig = new WifiConfiguration(config);
+
+        assertEquals(config.updateIdentifier, copyConfig.updateIdentifier);
+    }
+
+    /**
      * Verifies that the serialization/de-serialization for softap config works.
      */
     @Test
diff --git a/wifi/tests/src/android/net/wifi/aware/WifiAwareAgentNetworkSpecifierTest.java b/wifi/tests/src/android/net/wifi/aware/WifiAwareAgentNetworkSpecifierTest.java
index 4189e40..c3b6285 100644
--- a/wifi/tests/src/android/net/wifi/aware/WifiAwareAgentNetworkSpecifierTest.java
+++ b/wifi/tests/src/android/net/wifi/aware/WifiAwareAgentNetworkSpecifierTest.java
@@ -62,6 +62,7 @@
                 WifiAwareAgentNetworkSpecifier.CREATOR.createFromParcel(parcelR);
 
         assertEquals(dut, rereadDut);
+        assertEquals(dut.hashCode(), rereadDut.hashCode());
 
         // Ensure that individual network specifiers are satisfied by both the original & marshaled
         // |WifiAwareNetworkAgentSpecifier instances.
@@ -181,6 +182,6 @@
     WifiAwareNetworkSpecifier getDummyNetworkSpecifier(int clientId) {
         return new WifiAwareNetworkSpecifier(WifiAwareNetworkSpecifier.NETWORK_SPECIFIER_TYPE_OOB,
                 WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_INITIATOR, clientId, 0, 0, new byte[6],
-                null, null, 0);
+                null, null, 10, 5, 0);
     }
 }
diff --git a/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java b/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java
index ed38c76..6da6d4a 100644
--- a/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java
@@ -16,8 +16,11 @@
 
 package android.net.wifi.aware;
 
+import static android.net.wifi.aware.WifiAwareNetworkSpecifier.NETWORK_SPECIFIER_TYPE_IB;
+
 import static org.hamcrest.core.IsEqual.equalTo;
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyString;
@@ -913,9 +916,10 @@
         final int clientId = 4565;
         final int sessionId = 123;
         final PeerHandle peerHandle = new PeerHandle(123412);
-        final int role = WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_RESPONDER;
         final byte[] pmk = PMK_VALID;
         final String passphrase = PASSPHRASE_VALID;
+        final int port = 5;
+        final int transportProtocol = 10;
         final ConfigRequest configRequest = new ConfigRequest.Builder().build();
         final PublishConfig publishConfig = new PublishConfig.Builder().build();
 
@@ -959,56 +963,70 @@
                 .setPeerHandle(peerHandle).build();
 
         // validate format
-        collector.checkThat("role", role, equalTo(ns.role));
+        collector.checkThat("role", WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_RESPONDER,
+                equalTo(ns.role));
         collector.checkThat("client_id", clientId, equalTo(ns.clientId));
         collector.checkThat("session_id", sessionId, equalTo(ns.sessionId));
         collector.checkThat("peer_id", peerHandle.peerId, equalTo(ns.peerId));
 
-        collector.checkThat("role", role, equalTo(nsb.role));
+        collector.checkThat("role", WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_RESPONDER,
+                equalTo(nsb.role));
         collector.checkThat("client_id", clientId, equalTo(nsb.clientId));
         collector.checkThat("session_id", sessionId, equalTo(nsb.sessionId));
         collector.checkThat("peer_id", peerHandle.peerId, equalTo(nsb.peerId));
+        collector.checkThat("port", 0, equalTo(nsb.port));
+        collector.checkThat("transportProtocol", -1, equalTo(nsb.transportProtocol));
 
         // (4) request an encrypted (PMK) network specifier from the session
         ns = (WifiAwareNetworkSpecifier) publishSession.getValue().createNetworkSpecifierPmk(
                 peerHandle, pmk);
-        nsb =
-                (WifiAwareNetworkSpecifier) new WifiAwareManager.NetworkSpecifierBuilder()
-                        .setDiscoverySession(
-                        publishSession.getValue()).setPeerHandle(peerHandle).setPmk(pmk).build();
+        nsb = (WifiAwareNetworkSpecifier) new WifiAwareManager.NetworkSpecifierBuilder()
+                .setDiscoverySession(publishSession.getValue()).setPeerHandle(peerHandle)
+                .setPmk(pmk).setPort(port).setTransportProtocol(transportProtocol).build();
 
         // validate format
-        collector.checkThat("role", role, equalTo(ns.role));
+        collector.checkThat("role", WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_RESPONDER,
+                equalTo(ns.role));
         collector.checkThat("client_id", clientId, equalTo(ns.clientId));
         collector.checkThat("session_id", sessionId, equalTo(ns.sessionId));
         collector.checkThat("peer_id", peerHandle.peerId, equalTo(ns.peerId));
         collector.checkThat("pmk", pmk , equalTo(ns.pmk));
 
-        collector.checkThat("role", role, equalTo(nsb.role));
+        collector.checkThat("role", WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_RESPONDER,
+                equalTo(nsb.role));
         collector.checkThat("client_id", clientId, equalTo(nsb.clientId));
         collector.checkThat("session_id", sessionId, equalTo(nsb.sessionId));
         collector.checkThat("peer_id", peerHandle.peerId, equalTo(nsb.peerId));
         collector.checkThat("pmk", pmk , equalTo(nsb.pmk));
+        collector.checkThat("port", port, equalTo(nsb.port));
+        collector.checkThat("transportProtocol", transportProtocol, equalTo(nsb.transportProtocol));
 
         // (5) request an encrypted (Passphrase) network specifier from the session
-        ns = (WifiAwareNetworkSpecifier) publishSession.getValue().createNetworkSpecifierPassphrase(
-                peerHandle, passphrase);
+        ns =
+                (WifiAwareNetworkSpecifier) publishSession.getValue()
+                        .createNetworkSpecifierPassphrase(
+                        peerHandle, passphrase);
         nsb = (WifiAwareNetworkSpecifier) new WifiAwareManager.NetworkSpecifierBuilder()
                 .setDiscoverySession(publishSession.getValue()).setPeerHandle(peerHandle)
-                .setPskPassphrase(passphrase).build();
+                .setPskPassphrase(passphrase).setPort(port).setTransportProtocol(transportProtocol)
+                .build();
 
         // validate format
-        collector.checkThat("role", role, equalTo(ns.role));
+        collector.checkThat("role", WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_RESPONDER,
+                equalTo(ns.role));
         collector.checkThat("client_id", clientId, equalTo(ns.clientId));
         collector.checkThat("session_id", sessionId, equalTo(ns.sessionId));
         collector.checkThat("peer_id", peerHandle.peerId, equalTo(ns.peerId));
         collector.checkThat("passphrase", passphrase, equalTo(ns.passphrase));
 
-        collector.checkThat("role", role, equalTo(nsb.role));
+        collector.checkThat("role", WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_RESPONDER,
+                equalTo(nsb.role));
         collector.checkThat("client_id", clientId, equalTo(nsb.clientId));
         collector.checkThat("session_id", sessionId, equalTo(nsb.sessionId));
         collector.checkThat("peer_id", peerHandle.peerId, equalTo(nsb.peerId));
         collector.checkThat("passphrase", passphrase, equalTo(nsb.passphrase));
+        collector.checkThat("port", port, equalTo(nsb.port));
+        collector.checkThat("transportProtocol", transportProtocol, equalTo(nsb.transportProtocol));
 
         verifyNoMoreInteractions(mockCallback, mockSessionCallback, mockAwareService,
                 mockPublishSession, mockRttListener);
@@ -1325,6 +1343,140 @@
         executeNetworkSpecifierDirect(null, false, null, PASSPHRASE_VALID, false);
     }
 
+    /**
+     * Validate that get an exception when creating a network specifier with an invalid port number
+     * (<=0).
+     */
+    @Test(expected = IllegalArgumentException.class)
+    public void testNetworkSpecifierBuilderInvalidPortNumber() throws Exception {
+        final PeerHandle peerHandle = new PeerHandle(123412);
+        final byte[] pmk = PMK_VALID;
+        final int port = 0;
+
+        DiscoverySession publishSession = executeSessionStartup(true);
+
+        WifiAwareNetworkSpecifier nsb =
+                (WifiAwareNetworkSpecifier) new WifiAwareManager.NetworkSpecifierBuilder()
+                        .setDiscoverySession(publishSession).setPeerHandle(peerHandle)
+                        .setPmk(pmk).setPort(port).build();
+    }
+
+    /**
+     * Validate that get an exception when creating a network specifier with port information
+     * without also requesting a secure link.
+     */
+    @Test(expected = IllegalStateException.class)
+    public void testNetworkSpecifierBuilderInvalidPortOnInsecure() throws Exception {
+        final PeerHandle peerHandle = new PeerHandle(123412);
+        final int port = 5;
+
+        DiscoverySession publishSession = executeSessionStartup(true);
+
+        WifiAwareNetworkSpecifier nsb =
+                (WifiAwareNetworkSpecifier) new WifiAwareManager.NetworkSpecifierBuilder()
+                        .setDiscoverySession(publishSession).setPeerHandle(peerHandle)
+                        .setPort(port).build();
+    }
+
+    /**
+     * Validate that get an exception when creating a network specifier with port information on
+     * a responder.
+     */
+    @Test(expected = IllegalStateException.class)
+    public void testNetworkSpecifierBuilderInvalidPortOnResponder() throws Exception {
+        final PeerHandle peerHandle = new PeerHandle(123412);
+        final int port = 5;
+
+        DiscoverySession subscribeSession = executeSessionStartup(false);
+
+        WifiAwareNetworkSpecifier nsb =
+                (WifiAwareNetworkSpecifier) new WifiAwareManager.NetworkSpecifierBuilder()
+                        .setDiscoverySession(subscribeSession).setPeerHandle(peerHandle)
+                        .setPort(port).build();
+    }
+
+    /**
+     * Validate that get an exception when creating a network specifier with an invalid transport
+     * protocol number (not in [0, 255]).
+     */
+    @Test
+    public void testNetworkSpecifierBuilderInvalidTransportProtocolNumber() throws Exception {
+        final PeerHandle peerHandle = new PeerHandle(123412);
+        final byte[] pmk = PMK_VALID;
+        final int tpNegative = -1;
+        final int tpTooLarge = 256;
+        final int tpSmallest = 0;
+        final int tpLargest = 255;
+
+        DiscoverySession publishSession = executeSessionStartup(true);
+
+        try {
+            WifiAwareNetworkSpecifier nsb =
+                    (WifiAwareNetworkSpecifier) new WifiAwareManager.NetworkSpecifierBuilder()
+                            .setDiscoverySession(publishSession).setPeerHandle(peerHandle)
+                            .setPmk(pmk).setTransportProtocol(tpNegative).build();
+            assertTrue("No exception on negative transport protocol!", false);
+        } catch (IllegalArgumentException e) {
+            // nop - exception is correct!
+        }
+        try {
+            WifiAwareNetworkSpecifier nsb =
+                    (WifiAwareNetworkSpecifier) new WifiAwareManager.NetworkSpecifierBuilder()
+                            .setDiscoverySession(publishSession).setPeerHandle(peerHandle)
+                            .setPmk(pmk).setTransportProtocol(tpTooLarge).build();
+            assertTrue("No exception on >255 transport protocol!", false);
+        } catch (IllegalArgumentException e) {
+            // nop - exception is correct!
+        }
+        WifiAwareNetworkSpecifier nsb =
+                (WifiAwareNetworkSpecifier) new WifiAwareManager.NetworkSpecifierBuilder()
+                        .setDiscoverySession(publishSession).setPeerHandle(peerHandle)
+                        .setPmk(pmk).setTransportProtocol(tpSmallest).build();
+        nsb =
+                (WifiAwareNetworkSpecifier) new WifiAwareManager.NetworkSpecifierBuilder()
+                        .setDiscoverySession(
+                                publishSession).setPeerHandle(peerHandle).setPmk(
+                        pmk).setTransportProtocol(tpLargest).build();
+    }
+
+    /**
+     * Validate that get an exception when creating a network specifier with transport protocol
+     * information without also requesting a secure link.
+     */
+    @Test(expected = IllegalStateException.class)
+    public void testNetworkSpecifierBuilderInvalidTransportProtocolOnInsecure() throws Exception {
+        final PeerHandle peerHandle = new PeerHandle(123412);
+        final int transportProtocol = 5;
+
+        DiscoverySession publishSession = executeSessionStartup(true);
+
+        WifiAwareNetworkSpecifier nsb =
+                (WifiAwareNetworkSpecifier) new WifiAwareManager.NetworkSpecifierBuilder()
+                        .setDiscoverySession(publishSession).setPeerHandle(peerHandle)
+                        .setTransportProtocol(transportProtocol).build();
+    }
+
+    /**
+     * Validate that get an exception when creating a network specifier with transport protocol
+     * information on a responder.
+     */
+    @Test(expected = IllegalStateException.class)
+    public void testNetworkSpecifierBuilderInvalidTransportProtocolOnResponder() throws Exception {
+        final PeerHandle peerHandle = new PeerHandle(123412);
+        final int transportProtocol = 5;
+
+        DiscoverySession subscribeSession = executeSessionStartup(false);
+
+        WifiAwareNetworkSpecifier nsb =
+                (WifiAwareNetworkSpecifier) new WifiAwareManager.NetworkSpecifierBuilder()
+                        .setDiscoverySession(subscribeSession).setPeerHandle(peerHandle)
+                        .setTransportProtocol(transportProtocol).build();
+    }
+
+    /*
+     * Utilities
+     */
+
     private void executeNetworkSpecifierDirect(byte[] someMac, boolean doPmk, byte[] pmk,
             String passphrase, boolean doInitiator) throws Exception {
         final int clientId = 134;
@@ -1356,7 +1508,83 @@
         }
     }
 
-    // WifiAwareNetworkInfo tests
+    private DiscoverySession executeSessionStartup(boolean isPublish) throws Exception {
+        final int clientId = 4565;
+        final int sessionId = 123;
+        final PeerHandle peerHandle = new PeerHandle(123412);
+        final int port = 5;
+        final ConfigRequest configRequest = new ConfigRequest.Builder().build();
+        final SubscribeConfig subscribeConfig = new SubscribeConfig.Builder().build();
+        final PublishConfig publishConfig = new PublishConfig.Builder().build();
+
+        ArgumentCaptor<WifiAwareSession> sessionCaptor = ArgumentCaptor.forClass(
+                WifiAwareSession.class);
+        ArgumentCaptor<IWifiAwareEventCallback> clientProxyCallback = ArgumentCaptor
+                .forClass(IWifiAwareEventCallback.class);
+        ArgumentCaptor<IWifiAwareDiscoverySessionCallback> sessionProxyCallback = ArgumentCaptor
+                .forClass(IWifiAwareDiscoverySessionCallback.class);
+        ArgumentCaptor<PublishDiscoverySession> publishSession = ArgumentCaptor
+                .forClass(PublishDiscoverySession.class);
+        ArgumentCaptor<SubscribeDiscoverySession> subscribeSession = ArgumentCaptor
+                .forClass(SubscribeDiscoverySession.class);
+
+
+        InOrder inOrder = inOrder(mockCallback, mockSessionCallback, mockAwareService,
+                mockPublishSession, mockRttListener);
+
+        // (1) connect successfully
+        mDut.attach(mMockLooperHandler, configRequest, mockCallback, null);
+        inOrder.verify(mockAwareService).connect(any(), any(), clientProxyCallback.capture(),
+                eq(configRequest), eq(false));
+        clientProxyCallback.getValue().onConnectSuccess(clientId);
+        mMockLooper.dispatchAll();
+        inOrder.verify(mockCallback).onAttached(sessionCaptor.capture());
+        WifiAwareSession session = sessionCaptor.getValue();
+
+        if (isPublish) {
+            // (2) publish successfully
+            session.publish(publishConfig, mockSessionCallback, mMockLooperHandler);
+            inOrder.verify(mockAwareService).publish(any(), eq(clientId), eq(publishConfig),
+                    sessionProxyCallback.capture());
+            sessionProxyCallback.getValue().onSessionStarted(sessionId);
+            mMockLooper.dispatchAll();
+            inOrder.verify(mockSessionCallback).onPublishStarted(publishSession.capture());
+            return publishSession.getValue();
+        } else {
+            // (2) subscribe successfully
+            session.subscribe(subscribeConfig, mockSessionCallback, mMockLooperHandler);
+            inOrder.verify(mockAwareService).subscribe(any(), eq(clientId), eq(subscribeConfig),
+                    sessionProxyCallback.capture());
+            sessionProxyCallback.getValue().onSessionStarted(sessionId);
+            mMockLooper.dispatchAll();
+            inOrder.verify(mockSessionCallback).onSubscribeStarted(subscribeSession.capture());
+            return subscribeSession.getValue();
+        }
+    }
+
+    // WifiAwareNetworkSpecifier && WifiAwareNetworkInfo tests
+
+    @Test
+    public void testWifiAwareNetworkSpecifierParcel() {
+        WifiAwareNetworkSpecifier ns = new WifiAwareNetworkSpecifier(NETWORK_SPECIFIER_TYPE_IB,
+                WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_RESPONDER, 5, 568, 334,
+                HexEncoding.decode("000102030405".toCharArray(), false),
+                "01234567890123456789012345678901".getBytes(), "blah blah", 666, 4, 10001);
+
+        Parcel parcelW = Parcel.obtain();
+        ns.writeToParcel(parcelW, 0);
+        byte[] bytes = parcelW.marshall();
+        parcelW.recycle();
+
+        Parcel parcelR = Parcel.obtain();
+        parcelR.unmarshall(bytes, 0, bytes.length);
+        parcelR.setDataPosition(0);
+        WifiAwareNetworkSpecifier rereadNs =
+                WifiAwareNetworkSpecifier.CREATOR.createFromParcel(parcelR);
+
+        assertEquals(ns, rereadNs);
+        assertEquals(ns.hashCode(), rereadNs.hashCode());
+    }
 
     @Test
     public void testWifiAwareNetworkCapabilitiesParcel() throws UnknownHostException {
@@ -1364,9 +1592,11 @@
                 "11:22:33:44:55:66").getLinkLocalIpv6FromEui48Mac();
         // note: dummy scope = 5
         final Inet6Address inet6Scoped = Inet6Address.getByAddress(null, inet6.getAddress(), 5);
+        final int port = 5;
+        final int transportProtocol = 6;
 
         assertEquals(inet6Scoped.toString(), "/fe80::1322:33ff:fe44:5566%5");
-        WifiAwareNetworkInfo cap = new WifiAwareNetworkInfo(inet6Scoped);
+        WifiAwareNetworkInfo cap = new WifiAwareNetworkInfo(inet6Scoped, port, transportProtocol);
 
         Parcel parcelW = Parcel.obtain();
         cap.writeToParcel(parcelW, 0);
@@ -1380,6 +1610,7 @@
                 WifiAwareNetworkInfo.CREATOR.createFromParcel(parcelR);
 
         assertEquals(cap.getPeerIpv6Addr().toString(), "/fe80::1322:33ff:fe44:5566%5");
+        assertEquals(cap, rereadCap);
         assertEquals(cap.hashCode(), rereadCap.hashCode());
     }
 
diff --git a/wifi/tests/src/android/net/wifi/hotspot2/pps/CredentialTest.java b/wifi/tests/src/android/net/wifi/hotspot2/pps/CredentialTest.java
index a9d4b8f..1ecc3fe 100644
--- a/wifi/tests/src/android/net/wifi/hotspot2/pps/CredentialTest.java
+++ b/wifi/tests/src/android/net/wifi/hotspot2/pps/CredentialTest.java
@@ -16,6 +16,7 @@
 
 package android.net.wifi.hotspot2.pps;
 
+import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
@@ -44,17 +45,16 @@
      * @param userCred Instance of UserCredential
      * @param certCred Instance of CertificateCredential
      * @param simCred Instance of SimCredential
-     * @param caCert CA certificate
      * @param clientCertificateChain Chain of client certificates
      * @param clientPrivateKey Client private key
+     * @param caCerts CA certificates
      * @return {@link Credential}
      */
     private static Credential createCredential(Credential.UserCredential userCred,
-                                               Credential.CertificateCredential certCred,
-                                               Credential.SimCredential simCred,
-                                               X509Certificate caCert,
-                                               X509Certificate[] clientCertificateChain,
-                                               PrivateKey clientPrivateKey) {
+            Credential.CertificateCredential certCred,
+            Credential.SimCredential simCred,
+            X509Certificate[] clientCertificateChain, PrivateKey clientPrivateKey,
+            X509Certificate... caCerts) {
         Credential cred = new Credential();
         cred.setCreationTimeInMillis(123455L);
         cred.setExpirationTimeInMillis(2310093L);
@@ -63,7 +63,11 @@
         cred.setUserCredential(userCred);
         cred.setCertCredential(certCred);
         cred.setSimCredential(simCred);
-        cred.setCaCertificate(caCert);
+        if (caCerts != null && caCerts.length == 1) {
+            cred.setCaCertificate(caCerts[0]);
+        } else {
+            cred.setCaCertificates(caCerts);
+        }
         cred.setClientCertificateChain(clientCertificateChain);
         cred.setClientPrivateKey(clientPrivateKey);
         return cred;
@@ -80,8 +84,8 @@
         certCred.setCertType("x509v3");
         certCred.setCertSha256Fingerprint(
                 MessageDigest.getInstance("SHA-256").digest(FakeKeys.CLIENT_CERT.getEncoded()));
-        return createCredential(null, certCred, null, FakeKeys.CA_CERT0,
-                new X509Certificate[] {FakeKeys.CLIENT_CERT}, FakeKeys.RSA_KEY1);
+        return createCredential(null, certCred, null, new X509Certificate[] {FakeKeys.CLIENT_CERT},
+                FakeKeys.RSA_KEY1, FakeKeys.CA_CERT0, FakeKeys.CA_CERT1);
     }
 
     /**
@@ -93,7 +97,7 @@
         Credential.SimCredential simCred = new Credential.SimCredential();
         simCred.setImsi("1234*");
         simCred.setEapType(EAPConstants.EAP_SIM);
-        return createCredential(null, null, simCred, null, null, null);
+        return createCredential(null, null, simCred, null, null, (X509Certificate[]) null);
     }
 
     /**
@@ -110,7 +114,7 @@
         userCred.setSoftTokenApp("TestApp");
         userCred.setEapType(EAPConstants.EAP_TTLS);
         userCred.setNonEapInnerMethod("MS-CHAP");
-        return createCredential(userCred, null, null, FakeKeys.CA_CERT0, null, null);
+        return createCredential(userCred, null, null, null, null, FakeKeys.CA_CERT0);
     }
 
     private static void verifyParcel(Credential writeCred) {
@@ -120,6 +124,7 @@
         parcel.setDataPosition(0);    // Rewind data position back to the beginning for read.
         Credential readCred = Credential.CREATOR.createFromParcel(parcel);
         assertTrue(readCred.equals(writeCred));
+        assertEquals(writeCred.hashCode(), readCred.hashCode());
     }
 
     /**